Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 492   Methods: 26
NCLOC: 267   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
URIMapper.java 0% 0% 0% 0%
coverage
 1    /*
 2    * Licensed to the Apache Software Foundation (ASF) under one or more
 3    * contributor license agreements. See the NOTICE file distributed with
 4    * this work for additional information regarding copyright ownership.
 5    * The ASF licenses this file to You under the Apache License, Version 2.0
 6    * (the "License"); you may not use this file except in compliance with
 7    * the License. You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    *
 17    * $Id: URIMapper.java 541508 2007-05-25 01:54:12Z vgritsenko $
 18    */
 19   
 20    package org.apache.xindice.core.request;
 21   
 22    import org.apache.commons.logging.Log;
 23    import org.apache.commons.logging.LogFactory;
 24    import org.apache.xindice.core.Collection;
 25    import org.apache.xindice.core.Container;
 26    import org.apache.xindice.core.DBException;
 27    import org.apache.xindice.core.Database;
 28    import org.apache.xindice.core.FaultCodes;
 29    import org.apache.xindice.util.ObjectPool;
 30    import org.apache.xindice.util.Poolable;
 31    import org.apache.xindice.util.XindiceException;
 32    import org.apache.xindice.xml.TextWriter;
 33   
 34    import org.w3c.dom.Document;
 35   
 36    import java.io.ByteArrayInputStream;
 37    import java.io.IOException;
 38    import java.io.InputStream;
 39    import java.net.URL;
 40    import java.net.URLConnection;
 41    import java.util.Properties;
 42   
 43    /**
 44    * URIMapper maps a URI (from whence it came) to a Xindice object. Xindice
 45    * URIs can identify any of several different object types, each of which
 46    * exposes a different (or slightly different) interface to the outside
 47    * world.
 48    *
 49    * @version $Revision: 541508 $, $Date: 2007-05-24 18:54:12 -0700 (Thu, 24 May 2007) $
 50    */
 51    public final class URIMapper extends URLConnection implements Poolable {
 52   
 53    private static final Log log = LogFactory.getLog(URIMapper.class);
 54   
 55    public static final int UNKNOWN = -1;
 56    public static final int APPLICATION = 1;
 57    public static final int COLLECTION = 2;
 58    public static final int DOCUMENT = 3;
 59   
 60    public static final String DEFAULT_ENCODING = "UTF-8";
 61   
 62    private ObjectPool pool = null;
 63    private String uri = null;
 64    private int type = 0;
 65   
 66    private byte[] buf = null;
 67    private int pos = 0;
 68    private char lastChar = 0;
 69   
 70    private Database db = null;
 71    private Collection collection = null;
 72    private Document document = null;
 73    private String method = null;
 74    private Container container = null;
 75   
 76    private Properties params = null;
 77    private String[] args = null;
 78   
 79    private String urlresult = null; // Holds the value of the URL resolution results (XML doc or XMLObject call results )
 80    private boolean inputstreamset = false; // Flag to tell if the input stream has been initialized
 81   
 82    /**
 83    * Constructor for creating URIMapper instance using a standard URL
 84    */
 85  0 public URIMapper(URL u) {
 86  0 super(u);
 87  0 try {
 88  0 setURI(u.toString());
 89    } catch (Exception e) {
 90    // Put error code here!
 91  0 if (log.isWarnEnabled()) {
 92  0 log.warn("ignored exception", e);
 93    }
 94    }
 95    }
 96   
 97    /**
 98    * Constructor for older URIMapper instances
 99    */
 100  0 public URIMapper(String uri) throws XindiceException {
 101  0 super(null);
 102  0 setURI(uri);
 103    }
 104   
 105    /**
 106    * Constructor for older URIMapper instances
 107    */
 108  0 public URIMapper() {
 109  0 super(null);
 110    }
 111   
 112    /**
 113    * Opens a communications link to the resource referenced by this URL,
 114    * if such a connection has not already been established.
 115    */
 116  0 public void connect() {
 117  0 this.connected = true;
 118    }
 119   
 120    /**
 121    * Returns an input stream that reads from this open connection.
 122    */
 123  0 public InputStream getInputStream() throws IOException {
 124   
 125  0 String output = null;
 126   
 127    // Check if the inputstream has already been initialized, if not do now, else return the contents of urlresult
 128  0 if (!inputstreamset) {
 129  0 try {
 130  0 switch (type) {
 131   
 132  0 case URIMapper.DOCUMENT:
 133  0 output = TextWriter.toString(getDocument());
 134  0 break;
 135   
 136  0 default :
 137    // Document type not found, output error message
 138  0 throw new Exception("Content type unsupported");
 139    }
 140    } catch (Exception e) {
 141  0 if (log.isWarnEnabled()) {
 142  0 log.warn("ignored exception", e);
 143    }
 144    }
 145    }
 146    // Inputstream already initialized, set output to be value of urlresult
 147    else {
 148  0 output = urlresult;
 149    }
 150   
 151    // Save the value of output into local variable urlresult
 152  0 urlresult = output;
 153    // Set inpustreamset flag to signal that url has already been resolved
 154  0 inputstreamset = true;
 155   
 156    // Send the result to the client, sending blank string if NULL
 157  0 if (output == null) {
 158  0 return new ByteArrayInputStream(new byte[0]);
 159    } else {
 160  0 return new ByteArrayInputStream(output.getBytes(DEFAULT_ENCODING));
 161    }
 162    }
 163   
 164    /**
 165    * Returns the value of the content-encoding header field.
 166    * @return the content encoding of the resource that the URL references, or null if not known.
 167    */
 168  0 public String getContentEncoding() {
 169  0 return DEFAULT_ENCODING;
 170    }
 171   
 172    /**
 173    * Returns the value of the content-type header field.
 174    * @return the content type of the resource that the URL references, or null if not known.
 175    */
 176  0 public String getContentType() {
 177    // Return the docuement's content type, for now this can only be "text/xml"
 178  0 return "text/xml";
 179    }
 180   
 181    /**
 182    * Returns the value of the content-length header field.
 183    * @return the content length of the resource that this connection's URL references, or -1 if the content length is not known.
 184    */
 185  0 public int getContentLength() {
 186    // Check to see if this document has already been resolved, if not call getInputStream
 187  0 if (!inputstreamset) {
 188  0 try {
 189  0 this.getInputStream();
 190    } catch (IOException e) {
 191  0 return 0;
 192    }
 193    }
 194   
 195    // Return the lenght of this document/XMLObject call
 196  0 return urlresult.length();
 197    }
 198   
 199    /**
 200    * Returns the value of the last-modified header field. The result is the number of milliseconds since January 1, 1970 GMT.
 201    * @return the date the resource referenced by this URLConnection was last modified, or 0 if not known.
 202    */
 203  0 public long getLastModified() {
 204    // For now this functionality is not available, return 0
 205  0 return 0;
 206    }
 207   
 208  0 public void setPool(ObjectPool pool) {
 209  0 this.pool = pool;
 210    }
 211   
 212  0 public void reclaim() {
 213  0 reset();
 214  0 if (pool != null) {
 215  0 pool.putObject(this);
 216    }
 217    }
 218   
 219    /**
 220    * reset resets the state of the URIMapper.
 221    */
 222  0 private void reset() {
 223  0 type = UNKNOWN;
 224  0 lastChar = 0;
 225  0 method = "";
 226  0 db = null;
 227  0 collection = null;
 228  0 document = null;
 229  0 params = null;
 230  0 args = null;
 231  0 container = null;
 232   
 233    // reset urlresult and inputstreamset
 234  0 urlresult = null;
 235  0 inputstreamset = false;
 236    }
 237   
 238    /**
 239    * setURI sets the URI for the URIMapper and parses it. The parsed
 240    * components of a URI can be retrieved using getObjectType and any
 241    * of the get<component> methods.
 242    *
 243    * @param uri The URI
 244    */
 245  0 public void setURI(String uri) throws XindiceException {
 246  0 this.uri = uri;
 247  0 reset();
 248  0 parse();
 249    }
 250   
 251    /**
 252    * parseName parses an identifier up to any of the specified delimiters.
 253    *
 254    * @param delims The delimiters to use
 255    * @return The parsed name
 256    */
 257  0 private String parseName(String delims) {
 258  0 int start = pos;
 259  0 while (pos < buf.length) {
 260  0 lastChar = (char) buf[pos++];
 261  0 if (delims.indexOf(lastChar) != -1) {
 262  0 break;
 263    }
 264    }
 265  0 if (pos == buf.length && delims.indexOf(lastChar) == -1) {
 266  0 pos++;
 267    }
 268   
 269  0 return pos > start ? new String(buf, start, pos - start - 1) : "";
 270    }
 271   
 272    /**
 273    * parseParams parses a parameter set and produces either a Properties or
 274    * String[] Object representing those parameters.
 275    */
 276  0 private void parseParams() {
 277  0 if (lastChar == '?') {
 278    // Query String param list
 279  0 params = new Properties();
 280  0 String name;
 281  0 String value;
 282  0 String temp;
 283  0 while (true) {
 284  0 name = parseName("=");
 285  0 if (name.length() == 0) {
 286  0 break;
 287    }
 288  0 value = parseName("?&;");
 289  0 temp = params.getProperty(name);
 290  0 if (temp != null) {
 291  0 StringBuffer sb = new StringBuffer(32);
 292  0 sb.append(temp);
 293  0 sb.append('\u0001');
 294  0 sb.append(value);
 295  0 value = sb.toString();
 296    }
 297  0 params.setProperty(name, value);
 298    }
 299    } else
 300  0 params = new Properties();
 301    }
 302   
 303    /**
 304    * parse parses the URI.
 305    */
 306  0 private void parse() throws XindiceException {
 307  0 buf = uri.getBytes();
 308  0 pos = 0;
 309  0 String tmp;
 310   
 311  0 if (buf.length == 0) {
 312  0 throw new DBException(FaultCodes.URI_EMPTY);
 313    }
 314   
 315    // TODO: Be Able To Handle Remote URIs
 316  0 if ((char) buf[0] != '/') {
 317  0 parseName(":"); // Ignore Protocol
 318  0 parseName("/"); // Ignore Slash
 319  0 parseName("/"); // Ignore Slash
 320  0 parseName("/:"); // Ignore Host (For Now)
 321  0 if (lastChar == ':') {
 322  0 parseName("/"); // Ignore Port
 323    }
 324    } else {
 325  0 pos = 1;
 326    }
 327   
 328    // Database check
 329  0 tmp = parseName("/");
 330  0 if (tmp == null) {
 331  0 return;
 332    }
 333   
 334  0 db = Database.getDatabase(tmp);
 335  0 if (db == null) {
 336  0 return;
 337    }
 338   
 339  0 type = APPLICATION;
 340  0 tmp = parseName("/(?");
 341   
 342  0 int objType = getParsedObjectType(db, tmp);
 343    // If unknown then this URI just points to db and we're done.
 344    // Otherwise we need to keep walking down the URI.
 345  0 if (objType != UNKNOWN) {
 346  0 type = walkURI(db, tmp, objType);
 347    }
 348   
 349  0 if (lastChar == '?') {
 350  0 parseParams();
 351  0 return;
 352    }
 353    }
 354   
 355    /**
 356    * Recursive method to handle the parse of the URI and setup the instance
 357    * objects.
 358    */
 359  0 protected int walkURI(Collection col, String name, int objType) throws XindiceException {
 360  0 switch (objType) {
 361  0 case DOCUMENT:
 362  0 container = col.getContainer(name);
 363  0 document = container.getDocument();
 364  0 return DOCUMENT;
 365   
 366  0 case COLLECTION:
 367  0 Collection c = col.getCollection(name);
 368  0 if (c != null) {
 369  0 collection = c;
 370  0 String tmp = parseName("/(?");
 371    // If we have another name recurse to handle it.
 372  0 if (!tmp.equals("")) {
 373  0 return walkURI(c, tmp, getParsedObjectType(c, tmp));
 374    }
 375   
 376  0 return COLLECTION;
 377    }
 378   
 379  0 default:
 380  0 if (log.isWarnEnabled()) {
 381  0 log.warn("invalid object type : " + objType);
 382    }
 383    }
 384   
 385  0 return UNKNOWN;
 386    }
 387   
 388    /**
 389    * Determine the type of object. If more then one object has the same name
 390    * the order of precedence is COLLECTION - XMLOBJECT - DOCUMENT
 391    */
 392  0 protected int getParsedObjectType(Collection col, String name) throws XindiceException {
 393   
 394  0 if (col.getCollection(name) != null) {
 395  0 return COLLECTION;
 396  0 } else if ((col.getFiler() != null) && (col.getContainer(name) != null)) {
 397  0 return DOCUMENT;
 398    } else {
 399  0 return UNKNOWN;
 400    }
 401    }
 402   
 403    /**
 404    * getObjectType returns the type of Object that was identified in the
 405    * parsing of the URI. This method will return one of the following
 406    * values: UNKNOWN, APPLICATION, DATABASE, COLLECTION, DOCUMENT or
 407    * XMLOBJECT.
 408    *
 409    * @return The object type
 410    */
 411  0 public int getObjectType() {
 412  0 return type;
 413    }
 414   
 415    /**
 416    * getDatabase returns the Database that was resolved in the
 417    * parsing of this URI.
 418    *
 419    * @return The Database
 420    */
 421  0 public Database getDatabase() {
 422  0 return db;
 423    }
 424   
 425    /**
 426    * getCollection returns the Collection object that was resolved in the
 427    * parsing of the URI. If no Collection was resolved, this method will
 428    * return null.
 429    *
 430    * @return The Collection
 431    */
 432  0 public Collection getCollection() {
 433  0 return collection;
 434    }
 435   
 436    /**
 437    * getDocument returns the Document object that was resolved in the
 438    * parsing of the URI. If no Document was resolved, this method will
 439    * return null.
 440    *
 441    * @return The Document
 442    */
 443  0 public Document getDocument() {
 444  0 return document;
 445    }
 446   
 447    /**
 448    * getContainer returns the Document Container that was resolved in
 449    * the parsing of the URI. If no Container was resolved, this method
 450    * will return null.
 451    *
 452    * @return The Container
 453    */
 454  0 public Container getContainer() {
 455  0 return container;
 456    }
 457   
 458    /**
 459    * getMethod returns the method name that was resolved in the parsing
 460    * of the URI. Method names are associated with XMLObjects.
 461    * If no method name was resolved, this method will return null.
 462    *
 463    * @return The method name
 464    */
 465  0 public String getMethod() {
 466  0 return method;
 467    }
 468   
 469    /**
 470    * getProperties returns the Properties object that was produced in
 471    * parsing the URI's Query String. Properties are passed into methods
 472    * that are associated with XMLObjects. This method will return null if
 473    * no Properties were resolved.
 474    *
 475    * @return The Query String Properties
 476    */
 477  0 public Properties getProperties() {
 478  0 return params;
 479    }
 480   
 481    /**
 482    * getArguments returns method arguments in the form of a String array.
 483    * Method arguments are passed to a method in the order that they are
 484    * specified in the URI. If no arguments were parsed, this method will
 485    * return null.
 486    *
 487    * @return The method arguments
 488    */
 489  0 public String[] getArguments() {
 490  0 return args;
 491    }
 492    }