View Javadoc

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