/* * Copyright 2005 by Oracle USA * 500 Oracle Parkway, Redwood Shores, California, 94065, U.S.A. * All rights reserved. */ package javax.ide.net; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Iterator; import java.util.StringTokenizer; /** * An instance of URIPath represents a path that is made * up entirely of {@link URI}s. This can be a class path, source * path, doc path, etc.

*/ public final class URIPath { private final ArrayList _entriesList; //-------------------------------------------------------------------------- // constructors... //-------------------------------------------------------------------------- /** * Creates a URIPath that is initially empty. */ public URIPath() { _entriesList = new ArrayList(); } /** * Creates an empty URIPath with the specified initial * capacity. Used to construct a URIPath whose * contents are not initially known but where the number of entries * can be estimated ahead of time. * * @param initialCapacity the initial capacity of the * URIPath */ public URIPath( int initialCapacity ) { _entriesList = new ArrayList( initialCapacity ); } /** * Creates a URIPath that initially contains the * specified {@link URI} as its sole entry. If the entry is * null, then the URIPath created * is initially empty. */ public URIPath( URI entry ) { this(); add( entry ); } /** * Creates a URIPath initialized with the specified * array of {@link URI} objects. If the entries * array is null or empty, then the * URIPath created is initially empty. */ public URIPath( URI[] entries ) { this(); add( entries ); } /** * Copy constructor. */ public URIPath( URIPath uriPath ) { this(); if ( uriPath != null ) { _entriesList.addAll( uriPath._entriesList ); } } /** * Contructor for creating a URIPath * instance based on a typical path string, such as that that is * returned by System.getProperty( "java.class.path" ). * The exact format of the path string is platform-dependent, so the * path string is tokenized using {@link File#pathSeparator} as the * delimiter.

* * Relative paths are converted to absolute paths, and any path * entries whose name ends in ".jar" or * ".zip" will be created as jar {@link URI}s (i.e. * an {@link URI} with the "jar" scheme). */ public URIPath( String entries ) { this(); final StringTokenizer tokenizer = new StringTokenizer( entries, File.pathSeparator ); while ( tokenizer.hasMoreTokens() ) { final String path = tokenizer.nextToken(); _entriesList.add( pathToURI( path ) ); } } //-------------------------------------------------------------------------- // public API... //-------------------------------------------------------------------------- /** * Adds the given {@link URI} to the end of the URIPath, * if it is not already on the URIPath. If the * parameter is null, then this method returns without * doing anything. */ public void add( URI entry ) { if ( entry != null ) { if ( !contains( entry ) ) { _entriesList.add( entry ); } } } /** * Adds the given {@link URI} objects in order to the end of the * URIPath. Each {@link URI} is added only if it is * not already on the URIPath. Any null * entries are ignored. If the entries array itself * is null, then this method returns without doing anything. */ public void add( URI[] entries ) { if ( entries != null ) { final int numEntries = entries.length; for ( int i = 0; i < numEntries; i++ ) { add( entries[i] ); } } } /** * Adds the entries from the specified URIPath to this * instance. */ public void add( URIPath uriPath ) { if ( uriPath != null ) { add( uriPath.getEntries() ); } } /** * Returns the path represented by this URIPath * instance as an array of {@link URI}s. If the * URIPath is empty, then then this method returns an * {@link URI} array of size 0. */ public URI[] getEntries() { return ( URI[] ) _entriesList.toArray( new URI[_entriesList.size()] ); } /** * Returns true if the specified {@link URI} is * currently on this URIPath. */ public boolean contains( URI entry ) { return findEntry( entry ) >= 0; } /** * Remove the specified entry. * * @param entry the entry to remove. */ public void remove( URI entry ) { if ( entry != null ) { final int i = findEntry( entry ); if ( i >= 0 ) { _entriesList.remove( i ); } } } /** * Returns an Iterator whose elements are all instances of * {@link URI}. Calling the remove() method on the iterator will * write through and change the URIPath. */ public Iterator iterator() { return _entriesList.iterator(); } //-------------------------------------------------------------------------- // Convenience methods... //-------------------------------------------------------------------------- /** * Given an {@link URI}, this method attempts to derive its relative * path with respect to this instance of URIPath. If * the specified {@link URI} does not point to a location that is * on this URIPath, then null is returned. */ public String toRelativePath( URI uri ) { if ( uri != null ) { final URI[] entries = getEntries(); for ( int i = 0, n = entries.length; i < n; i++ ) { final URI pathElem = entries[ i ]; final String relativePath = VirtualFileSystem.getVirtualFileSystem().toRelativeSpec( uri, pathElem, true ); if ( relativePath != null ) { return relativePath; } } } return null; } /** * Given a relative spec, this method attempts to construct a fully * qualified {@link URI} that points to the corresponding resource * on this URIPath. If no matching {@link URI} can be * constructed, then null is returned. An {@link URI} is * deemed to match iff the {@link URI} points to an existing resource. * In practical terms, it means that calling the method exists() * on {@link VirtualFileSystem} returns true. * * Note that toQualifiedURI will generally return null if the * desired resource only exists in memory (for example, if it is bound to * an unsaved {@link javax.ide.model.Document}). * @see VirtualFileSystem#exists */ public URI toQualifiedURI( String relativePath ) { if ( relativePath != null ) { final URI[] entries = getEntries(); for ( int i = 0; i < entries.length; i++ ) { final URI pathElem = entries[ i ]; final URI fullURI; // Must have a special case for jar URI, because its syntax is // opaque to the generic URI parser (see RFC 2396). if ( pathElem.getScheme().equals( VirtualFileSystem.JAR_SCHEME ) ) { fullURI = URIFactory.newJarURI( pathElem, relativePath ); } else { fullURI = URIFactory.newURI( pathElem, relativePath ); } if ( VirtualFileSystem.getVirtualFileSystem().exists( fullURI ) ) { return fullURI; } } } return null; } //-------------------------------------------------------------------------- // Object overrides... //-------------------------------------------------------------------------- /** * Returns true if the specified object equals this URI path. */ public boolean equals( Object o ) { if ( o == null || o.getClass() != getClass() ) { return false; } else { return equalsImpl( (URIPath) o ); } } /** * This is a helper method for {@link #equals(Object)} that can * also be used by subclasses that implement {@link #equals(Object)}. * It assumes that the argument is not null.

*/ protected final boolean equalsImpl( URIPath uriPath ) { if ( this == uriPath ) { return true; } if ( _entriesList.size() != uriPath._entriesList.size() ) { return false; } final Iterator iter1 = _entriesList.iterator(); final Iterator iter2 = uriPath._entriesList.iterator(); while ( iter1.hasNext() ) { final URI uri1 = (URI) iter1.next(); final URI uri2 = (URI) iter2.next(); if ( !VirtualFileSystem.getVirtualFileSystem().equals( uri1, uri2 ) ) { return false; } } return true; } public String toString() { final StringBuffer uriPath = new StringBuffer(); final URI[] entries = getEntries(); for ( int i = 0, n = entries.length; i < n; i++ ) { final URI uri = entries[ i ]; final String path = VirtualFileSystem.getVirtualFileSystem().getPlatformPathName( uri ); uriPath.append( path ).append( File.pathSeparatorChar ); } // Trim off the trailing File.pathSeparatorChar, if any. final int len = uriPath.length(); if ( len > 0 ) { uriPath.setLength( len - 1 ); } return uriPath.toString(); } public int hashCode() { return toString().hashCode(); } //-------------------------------------------------------------------------- // implementation details... //-------------------------------------------------------------------------- private int findEntry( URI entry ) { if ( entry != null ) { // We use VirtualFileSystem.equals() instead of List.contains() in order // to ensure that we control the equals comparison for URIs. final URI[] entries = getEntries(); for ( int i = 0, n = entries.length; i < n; i++ ) { if ( VirtualFileSystem.getVirtualFileSystem().equals( entry, entries[ i ] ) ) { return i; } } } return -1; } /** * Given a file path, create an URI representing that file. */ private URI pathToURI( String pathname ) { if ( URIFactory.isArchive( pathname ) ) { final URI uri = URIFactory.newFileURI( pathname ); return URIFactory.newJarURI( uri, "" ); //NOTRANS } else { final File dir = new File( pathname ); try { // Attempt to canonicalize the directory path first. return URIFactory.newDirURI( dir.getCanonicalPath() ); } catch ( IOException e ) { // If canonicalization fails for some reason, fall back on // the absolute path. return URIFactory.newDirURI( dir.getAbsolutePath() ); } } } }