001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.batch;
011:
012: import java.io.File;
013: import java.io.IOException;
014: import java.util.ArrayList;
015: import java.util.HashSet;
016: import java.util.Iterator;
017: import java.util.Set;
018:
019: import org.eclipse.jdt.core.compiler.CharOperation;
020: import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
021: import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
022: import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
023: import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
024: import org.eclipse.jdt.internal.compiler.util.Util;
025:
026: public class FileSystem implements INameEnvironment, SuffixConstants {
027: public interface Classpath {
028: char[][][] findTypeNames(String qualifiedPackageName);
029:
030: NameEnvironmentAnswer findClass(char[] typeName,
031: String qualifiedPackageName,
032: String qualifiedBinaryFileName);
033:
034: NameEnvironmentAnswer findClass(char[] typeName,
035: String qualifiedPackageName,
036: String qualifiedBinaryFileName, boolean asBinaryOnly);
037:
038: boolean isPackage(String qualifiedPackageName);
039:
040: /**
041: * This method resets the environment. The resulting state is equivalent to
042: * a new name environment without creating a new object.
043: */
044: void reset();
045:
046: /**
047: * Return a normalized path for file based classpath entries. This is an absolute path
048: * ending with a file separator for directories, an absolute path deprived from the '.jar'
049: * (resp. '.zip') extension for jar (resp. zip) files.
050: * @return a normalized path for file based classpath entries
051: */
052: char[] normalizedPath();
053:
054: /**
055: * Return the path for file based classpath entries. This is an absolute path
056: * ending with a file separator for directories, an absolute path including the '.jar'
057: * (resp. '.zip') extension for jar (resp. zip) files.
058: * @return the path for file based classpath entries
059: */
060: String getPath();
061:
062: /**
063: * Initialize the entry
064: */
065: void initialize() throws IOException;
066: }
067:
068: /**
069: * This class is defined how to normalize the classpath entries.
070: * It removes duplicate entries.
071: */
072: public static class ClasspathNormalizer {
073: /**
074: * Returns the normalized classpath entries (no duplicate).
075: * <p>The given classpath entries are FileSystem.Classpath. We check the getPath() in order to find
076: * duplicate entries.</p>
077: *
078: * @param classpaths the given classpath entries
079: * @return the normalized classpath entries
080: */
081: public static ArrayList normalize(ArrayList classpaths) {
082: ArrayList normalizedClasspath = new ArrayList();
083: HashSet cache = new HashSet();
084: for (Iterator iterator = classpaths.iterator(); iterator
085: .hasNext();) {
086: FileSystem.Classpath classpath = (FileSystem.Classpath) iterator
087: .next();
088: String path = classpath.getPath();
089: if (!cache.contains(path)) {
090: normalizedClasspath.add(classpath);
091: cache.add(path);
092: }
093: }
094: return normalizedClasspath;
095: }
096: }
097:
098: Classpath[] classpaths;
099: Set knownFileNames;
100:
101: /*
102: classPathNames is a collection is Strings representing the full path of each class path
103: initialFileNames is a collection is Strings, the trailing '.java' will be removed if its not already.
104: */
105: public FileSystem(String[] classpathNames,
106: String[] initialFileNames, String encoding) {
107: final int classpathSize = classpathNames.length;
108: this .classpaths = new Classpath[classpathSize];
109: int counter = 0;
110: for (int i = 0; i < classpathSize; i++) {
111: Classpath classpath = getClasspath(classpathNames[i],
112: encoding, null);
113: try {
114: classpath.initialize();
115: this .classpaths[counter++] = classpath;
116: } catch (IOException e) {
117: // ignore
118: }
119: }
120: if (counter != classpathSize) {
121: System.arraycopy(this .classpaths, 0,
122: (this .classpaths = new Classpath[counter]), 0,
123: counter);
124: }
125: initializeKnownFileNames(initialFileNames);
126: }
127:
128: FileSystem(Classpath[] paths, String[] initialFileNames) {
129: final int length = paths.length;
130: int counter = 0;
131: this .classpaths = new FileSystem.Classpath[length];
132: for (int i = 0; i < length; i++) {
133: final Classpath classpath = paths[i];
134: try {
135: classpath.initialize();
136: this .classpaths[counter++] = classpath;
137: } catch (IOException exception) {
138: // ignore
139: }
140: }
141: if (counter != length) {
142: // should not happen
143: System
144: .arraycopy(
145: this .classpaths,
146: 0,
147: (this .classpaths = new FileSystem.Classpath[counter]),
148: 0, counter);
149: }
150: initializeKnownFileNames(initialFileNames);
151: }
152:
153: public static Classpath getClasspath(String classpathName,
154: String encoding, AccessRuleSet accessRuleSet) {
155: return getClasspath(classpathName, encoding, false,
156: accessRuleSet, null);
157: }
158:
159: public static Classpath getClasspath(String classpathName,
160: String encoding, boolean isSourceOnly,
161: AccessRuleSet accessRuleSet, String destinationPath) {
162: Classpath result = null;
163: File file = new File(convertPathSeparators(classpathName));
164: if (file.isDirectory()) {
165: if (file.exists()) {
166: result = new ClasspathDirectory(
167: file,
168: encoding,
169: isSourceOnly ? ClasspathLocation.SOURCE
170: : ClasspathLocation.SOURCE
171: | ClasspathLocation.BINARY,
172: accessRuleSet,
173: destinationPath == null
174: || destinationPath == Main.NONE ? destinationPath
175: : // keep == comparison valid
176: convertPathSeparators(destinationPath));
177: }
178: } else {
179: String lowercaseClasspathName = classpathName.toLowerCase();
180: if (lowercaseClasspathName.endsWith(SUFFIX_STRING_jar)
181: || lowercaseClasspathName
182: .endsWith(SUFFIX_STRING_zip)) {
183: if (isSourceOnly) {
184: // source only mode
185: result = new ClasspathSourceJar(
186: file,
187: true,
188: accessRuleSet,
189: encoding,
190: destinationPath == null
191: || destinationPath == Main.NONE ? destinationPath
192: : // keep == comparison valid
193: convertPathSeparators(destinationPath));
194: } else if (destinationPath == null) {
195: // class file only mode
196: result = new ClasspathJar(file, true,
197: accessRuleSet, null);
198: }
199: }
200: }
201: return result;
202: }
203:
204: private void initializeKnownFileNames(String[] initialFileNames) {
205: if (initialFileNames == null) {
206: this .knownFileNames = new HashSet(0);
207: return;
208: }
209: this .knownFileNames = new HashSet(initialFileNames.length * 2);
210: for (int i = initialFileNames.length; --i >= 0;) {
211: char[] fileName = initialFileNames[i].toCharArray();
212: char[] matchingPathName = null;
213: final int lastIndexOf = CharOperation.lastIndexOf('.',
214: fileName);
215: if (lastIndexOf != -1) {
216: fileName = CharOperation.subarray(fileName, 0,
217: lastIndexOf);
218: }
219: CharOperation.replace(fileName, '\\', '/');
220: for (int j = 0; j < classpaths.length; j++) {
221: char[] matchCandidate = this .classpaths[j]
222: .normalizedPath();
223: if (this .classpaths[j] instanceof ClasspathDirectory
224: && CharOperation.prefixEquals(matchCandidate,
225: fileName)
226: && (matchingPathName == null || matchCandidate.length < matchingPathName.length))
227: matchingPathName = matchCandidate;
228: }
229: if (matchingPathName == null) {
230: this .knownFileNames.add(new String(fileName)); // leave as is...
231: } else {
232: this .knownFileNames.add(new String(CharOperation
233: .subarray(fileName, matchingPathName.length,
234: fileName.length)));
235: }
236: matchingPathName = null;
237: }
238: }
239:
240: public void cleanup() {
241: for (int i = 0, max = this .classpaths.length; i < max; i++)
242: this .classpaths[i].reset();
243: }
244:
245: private static String convertPathSeparators(String path) {
246: return File.separatorChar == '/' ? path.replace('\\', '/')
247: : path.replace('/', '\\');
248: }
249:
250: private NameEnvironmentAnswer findClass(String qualifiedTypeName,
251: char[] typeName, boolean asBinaryOnly) {
252: if (this .knownFileNames.contains(qualifiedTypeName))
253: return null; // looking for a file which we know was provided at the beginning of the compilation
254:
255: String qualifiedBinaryFileName = qualifiedTypeName
256: + SUFFIX_STRING_class;
257: String qualifiedPackageName = qualifiedTypeName.length() == typeName.length ? Util.EMPTY_STRING
258: : qualifiedBinaryFileName.substring(0,
259: qualifiedTypeName.length() - typeName.length
260: - 1);
261: String qp2 = File.separatorChar == '/' ? qualifiedPackageName
262: : qualifiedPackageName.replace('/', File.separatorChar);
263: NameEnvironmentAnswer suggestedAnswer = null;
264: if (qualifiedPackageName == qp2) {
265: for (int i = 0, length = this .classpaths.length; i < length; i++) {
266: NameEnvironmentAnswer answer = this .classpaths[i]
267: .findClass(typeName, qualifiedPackageName,
268: qualifiedBinaryFileName, asBinaryOnly);
269: if (answer != null) {
270: if (!answer.ignoreIfBetter()) {
271: if (answer.isBetter(suggestedAnswer))
272: return answer;
273: } else if (answer.isBetter(suggestedAnswer))
274: // remember suggestion and keep looking
275: suggestedAnswer = answer;
276: }
277: }
278: } else {
279: String qb2 = qualifiedBinaryFileName.replace('/',
280: File.separatorChar);
281: for (int i = 0, length = this .classpaths.length; i < length; i++) {
282: Classpath p = this .classpaths[i];
283: NameEnvironmentAnswer answer = (p instanceof ClasspathJar) ? p
284: .findClass(typeName, qualifiedPackageName,
285: qualifiedBinaryFileName, asBinaryOnly)
286: : p.findClass(typeName, qp2, qb2, asBinaryOnly);
287: if (answer != null) {
288: if (!answer.ignoreIfBetter()) {
289: if (answer.isBetter(suggestedAnswer))
290: return answer;
291: } else if (answer.isBetter(suggestedAnswer))
292: // remember suggestion and keep looking
293: suggestedAnswer = answer;
294: }
295: }
296: }
297: if (suggestedAnswer != null)
298: // no better answer was found
299: return suggestedAnswer;
300: return null;
301: }
302:
303: public NameEnvironmentAnswer findType(char[][] compoundName) {
304: if (compoundName != null)
305: return findClass(new String(CharOperation.concatWith(
306: compoundName, '/')),
307: compoundName[compoundName.length - 1], false);
308: return null;
309: }
310:
311: public char[][][] findTypeNames(char[][] packageName) {
312: char[][][] result = null;
313: if (packageName != null) {
314: String qualifiedPackageName = new String(CharOperation
315: .concatWith(packageName, '/'));
316: String qualifiedPackageName2 = File.separatorChar == '/' ? qualifiedPackageName
317: : qualifiedPackageName.replace('/',
318: File.separatorChar);
319: if (qualifiedPackageName == qualifiedPackageName2) {
320: for (int i = 0, length = this .classpaths.length; i < length; i++) {
321: char[][][] answers = this .classpaths[i]
322: .findTypeNames(qualifiedPackageName);
323: if (answers != null) {
324: // concat with previous answers
325: if (result == null) {
326: result = answers;
327: } else {
328: int resultLength = result.length;
329: int answersLength = answers.length;
330: System.arraycopy(result, 0,
331: (result = new char[answersLength
332: + resultLength][][]), 0,
333: resultLength);
334: System.arraycopy(answers, 0, result,
335: resultLength, answersLength);
336: }
337: }
338: }
339: } else {
340: for (int i = 0, length = this .classpaths.length; i < length; i++) {
341: Classpath p = this .classpaths[i];
342: char[][][] answers = (p instanceof ClasspathJar) ? p
343: .findTypeNames(qualifiedPackageName)
344: : p.findTypeNames(qualifiedPackageName2);
345: if (answers != null) {
346: // concat with previous answers
347: if (result == null) {
348: result = answers;
349: } else {
350: int resultLength = result.length;
351: int answersLength = answers.length;
352: System.arraycopy(result, 0,
353: (result = new char[answersLength
354: + resultLength][][]), 0,
355: resultLength);
356: System.arraycopy(answers, 0, result,
357: resultLength, answersLength);
358: }
359: }
360: }
361: }
362: }
363: return result;
364: }
365:
366: public NameEnvironmentAnswer findType(char[][] compoundName,
367: boolean asBinaryOnly) {
368: if (compoundName != null)
369: return findClass(new String(CharOperation.concatWith(
370: compoundName, '/')),
371: compoundName[compoundName.length - 1], asBinaryOnly);
372: return null;
373: }
374:
375: public NameEnvironmentAnswer findType(char[] typeName,
376: char[][] packageName) {
377: if (typeName != null)
378: return findClass(new String(CharOperation.concatWith(
379: packageName, typeName, '/')), typeName, false);
380: return null;
381: }
382:
383: public boolean isPackage(char[][] compoundName, char[] packageName) {
384: String qualifiedPackageName = new String(CharOperation
385: .concatWith(compoundName, packageName, '/'));
386: String qp2 = File.separatorChar == '/' ? qualifiedPackageName
387: : qualifiedPackageName.replace('/', File.separatorChar);
388: if (qualifiedPackageName == qp2) {
389: for (int i = 0, length = this .classpaths.length; i < length; i++)
390: if (this .classpaths[i].isPackage(qualifiedPackageName))
391: return true;
392: } else {
393: for (int i = 0, length = this .classpaths.length; i < length; i++) {
394: Classpath p = this .classpaths[i];
395: if ((p instanceof ClasspathJar) ? p
396: .isPackage(qualifiedPackageName) : p
397: .isPackage(qp2))
398: return true;
399: }
400: }
401: return false;
402: }
403: }
|