/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.compiler.batch;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.wst.jsdt.core.compiler.CharOperation;
import org.eclipse.wst.jsdt.internal.compiler.env.AccessRuleSet;
import org.eclipse.wst.jsdt.internal.compiler.env.INameEnvironment;
import org.eclipse.wst.jsdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.wst.jsdt.internal.compiler.impl.ITypeRequestor;
import org.eclipse.wst.jsdt.internal.compiler.util.SuffixConstants;
import org.eclipse.wst.jsdt.internal.compiler.util.Util;
import org.eclipse.wst.jsdt.internal.oaametadata.IOAAMetaDataConstants;
public class FileSystem implements INameEnvironment, SuffixConstants {
public interface Classpath {
char[][][] findTypeNames(String qualifiedPackageName);
NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName);
boolean isPackage(String qualifiedPackageName);
NameEnvironmentAnswer findBinding(char[] typeName, String qualifiedPackageName, int type, ITypeRequestor requestor);
NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName, boolean asBinaryOnly);
/**
* This method resets the environment. The resulting state is equivalent to
* a new name environment without creating a new object.
*/
void reset();
/**
* Return a normalized path for file based classpath entries. This is an absolute path
* ending with a file separator for directories, an absolute path deprived from the '.jar'
* (resp. '.zip') extension for jar (resp. zip) files.
* @return a normalized path for file based classpath entries
*/
char[] normalizedPath();
/**
* Return the path for file based classpath entries. This is an absolute path
* ending with a file separator for directories, an absolute path including the '.jar'
* (resp. '.zip') extension for jar (resp. zip) files.
* @return the path for file based classpath entries
*/
String getPath();
/**
* Initialize the entry
*/
void initialize() throws IOException;
}
/**
* This class is defined how to normalize the classpath entries.
* It removes duplicate entries.
*/
public static class ClasspathNormalizer {
/**
* Returns the normalized classpath entries (no duplicate).
* <p>The given classpath entries are FileSystem.Classpath. We check the getPath() in order to find
* duplicate entries.</p>
*
* @param classpaths the given classpath entries
* @return the normalized classpath entries
*/
public static ArrayList normalize(ArrayList classpaths) {
ArrayList normalizedClasspath = new ArrayList();
HashSet cache = new HashSet();
for (Iterator iterator = classpaths.iterator(); iterator.hasNext(); ) {
FileSystem.Classpath classpath = (FileSystem.Classpath) iterator.next();
String path = classpath.getPath();
if (!cache.contains(path)) {
normalizedClasspath.add(classpath);
cache.add(path);
}
}
return normalizedClasspath;
}
}
Classpath[] classpaths;
Set knownFileNames;
/*
classPathNames is a collection is Strings representing the full path of each class path
initialFileNames is a collection is Strings, the trailing '.js' will be removed if its not already.
*/
public FileSystem(String[] classpathNames, String[] initialFileNames, String encoding) {
final int classpathSize = classpathNames.length;
this.classpaths = new Classpath[classpathSize];
int counter = 0;
for (int i = 0; i < classpathSize; i++) {
Classpath classpath = getClasspath(classpathNames[i], encoding, null);
if (classpath!=null) {
try {
classpath.initialize();
this.classpaths[counter++] = classpath;
} catch (IOException e) {
// ignore
}
}
}
if (counter != classpathSize) {
System.arraycopy(this.classpaths, 0, (this.classpaths = new Classpath[counter]), 0, counter);
}
initializeKnownFileNames(initialFileNames);
}
FileSystem(Classpath[] paths, String[] initialFileNames) {
final int length = paths.length;
int counter = 0;
this.classpaths = new FileSystem.Classpath[length];
for (int i = 0; i < length; i++) {
final Classpath classpath = paths[i];
try {
classpath.initialize();
this.classpaths[counter++] = classpath;
} catch(IOException exception) {
// ignore
}
}
if (counter != length) {
// should not happen
System.arraycopy(this.classpaths, 0, (this.classpaths = new FileSystem.Classpath[counter]), 0, counter);
}
initializeKnownFileNames(initialFileNames);
}
public static Classpath getClasspath(String classpathName, String encoding, AccessRuleSet accessRuleSet) {
return getClasspath(classpathName, encoding, false, accessRuleSet, null);
}
static Classpath getClasspath(String classpathName, String encoding,
boolean isSourceOnly, AccessRuleSet accessRuleSet,
String destinationPath) {
Classpath result = null;
File file = new File(convertPathSeparators(classpathName));
if (file.isDirectory()) {
if (file.exists()) {
// result = new ClasspathDirectory(file, encoding,
// isSourceOnly ? ClasspathLocation.SOURCE :
// ClasspathLocation.SOURCE | ClasspathLocation.BINARY,
// accessRuleSet,
// destinationPath == null || destinationPath == Main.NONE ?
// destinationPath : // keep == comparison valid
// convertPathSeparators(destinationPath));
}
} else {
String lowercaseClasspathName = classpathName.toLowerCase();
// if (lowercaseClasspathName.endsWith(SUFFIX_STRING_jar)
// || lowercaseClasspathName.endsWith(SUFFIX_STRING_zip)) {
// if (isSourceOnly) {
// // source only mode
// result = new ClasspathSourceJar(file, true, accessRuleSet,
// encoding,
// destinationPath == null || destinationPath == Main.NONE ?
// destinationPath : // keep == comparison valid
// convertPathSeparators(destinationPath));
// } else {
// // class file only mode
// if (destinationPath == null) {
// result = new ClasspathJar(file, true, accessRuleSet, null);
// }
// }
// }
// else
if (lowercaseClasspathName.endsWith(SUFFIX_STRING_java))
{
result=new ClasspathFile(file, encoding,accessRuleSet,destinationPath == null || destinationPath == Main.NONE ?
destinationPath : // keep == comparison valid
convertPathSeparators(destinationPath));
}
else if (lowercaseClasspathName.endsWith(IOAAMetaDataConstants.METADATA_FILE.toLowerCase()))
{
result=new ClasspathMetadataFile(file, encoding,accessRuleSet,destinationPath == null || destinationPath == Main.NONE ?
destinationPath : // keep == comparison valid
convertPathSeparators(destinationPath));
}
}
return result;
}
private void initializeKnownFileNames(String[] initialFileNames) {
if (initialFileNames == null) {
this.knownFileNames = new HashSet(0);
return;
}
this.knownFileNames = new HashSet(initialFileNames.length * 2);
for (int i = initialFileNames.length; --i >= 0;) {
char[] fileName = initialFileNames[i].toCharArray();
char[] matchingPathName = null;
final int lastIndexOf = CharOperation.lastIndexOf('.', fileName);
if (lastIndexOf != -1) {
fileName = CharOperation.subarray(fileName, 0, lastIndexOf);
}
CharOperation.replace(fileName, '\\', '/');
// for (int j = 0; j < classpaths.length; j++){
// char[] matchCandidate = this.classpaths[j].normalizedPath();
// if (this.classpaths[j] instanceof ClasspathDirectory &&
// CharOperation.prefixEquals(matchCandidate, fileName) &&
// (matchingPathName == null ||
// matchCandidate.length < matchingPathName.length))
// matchingPathName = matchCandidate;
// }
if (matchingPathName == null) {
this.knownFileNames.add(new String(fileName)); // leave as is...
}
else {
this.knownFileNames.add(new String(CharOperation.subarray(fileName, matchingPathName.length, fileName.length)));
}
matchingPathName = null;
}
}
public void cleanup() {
for (int i = 0, max = this.classpaths.length; i < max; i++)
this.classpaths[i].reset();
}
private static String convertPathSeparators(String path) {
return File.separatorChar == '/'
? path.replace('\\', '/')
: path.replace('/', '\\');
}
private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName, boolean asBinaryOnly){
if (this.knownFileNames.contains(qualifiedTypeName)) return null; // looking for a file which we know was provided at the beginning of the compilation
String qualifiedBinaryFileName = qualifiedTypeName + SUFFIX_STRING_java;
String qualifiedPackageName =
qualifiedTypeName.length() == typeName.length
? Util.EMPTY_STRING
: qualifiedBinaryFileName.substring(0, qualifiedTypeName.length() - typeName.length - 1);
String qp2 = File.separatorChar == '/' ? qualifiedPackageName : qualifiedPackageName.replace('/', File.separatorChar);
NameEnvironmentAnswer suggestedAnswer = null;
if (qualifiedPackageName == qp2) {
for (int i = 0, length = this.classpaths.length; i < length; i++) {
NameEnvironmentAnswer answer = this.classpaths[i].findClass(typeName, qualifiedPackageName, qualifiedBinaryFileName, asBinaryOnly);
if (answer != null) {
if (!answer.ignoreIfBetter()) {
if (answer.isBetter(suggestedAnswer))
return answer;
} else if (answer.isBetter(suggestedAnswer))
// remember suggestion and keep looking
suggestedAnswer = answer;
}
}
} else {
String qb2 = qualifiedBinaryFileName.replace('/', File.separatorChar);
for (int i = 0, length = this.classpaths.length; i < length; i++) {
Classpath p = this.classpaths[i];
NameEnvironmentAnswer answer = p.findClass(typeName, qp2, qb2, asBinaryOnly);
if (answer != null) {
if (!answer.ignoreIfBetter()) {
if (answer.isBetter(suggestedAnswer))
return answer;
} else if (answer.isBetter(suggestedAnswer))
// remember suggestion and keep looking
suggestedAnswer = answer;
}
}
}
if (suggestedAnswer != null)
// no better answer was found
return suggestedAnswer;
return null;
}
private NameEnvironmentAnswer findBinding(String qualifiedTypeName, char[] typeName, int type, ITypeRequestor requestor){
if (this.knownFileNames.contains(qualifiedTypeName)) return null; // looking for a file which we know was provided at the beginning of the compilation
String qualifiedPackageName =
qualifiedTypeName.length() == typeName.length
? "" //$NON-NLS-1$
: qualifiedTypeName.substring(0, qualifiedTypeName.length() - typeName.length - 1);
NameEnvironmentAnswer suggestedAnswer = null;
for (int i = 0, length = this.classpaths.length; i < length; i++) {
NameEnvironmentAnswer answer = this.classpaths[i].findBinding(typeName, qualifiedPackageName, type,requestor);
if (answer != null) {
if (!answer.ignoreIfBetter()) {
if (answer.isBetter(suggestedAnswer))
return answer;
} else if (answer.isBetter(suggestedAnswer))
// remember suggestion and keep looking
suggestedAnswer = answer;
}
}
if (suggestedAnswer != null)
// no better answer was found
return suggestedAnswer;
return null;
}
public NameEnvironmentAnswer findBinding(char[] typeName, char[][] packageName, int type, ITypeRequestor requestor, boolean returnMultiple, String excludePath) {
if (typeName != null)
return findBinding(
new String(CharOperation.concatWith(packageName, typeName, '/')),
typeName,type,requestor);
return null;
}
public NameEnvironmentAnswer findType(char[][] compoundName, ITypeRequestor requestor) {
if (compoundName != null)
return findClass(
new String(CharOperation.concatWith(compoundName, '/')),
compoundName[compoundName.length - 1],false);
return null;
}
public char[][][] findTypeNames(char[][] packageName) {
char[][][] result = null;
if (packageName != null) {
String qualifiedPackageName = new String(CharOperation.concatWith(packageName, '/'));
String qualifiedPackageName2 = File.separatorChar == '/' ? qualifiedPackageName : qualifiedPackageName.replace('/', File.separatorChar);
if (qualifiedPackageName == qualifiedPackageName2) {
for (int i = 0, length = this.classpaths.length; i < length; i++) {
char[][][] answers = this.classpaths[i].findTypeNames(qualifiedPackageName);
if (answers != null) {
// concat with previous answers
if (result == null) {
result = answers;
} else {
int resultLength = result.length;
int answersLength = answers.length;
System.arraycopy(result, 0, (result = new char[answersLength + resultLength][][]), 0, resultLength);
System.arraycopy(answers, 0, result, resultLength, answersLength);
}
}
}
} else {
for (int i = 0, length = this.classpaths.length; i < length; i++) {
Classpath p = this.classpaths[i];
char[][][] answers =p.findTypeNames(qualifiedPackageName2);
if (answers != null) {
// concat with previous answers
if (result == null) {
result = answers;
} else {
int resultLength = result.length;
int answersLength = answers.length;
System.arraycopy(result, 0, (result = new char[answersLength + resultLength][][]), 0, resultLength);
System.arraycopy(answers, 0, result, resultLength, answersLength);
}
}
}
}
}
return result;
}
public NameEnvironmentAnswer findType(char[][] compoundName, boolean asBinaryOnly) {
if (compoundName != null)
return findClass(
new String(CharOperation.concatWith(compoundName, '/')),
compoundName[compoundName.length - 1],
asBinaryOnly);
return null;
}
public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName, ITypeRequestor requestor) {
if (typeName != null)
return findClass(
new String(CharOperation.concatWith(packageName, typeName, '/')),
typeName,false);
return null;
}
public boolean isPackage(char[][] compoundName, char[] packageName) {
String qualifiedPackageName = new String(CharOperation.concatWith(compoundName, packageName, '/'));
String qp2 = File.separatorChar == '/' ? qualifiedPackageName : qualifiedPackageName.replace('/', File.separatorChar);
if (qualifiedPackageName == qp2) {
for (int i = 0, length = this.classpaths.length; i < length; i++)
if (this.classpaths[i].isPackage(qualifiedPackageName))
return true;
} else {
for (int i = 0, length = this.classpaths.length; i < length; i++) {
Classpath p = this.classpaths[i];
if ( p.isPackage(qp2))
return true;
}
}
return false;
}
}