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: * Matt Chapman, mpchapman@gmail.com - 89977 Make JDT .java agnostic
011: *******************************************************************************/package org.eclipse.jdt.internal.ui.wizards;
012:
013: import com.ibm.icu.text.Collator;
014:
015: import java.io.IOException;
016: import java.io.InputStream;
017: import java.util.ArrayList;
018: import java.util.Collections;
019: import java.util.Comparator;
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Set;
025:
026: import org.eclipse.core.runtime.CoreException;
027: import org.eclipse.core.runtime.IPath;
028: import org.eclipse.core.runtime.IProgressMonitor;
029: import org.eclipse.core.runtime.IStatus;
030: import org.eclipse.core.runtime.NullProgressMonitor;
031: import org.eclipse.core.runtime.OperationCanceledException;
032: import org.eclipse.core.runtime.Path;
033: import org.eclipse.core.runtime.Status;
034:
035: import org.eclipse.core.resources.IFile;
036: import org.eclipse.core.resources.IProject;
037: import org.eclipse.core.resources.IResource;
038: import org.eclipse.core.resources.IResourceProxy;
039: import org.eclipse.core.resources.IResourceProxyVisitor;
040:
041: import org.eclipse.jdt.core.IClasspathEntry;
042: import org.eclipse.jdt.core.ICompilationUnit;
043: import org.eclipse.jdt.core.JavaConventions;
044: import org.eclipse.jdt.core.JavaCore;
045: import org.eclipse.jdt.core.JavaModelException;
046: import org.eclipse.jdt.core.ToolFactory;
047: import org.eclipse.jdt.core.compiler.CharOperation;
048: import org.eclipse.jdt.core.compiler.IScanner;
049: import org.eclipse.jdt.core.compiler.ITerminalSymbols;
050: import org.eclipse.jdt.core.compiler.InvalidInputException;
051: import org.eclipse.jdt.core.util.IClassFileReader;
052: import org.eclipse.jdt.core.util.ISourceAttribute;
053:
054: import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
055: import org.eclipse.jdt.internal.corext.util.Messages;
056:
057: import org.eclipse.jdt.ui.PreferenceConstants;
058:
059: import org.eclipse.jdt.internal.ui.JavaPlugin;
060:
061: /**
062: */
063: public class ClassPathDetector implements IResourceProxyVisitor {
064:
065: private HashMap fSourceFolders;
066: private List fClassFiles;
067: private HashSet fJARFiles;
068:
069: private IProject fProject;
070:
071: private IPath fResultOutputFolder;
072: private IClasspathEntry[] fResultClasspath;
073:
074: private IProgressMonitor fMonitor;
075:
076: private static class CPSorter implements Comparator {
077: private Collator fCollator = Collator.getInstance();
078:
079: public int compare(Object o1, Object o2) {
080: IClasspathEntry e1 = (IClasspathEntry) o1;
081: IClasspathEntry e2 = (IClasspathEntry) o2;
082: return fCollator.compare(e1.getPath().toString(), e2
083: .getPath().toString());
084: }
085: }
086:
087: public ClassPathDetector(IProject project, IProgressMonitor monitor)
088: throws CoreException {
089: fSourceFolders = new HashMap();
090: fJARFiles = new HashSet(10);
091: fClassFiles = new ArrayList(100);
092: fProject = project;
093:
094: fResultClasspath = null;
095: fResultOutputFolder = null;
096:
097: if (monitor == null) {
098: monitor = new NullProgressMonitor();
099: }
100:
101: detectClasspath(monitor);
102: }
103:
104: private boolean isNested(IPath path, Iterator iter) {
105: while (iter.hasNext()) {
106: IPath other = (IPath) iter.next();
107: if (other.isPrefixOf(path)) {
108: return true;
109: }
110: }
111: return false;
112: }
113:
114: /**
115: * Method detectClasspath.
116: * @param monitor The progress monitor (not null)
117: * @throws CoreException
118: */
119: private void detectClasspath(IProgressMonitor monitor)
120: throws CoreException {
121: try {
122: monitor
123: .beginTask(
124: NewWizardMessages.ClassPathDetector_operation_description,
125: 4);
126:
127: fMonitor = monitor;
128: fProject.accept(this , IResource.NONE);
129: monitor.worked(1);
130:
131: ArrayList cpEntries = new ArrayList();
132:
133: detectSourceFolders(cpEntries);
134: if (monitor.isCanceled()) {
135: throw new OperationCanceledException();
136: }
137: monitor.worked(1);
138:
139: IPath outputLocation = detectOutputFolder(cpEntries);
140: if (monitor.isCanceled()) {
141: throw new OperationCanceledException();
142: }
143: monitor.worked(1);
144:
145: detectLibraries(cpEntries, outputLocation);
146: if (monitor.isCanceled()) {
147: throw new OperationCanceledException();
148: }
149: monitor.worked(1);
150:
151: if (cpEntries.isEmpty() && fClassFiles.isEmpty()) {
152: return;
153: }
154: IClasspathEntry[] jreEntries = PreferenceConstants
155: .getDefaultJRELibrary();
156: for (int i = 0; i < jreEntries.length; i++) {
157: cpEntries.add(jreEntries[i]);
158: }
159:
160: IClasspathEntry[] entries = (IClasspathEntry[]) cpEntries
161: .toArray(new IClasspathEntry[cpEntries.size()]);
162: if (!JavaConventions.validateClasspath(
163: JavaCore.create(fProject), entries, outputLocation)
164: .isOK()) {
165: return;
166: }
167:
168: fResultClasspath = entries;
169: fResultOutputFolder = outputLocation;
170: } finally {
171: monitor.done();
172: }
173: }
174:
175: private IPath findInSourceFolders(IPath path) {
176: Iterator iter = fSourceFolders.keySet().iterator();
177: while (iter.hasNext()) {
178: Object key = iter.next();
179: List cus = (List) fSourceFolders.get(key);
180: if (cus.contains(path)) {
181: return (IPath) key;
182: }
183: }
184: return null;
185: }
186:
187: private IPath detectOutputFolder(List entries) throws CoreException {
188: HashSet classFolders = new HashSet();
189:
190: for (Iterator iter = fClassFiles.iterator(); iter.hasNext();) {
191: IFile file = (IFile) iter.next();
192: IClassFileReader reader = null;
193: InputStream content = null;
194: try {
195: content = file.getContents();
196: reader = ToolFactory.createDefaultClassFileReader(
197: content, IClassFileReader.CLASSFILE_ATTRIBUTES);
198: } finally {
199: try {
200: if (content != null)
201: content.close();
202: } catch (IOException e) {
203: throw new CoreException(
204: new Status(
205: IStatus.ERROR,
206: JavaPlugin.getPluginId(),
207: IStatus.ERROR,
208: Messages
209: .format(
210: NewWizardMessages.ClassPathDetector_error_closing_file,
211: file.getFullPath()
212: .toString()),
213: e));
214: }
215: }
216: if (reader == null) {
217: continue; // problematic class file
218: }
219: char[] className = reader.getClassName();
220: ISourceAttribute sourceAttribute = reader
221: .getSourceFileAttribute();
222: if (className != null && sourceAttribute != null
223: && sourceAttribute.getSourceFileName() != null) {
224: IPath packPath = file.getParent().getFullPath();
225: int idx = CharOperation.lastIndexOf('/', className) + 1;
226: IPath relPath = new Path(new String(className, 0, idx));
227: IPath cuPath = relPath.append(new String(
228: sourceAttribute.getSourceFileName()));
229:
230: IPath resPath = null;
231: if (idx == 0) {
232: resPath = packPath;
233: } else {
234: IPath folderPath = getFolderPath(packPath, relPath);
235: if (folderPath != null) {
236: resPath = folderPath;
237: }
238: }
239: if (resPath != null) {
240: IPath path = findInSourceFolders(cuPath);
241: if (path != null) {
242: return resPath;
243: } else {
244: classFolders.add(resPath);
245: }
246: }
247: }
248: }
249: IPath projPath = fProject.getFullPath();
250: if (fSourceFolders.size() == 1 && classFolders.isEmpty()
251: && fSourceFolders.get(projPath) != null) {
252: return projPath;
253: } else {
254: IPath path = projPath.append(PreferenceConstants
255: .getPreferenceStore().getString(
256: PreferenceConstants.SRCBIN_BINNAME));
257: while (classFolders.contains(path)) {
258: path = new Path(path.toString() + '1');
259: }
260: return path;
261: }
262: }
263:
264: private void detectLibraries(ArrayList cpEntries,
265: IPath outputLocation) {
266: ArrayList res = new ArrayList();
267: Set sourceFolderSet = fSourceFolders.keySet();
268: for (Iterator iter = fJARFiles.iterator(); iter.hasNext();) {
269: IPath path = (IPath) iter.next();
270: if (isNested(path, sourceFolderSet.iterator())) {
271: continue;
272: }
273: if (outputLocation != null
274: && outputLocation.isPrefixOf(path)) {
275: continue;
276: }
277: IClasspathEntry entry = JavaCore.newLibraryEntry(path,
278: null, null);
279: res.add(entry);
280: }
281: Collections.sort(res, new CPSorter());
282: cpEntries.addAll(res);
283: }
284:
285: private void detectSourceFolders(ArrayList resEntries) {
286: ArrayList res = new ArrayList();
287: Set sourceFolderSet = fSourceFolders.keySet();
288: for (Iterator iter = sourceFolderSet.iterator(); iter.hasNext();) {
289: IPath path = (IPath) iter.next();
290: ArrayList excluded = new ArrayList();
291: for (Iterator inner = sourceFolderSet.iterator(); inner
292: .hasNext();) {
293: IPath other = (IPath) inner.next();
294: if (!path.equals(other) && path.isPrefixOf(other)) {
295: IPath pathToExclude = other.removeFirstSegments(
296: path.segmentCount()).addTrailingSeparator();
297: excluded.add(pathToExclude);
298: }
299: }
300: IPath[] excludedPaths = (IPath[]) excluded
301: .toArray(new IPath[excluded.size()]);
302: IClasspathEntry entry = JavaCore.newSourceEntry(path,
303: excludedPaths);
304: res.add(entry);
305: }
306: Collections.sort(res, new CPSorter());
307: resEntries.addAll(res);
308: }
309:
310: private void visitCompilationUnit(IFile file) {
311: ICompilationUnit cu = JavaCore.createCompilationUnitFrom(file);
312: if (cu != null) {
313: ICompilationUnit workingCopy = null;
314: try {
315: workingCopy = cu.getWorkingCopy(null);
316: IPath relPath = getPackagePath(workingCopy.getSource());
317: IPath packPath = file.getParent().getFullPath();
318: String cuName = file.getName();
319: if (relPath == null) {
320: addToMap(fSourceFolders, packPath, new Path(cuName));
321: } else {
322: IPath folderPath = getFolderPath(packPath, relPath);
323: if (folderPath != null) {
324: addToMap(fSourceFolders, folderPath, relPath
325: .append(cuName));
326: }
327: }
328: } catch (JavaModelException e) {
329: // ignore
330: } catch (InvalidInputException e) {
331: // ignore
332: } finally {
333: if (workingCopy != null) {
334: try {
335: workingCopy.discardWorkingCopy();
336: } catch (JavaModelException ignore) {
337: }
338: }
339: }
340: }
341: }
342:
343: private IPath getPackagePath(String source)
344: throws InvalidInputException {
345: IScanner scanner = ToolFactory.createScanner(false, false,
346: false, false);
347: scanner.setSource(source.toCharArray());
348: scanner.resetTo(0, source.length() - 1);
349: int tok = scanner.getNextToken();
350: if (tok != ITerminalSymbols.TokenNamepackage) {
351: return null;
352: }
353: IPath res = Path.EMPTY;
354: do {
355: tok = scanner.getNextToken();
356: if (tok == ITerminalSymbols.TokenNameIdentifier) {
357: res = res.append(new String(scanner
358: .getCurrentTokenSource()));
359: } else {
360: return res;
361: }
362: tok = scanner.getNextToken();
363: } while (tok == ITerminalSymbols.TokenNameDOT);
364:
365: return res;
366: }
367:
368: private void addToMap(HashMap map, IPath folderPath, IPath relPath) {
369: List list = (List) map.get(folderPath);
370: if (list == null) {
371: list = new ArrayList(50);
372: map.put(folderPath, list);
373: }
374: list.add(relPath);
375: }
376:
377: private IPath getFolderPath(IPath packPath, IPath relpath) {
378: int remainingSegments = packPath.segmentCount()
379: - relpath.segmentCount();
380: if (remainingSegments >= 0) {
381: IPath common = packPath
382: .removeFirstSegments(remainingSegments);
383: if (common.equals(relpath)) {
384: return packPath.uptoSegment(remainingSegments);
385: }
386: }
387: return null;
388: }
389:
390: private boolean hasExtension(String name, String ext) {
391: return name.endsWith(ext) && (ext.length() != name.length());
392: }
393:
394: private boolean isValidCUName(String name) {
395: return !JavaConventionsUtil.validateCompilationUnitName(name,
396: JavaCore.create(fProject)).matches(IStatus.ERROR);
397: }
398:
399: /* (non-Javadoc)
400: * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy)
401: */
402: public boolean visit(IResourceProxy proxy) {
403: if (fMonitor.isCanceled()) {
404: throw new OperationCanceledException();
405: }
406:
407: if (proxy.getType() == IResource.FILE) {
408: String name = proxy.getName();
409: if (isValidCUName(name)) {
410: visitCompilationUnit((IFile) proxy.requestResource());
411: } else if (hasExtension(name, ".class")) { //$NON-NLS-1$
412: fClassFiles.add(proxy.requestResource());
413: } else if (hasExtension(name, ".jar")) { //$NON-NLS-1$
414: fJARFiles.add(proxy.requestFullPath());
415: }
416: return false;
417: }
418: return true;
419: }
420:
421: public IPath getOutputLocation() {
422: return fResultOutputFolder;
423: }
424:
425: public IClasspathEntry[] getClasspath() {
426: if (fResultClasspath == null)
427: return new IClasspathEntry[0];
428: return fResultClasspath;
429: }
430: }
|