/*
* 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() ); } } } }