001: /* Copyright 2001 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal;
007:
008: import java.util.StringTokenizer;
009:
010: import javax.servlet.http.HttpServletRequest;
011:
012: /**
013: * This helper class allows for easy access to the information contained in the ever-changing
014: * uP file URL spec. The uP file syntax is likely to change often, therefore we encourage developers
015: * to use this class instead of trying to parse the uP file on your own.
016: * <p>Note: in case you're wondering what in the world "uP file" is, take a look at the portal URLs.
017: * The context path ends with a file-like specification that always has ".uP" at the end ...
018: * that's what we call a "uP" file. It is used to provide information on how different requests
019: * should be processed.</p>
020: * <p>Current uP file syntax looks like this: <code><b>"[tag.tagId.]{method}.methodId.[target.targetId.][*.]uP"</b></code>,
021: * where "[]" denote optional expressions and "{}" choice-defined expressions. The "{method}" field, at the moment has
022: * two choices: "render" and "worker".</p>
023: * <p> uPortal will assume that the .uP file spec is always well-formed, so don't try to construct it on your own, use
024: * <code>baseActionURL</code> or one of the <code>workerActionURL</code>s. </p>
025: *
026: * @author Peter Kharchenko {@link <a href="mailto:pkharchenko@interactivebusiness.com"">pkharchenko@interactivebusiness.com"</a>}
027: * @version $Revision: 36690 $
028: */
029: public class UPFileSpec {
030: // some URL construction elements
031: public static final String TAG_URL_ELEMENT = "tag";
032: public static final String TARGET_URL_ELEMENT = "target";
033: public static final String WORKER_URL_ELEMENT = "worker";
034: public static final String DETACH_URL_ELEMENT = "detach";
035: public static final String RENDER_URL_ELEMENT = "render";
036: public static final String PORTAL_URL_SEPARATOR = ".";
037: public static final String PORTAL_URL_SUFFIX = "uP";
038:
039: // individual worker URL elements
040: public static final String FILE_DOWNLOAD_WORKER = "download";
041:
042: // int values for methods
043: public static final int RENDER_METHOD = 0;
044: public static final int WORKER_METHOD = 1;
045:
046: String tagId = null;
047: String method = null;
048: String methodNodeId = null;
049: String targetNodeId = null;
050: String uPFile_extras = null;
051:
052: /**
053: * Creates a new <code>UPFileSpec</code> instance with all values being null.
054: *
055: */
056: public UPFileSpec() {
057: }
058:
059: /**
060: * Construct a .uP file spec object for a .uP file contained in a given request.
061: *
062: * @param req a <code>HttpServletRequest</code> value
063: */
064: public UPFileSpec(HttpServletRequest req) {
065: String servletPath = req.getServletPath();
066: int firstChar = 0;
067: if (servletPath.startsWith("/" + WORKER_URL_ELEMENT + "/"
068: + FILE_DOWNLOAD_WORKER)) {
069: servletPath = req.getPathInfo();
070: }
071: if (servletPath.charAt(firstChar) == '/') {
072: firstChar += 1;
073: }
074: int slash = servletPath.indexOf('/', firstChar);
075: if (slash == -1) {
076: slash = servletPath.length();
077: }
078: String uPFile = servletPath.substring(firstChar, slash);
079:
080: analyze(uPFile);
081: }
082:
083: /**
084: * Construct a .uP file spec object by providing the actual .uP file string
085: *
086: * @param uPFile a <code>String</code> value
087: */
088: public UPFileSpec(String uPFile) {
089: analyze(uPFile);
090: }
091:
092: /**
093: * Copy constructor.
094: *
095: * @param up an <code>UPFileSpec</code> value to copy the values from
096: */
097: public UPFileSpec(UPFileSpec up) {
098: this .tagId = up.getTagId();
099: this .method = up.getMethod();
100: this .methodNodeId = up.getMethodNodeId();
101: this .targetNodeId = up.getTargetNodeId();
102: this .uPFile_extras = up.getUPFileExtras();
103: }
104:
105: /**
106: * A building constructor.
107: *
108: * @param tagId a tag id <code>String</code> value (can be <code>null</code>)
109: * @param method a method <code>String</code> value (required, must be one of the <code>UPFileSpec.*_METHOD</code> constants, i.e. {@link #RENDER_METHOD} or {@link #WORKER_METHOD})
110: * @param methodNodeId a method node id <code>String</code> value (required value, can <b>not</b> be <code>null</code>)
111: * @param targetNodeId a target id <code>String</code> value (can be <code>null</code>)
112: * @param extraElements a <code>String</code> to be incorporated into the file name before the suffix (".uP"). These values will be available from the {@link #getUPFileExtras()} result when .uP file is parsed. (can be <code>null</code>)
113: * @exception PortalException if an invalid method code is passed or no methodNodeId is present.
114: */
115: public UPFileSpec(String tagId, int method, String methodNodeId,
116: String targetNodeId, String extraElements)
117: throws PortalException {
118: this .setTagId(tagId);
119: this .setMethod(method);
120: this .setMethodNodeId(methodNodeId);
121: this .setTargetNodeId(targetNodeId);
122: this .setUPFileExtras(extraElements);
123: }
124:
125: /**
126: * Set a tag id
127: *
128: * @param id a <code>String</code> value
129: */
130: public void setTagId(String id) {
131: this .tagId = id;
132: }
133:
134: /**
135: * Set a method.
136: *
137: * @param method a method <code>String</code> value (required, must be one of the <code>UPFileSpec.*_METHOD</code> constants, i.e. {@link #RENDER_METHOD} or {@link #WORKER_METHOD})
138: * @exception PortalException if an invalid method id is passed.
139: */
140: public void setMethod(int method) throws PortalException {
141: if (method == RENDER_METHOD) {
142: this .method = RENDER_URL_ELEMENT;
143: } else if (method == WORKER_METHOD) {
144: this .method = WORKER_URL_ELEMENT;
145: } else {
146: throw new PortalException("Invalid method code!");
147: }
148: }
149:
150: /**
151: * Set method node id.
152: *
153: * @param nodeId a <code>String</code> value
154: */
155: public void setMethodNodeId(String nodeId) {
156: this .methodNodeId = nodeId;
157: }
158:
159: /**
160: * Set target node id
161: *
162: * @param nodeId a <code>String</code> value
163: */
164: public void setTargetNodeId(String nodeId) {
165: this .targetNodeId = nodeId;
166: }
167:
168: /**
169: * Set extras to be appended to the spec before the suffix element (".uP")
170: *
171: * @param extras a <code>String</code> value
172: */
173: public void setUPFileExtras(String extras) {
174: this .uPFile_extras = extras;
175: }
176:
177: /**
178: * Returns a tag identifier.
179: *
180: * @return a <code>String</code> tag value, <code>null</code> if no tag was specified.
181: */
182: public String getTagId() {
183: return tagId;
184: }
185:
186: /**
187: * Determine method name
188: *
189: * @return a <code>String</code> method name, <code>null</code> if no method was specified.
190: */
191: public String getMethod() {
192: return method;
193: }
194:
195: /**
196: * Determine Id specified by the method element.
197: *
198: * @return a <code>String</code> method node Id value, <code>null</code> if no method was specified.
199: */
200: public String getMethodNodeId() {
201: return methodNodeId;
202: }
203:
204: /**
205: * Determine Id specified by the "target" element.
206: *
207: * @return a <code>String</code> target Id value, <code>null</code> if no target was specified.
208: */
209: public String getTargetNodeId() {
210: return targetNodeId;
211: }
212:
213: /**
214: * Get the full .uP file <code>String</code>.
215: *
216: * @return a <code>String</code> value
217: */
218: public String getUPFile() throws PortalException {
219: return (buildUPFileBase(tagId, method, methodNodeId,
220: targetNodeId, uPFile_extras)).concat(PORTAL_URL_SUFFIX);
221: }
222:
223: /**
224: * Returns a "cleaned-up" version of the uP file with all known
225: * fields such as tag, method, and target, removed. This can be used by...
226: *
227: * @return a <code>String</code> value, <code>null</code> if none were encountered.
228: */
229: public String getUPFileExtras() {
230: return uPFile_extras;
231: }
232:
233: /**
234: * Constructs a .uP file
235: *
236: * @param tagId a tag id <code>String</code> value (can be <code>null</code>)
237: * @param method a method <code>String</code> value (required, must be one of the <code>UPFileSpec.*_METHOD</code> constants, i.e. {@link #RENDER_METHOD} or {@link #WORKER_METHOD})
238: * @param methodNodeId a method node id <code>String</code> value (required value, can <b>not</b> be <code>null</code>)
239: * @param targetNodeId a target id <code>String</code> value (can be <code>null</code>)
240: * @param extraElements a <code>String</code> to be incorporated into the file name before the suffix (".uP"). These values will be available from the {@link #getUPFileExtras()} result when .uP file is parsed. (can be <code>null</code>)
241: * @return a <code>String</code> value
242: * @exception PortalException if an invalid method code is passed or no methodNodeId is present.
243: */
244: public static String buildUPFile(String tagId, int method,
245: String methodNodeId, String targetNodeId,
246: String extraElements) throws PortalException {
247: return (buildUPFileBase(tagId, method, methodNodeId,
248: targetNodeId, extraElements)).concat(PORTAL_URL_SUFFIX);
249: }
250:
251: /**
252: * Constructs a .uP file, without the suffix (actual ".uP") so it can be extended further.
253: *
254: * @param tagId a tag id <code>String</code> value (can be <code>null</code>)
255: * @param method a method <code>String</code> value (required, must be one of the <code>UPFileSpec.*_METHOD</code> constants, i.e. {@link #RENDER_METHOD} or {@link #WORKER_METHOD})
256: * @param methodNodeId a method node id <code>String</code> value (required value, can <b>not</b> be <code>null</code>)
257: * @param targetNodeId a target id <code>String</code> value (can be <code>null</code>)
258: * @param extraElements a <code>String</code> to be incorporated into the file name before the suffix (".uP"). These values will be available from the {@link #getUPFileExtras()} result when .uP file is parsed. (can be <code>null</code>)
259: * @return a <code>String</code> value
260: * @exception PortalException if an invalid method code is passed or no methodNodeId is present.
261: */
262: public static String buildUPFileBase(String tagId, int method,
263: String methodNodeId, String targetNodeId,
264: String extraElements) throws PortalException {
265: String methodName = null;
266:
267: if (method == RENDER_METHOD) {
268: methodName = RENDER_URL_ELEMENT;
269: } else if (method == WORKER_METHOD) {
270: methodName = WORKER_URL_ELEMENT;
271: } else {
272: throw new PortalException("Invalid method code!");
273: }
274: return buildUPFileBase(tagId, methodName, methodNodeId,
275: targetNodeId, extraElements);
276: }
277:
278: protected static String buildUPFileBase(String tagId,
279: String method, String methodNodeId, String targetNodeId,
280: String extraElements) throws PortalException {
281: StringBuffer sb = new StringBuffer();
282: if (method != null && method.equals(WORKER_URL_ELEMENT)
283: && methodNodeId != null
284: && methodNodeId.equals(FILE_DOWNLOAD_WORKER)) {
285: sb.append(method).append('/').append(methodNodeId).append(
286: '/');
287: }
288:
289: if (tagId != null) {
290: sb.append("tag").append(PORTAL_URL_SEPARATOR);
291: sb.append(tagId).append(PORTAL_URL_SEPARATOR);
292: }
293:
294: if (method != null) {
295: sb.append(method).append(PORTAL_URL_SEPARATOR);
296: } else {
297: throw new PortalException(
298: "UPFileSpec: method can not be null!");
299: }
300:
301: if (methodNodeId != null) {
302: sb.append(methodNodeId).append(PORTAL_URL_SEPARATOR);
303: } else {
304: throw new PortalException(
305: "UPFileSpec: method node Id can not be null!");
306: }
307:
308: if (targetNodeId != null) {
309: sb.append("target").append(PORTAL_URL_SEPARATOR);
310: sb.append(targetNodeId).append(PORTAL_URL_SEPARATOR);
311: }
312:
313: if (extraElements != null) {
314: sb.append(extraElements).append(PORTAL_URL_SEPARATOR);
315: }
316:
317: return sb.toString();
318:
319: }
320:
321: protected void analyze(String uPFile) {
322: StringTokenizer uPTokenizer = new StringTokenizer(uPFile,
323: PORTAL_URL_SEPARATOR);
324: // determine tag or method
325: if (uPTokenizer.hasMoreTokens()) {
326: String currentToken = uPTokenizer.nextToken();
327: // is it a "tag" ?
328: if (currentToken.equals(TAG_URL_ELEMENT)) {
329: // yes it's, a tag
330: if (uPTokenizer.hasMoreElements()) {
331: // we'll assume that the next toke is always an Id ...
332: tagId = uPTokenizer.nextToken();
333: if (uPTokenizer.hasMoreElements()) {
334: currentToken = uPTokenizer.nextToken();
335: } else {
336: return;
337: }
338: } else {
339: // nothing after the "tag" element
340: return;
341: }
342: }
343:
344: // determine method
345: if (currentToken.equals(RENDER_URL_ELEMENT)) {
346: // render method
347: method = currentToken;
348: } else if (currentToken.equals(WORKER_URL_ELEMENT)) {
349: // worker method
350: method = currentToken;
351: } else {
352: // unknown method
353: uPFile_extras = sinkTokenization(uPTokenizer,
354: PORTAL_URL_SEPARATOR, currentToken);
355: return;
356: }
357:
358: // determine method target
359: if (uPTokenizer.hasMoreElements()) {
360: methodNodeId = uPTokenizer.nextToken();
361: } else {
362: return;
363: }
364:
365: // see if a target is specified
366: if (uPTokenizer.hasMoreElements()) {
367: currentToken = uPTokenizer.nextToken();
368: } else {
369: return;
370: }
371:
372: if (currentToken.equals(TARGET_URL_ELEMENT)) {
373: // yes, target is specified
374: if (uPTokenizer.hasMoreElements()) {
375: targetNodeId = uPTokenizer.nextToken();
376: if (uPTokenizer.hasMoreElements()) {
377: currentToken = uPTokenizer.nextToken();
378: } else {
379: currentToken = null;
380: }
381: } else {
382: return;
383: }
384: }
385:
386: // sink the rest into the uPFile_extras
387: uPFile_extras = sinkTokenization(uPTokenizer,
388: PORTAL_URL_SEPARATOR, currentToken);
389:
390: } else {
391: // blank .uP file ?
392: return;
393: }
394: }
395:
396: /**
397: * Sinks tokens back into a string. All except for the last one.
398: *
399: * @param st a <code>StringTokenizer</code> value
400: * @param delimiter a <code>String</code> delimeter value used to produce the tokenization
401: * @param initialValue a <code>String</code> value to which to append remaining tokens
402: * @return a <code>String</code> value
403: */
404: private static String sinkTokenization(StringTokenizer st,
405: String delimiter, String initialValue) {
406: StringBuffer sb;
407: if (initialValue != null
408: && !PORTAL_URL_SUFFIX.equals(initialValue)) {
409: sb = new StringBuffer(initialValue);
410: } else {
411: sb = new StringBuffer();
412: }
413:
414: while (st.hasMoreTokens()) {
415: String token = st.nextToken();
416: if (!PORTAL_URL_SUFFIX.equals(token)) {
417: if (st.hasMoreTokens()) {
418: sb.append(delimiter);
419: sb.append(token);
420: }
421: }
422: }
423: return sb.length() == 0 ? null : sb.toString();
424: }
425: }
|