View Javadoc

1   package org.apache.turbine.util.uri;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.net.URLEncoder;
23  
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  import org.apache.commons.lang.StringUtils;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  import org.apache.turbine.util.RunData;
35  import org.apache.turbine.util.ServerData;
36  import org.apache.turbine.util.parser.ParameterParser;
37  import org.apache.turbine.util.parser.ParserUtils;
38  
39  /***
40   * This class allows you to keep all the information needed for a single
41   * link at one place. It keeps your query data, path info, the server
42   * scheme, name, port and the script path.
43   *
44   * If you must generate a Turbine Link, use this class.
45   *
46   * @author <a href="mailto:jon@clearink.com">Jon S. Stevens</a>
47   * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
48   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
49   * @version $Id: TurbineURI.java 638205 2008-03-18 04:10:39Z seade $
50   */
51  
52  public class TurbineURI
53          extends BaseURI
54  {
55      /*** Logging */
56      private static Log log = LogFactory.getLog(TurbineURI.class);
57  
58      /*** Contains the PathInfo and QueryData vectors */
59      private List [] dataVectors = null;
60  
61      /*
62       * ========================================================================
63       *
64       * Constructors
65       *
66       * ========================================================================
67       *
68       */
69  
70      /***
71       * Empty C'tor. Uses Turbine.getDefaultServerData().
72       */
73      public TurbineURI()
74      {
75          super();
76          init();
77      }
78  
79      /***
80       * Constructor with a RunData object.
81       *
82       * @param runData A RunData object
83       */
84      public TurbineURI(RunData runData)
85      {
86          super(runData);
87          init();
88      }
89  
90      /***
91       * Constructor, set explicit redirection.
92       *
93       * @param runData A RunData object
94       * @param redirect True if redirection allowed.
95       */
96      public TurbineURI(RunData runData, boolean redirect)
97      {
98          super(runData, redirect);
99          init();
100     }
101 
102     /***
103      * Constructor, set Screen.
104      *
105      * @param runData A RunData object
106      * @param screen A Screen Name
107      */
108     public TurbineURI(RunData runData, String screen)
109     {
110         this(runData);
111         setScreen(screen);
112     }
113 
114     /***
115      * Constructor, set Screen, set explicit redirection.
116      *
117      * @param runData A RunData object
118      * @param screen A Screen Name
119      * @param redirect True if redirection allowed.
120      */
121     public TurbineURI(RunData runData, String screen, boolean redirect)
122     {
123         this(runData, redirect);
124         setScreen(screen);
125     }
126 
127     /***
128      * Constructor, set Screen and Action.
129      *
130      * @param runData A RunData object
131      * @param screen A Screen Name
132      * @param action An Action Name
133      */
134     public TurbineURI(RunData runData, String screen, String action)
135     {
136         this(runData, screen);
137         setAction(action);
138     }
139 
140     /***
141      * Constructor, set Screen and Action, set explicit redirection.
142      *
143      * @param runData A RunData object
144      * @param screen A Screen Name
145      * @param action An Action Name
146      * @param redirect True if redirection allowed.
147      */
148     public TurbineURI(RunData runData, String screen, String action, boolean redirect)
149     {
150         this(runData, screen, redirect);
151         setAction(action);
152     }
153 
154     /***
155      * Constructor with a ServerData object.
156      *
157      * @param serverData A ServerData object
158      */
159     public TurbineURI(ServerData serverData)
160     {
161         super(serverData);
162         init();
163     }
164 
165     /***
166      * Constructor, set explicit redirection.
167      *
168      * @param serverData A ServerData object
169      * @param redirect True if redirection allowed.
170      */
171     public TurbineURI(ServerData serverData, boolean redirect)
172     {
173         super(serverData, redirect);
174         init();
175     }
176 
177     /***
178      * Constructor, set Screen.
179      *
180      * @param serverData A ServerData object
181      * @param screen A Screen Name
182      */
183     public TurbineURI(ServerData serverData, String screen)
184     {
185         this(serverData);
186         setScreen(screen);
187     }
188 
189     /***
190      * Constructor, set Screen, set explicit redirection.
191      *
192      * @param serverData A ServerData object
193      * @param screen A Screen Name
194      * @param redirect True if redirection allowed.
195      */
196     public TurbineURI(ServerData serverData, String screen, boolean redirect)
197     {
198         this(serverData, redirect);
199         setScreen(screen);
200     }
201 
202     /***
203      * Constructor, set Screen and Action.
204      *
205      * @param serverData A ServerData object
206      * @param screen A Screen Name
207      * @param action An Action Name
208      */
209     public TurbineURI(ServerData serverData, String screen, String action)
210     {
211         this(serverData, screen);
212         setAction(action);
213     }
214 
215     /***
216      * Constructor, set Screen and Action, set explicit redirection.
217      *
218      * @param serverData A ServerData object
219      * @param screen A Screen Name
220      * @param action An Action Name
221      * @param redirect True if redirection allowed.
222      */
223     public TurbineURI(ServerData serverData, String screen, String action,
224                       boolean redirect)
225     {
226         this(serverData, screen, redirect);
227         setAction(action);
228     }
229 
230     /***
231      * Constructor, user Turbine.getDefaultServerData(), set Screen and Action.
232      *
233      * @param screen A Screen Name
234      * @param action An Action Name
235      */
236     public TurbineURI(String screen, String action)
237     {
238         this();
239         setScreen(screen);
240         setAction(action);
241     }
242 
243     /*
244      * ========================================================================
245      *
246      * Init
247      *
248      * ========================================================================
249      *
250      */
251 
252     /***
253      * Init the TurbineURI.
254      */
255     private void init()
256     {
257         dataVectors = new List[2];
258         dataVectors[PATH_INFO]  = new ArrayList();
259         dataVectors[QUERY_DATA] = new ArrayList();
260     }
261 
262     /***
263      * Sets the action= value for this URL.
264      *
265      * By default it adds the information to the path_info instead
266      * of the query data. An empty value (null or "") cleans out
267      * an existing value.
268      *
269      * @param action A String with the action value.
270      */
271     public void setAction(String action)
272     {
273         if(StringUtils.isNotEmpty(action))
274         {
275             add(PATH_INFO, CGI_ACTION_PARAM, action);
276         }
277         else
278         {
279             clearAction();
280         }
281     }
282 
283     /***
284      * Sets the fired eventSubmit= value for this URL.
285      *
286      * @param event The event to fire.
287      *
288      */
289     public void setEvent(String event)
290     {
291         add(PATH_INFO, EVENT_PREFIX + event, event);
292     }
293 
294     /***
295      * Sets the action= and eventSubmit= values for this URL.
296      *
297      * By default it adds the information to the path_info instead
298      * of the query data. An empty value (null or "") for the action cleans out
299      * an existing value.  An empty value (null or "") for the event has no
300      * effect.
301      *
302      * @param action A String with the action value.
303      * @param event A string with the event name.
304      */
305     public void setActionEvent(String action, String event)
306     {
307         setAction(action);
308         if(StringUtils.isNotEmpty(event))
309         {
310             setEvent(event);
311         }
312     }
313 
314     /***
315      * Clears the action= value for this URL.
316      */
317     public void clearAction()
318     {
319         removePathInfo(CGI_ACTION_PARAM);
320     }
321 
322     /***
323      * Sets the screen= value for this URL.
324      *
325      * By default it adds the information to the path_info instead
326      * of the query data. An empty value (null or "") cleans out
327      * an existing value.
328      *
329      * @param screen A String with the screen value.
330      */
331     public void setScreen(String screen)
332     {
333         if(StringUtils.isNotEmpty(screen))
334         {
335             add(PATH_INFO, CGI_SCREEN_PARAM, screen);
336         }
337         else
338         {
339             clearScreen();
340         }
341     }
342 
343     /***
344      * Clears the screen= value for this URL.
345      */
346     public void clearScreen()
347     {
348         removePathInfo(CGI_SCREEN_PARAM);
349     }
350 
351     /*
352      * ========================================================================
353      *
354      * Adding and removing Data from the Path Info and Query Data
355      *
356      * ========================================================================
357      */
358 
359 
360     /***
361      * Adds a name=value pair for every entry in a ParameterParser
362      * object to the path_info string.
363      *
364      * @param pp A ParameterParser.
365      */
366     public void addPathInfo(ParameterParser pp)
367     {
368         add(PATH_INFO, pp);
369     }
370 
371     /***
372      * Adds an existing List of URIParam objects to
373      * the path_info string.
374      *
375      * @param list A list with URIParam objects.
376      */
377     public void addPathInfo(List list)
378     {
379         add(PATH_INFO, list);
380     }
381 
382     /***
383      * Adds a name=value pair to the path_info string.
384      *
385      * @param name A String with the name to add.
386      * @param value An Object with the value to add.
387      */
388     public void addPathInfo(String name, Object value)
389     {
390         add(PATH_INFO, name, null == value ? null : value.toString());
391     }
392 
393     /***
394      * Adds a name=value pair to the path_info string.
395      *
396      * @param name A String with the name to add.
397      * @param value A String with the value to add.
398      */
399     public void addPathInfo(String name, String value)
400     {
401         add(PATH_INFO, name, value);
402     }
403 
404     /***
405      * Adds a name=value pair to the path_info string.
406      *
407      * @param name A String with the name to add.
408      * @param value A double with the value to add.
409      */
410     public void addPathInfo(String name, double value)
411     {
412         add(PATH_INFO, name, Double.toString(value));
413     }
414 
415     /***
416      * Adds a name=value pair to the path_info string.
417      *
418      * @param name A String with the name to add.
419      * @param value An int with the value to add.
420      */
421     public void addPathInfo(String name, int value)
422     {
423         add(PATH_INFO, name, Integer.toString(value));
424     }
425 
426     /***
427      * Adds a name=value pair to the path_info string.
428      *
429      * @param name A String with the name to add.
430      * @param value A long with the value to add.
431      */
432     public void addPathInfo(String name, long value)
433     {
434         add(PATH_INFO, name, Long.toString(value));
435     }
436 
437     /***
438      * Adds a name=value pair to the query string.
439      *
440      * @param name A String with the name to add.
441      * @param value An Object with the value to add.
442      */
443     public void addQueryData(String name, Object value)
444     {
445         add(QUERY_DATA, name, null == value ? null : value.toString());
446     }
447 
448     /***
449      * Adds a name=value pair to the query string.
450      *
451      * @param name A String with the name to add.
452      * @param value A String with the value to add.
453      */
454     public void addQueryData(String name, String value)
455     {
456         add(QUERY_DATA, name, value);
457     }
458 
459     /***
460      * Adds a name=value pair to the query string.
461      *
462      * @param name A String with the name to add.
463      * @param value A double with the value to add.
464      */
465     public void addQueryData(String name, double value)
466     {
467         add(QUERY_DATA, name, Double.toString(value));
468     }
469 
470     /***
471      * Adds a name=value pair to the query string.
472      *
473      * @param name A String with the name to add.
474      * @param value An int with the value to add.
475      */
476     public void addQueryData(String name, int value)
477     {
478         add(QUERY_DATA, name, Integer.toString(value));
479     }
480 
481     /***
482      * Adds a name=value pair to the query string.
483      *
484      * @param name A String with the name to add.
485      * @param value A long with the value to add.
486      */
487     public void addQueryData(String name, long value)
488     {
489         add(QUERY_DATA, name, Long.toString(value));
490     }
491 
492     /***
493      * Adds a name=value pair for every entry in a ParameterParser
494      * object to the query string.
495      *
496      * @param pp A ParameterParser.
497      */
498     public void addQueryData(ParameterParser pp)
499     {
500         add(QUERY_DATA, pp);
501     }
502 
503     /***
504      * Adds an existing List of URIParam objects to the query data.
505      *
506      * @param list A list with URIParam objects.
507      */
508     public void addQueryData(List list)
509     {
510         add(QUERY_DATA, list);
511     }
512 
513     /***
514      * Is Path Info data set in this URI?
515      *
516      * @return true if Path Info has values
517      */
518     public boolean hasPathInfo()
519     {
520         return !dataVectors[PATH_INFO].isEmpty();
521     }
522 
523     /***
524      * Removes all the path info elements.
525      */
526     public void removePathInfo()
527     {
528         dataVectors[PATH_INFO].clear();
529     }
530 
531     /***
532      * Removes a name=value pair from the path info.
533      *
534      * @param name A String with the name to be removed.
535      */
536     public void removePathInfo(String name)
537     {
538         remove(PATH_INFO, name);
539     }
540 
541     /***
542      * Is Query data set in this URI?
543      *
544      * @return true if Query data has values
545      */
546     public boolean hasQueryData()
547     {
548         return !dataVectors[QUERY_DATA].isEmpty();
549     }
550 
551     /***
552      * Removes all the query string elements.
553      */
554     public void removeQueryData()
555     {
556         dataVectors[QUERY_DATA].clear();
557     }
558 
559     /***
560      * Removes a name=value pair from the query string.
561      *
562      * @param name A String with the name to be removed.
563      */
564     public void removeQueryData(String name)
565     {
566         remove (QUERY_DATA, name);
567     }
568 
569     /***
570      * Template Link and friends want to be able to turn the encoding
571      * of the servlet container off. After calling this method,
572      * the no encoding will happen any longer. If you think, that you
573      * need this outside a template context, think again.
574      */
575     public void clearResponse()
576     {
577         setResponse(null);
578     }
579 
580 
581     /***
582      * Builds the URL with all of the data URL-encoded as well as
583      * encoded using HttpServletResponse.encodeUrl(). The resulting
584      * URL is absolute; it starts with http/https...
585      *
586      * <p>
587      * <code><pre>
588      * TurbineURI tui = new TurbineURI (data, "UserScreen");
589      * tui.addPathInfo("user","jon");
590      * tui.getAbsoluteLink();
591      * </pre></code>
592      *
593      *  The above call to absoluteLink() would return the String:
594      *
595      * <p>
596      * http://www.server.com/servlets/Turbine/screen/UserScreen/user/jon
597      *
598      * @return A String with the built URL.
599      */
600     public String getAbsoluteLink()
601     {
602         StringBuffer output = new StringBuffer();
603 
604         getSchemeAndPort(output);
605 
606         buildRelativeLink(output);
607 
608         //
609         // Encode Response does all the fixup for the Servlet Container
610         //
611         return encodeResponse(output.toString());
612     }
613 
614     /***
615      * Builds the URL with all of the data URL-encoded as well as
616      * encoded using HttpServletResponse.encodeUrl(). The resulting
617      * URL is relative to the webserver root.
618      *
619      * <p>
620      * <code><pre>
621      * TurbineURI tui = new TurbineURI (data, "UserScreen");
622      * tui.addPathInfo("user","jon");
623      * tui.getRelativeLink();
624      * </pre></code>
625      *
626      *  The above call to relativeLink() would return the String:
627      *
628      * <p>
629      * /servlets/Turbine/screen/UserScreen/user/jon
630      *
631      * @return A String with the built URL.
632      */
633     public String getRelativeLink()
634     {
635         StringBuffer output = new StringBuffer();
636 
637         buildRelativeLink(output);
638 
639         //
640         // Encode Response does all the fixup for the Servlet Container
641         //
642         return encodeResponse(output.toString());
643     }
644 
645     /***
646      * Add everything needed for a relative link to the passed StringBuffer.
647      *
648      * @param output A Stringbuffer
649      */
650     private void buildRelativeLink(StringBuffer output)
651     {
652         getContextAndScript(output);
653 
654         if (hasPathInfo())
655         {
656             output.append('/');
657             getPathInfoAsString(output);
658         }
659 
660         if (hasReference())
661         {
662             output.append('#');
663             output.append(getReference());
664         }
665 
666         if (hasQueryData())
667         {
668             output.append('?');
669             getQueryDataAsString(output);
670         }
671     }
672 
673     /***
674      * Gets the current Query Data List.
675      *
676      * @return A List which contains all query data keys. The keys
677      * are URIParam objects.
678      */
679     public List getPathInfo()
680     {
681         return dataVectors[PATH_INFO];
682     }
683 
684     /***
685      * Sets the Query Data List. Replaces the current query data list
686      * with the one supplied. The list must contain only URIParam
687      * objects!
688      *
689      * @param pathInfo A List with new param objects.
690      */
691 
692     public void setPathInfo(List pathInfo)
693     {
694         dataVectors[PATH_INFO] = pathInfo;
695     }
696 
697     /***
698      * Gets the current Query Data List.
699      *
700      * @return A List which contains all query data keys. The keys
701      * are URIParam objects.
702      */
703     public List getQueryData()
704     {
705         return dataVectors[QUERY_DATA];
706     }
707 
708     /***
709      * Sets the Query Data List. Replaces the current query data list
710      * with the one supplied. The list must contain only URIParam
711      * objects!
712      *
713      * @param queryData A List with new param objects.
714      */
715 
716     public void setQueryData(List queryData)
717     {
718         dataVectors[QUERY_DATA] = queryData;
719     }
720 
721     /***
722      * Simply calls getAbsoluteLink(). You should not use this in your
723      * code unless you have to. Use getAbsoluteLink.
724      *
725      * @return This URI as a String
726      *
727      */
728     public String toString()
729     {
730         return getAbsoluteLink();
731     }
732 
733     /*
734      * ========================================================================
735      *
736      * Protected / Private Methods
737      *
738      * ========================================================================
739      *
740      */
741 
742     /***
743      * Returns the Path Info data as a String.
744      *
745      * @param output The StringBuffer that should hold the path info.
746      */
747     private void getPathInfoAsString(StringBuffer output)
748     {
749         doEncode(output, dataVectors[PATH_INFO], '/', '/');
750     }
751 
752     /***
753      * Returns the Query data as a String.
754      *
755      * @param output The StringBuffer that should hold the query data.
756      */
757     private void getQueryDataAsString(StringBuffer output)
758     {
759         doEncode(output, dataVectors[QUERY_DATA], '&', '=');
760     }
761 
762     /***
763      * Does the actual encoding for pathInfoAsString and queryDataAsString.
764      *
765      * @param output The Stringbuffer that should contain the information.
766      * @param list A Collection
767      * @param fieldDelim A char which is used to separate key/value pairs
768      * @param valueDelim A char which is used to separate key and value
769      */
770     private void doEncode(StringBuffer output, Collection list, char fieldDelim, char valueDelim)
771     {
772         if(!list.isEmpty())
773         {
774             for(Iterator it = list.iterator(); it.hasNext();)
775             {
776                 URIParam uriParam = (URIParam) it.next();
777                 String key = URLEncoder.encode(uriParam.getKey());
778                 String val = null == uriParam.getValue()
779                         ? null : String.valueOf(uriParam.getValue());
780 
781                 output.append(key);
782                 output.append(valueDelim);
783 
784                 if(StringUtils.isEmpty(val))
785                 {
786                     if (val == null)
787                     {
788                         if (log.isWarnEnabled())
789                         {
790                             log.warn("Found a null value for " + key);
791                         }
792                         // For backwards compatibility:
793                         val = "null";
794                     }
795                     output.append(val);
796                 }
797                 else
798                 {
799                     output.append(URLEncoder.encode(val));
800                 }
801 
802                 if (it.hasNext())
803                 {
804                     output.append(fieldDelim);
805                 }
806             }
807         }
808     }
809 
810     /***
811      * If the type is PATH_INFO, then add name/value to the pathInfo
812      * hashtable.
813      * <p>
814      * If the type is QUERY_DATA, then add name/value to the queryData
815      * hashtable.
816      *
817      * @param type Type (PATH_INFO or QUERY_DATA) of insertion.
818      * @param name A String with the name to add.
819      * @param value A String with the value to add.
820      */
821     protected void add(int type,
822             String name,
823             String value)
824     {
825         URIParam uriParam = new URIParam(ParserUtils.convertAndTrim(name), value);
826 
827         dataVectors[type].add(uriParam); // Code so clean you can eat from...
828     }
829 
830     /***
831      * Method for a quick way to add all the parameters in a
832      * ParameterParser.
833      *
834      * <p>If the type is P (0), then add name/value to the pathInfo
835      * hashtable.
836      *
837      * <p>If the type is Q (1), then add name/value to the queryData
838      * hashtable.
839      *
840      * @param type Type of insertion (@see #add(char type, String name, String value))
841      * @param pp A ParameterParser.
842      */
843     protected void add(int type,
844             ParameterParser pp)
845     {
846         for(Iterator it = pp.keySet().iterator(); it.hasNext();)
847         {
848             String key = (String) it.next();
849 
850             if (!key.equalsIgnoreCase(CGI_ACTION_PARAM) &&
851                     !key.equalsIgnoreCase(CGI_SCREEN_PARAM))
852             {
853                 String[] values = pp.getStrings(key);
854                 if(values != null)
855                 {
856                     for (int i = 0; i < values.length; i++)
857                     {
858                         add(type, key, values[i]);
859                     }
860                 }
861                 else
862                 {
863                     add(type, key, "");
864                 }
865             }
866         }
867     }
868 
869     /***
870      * Method for a quick way to add all the parameters in a
871      * List with URIParam objects.
872      *
873      * <p>If the type is P (0), then add name/value to the pathInfo
874      * hashtable.
875      *
876      * <p>If the type is Q (1), then add name/value to the queryData
877      * hashtable.
878      *
879      * @param type Type of insertion (@see #add(char type, String name, String value))
880      * @param list A List of URIParam objects
881      */
882     protected void add(int type,
883             List list)
884     {
885         for (Iterator it = list.iterator(); it.hasNext();)
886         {
887             // Strictly spoken we don't need this cast. But if we do,
888             // we get class cast right here is someone tries to put
889             // a list with wrong objects into this method.
890             URIParam uriParam = (URIParam) it.next();
891             dataVectors[type].add(uriParam);
892         }
893     }
894 
895     /***
896      * If the type is P (0), then remove name/value from the
897      * pathInfo hashtable.
898      *
899      * <p>If the type is Q (1), then remove name/value from the
900      * queryData hashtable.
901      *
902      * @param type Type (P or Q) of removal.
903      * @param name A String with the name to be removed.
904      */
905     protected void remove (int type,
906             String name)
907     {
908         Collection c = dataVectors[type];
909         String key = ParserUtils.convertAndTrim(name);
910 
911         for (Iterator it = c.iterator(); it.hasNext();)
912         {
913             URIParam uriParam = (URIParam) it.next();
914 
915             if (key.equals(uriParam.getKey()))
916             {
917                 it.remove();
918             }
919         }
920     }
921 }