// =========================================================================== // CONTENT : CLASS FileLocator // AUTHOR : Manfred Duchrow // VERSION : 1.3 - 14/03/2003 // HISTORY : // 17/05/2002 duma CREATED // 24/05/2002 duma added -> toURL(), isFile(), isDirectory(), getAbsolutePath() // 21/06/2002 duma added -> realFile() // 14/03/2003 duma added -> getStandardizedPath(), getStandardizedAbsolutePath() // // Copyright (c) 2002-2003, by Manfred Duchrow. All rights reserved. // =========================================================================== package org.pf.file ; // =========================================================================== // IMPORTS // =========================================================================== import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.pf.text.StringUtil; /** * This class mainly supports access to files which can be in the normal * file directory structure or inside zip archives. * The main purpose is to provide methods that transparently treat files * the same way whether they are in the normal directory structure or * inside archives. * The syntax is simply to allow archive names in a path name at any place * where a sub-directory name can be. <br> * Examples: <br> * <ul> * <li>d:\temp\archive.zip\config\nls.properties</li> * <li>/usr/java/jdk1.3/src.jar/java/io/File.java</li> * </ul> * @author Manfred Duchrow * @version 1.3 */ public class FileLocator { // ========================================================================= // CONSTANTS // ========================================================================= private static final boolean DEBUG = false ; private static final String FILE_PROTOCOL_INDICATOR = "file:" + File.separator ; private static final String ARCHIVE_INDICATOR = "!" + File.separator ; // ========================================================================= // INSTANCE VARIABLES // ========================================================================= private FileLocator parent = null ; protected FileLocator getParent() { return parent ; } protected void setParent( FileLocator newValue ) { parent = newValue ; } private File file = null ; protected File getFile() { return file ; } protected void setFile( File newValue ) { file = newValue ; } private ZipFile zipFile = null ; protected ZipFile getZipFile() { return zipFile ; } protected void setZipFile( ZipFile newValue ) { zipFile = newValue ; } private boolean exists = true ; protected boolean getExists() { return exists ; } protected void setExists( boolean newValue ) { exists = newValue ; } private Exception exception = null ; protected Exception getException() { return exception ; } protected void setException( Exception newValue ) { exception = newValue ; } // ========================================================================= // CLASS METHODS // ========================================================================= /** * Create a file locator that corresponds to the given file name. */ public static FileLocator create( File file ) { FileLocator locator = new FileLocator() ; return locator.createFrom( file ) ; } // create() // ------------------------------------------------------------------------- /** * Create a file locator that corresponds to the given file name. */ public static FileLocator create( String filename ) { return create( new File( filename ) ) ; } // create() // ------------------------------------------------------------------------- private static FileLocator newWith( FileLocator aParent, String[] pathElements ) throws Exception { FileLocator locator = new FileLocator() ; return locator.createFrom( aParent, pathElements ) ; } // newWith() // ------------------------------------------------------------------------- // ========================================================================= // CONSTRUCTORS // ========================================================================= /** * Initialize the new instance with default values. */ private FileLocator() { super() ; } // FileLocator() // ------------------------------------------------------------------------- // ========================================================================= // PUBLIC INSTANCE METHODS // ========================================================================= /** * Returns the file that contains the data the locator points to. * If the locator points to a normal file in a directory, than * this file will be returned. * If the locator points to a file inside an archive, the file * will be unzipped into the <i><b>temp</i></b> directory and this * temp file will be returned. * If the locator points to a none existing file, this method * returns false. */ public File realFile() { File aFile ; try { aFile = this.fileRef() ; } catch (Exception e) { aFile = null ; } return aFile ; } // realFile() // ------------------------------------------------------------------------- /** * Returns whether or not the file specified by this locator exists. */ public boolean exists() { return this.getExists() ; } // exists() // ------------------------------------------------------------------------- /** * Returns whether or not the name specified by this locator * points to a file. */ public boolean isFile() { try { if ( this.exists() ) return this.isFileElement( this.getFile() ) ; else return false ; } catch(Exception e) { return false ; } } // isFile() // ------------------------------------------------------------------------- /** * Returns whether or not the name specified by this locator * points to a directory. */ public boolean isDirectory() { try { if ( this.exists() ) return ! this.isFileElement( this.getFile() ) ; else return false ; } catch(Exception e) { return false ; } } // isDirectory() // ------------------------------------------------------------------------- /** * Returns the size of the file or 0 if it does not exist. */ public long size() { ZipEntry entry ; try { if ( this.isInArchive() ) { entry = this.archiveEntry() ; // if ( DEBUG ) com.mdcs.joi.Inspector.inspectWait( entry ) ; return entry.getSize() ; } else { return this.getFile().length() ; } } catch(Exception ex) { if ( DEBUG ) ex.printStackTrace() ; return 0L ; } } // size() // ------------------------------------------------------------------------- /** * Returns the timestamp of when the file was last modified * or 0 in any case of error. */ public long lastModified() { ZipEntry entry ; try { if ( this.isInArchive() ) { entry = this.archiveEntry() ; return entry.getTime() ; } else { return this.getFile().lastModified() ; } } catch(Exception ex) { if ( DEBUG ) ex.printStackTrace() ; return 0L ; } } // lastModified() // ------------------------------------------------------------------------- /** * Returns an opened input stream on the file defined by this locator. */ public InputStream getInputStream() throws Exception { ZipEntry entry ; if ( this.isInArchive() ) { entry = this.archiveEntry() ; return this.container().getInputStream( entry ) ; } else { return new FileInputStream( this.getFile() ) ; } } // getInputStream() // ------------------------------------------------------------------------- /** * Returns whether or not the file specified by this locator * is inside an archive. */ public boolean isInArchive() { return this.getParent() != null ; } // isInArchive() // ------------------------------------------------------------------------- /** * Returns the full pathname. */ public String getPath() { return this.fullFilePath( false ).getPath() ; } // getPath() // ------------------------------------------------------------------------- /** * Returns the full absolute pathname. */ public String getAbsolutePath() { return this.fullFilePath( true ).getPath() ; } // getAbsolutePath() // ------------------------------------------------------------------------- /** * Returns the full pathname in a standardized for. * That is all ".." and "." elements are removed and forward slashes are * used as separators of the remaining elements. */ public String getStandardizedPath() { return this.fileUtil().standardize( this.getPath() ) ; } // getStandardizedPath() // ------------------------------------------------------------------------- /** * Returns the full absolute pathname in a standardized form. * That is all ".." and "." elements are removed and forward slashes are * used as separators of the remaining elements. */ public String getStandardizedAbsolutePath() { return this.fileUtil().standardize( this.getAbsolutePath() ) ; } // getStandardizedAbsolutePath() // ------------------------------------------------------------------------- /** * Returns the last exception that occured while using this locator * or null, if no exception was thrown at all. */ public Exception exception() { return this.getException() ; } // exception() // ------------------------------------------------------------------------- /** * Returns the name of the file as an URL. */ public URL toURL() throws MalformedURLException { StringBuffer buffer = new StringBuffer( 128 ) ; this.urlPath( buffer ) ; return new URL( buffer.toString() ) ; } // toURL() // ------------------------------------------------------------------------- // ========================================================================= // PROTECTED INSTANCE METHODS // ========================================================================= protected FileLocator createFrom( File filePath ) { FileLocator locator = null ; String[] parts = null ; File path = filePath ; if ( path.getPath().startsWith( FILE_PROTOCOL_INDICATOR ) ) path = this.convertFromURLSyntax( path ) ; parts = str().parts( path.getPath(), File.separator ) ; try { locator = this.initFromPath( parts, path.getPath().startsWith( File.separator ) ) ; } catch ( Exception ex ) { this.setException( ex ) ; this.doesNotExist( path ) ; locator = this ; } return locator ; } // createFrom() // ------------------------------------------------------------------------- private FileLocator createFrom( FileLocator aParent, String[] pathElements ) throws Exception { this.setParent( aParent ) ; return this.initFromPath( pathElements, false ) ; } // createFrom() // ------------------------------------------------------------------------- protected FileLocator initFromPath( String[] parts, boolean startsFromRoot ) throws Exception { FileLocator locator = this ; File pathElement = null ; String[] rest = null ; boolean elementExists = false ; if ( startsFromRoot ) pathElement = new File( File.separator ) ; for ( int i = 0 ; i < parts.length ; i++ ) { if ( pathElement == null ) pathElement = new File( parts[i] ) ; else pathElement = new File( pathElement, parts[i] ) ; elementExists = this.doesElementExist( pathElement ) ; if ( elementExists ) { this.setFile( pathElement ) ; if ( this.isFileElement( pathElement ) ) { if ( DEBUG ) System.out.println( "Locator(" + pathElement + ")" ) ; if ( i < ( parts.length - 1 ) ) // Is not last element ? { rest = str().copyFrom( parts, i + 1 ) ; // if (DEBUG) com.mdcs.joi.Inspector.inspect( "SubLocator", rest ) ; locator = FileLocator.newWith( this, rest ) ; } break ; } } else { if ( this.isInArchive() ) { if ( i < ( parts.length - 1 ) ) // Is not last element ? { // Directories are not always identifiable individually in zip archives. // Therefore it must be accepted that they are not found. // So in such case no exception will be thrown. } else { throw new Exception( "\"" + pathElement.getPath() + "\" does not exist" ); } } else { throw new Exception( "\"" + pathElement.getPath() + "\" does not exist" ); } } } return locator ; } // initFromPath() // ------------------------------------------------------------------------- protected boolean doesElementExist( File element ) throws Exception { if ( this.isInArchive() ) { return doesElementExistInArchive( element.getPath() ) ; } else { return element.exists() ; } } // doesElementExist() // ------------------------------------------------------------------------- protected boolean isFileElement( File element ) throws Exception { if ( this.isInArchive() ) { return isFileInArchive( element.getPath() ) ; } else { return element.isFile() ; } } // isFileElement() // ------------------------------------------------------------------------- protected boolean doesElementExistInArchive( String elementName ) throws Exception { ZipEntry entry ; entry = this.entryFromArchive( elementName ) ; return ( entry != null ) ; } // doesElementExistInArchive() // ------------------------------------------------------------------------- protected boolean isFileInArchive( String elementName ) throws Exception { ZipEntry entry ; entry = this.entryFromArchive( elementName ) ; // Unfortunately entry.isDirectory() returns false even for // pure directory entries inside a zip archive, so it can't be used here. // The trick below is problematic, because apart from // directories it will also not recognize files with size 0. return ( entry != null ) && ( entry.getSize() > 0 ) ; } // isFileInArchive() // ------------------------------------------------------------------------- protected ZipEntry entryFromArchive( String elementName ) throws Exception { ZipEntry entry ; ZipFile archive ; String name ; name = str().replaceAll( elementName, "\\", "/" ) ; archive = this.container() ; entry = archive.getEntry( name ) ; if (DEBUG) { // if ( entry == null ) com.mdcs.joi.Inspector.inspect( name ) ; System.out.print( archive.getName() + "::" + name + " --- " + ( entry != null ) ) ; if ( entry == null ) { System.out.println() ; } else { System.out.print( " (" + entry.getSize() + ")" ) ; System.out.print( " (T:" + entry.getTime() + ")" ) ; System.out.println( " (" + ( entry.isDirectory() ? "Dir" : "File" ) + ")" ) ; } } return entry ; } // entryFromArchive() // ------------------------------------------------------------------------- protected ZipEntry archiveEntry() throws Exception { return this.entryFromArchive( this.getFile().getPath() ) ; } // archiveEntry() // ------------------------------------------------------------------------- protected void doesNotExist( File file ) { this.setExists( false ) ; this.setFile( file ) ; } // doesNoTExist() // ------------------------------------------------------------------------- protected File fullFilePath( boolean absolute ) { File full ; if ( this.isInArchive() ) { full = new File( this.getParent().fullFilePath( absolute ), this.getFile().getPath() ) ; } else { if ( absolute ) full = this.getFile().getAbsoluteFile() ; else full = this.getFile() ; } return full ; } // fullFilePath() // ------------------------------------------------------------------------- protected void urlPath( StringBuffer buffer ) { if ( this.isInArchive() ) { this.getParent().urlPath( buffer ) ; buffer.append( ARCHIVE_INDICATOR ) ; } else { buffer.append( FILE_PROTOCOL_INDICATOR ) ; } buffer.append( this.getFile().getPath() ) ; } // urlPath() // ------------------------------------------------------------------------- protected File fileRef() throws Exception { InputStream archiveStream ; FileOutputStream fileStream ; ZipEntry entry ; File tempFile ; if ( this.isInArchive() ) { entry = this.archiveEntry() ; archiveStream = this.container().getInputStream( entry ) ; tempFile = File.createTempFile( "FLOC_", ".xtr" ) ; tempFile.deleteOnExit() ; fileStream = new FileOutputStream( tempFile ) ; fileUtil().copyStream( archiveStream, fileStream ) ; return tempFile ; } else { return this.getFile() ; } } // fileRef() // ------------------------------------------------------------------------- /** * Returns the file this locator presents as opened zip file or * null in any case of error. */ protected ZipFile archive() throws Exception { if ( this.getZipFile() == null ) { this.setZipFile( new ZipFile( this.fileRef() ) ) ; } return this.getZipFile() ; } // archive() // ------------------------------------------------------------------------- /** * Returns the zip file which is presented by the parent container * or null in any case of error. */ protected ZipFile container() throws Exception { if ( this.isInArchive() ) return this.getParent().archive() ; else return null ; } // container() // ------------------------------------------------------------------------- protected File convertFromURLSyntax( File file) { String newStr ; newStr = file.getPath().substring( FILE_PROTOCOL_INDICATOR.length() ) ; newStr = str().replaceAll( newStr, ARCHIVE_INDICATOR, File.separator ) ; return new File( newStr ) ; } // convertFromURLSyntax() // ------------------------------------------------------------------------- protected StringUtil str() { return StringUtil.current() ; } // str() // ------------------------------------------------------------------------- protected FileUtil fileUtil() { return FileUtil.current() ; } // fileUtil() // ------------------------------------------------------------------------- } // class FileLocator