/******************************************************************************* * Copyright (c) 2004, 2012 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.jdt.internal.core; import org.eclipse.che.jdt.internal.core.util.Util; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.core.JavaModelStatus; import org.eclipse.jdt.internal.core.util.MementoTokenizer; import java.io.File; import java.util.ArrayList; import java.util.Map; /** * @author Evgen Vidolob */ public class PackageFragmentRoot extends Openable implements IPackageFragmentRoot { /* * No source attachment property */ public final static String NO_SOURCE_ATTACHMENT = ""; //$NON-NLS-1$ /** * The delimiter between the source path and root path in the * attachment server property. */ protected final static char ATTACHMENT_PROPERTY_DELIMITER = '*'; private final IPath path; protected PackageFragmentRoot(File folder, JavaProject project, JavaModelManager manager) { super(project, manager); path = new Path(folder.getPath()); } /* * Returns the exclusion patterns from the classpath entry associated with this root. */ public char[][] fullExclusionPatternChars() { try { if (getKind() != IPackageFragmentRoot.K_SOURCE) return null; ClasspathEntry entry = (ClasspathEntry)getRawClasspathEntry(); if (entry == null) { return null; } else { return entry.fullExclusionPatternChars(); } } catch (JavaModelException e) { return null; } } /* * Returns the inclusion patterns from the classpath entry associated with this root. */ public char[][] fullInclusionPatternChars() { try { if (getKind() != IPackageFragmentRoot.K_SOURCE) return null; ClasspathEntry entry = (ClasspathEntry)getRawClasspathEntry(); if (entry == null) { return null; } else { return entry.fullInclusionPatternChars(); } } catch (JavaModelException e) { return null; } } @Override public void attachSource(IPath sourcePath, IPath rootPath, IProgressMonitor monitor) throws JavaModelException { } @Override public void copy(IPath destination, int updateResourceFlags, int updateModelFlags, IClasspathEntry sibling, IProgressMonitor monitor) throws JavaModelException { } @Override public IPackageFragment createPackageFragment(String name, boolean force, IProgressMonitor monitor) throws JavaModelException { return null; } @Override public void delete(int updateResourceFlags, int updateModelFlags, IProgressMonitor monitor) throws JavaModelException { } @Override public int getKind() throws JavaModelException { return IPackageFragmentRoot.K_SOURCE; } @Override public Object[] getNonJavaResources() throws JavaModelException { return new Object[0]; } @Override public IPackageFragment getPackageFragment(String packageName) { // tolerate package names with spaces (e.g. 'x . y') (http://bugs.eclipse.org/bugs/show_bug.cgi?id=21957) String[] pkgName = org.eclipse.jdt.internal.core.util.Util.getTrimmedSimpleNames(packageName); return getPackageFragment(pkgName); } @Override public IClasspathEntry getRawClasspathEntry() throws JavaModelException { IClasspathEntry rawEntry = null; JavaProject project = (JavaProject)getJavaProject(); project.getResolvedClasspath(); // force the reverse rawEntry cache to be populated Map rootPathToRawEntries = project.resolvedClasspath().rawReverseMap; if (rootPathToRawEntries != null) { rawEntry = (IClasspathEntry)rootPathToRawEntries.get(getPath()); } if (rawEntry == null) { throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH, this)); } return rawEntry; } @Override public IClasspathEntry getResolvedClasspathEntry() throws JavaModelException { IClasspathEntry rawEntry = null; JavaProject project = (JavaProject)getJavaProject(); project.getResolvedClasspath(); // force the reverse rawEntry cache to be populated Map rootPathToRawEntries = project.resolvedClasspath().rootPathToResolvedEntries; if (rootPathToRawEntries != null) { rawEntry = (IClasspathEntry)rootPathToRawEntries.get(getPath()); } if (rawEntry == null) { throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH, this)); } return rawEntry; } @Override public IPath getSourceAttachmentPath() throws JavaModelException { if (getKind() != K_BINARY) return null; // // 1) look source attachment property (set iff attachSource(...) was called // IPath path = getPath(); // String serverPathString= Util.getSourceAttachmentProperty(path); // if (serverPathString != null) { // int index= serverPathString.lastIndexOf(ATTACHMENT_PROPERTY_DELIMITER); // if (index < 0) { // // no root path specified // return new Path(serverPathString); // } else { // String serverSourcePathString= serverPathString.substring(0, index); // return new Path(serverSourcePathString); // } // } // 2) look at classpath entry IClasspathEntry entry = ((JavaProject)getParent()).getClasspathEntryFor(path); IPath sourceAttachmentPath; if (entry != null && (sourceAttachmentPath = entry.getSourceAttachmentPath()) != null) return sourceAttachmentPath; // 3) look for a recommendation entry = findSourceAttachmentRecommendation(); if (entry != null && (sourceAttachmentPath = entry.getSourceAttachmentPath()) != null) { return sourceAttachmentPath; } return null; } @Override public IPath getSourceAttachmentRootPath() throws JavaModelException { if (getKind() != K_BINARY) return null; // // 1) look source attachment property (set iff attachSource(...) was called // IPath path = getPath(); // String serverPathString= Util.getSourceAttachmentProperty(path); // if (serverPathString != null) { // int index = serverPathString.lastIndexOf(ATTACHMENT_PROPERTY_DELIMITER); // if (index == -1) return null; // String serverRootPathString= IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH; // if (index != serverPathString.length() - 1) { // serverRootPathString= serverPathString.substring(index + 1); // } // return new Path(serverRootPathString); // } // 2) look at classpath entry IClasspathEntry entry = ((JavaProject)getParent()).getClasspathEntryFor(path); IPath sourceAttachmentRootPath; if (entry != null && (sourceAttachmentRootPath = entry.getSourceAttachmentRootPath()) != null) return sourceAttachmentRootPath; // 3) look for a recomendation entry = findSourceAttachmentRecommendation(); if (entry != null && (sourceAttachmentRootPath = entry.getSourceAttachmentRootPath()) != null) return sourceAttachmentRootPath; return null; } private IClasspathEntry findSourceAttachmentRecommendation() { // try { // IPath rootPath = getPath(); // IClasspathEntry entry; // // // try on enclosing project first // JavaProject parentProject = (JavaProject) getJavaProject(); // try { // entry = parentProject.getClasspathEntryFor(rootPath); // if (entry != null) { // Object target = JavaModel.getTarget(entry.getSourceAttachmentPath(), true); // if (target != null) { // return entry; // } // } // } catch(JavaModelException e){ // // ignore // } // // // iterate over all projects // IJavaModel model = getJavaModel(); // IJavaProject[] jProjects = model.getJavaProjects(); // for (int i = 0, max = jProjects.length; i < max; i++){ // JavaProject jProject = (JavaProject) jProjects[i]; // if (jProject == parentProject) continue; // already done // try { // entry = jProject.getClasspathEntryFor(rootPath); // if (entry != null){ // Object target = JavaModel.getTarget(entry.getSourceAttachmentPath(), true); // if (target != null) { // return entry; // } // } // } catch(JavaModelException e){ // // ignore // } // } // } catch(JavaModelException e){ // // ignore // } return null; } @Override public boolean isArchive() { return false; } @Override public boolean isExternal() { return false; } @Override public void move(IPath destination, int updateResourceFlags, int updateModelFlags, IClasspathEntry sibling, IProgressMonitor monitor) throws JavaModelException { } @Override protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, File underlyingResource) throws JavaModelException { ((PackageFragmentRootInfo)info).setRootKind(determineKind(underlyingResource)); return computeChildren(info, underlyingResource); } /** * Returns the root's kind - K_SOURCE or K_BINARY, defaults * to K_SOURCE if it is not on the classpath. * * @throws JavaModelException * if the project and root do * not exist. */ protected int determineKind(File underlyingResource) throws JavaModelException { IClasspathEntry entry = ((JavaProject)getJavaProject()).getClasspathEntryFor(new Path(underlyingResource.getAbsolutePath())); if (entry != null) { return entry.getContentKind(); } return IPackageFragmentRoot.K_SOURCE; } public PackageFragment getPackageFragment(String[] pkgName) { return new PackageFragment(this, manager, pkgName); } /** * Compute the package fragment children of this package fragment root. * * @throws JavaModelException * The resource associated with this package fragment root does not exist */ protected boolean computeChildren(OpenableElementInfo info, File underlyingResource) throws JavaModelException { // Note the children are not opened (so not added to newElements) for a regular package fragment root // However they are opened for a Jar package fragment root (see JarPackageFragmentRoot#computeChildren) try { // the underlying resource may be a folder or a project (in the case that the project folder // is actually the package fragment root) if (underlyingResource.isDirectory() || underlyingResource.isFile()) { ArrayList vChildren = new ArrayList(5); // IContainer rootFolder = (IContainer) underlyingResource; char[][] inclusionPatterns = fullInclusionPatternChars(); char[][] exclusionPatterns = fullExclusionPatternChars(); computeFolderChildren(underlyingResource, !Util.isExcluded(new Path(underlyingResource.getAbsolutePath()), inclusionPatterns, exclusionPatterns, true), CharOperation.NO_STRINGS, vChildren, inclusionPatterns, exclusionPatterns); IJavaElement[] children = new IJavaElement[vChildren.size()]; vChildren.toArray(children); info.setChildren(children); } } catch (JavaModelException e) { //problem resolving children; structure remains unknown info.setChildren(new IJavaElement[]{}); throw e; } return true; } /** * Starting at this folder, create package fragments and add the fragments that are not exclused * to the collection of children. * * @throws JavaModelException * The resource associated with this package fragment does not exist */ protected void computeFolderChildren(File folder, boolean isIncluded, String[] pkgName, ArrayList vChildren, char[][] inclusionPatterns, char[][] exclusionPatterns) throws JavaModelException { if (isIncluded) { IPackageFragment pkg = getPackageFragment(pkgName); vChildren.add(pkg); } try { File[] members = folder.listFiles(); boolean hasIncluded = isIncluded; int length = members.length; if (length > 0) { // if package fragment root refers to folder in another IProject, then // folder.getProject() is different than getJavaProject().getProject() // use the other java project's options to verify the name // IJavaProject otherJavaProject = JavaCore.create(folder.getProject()); JavaProject javaProject = (JavaProject)getJavaProject(); String sourceLevel = javaProject.getOption(JavaCore.COMPILER_SOURCE, true); String complianceLevel = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true); // JavaModelManager manager = JavaModelManager.getJavaModelManager(); for (int i = 0; i < length; i++) { File member = members[i]; String memberName = member.getName(); if (member.isDirectory()) { // recurse into sub folders even even parent not included as a sub folder could be included // // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=65637) if (Util.isValidFolderNameForPackage(memberName, sourceLevel, complianceLevel)) { // eliminate binary output only if nested inside direct subfolders // if (javaProject.contains(member)) { String[] newNames = Util.arrayConcat(pkgName, manager.intern(memberName)); boolean isMemberIncluded = false;//!Util.isExcluded(member, inclusionPatterns, exclusionPatterns); computeFolderChildren(member, isMemberIncluded, newNames, vChildren, inclusionPatterns, exclusionPatterns); // } } } else { if (!hasIncluded && Util.isValidCompilationUnitName(memberName, sourceLevel, complianceLevel) /*&& !Util.isExcluded(member, inclusionPatterns, exclusionPatterns)*/) { hasIncluded = true; IPackageFragment pkg = getPackageFragment(pkgName); vChildren.add(pkg); } } // switch(member.getType()) { // // case IResource.FOLDER: // // recurse into sub folders even even parent not included as a sub folder could be included // // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=65637) // if (Util.isValidFolderNameForPackage(memberName, sourceLevel, complianceLevel)) { // // eliminate binary output only if nested inside direct subfolders // if (javaProject.contains(member)) { // String[] newNames = Util.arrayConcat(pkgName, manager.intern(memberName)); // boolean isMemberIncluded = !Util.isExcluded(member, inclusionPatterns, exclusionPatterns); // computeFolderChildren((IFolder) member, isMemberIncluded, newNames, vChildren, inclusionPatterns, // exclusionPatterns); // } // } // break; // case IResource.FILE: // // inclusion filter may only include files, in which case we still want to include the immediate parent // package (lazily) // if (!hasIncluded // && Util.isValidCompilationUnitName(memberName, sourceLevel, complianceLevel) // && !Util.isExcluded(member, inclusionPatterns, exclusionPatterns)) { // hasIncluded = true; // IPackageFragment pkg = getPackageFragment(pkgName); // vChildren.add(pkg); // } // break; // } } } } catch (IllegalArgumentException e) { throw new JavaModelException(e, IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST); // could be thrown by ElementTree when path // is not found } catch (CoreException e) { throw new JavaModelException(e); } } @Override protected File resource(PackageFragmentRoot root) { return path.toFile(); } @Override protected IStatus validateExistence(File underlyingResource) { return JavaModelStatus.VERIFIED_OK; } @Override public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) { switch (token.charAt(0)) { case JEM_PACKAGEFRAGMENT: String[] pkgName; if (memento.hasMoreTokens()) { token = memento.nextToken(); char firstChar = token.charAt(0); if (firstChar == JEM_CLASSFILE || firstChar == JEM_COMPILATIONUNIT || firstChar == JEM_COUNT) { pkgName = CharOperation.NO_STRINGS; } else { pkgName = Util.splitOn('.', token, 0, token.length()); token = null; } } else { pkgName = CharOperation.NO_STRINGS; token = null; } JavaElement pkg = getPackageFragment(pkgName); if (token == null) { return pkg.getHandleFromMemento(memento, owner); } else { return pkg.getHandleFromMemento(token, memento, owner); } } return null; } @Override protected char getHandleMementoDelimiter() { return JavaElement.JEM_PACKAGEFRAGMENTROOT; } /** * @see org.eclipse.jdt.internal.core.JavaElement#getHandleMemento(StringBuffer) */ protected void getHandleMemento(StringBuffer buff) { IPath path; File underlyingResource = resource(); if (underlyingResource != null) { // internal jar or regular root // if (resource().getProject().equals(getJavaProject().getProject())) { // path = underlyingResource.getProjectRelativePath(); // } else { path = new Path(underlyingResource.getAbsolutePath()); // } } else { // external jar path = getPath(); } ((JavaElement)getParent()).getHandleMemento(buff); buff.append(getHandleMementoDelimiter()); escapeMementoName(buff, path.toString()); } @Override public int getElementType() { return PACKAGE_FRAGMENT_ROOT; } @Override public IPath getPath() { return path; } public IPath internalPath() { return path; } SourceMapper createSourceMapper(IPath sourcePath, IPath rootPath) throws JavaModelException { IClasspathEntry entry = ((JavaProject)getParent()).getClasspathEntryFor(getPath()); String encoding = (entry == null) ? null : ((ClasspathEntry)entry).getSourceAttachmentEncoding(); SourceMapper mapper = new SourceMapper( sourcePath, rootPath == null ? null : rootPath.toOSString(), getJavaProject().getOptions(true), // cannot use workspace options if external jar is 1.5 jar and workspace options are 1.4 options encoding, manager); return mapper; } public int hashCode() { return resource().getAbsolutePath().hashCode(); } /** * Compares two objects for equality; * for <code>PackageFragmentRoot</code>s, equality is having the * same parent, same resources, and occurrence count. */ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PackageFragmentRoot)) return false; PackageFragmentRoot other = (PackageFragmentRoot)o; return resource().equals(other.resource()) && this.parent.equals(other.parent); } /** * @see org.eclipse.jdt.internal.core.JavaElement */ public SourceMapper getSourceMapper() { SourceMapper mapper; try { PackageFragmentRootInfo rootInfo = (PackageFragmentRootInfo)getElementInfo(); mapper = rootInfo.getSourceMapper(); if (mapper == null) { // first call to this method IPath sourcePath = getSourceAttachmentPath(); IPath rootPath = getSourceAttachmentRootPath(); if (sourcePath == null) mapper = createSourceMapper(getPath(), rootPath); // attach root to itself else mapper = createSourceMapper(sourcePath, rootPath); rootInfo.setSourceMapper(mapper); } } catch (JavaModelException e) { // no source can be attached mapper = null; } return mapper; } /** * Returns a new element info for this element. */ protected Object createElementInfo() { return new PackageFragmentRootInfo(); } /* * A version of getKind() that doesn't update the timestamp of the info in the Java model cache * to speed things up */ int internalKind() throws JavaModelException { // org.eclipse.jdt.internal.core.JavaModelManager manager = org.eclipse.jdt.internal.core.JavaModelManager.getJavaModelManager(); PackageFragmentRootInfo info = (PackageFragmentRootInfo)manager.peekAtInfo(this); if (info == null) { info = (PackageFragmentRootInfo)openWhenClosed(createElementInfo(), false, null); } return info.getRootKind(); } }