/******************************************************************************* * Copyright (c) 2000, 2010 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.che.ide.ext.java.emul; import org.eclipse.che.ide.ext.java.jdt.core.compiler.CharOperation; import org.eclipse.che.ide.ext.java.jdt.internal.codeassist.ISearchRequestor; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.SuffixConstants; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; public class FileSystem implements INameEnvironment, SuffixConstants { 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 '.java' 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); 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); } protected 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); } public 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 { if (isPotentialZipArchive(classpathName)) { if (isSourceOnly) { // source only mode result = new ClasspathSourceJar(file, true, accessRuleSet, encoding, destinationPath == null || destinationPath == Main.NONE ? destinationPath : // keep == comparison valid convertPathSeparators(destinationPath)); } else if (destinationPath == null) { // class file only mode result = new ClasspathJar(file, true, accessRuleSet, null); } } } return result; } private static String convertPathSeparators(String path) { return File.separatorChar == '/' ? path.replace('\\', '/') : path.replace('/', '\\'); } /** * Returns whether the given name is potentially a zip archive file name * (it has a file extension and it is not ".java" nor ".class") */ public final static boolean isPotentialZipArchive(String name) { int lastDot = name.lastIndexOf('.'); if (lastDot == -1) { return false; // no file extension, it cannot be a zip archive name } if (name.lastIndexOf(File.separatorChar) > lastDot) { return false; // dot was before the last file separator, it cannot be a zip archive name } int length = name.length(); int extensionLength = length - lastDot - 1; if (extensionLength == EXTENSION_java.length()) { for (int i = extensionLength - 1; i >= 0; i--) { if (Character.toLowerCase(name.charAt(length - extensionLength + i)) != EXTENSION_java.charAt(i)) { break; // not a ".java" file, check ".class" file case below } if (i == 0) { return false; // it is a ".java" file, it cannot be a zip archive name } } } if (extensionLength == EXTENSION_class.length()) { for (int i = extensionLength - 1; i >= 0; i--) { if (Character.toLowerCase(name.charAt(length - extensionLength + i)) != EXTENSION_class.charAt(i)) { return true; // not a ".class" file, so this is a potential archive name } } return false; // it is a ".class" file, it cannot be a zip archive name } return true; // it is neither a ".java" file nor a ".class" file, so this is a potential archive name } 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; ) { File compilationUnitFile = new File(initialFileNames[i]); char[] fileName = null; try { fileName = compilationUnitFile.getCanonicalPath().toCharArray(); } catch (IOException e) { // this should not happen as the file exists continue; } char[] matchingPathName = null; final int lastIndexOf = CharOperation.lastIndexOf('.', fileName); if (lastIndexOf != -1) { fileName = CharOperation.subarray(fileName, 0, lastIndexOf); } CharOperation.replace(fileName, '\\', '/'); boolean globalPathMatches = false; // the most nested path should be the selected one for (int j = 0, max = this.classpaths.length; j < max; j++) { char[] matchCandidate = this.classpaths[j].normalizedPath(); boolean currentPathMatch = false; if (this.classpaths[j] instanceof ClasspathDirectory && CharOperation.prefixEquals(matchCandidate, fileName)) { currentPathMatch = true; if (matchingPathName == null) { matchingPathName = matchCandidate; } else { if (currentPathMatch) { // we have a second source folder that matches the path of the source file if (matchCandidate.length > matchingPathName.length) { // we want to preserve the shortest possible path matchingPathName = matchCandidate; } } else { // we want to preserve the shortest possible path if (!globalPathMatches && matchCandidate.length < matchingPathName.length) { matchingPathName = matchCandidate; } } } if (currentPathMatch) { globalPathMatches = true; } } } 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; } } @Override public void cleanup() { for (int i = 0, max = this.classpaths.length; i < max; i++) { this.classpaths[i].reset(); } } 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_class; String qualifiedPackageName = qualifiedTypeName.length() == typeName.length ? "" : 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 instanceof ClasspathJar) ? p.findClass(typeName, qualifiedPackageName, qualifiedBinaryFileName, asBinaryOnly) : 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; } @Override public NameEnvironmentAnswer findType(char[][] compoundName) { 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 instanceof ClasspathJar) ? p.findTypeNames(qualifiedPackageName) : 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; } @Override public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) { if (typeName != null) { return findClass(new String(CharOperation.concatWith(packageName, typeName, '/')), typeName, false); } return null; } @Override 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 instanceof ClasspathJar) ? p.isPackage(qualifiedPackageName) : p.isPackage(qp2)) { return true; } } } return false; } @Override public void findTypes(char[] qualifiedName, boolean b, boolean camelCaseMatch, int searchFor, final ISearchRequestor requestor) { String fqn = new String(qualifiedName); if(fqn.equals("AtomicBoolean")){ NameEnvironmentAnswer answer = findClass("java/util/concurrent/atomic/AtomicBoolean", qualifiedName, true); requestor.acceptType("java.util.concurrent.atomic".toCharArray(),qualifiedName, null, answer.getBinaryType().getModifiers(), null); } } @Override public void findPackages(char[] qualifiedName, ISearchRequestor requestor) { } @Override public void findConstructorDeclarations(char[] prefix, boolean camelCaseMatch, final ISearchRequestor requestor) { } @Override public void findExactTypes(char[] missingSimpleName, boolean b, int type, ISearchRequestor storage) { } @Override public void setProjectPath(String projectPath) { } @Override public void clearBlackList() { } public interface Classpath { char[][][] findTypeNames(String qualifiedPackageName); NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName); NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName, boolean asBinaryOnly); boolean isPackage(String qualifiedPackageName); /** * Return a list of the jar file names defined in the Class-Path section * of the jar file manifest if any, null else. Only ClasspathJar (and * extending classes) instances may return a non-null result. * * @param problemReporter * problem reporter with which potential * misconfiguration issues are raised * @return a list of the jar file names defined in the Class-Path * section of the jar file manifest if any */ List fetchLinkedJars(ClasspathSectionProblemReporter problemReporter); /** * 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 in which file separators are transformed to the * platform-agnostic '/', ending with a '/' for directories. This is an * absolute path in which file separators are transformed to the * platform-agnostic '/', 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; } public interface ClasspathSectionProblemReporter { void invalidClasspathSection(String jarFilePath); void multipleClasspathSections(String jarFilePath); } /** * 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(); if (!cache.contains(classpath)) { normalizedClasspath.add(classpath); cache.add(classpath); } } return normalizedClasspath; } } }