Annotation of java/classes/org/w3c/tools/resources/LookupState.java, revision 1.6.4.2
1.4 ylafon 1: // LookupState.java
1.6.4.2 ! ylafon 2: // $Id: LookupState.java,v 1.11 2002/01/30 15:10:17 ylafon Exp $
1.5 bmahe 3: // (c) COPYRIGHT MIT and INRIA, 1996.
1.4 ylafon 4: // Please first read the full copyright statement in file COPYRIGHT.html
5:
1.5 bmahe 6: package org.w3c.tools.resources ;
1.1 abaird 7:
1.5 bmahe 8: import java.util.Vector ;
1.3 bmahe 9:
1.5 bmahe 10: /**
11: * This object keeps the state info around while looking up an entity.
12: */
13:
14: public class LookupState {
15: private int index ;
16: private String components[] ;
1.6.4.2 ! ylafon 17: private String componentstype[];
1.5 bmahe 18: private RequestInterface request ;
19: private boolean is_directory = false ;
20: private boolean is_internal = false ;
21: private String uri = null ;
22: private String query = null;
23: private String fragment = null;
1.6.4.1 ylafon 24: private String type = null; // rfc1630 type as defined in ftp
1.5 bmahe 25:
26: /**
27: * Unescape a escaped string
28: * @param s The string to be unescaped
29: * @return the unescaped string.
30: */
31:
32: public static String unescape (String s) {
33: StringBuffer sbuf = new StringBuffer () ;
34: int l = s.length() ;
35: int ch = -1 ;
36: for (int i = 0 ; i < l ; i++) {
37: switch (ch = s.charAt(i)) {
38: case '%':
39: ch = s.charAt (++i) ;
40: int hb = (Character.isDigit ((char) ch)
41: ? ch - '0'
42: : 10+Character.toLowerCase((char) ch) - 'a') & 0xF ;
43: ch = s.charAt (++i) ;
44: int lb = (Character.isDigit ((char) ch)
45: ? ch - '0'
46: : 10+Character.toLowerCase((char) ch) - 'a') & 0xF ;
1.6.4.2 ! ylafon 47: // remove if equal to 0 FIXME for utf8
! 48: if (((hb << 4) | lb) > 0) {
! 49: sbuf.append ((char) ((hb << 4) | lb)) ;
! 50: }
1.5 bmahe 51: break ;
52: case '+':
53: sbuf.append (' ') ;
54: break ;
55: default:
56: sbuf.append ((char) ch) ;
57: }
58: }
59: return sbuf.toString() ;
1.1 abaird 60: }
1.6.4.2 ! ylafon 61:
! 62:
1.5 bmahe 63: /**
64: * Parse the given URI into an array of hierarchical components.
65: * The optional query string and an optional fragment are recorded into
66: * the request as new fields.
67: * <p>The query string and the fragment are recorded into the request
68: * as the <strong>query</strong> and <strong>frag</strong> attributes.
1.6 bmahe 69: * @exception ProtocolException if unable to parse
1.5 bmahe 70: */
71: protected void parseURI ()
72: throws ProtocolException
73: {
74: int urilen = uri.length() ;
75: int start = 0 ;
76: int slash = -1 ;
1.6.4.2 ! ylafon 77: int t = -1;
1.5 bmahe 78: Vector comps = new Vector(8) ;
79: int q = uri.indexOf ('?', start);
80: int f = uri.indexOf ('#', start);
81: int stop = -1;
1.1 abaird 82:
1.6.4.2 ! ylafon 83: if ((q >= 0) && (f >= 0)) {
! 84: stop = Math.min(q, f);
! 85: } else if (q >= 0) {
! 86: stop = q;
! 87: } else if (f >= 0) {
! 88: stop = f;
1.5 bmahe 89: } else {
1.6.4.2 ! ylafon 90: stop = urilen;
1.5 bmahe 91: }
1.6.4.2 ! ylafon 92: if ( stop < 0 )
! 93: stop = urilen;
1.5 bmahe 94: this.uri = uri ;
95: loop:
96: while ( true ) {
97: slash = uri.indexOf ('/', start) ;
98: if ((slash >= stop) || (slash < 0)) {
99: break loop;
100: } else if ( slash == start ) {
101: start = slash + 1;
102: continue loop;
103: } else if ( slash > 0 ) {
104: String part = unescape(uri.substring (start, slash)) ;
1.6.4.2 ! ylafon 105: // detect / and
! 106: t = part.indexOf(';');
! 107: if (t == -1) {
! 108: if (part.indexOf('/') != -1) {
! 109: // FIXME currently using unescaped string
! 110: comps.addElement ((uri.substring (start, slash))) ;
1.6.4.1 ylafon 111: } else {
1.6.4.2 ! ylafon 112: comps.addElement (part) ;
1.6.4.1 ylafon 113: }
114: } else {
1.6.4.2 ! ylafon 115: int sl = part.indexOf('/');
! 116: if (sl >= 0) {
! 117: if (t < sl) {
! 118: comps.addElement (part) ;
1.6.4.1 ylafon 119: } else {
1.6.4.2 ! ylafon 120: // FIXME currently using unescaped string
! 121: comps.addElement ((uri.substring (start, slash))) ;
1.6.4.1 ylafon 122: }
123: }
1.6.4.2 ! ylafon 124: }
! 125: start = slash + 1;
! 126: continue loop;
! 127: }
! 128: }
! 129: // Deal with any ? or # fragments:
! 130: if ((q >= 0) || (f >= 0)) {
! 131: if ((q >= 0) && (f > q)) {
! 132: // ?q#f
! 133: if (q+1 < f)
! 134: this.query = uri.substring(q+1, f);
! 135: if (f+1 < urilen)
! 136: this.fragment = uri.substring(f+1, urilen);
! 137: } else if ((f >= 0) && (q > f)) {
! 138: // #f?q
! 139: if (f+1 < q)
! 140: this.fragment = uri.substring(f+1, q);
! 141: if (q+1 < urilen)
! 142: this.query = uri.substring(q+1, urilen);
! 143: } else if ( f >= 0 ) {
! 144: // #f
! 145: if (f+1 < urilen)
! 146: this.fragment = uri.substring(f+1, urilen);
! 147: } else if ( q >= 0 ) {
! 148: // ?q
! 149: if (q+1 < urilen)
! 150: this.query = uri.substring(q+1, urilen);
1.5 bmahe 151: }
152: }
153: // Update query states:
154: if ( request != null ) {
155: if ( query != null )
156: request.setState("query", query);
157: if ( fragment != null )
158: request.setState("frag", fragment);
159: }
160: // Keep track of last frament, and wrap up the result:
161: if (start < stop)
162: comps.addElement(unescape(uri.substring(start, stop)));
163: if (--stop >= 0 )
164: is_directory = (uri.charAt(stop) == '/');
165: components = new String[comps.size()] ;
1.6.4.2 ! ylafon 166: componentstype = new String[comps.size()] ;
1.5 bmahe 167: comps.copyInto (components) ;
1.6.4.2 ! ylafon 168: // now cut possible comments inside the URL, per rfc 1630
! 169: for (int i=0; i< components.length; i++) {
! 170: t = components[i].indexOf(';');
! 171: if (t >= 0) {
! 172: componentstype[i] = components[i].substring(t+1);
! 173: components[i] = components[i].substring(0,t);
! 174: }
! 175: }
1.5 bmahe 176: index = 0 ;
177: }
1.6.4.2 ! ylafon 178:
1.5 bmahe 179: /**
180: * Get the fragment part of the URL, if any.
181: * The fragment is anything beyond the # character in a URL.
182: * @return A String instance, or <strong>null</strong>.
183: */
184:
185: public String getFragment() {
186: return fragment;
187: }
188:
189: /**
190: * Get the query part of the URL, if any.
191: * The query is anything beyond a ? character in a URL.
192: * @return A String instance, or <strong>null</strong>.
193: */
194:
195: public String getQuery() {
196: return query;
1.6.4.1 ylafon 197: }
198:
199: /**
200: * Get the type part of the URL, if any.
201: * The type is anything beyond a ; character in a URL.
202: * @return A String instance, or <strong>null</strong>.
203: */
204:
205: public String getType() {
1.6.4.2 ! ylafon 206: return componentstype[index];
1.5 bmahe 207: }
208:
209: /**
210: * Is the requested URI a directory URI ?
211: * @return A boolean <strong>true</strong> if the requested URI ends with
212: * a slash, <strong>false</strong> otherwise.
213: */
1.6.4.2 ! ylafon 214:
1.5 bmahe 215: public boolean isDirectory() {
216: return is_directory ;
217: }
218:
219: /**
220: * Get this lookpu state full URI.
221: */
222:
223: public String getURI() {
224: return uri ;
225: }
226:
227: /**
228: * Get next part of the URL to be look for.
229: * @return A String giving the next component.
230: */
231:
232: public final String getNextComponent() {
1.6.4.2 ! ylafon 233: if (request != null) {
! 234: request.setState("type", componentstype[index]);
! 235: }
1.5 bmahe 236: return components[index++] ;
237: }
238:
239: /**
240: * Get the next component, without consuming it.
241: * @return A String giving the next component, or <strong>null</strong>
242: * if none is available.
243: */
244:
245: public final String peekNextComponent() {
246: if ( index < components.length )
247: return components[index] ;
248: return null ;
249: }
250:
251: /**
252: * Get the remaining path.
253: * @param consume If <strong>true</strong>, consume the components,
254: * otherwise, just peek them.
255: * @return A String giving the remaining URL.
256: */
257:
258: public final String getRemainingPath(boolean consume) {
259: StringBuffer sb = new StringBuffer() ;
260: for (int i = index ; i < components.length ; i++) {
261: sb.append("/"+components[i]);
262: }
263: if ( consume )
264: index = components.length;
265: return sb.toString() ;
266: }
267:
268: /**
269: * Get the remaiing path, without consuming it.
270: * @return The remaining path.
271: */
272:
273: public final String getRemainingPath() {
274: return getRemainingPath(false);
275: }
276:
277: /**
278: * Does this look up state has more components to be looked for.
279: * @return <strong>true</strong> if more components are to be looked for.
280: */
281:
282: public boolean hasMoreComponents() {
283: return index < components.length ;
284: }
285:
286: /**
287: * How much components have not yet been looked up in this state.
288: */
289:
290: public int countRemainingComponents() {
291: return components.length - index ;
292: }
293:
294: /**
295: * Get this lookup state request.
296: * @return An instance of RequestInterface, or <strong>null</strong>
297: * if this is an internal request.
298: */
299:
300: public final RequestInterface getRequest () {
301: return request ;
302: }
303:
304: /**
305: * Is this lookup state object associated with a request ?
306: * @return A boolean <strong>true</strong> if a request is associated.
307: */
308:
309: public boolean hasRequest() {
310: return (request != null) ;
311: }
312:
313: /**
314: * Mark this lookup state as being done internally.
315: * This allows lookup methods to be more kind (for example, not throwing
316: * redirections error, etc).
317: */
318:
319: public void markInternal() {
320: is_internal = true ;
321: }
322:
323: /**
324: * Is this lookup state internal to the server.
325: * Internal lookup state may not have an associated request.
326: * @return A boolean <strong>true</strong> if this is an internal request.
327: */
328:
329: public boolean isInternal() {
330: return is_internal ;
331: }
1.6.4.2 ! ylafon 332:
1.5 bmahe 333: /**
334: * Create a lookup state to handle the given request on behalf of client.
335: * @param client The client that issued the request.
336: * @param request The request whose URI is to bee looked up.
1.6 bmahe 337: * @exception ProtocolException if an error relative to the protocol occurs
1.5 bmahe 338: */
339:
340: public LookupState (RequestInterface request)
341: throws ProtocolException
342: {
343: this.request = request ;
344: this.uri = request.getURLPath();
345: this.is_internal = request.isInternal();
346: if ( uri == null ) {
347: ReplyInterface reply = request.makeBadRequestReply() ;
348: reply.setContent ("Invalid URI (unparsable)") ;
349: throw new ProtocolException (reply) ;
350: }
351: parseURI () ;
352: }
353:
354: /**
355: * Construct a lookup state to be resolved internnaly by the server.
356: * This method allows for internal lookup of object, even if there is no
357: * real client making the request.
358: * @param uri The URI to be looked up.
1.6 bmahe 359: * @exception ProtocolException if an error relative to the protocol occurs
1.5 bmahe 360: */
361:
362: public LookupState(String uri)
363: throws ProtocolException
364: {
365: this.request = null ;
366: this.is_internal = true ;
367: this.uri = uri ;
368: parseURI() ;
369: }
1.1 abaird 370:
371: }
Webmaster