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