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