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