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 534527 2007-05-02 16:10:59Z tv $
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, 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, 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 = String.valueOf(uriParam.getValue());
779 
780                 output.append(key);
781                 output.append(valueDelim);
782 
783                 if(StringUtils.isEmpty(val))
784                 {
785                     if (val == null && log.isDebugEnabled())
786                     {
787                         log.debug("Found a null value for " + key);
788                     }
789                     output.append(val);
790                 }
791                 else
792                 {
793                     output.append(URLEncoder.encode(val));
794                 }
795 
796                 if (it.hasNext())
797                 {
798                     output.append(fieldDelim);
799                 }
800             }
801         }
802     }
803 
804     /***
805      * If the type is PATH_INFO, then add name/value to the pathInfo
806      * hashtable.
807      * <p>
808      * If the type is QUERY_DATA, then add name/value to the queryData
809      * hashtable.
810      *
811      * @param type Type (PATH_INFO or QUERY_DATA) of insertion.
812      * @param name A String with the name to add.
813      * @param value A String with the value to add.
814      */
815     protected void add(int type,
816             String name,
817             String value)
818     {
819         URIParam uriParam = new URIParam(ParserUtils.convertAndTrim(name), value);
820 
821         dataVectors[type].add(uriParam); // Code so clean you can eat from...
822     }
823 
824     /***
825      * Method for a quick way to add all the parameters in a
826      * ParameterParser.
827      *
828      * <p>If the type is P (0), then add name/value to the pathInfo
829      * hashtable.
830      *
831      * <p>If the type is Q (1), then add name/value to the queryData
832      * hashtable.
833      *
834      * @param type Type of insertion (@see #add(char type, String name, String value))
835      * @param pp A ParameterParser.
836      */
837     protected void add(int type,
838             ParameterParser pp)
839     {
840         for(Iterator it = pp.keySet().iterator(); it.hasNext();)
841         {
842             String key = (String) it.next();
843 
844             if (!key.equalsIgnoreCase(CGI_ACTION_PARAM) &&
845                     !key.equalsIgnoreCase(CGI_SCREEN_PARAM))
846             {
847                 String[] values = pp.getStrings(key);
848                 if(values != null)
849                 {
850                     for (int i = 0; i < values.length; i++)
851                     {
852                         add(type, key, values[i]);
853                     }
854                 }
855                 else
856                 {
857                     add(type, key, "");
858                 }
859             }
860         }
861     }
862 
863     /***
864      * Method for a quick way to add all the parameters in a
865      * List with URIParam objects.
866      *
867      * <p>If the type is P (0), then add name/value to the pathInfo
868      * hashtable.
869      *
870      * <p>If the type is Q (1), then add name/value to the queryData
871      * hashtable.
872      *
873      * @param type Type of insertion (@see #add(char type, String name, String value))
874      * @param list A List of URIParam objects
875      */
876     protected void add(int type,
877             List list)
878     {
879         for (Iterator it = list.iterator(); it.hasNext();)
880         {
881             // Strictly spoken we don't need this cast. But if we do,
882             // we get class cast right here is someone tries to put
883             // a list with wrong objects into this method.
884             URIParam uriParam = (URIParam) it.next();
885             dataVectors[type].add(uriParam);
886         }
887     }
888 
889     /***
890      * If the type is P (0), then remove name/value from the
891      * pathInfo hashtable.
892      *
893      * <p>If the type is Q (1), then remove name/value from the
894      * queryData hashtable.
895      *
896      * @param type Type (P or Q) of removal.
897      * @param name A String with the name to be removed.
898      */
899     protected void remove (int type,
900             String name)
901     {
902         Collection c = dataVectors[type];
903         String key = ParserUtils.convertAndTrim(name);
904 
905         for (Iterator it = c.iterator(); it.hasNext();)
906         {
907             URIParam uriParam = (URIParam) it.next();
908 
909             if (key.equals(uriParam.getKey()))
910             {
911                 it.remove();
912             }
913         }
914     }
915 }