blob: 9ccad47da3e81431210e8de789e80f6491bbbec0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 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.jdt.internal.core;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
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.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.ISourceManipulation;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
import org.eclipse.jdt.internal.core.util.MementoTokenizer;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jdt.internal.core.util.Util;
/**
* @see IPackageFragment
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class PackageFragment extends Openable implements IPackageFragment, SuffixConstants {
/**
* Constant empty list of class files
*/
protected static final IClassFile[] NO_CLASSFILES = new IClassFile[] {};
/**
* Constant empty list of compilation units
*/
protected static final ICompilationUnit[] NO_COMPILATION_UNITS = new ICompilationUnit[] {};
public String[] names;
private boolean isValidPackageName;
protected PackageFragment(PackageFragmentRoot root, String[] names) {
super(root);
this.names = names;
this.isValidPackageName = internalIsValidPackageName();
}
/**
* @see Openable
*/
protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException {
// add compilation units/class files from resources
HashSet vChildren = new HashSet();
int kind = getKind();
try {
PackageFragmentRoot root = getPackageFragmentRoot();
char[][] inclusionPatterns = root.fullInclusionPatternChars();
char[][] exclusionPatterns = root.fullExclusionPatternChars();
IResource[] members = ((IContainer) underlyingResource).members();
int length = members.length;
if (length > 0) {
IJavaProject project = getJavaProject();
String sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true);
String complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
for (int i = 0; i < length; i++) {
IResource child = members[i];
if (child.getType() != IResource.FOLDER
&& !Util.isExcluded(child, inclusionPatterns, exclusionPatterns)) {
IJavaElement childElement;
if (kind == IPackageFragmentRoot.K_SOURCE && Util.isValidCompilationUnitName(child.getName(), sourceLevel, complianceLevel)) {
childElement = new CompilationUnit(this, child.getName(), DefaultWorkingCopyOwner.PRIMARY);
vChildren.add(childElement);
} else if (kind == IPackageFragmentRoot.K_BINARY && Util.isValidClassFileName(child.getName(), sourceLevel, complianceLevel)) {
childElement = getClassFile(child.getName());
vChildren.add(childElement);
}
}
}
}
} catch (CoreException e) {
throw new JavaModelException(e);
}
if (kind == IPackageFragmentRoot.K_SOURCE) {
// add primary compilation units
ICompilationUnit[] primaryCompilationUnits = getCompilationUnits(DefaultWorkingCopyOwner.PRIMARY);
for (int i = 0, length = primaryCompilationUnits.length; i < length; i++) {
ICompilationUnit primary = primaryCompilationUnits[i];
vChildren.add(primary);
}
}
IJavaElement[] children = new IJavaElement[vChildren.size()];
vChildren.toArray(children);
info.setChildren(children);
return true;
}
/**
* Returns true if this fragment contains at least one java resource.
* Returns false otherwise.
*/
public boolean containsJavaResources() throws JavaModelException {
return ((PackageFragmentInfo) getElementInfo()).containsJavaResources();
}
/**
* @see ISourceManipulation
*/
public void copy(IJavaElement container, IJavaElement sibling, String rename, boolean force, IProgressMonitor monitor) throws JavaModelException {
if (container == null) {
throw new IllegalArgumentException(Messages.operation_nullContainer);
}
IJavaElement[] elements= new IJavaElement[] {this};
IJavaElement[] containers= new IJavaElement[] {container};
IJavaElement[] siblings= null;
if (sibling != null) {
siblings= new IJavaElement[] {sibling};
}
String[] renamings= null;
if (rename != null) {
renamings= new String[] {rename};
}
getJavaModel().copy(elements, containers, siblings, renamings, force, monitor);
}
/**
* @see IPackageFragment
*/
public ICompilationUnit createCompilationUnit(String cuName, String contents, boolean force, IProgressMonitor monitor) throws JavaModelException {
CreateCompilationUnitOperation op= new CreateCompilationUnitOperation(this, cuName, contents, force);
op.runOperation(monitor);
return new CompilationUnit(this, cuName, DefaultWorkingCopyOwner.PRIMARY);
}
/**
* @see JavaElement
*/
protected Object createElementInfo() {
return new PackageFragmentInfo();
}
/**
* @see ISourceManipulation
*/
public void delete(boolean force, IProgressMonitor monitor) throws JavaModelException {
IJavaElement[] elements = new IJavaElement[] {this};
getJavaModel().delete(elements, force, monitor);
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PackageFragment)) return false;
PackageFragment other = (PackageFragment) o;
return Util.equalArraysOrNull(this.names, other.names) &&
this.parent.equals(other.parent);
}
public boolean exists() {
// super.exist() only checks for the parent and the resource existence
// so also ensure that:
// - the package is not excluded (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=138577)
// - its name is valide (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=108456)
return super.exists() && !Util.isExcluded(this) && isValidPackageName();
}
/**
* @see IPackageFragment#getClassFile(String)
* @exception IllegalArgumentException if the name does not end with ".class"
*/
public IClassFile getClassFile(String classFileName) {
if (!org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(classFileName)) {
throw new IllegalArgumentException(Messages.bind(Messages.element_invalidClassFileName, classFileName));
}
// don't hold on the .class file extension to save memory
// also make sure to not use substring as the resulting String may hold on the underlying char[] which might be much bigger than necessary
int length = classFileName.length() - 6;
char[] nameWithoutExtension = new char[length];
classFileName.getChars(0, length, nameWithoutExtension, 0);
return new ClassFile(this, new String(nameWithoutExtension));
}
/**
* Returns a the collection of class files in this - a folder package fragment which has a root
* that has its kind set to <code>IPackageFragmentRoot.K_Source</code> does not
* recognize class files.
*
* @see IPackageFragment#getClassFiles()
*/
public IClassFile[] getClassFiles() throws JavaModelException {
if (getKind() == IPackageFragmentRoot.K_SOURCE) {
return NO_CLASSFILES;
}
ArrayList list = getChildrenOfType(CLASS_FILE);
IClassFile[] array= new IClassFile[list.size()];
list.toArray(array);
return array;
}
/**
* @see IPackageFragment#getCompilationUnit(String)
* @exception IllegalArgumentException if the name does not end with ".java"
*/
public ICompilationUnit getCompilationUnit(String cuName) {
if (!org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(cuName)) {
throw new IllegalArgumentException(Messages.convention_unit_notJavaName);
}
return new CompilationUnit(this, cuName, DefaultWorkingCopyOwner.PRIMARY);
}
/**
* @see IPackageFragment#getCompilationUnits()
*/
public ICompilationUnit[] getCompilationUnits() throws JavaModelException {
if (getKind() == IPackageFragmentRoot.K_BINARY) {
return NO_COMPILATION_UNITS;
}
ArrayList list = getChildrenOfType(COMPILATION_UNIT);
ICompilationUnit[] array= new ICompilationUnit[list.size()];
list.toArray(array);
return array;
}
/**
* @see IPackageFragment#getCompilationUnits(WorkingCopyOwner)
*/
public ICompilationUnit[] getCompilationUnits(WorkingCopyOwner owner) {
ICompilationUnit[] workingCopies = JavaModelManager.getJavaModelManager().getWorkingCopies(owner, false/*don't add primary*/);
if (workingCopies == null) return JavaModelManager.NO_WORKING_COPY;
int length = workingCopies.length;
ICompilationUnit[] result = new ICompilationUnit[length];
int index = 0;
for (int i = 0; i < length; i++) {
ICompilationUnit wc = workingCopies[i];
if (equals(wc.getParent()) && !Util.isExcluded(wc)) { // 59933 - excluded wc shouldn't be answered back
result[index++] = wc;
}
}
if (index != length) {
System.arraycopy(result, 0, result = new ICompilationUnit[index], 0, index);
}
return result;
}
public String getElementName() {
if (this.names.length == 0)
return DEFAULT_PACKAGE_NAME;
return Util.concatWith(this.names, '.');
}
/**
* @see IJavaElement
*/
public int getElementType() {
return PACKAGE_FRAGMENT;
}
/*
* @see JavaElement
*/
public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) {
switch (token.charAt(0)) {
case JEM_CLASSFILE:
if (!memento.hasMoreTokens()) return this;
String classFileName = memento.nextToken();
JavaElement classFile = (JavaElement)getClassFile(classFileName);
return classFile.getHandleFromMemento(memento, owner);
case JEM_COMPILATIONUNIT:
if (!memento.hasMoreTokens()) return this;
String cuName = memento.nextToken();
JavaElement cu = new CompilationUnit(this, cuName, owner);
return cu.getHandleFromMemento(memento, owner);
}
return null;
}
/**
* @see JavaElement#getHandleMementoDelimiter()
*/
protected char getHandleMementoDelimiter() {
return JavaElement.JEM_PACKAGEFRAGMENT;
}
/**
* @see IPackageFragment#getKind()
*/
public int getKind() throws JavaModelException {
return ((IPackageFragmentRoot)getParent()).getKind();
}
/**
* Returns an array of non-java resources contained in the receiver.
*/
public Object[] getNonJavaResources() throws JavaModelException {
if (isDefaultPackage()) {
// We don't want to show non java resources of the default package (see PR #1G58NB8)
return JavaElementInfo.NO_NON_JAVA_RESOURCES;
} else {
return ((PackageFragmentInfo) getElementInfo()).getNonJavaResources(resource(), getPackageFragmentRoot());
}
}
/**
* @see IJavaElement#getPath()
*/
public IPath getPath() {
PackageFragmentRoot root = getPackageFragmentRoot();
if (root.isArchive()) {
return root.getPath();
} else {
IPath path = root.getPath();
for (int i = 0, length = this.names.length; i < length; i++) {
String name = this.names[i];
path = path.append(name);
}
return path;
}
}
/**
* @see JavaElement#resource()
*/
public IResource resource(PackageFragmentRoot root) {
int length = this.names.length;
if (length == 0) {
return root.resource(root);
} else {
IPath path = new Path(this.names[0]);
for (int i = 1; i < length; i++)
path = path.append(this.names[i]);
return ((IContainer)root.resource(root)).getFolder(path);
}
}
/**
* @see IJavaElement#getUnderlyingResource()
*/
public IResource getUnderlyingResource() throws JavaModelException {
IResource rootResource = this.parent.getUnderlyingResource();
if (rootResource == null) {
//jar package fragment root that has no associated resource
return null;
}
// the underlying resource may be a folder or a project (in the case that the project folder
// is atually the package fragment root)
if (rootResource.getType() == IResource.FOLDER || rootResource.getType() == IResource.PROJECT) {
IContainer folder = (IContainer) rootResource;
String[] segs = this.names;
for (int i = 0; i < segs.length; ++i) {
IResource child = folder.findMember(segs[i]);
if (child == null || child.getType() != IResource.FOLDER) {
throw newNotPresentException();
}
folder = (IFolder) child;
}
return folder;
} else {
return rootResource;
}
}
public int hashCode() {
int hash = this.parent.hashCode();
for (int i = 0, length = this.names.length; i < length; i++)
hash = Util.combineHashCodes(this.names[i].hashCode(), hash);
return hash;
}
/**
* @see IParent
*/
public boolean hasChildren() throws JavaModelException {
return getChildren().length > 0;
}
/**
* @see IPackageFragment#hasSubpackages()
*/
public boolean hasSubpackages() throws JavaModelException {
IJavaElement[] packages= ((IPackageFragmentRoot)getParent()).getChildren();
int namesLength = this.names.length;
nextPackage: for (int i= 0, length = packages.length; i < length; i++) {
String[] otherNames = ((PackageFragment) packages[i]).names;
if (otherNames.length <= namesLength) continue nextPackage;
for (int j = 0; j < namesLength; j++)
if (!this.names[j].equals(otherNames[j]))
continue nextPackage;
return true;
}
return false;
}
protected boolean internalIsValidPackageName() {
// if package fragment refers to folder in another IProject, then
// resource().getProject() is different than getJavaProject().getProject()
// use the other java project's options to verify the name
IJavaProject javaProject = JavaCore.create(resource().getProject());
String sourceLevel = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
String complianceLevel = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
for (int i = 0, length = this.names.length; i < length; i++) {
if (!Util.isValidFolderNameForPackage(this.names[i], sourceLevel, complianceLevel))
return false;
}
return true;
}
/**
* @see IPackageFragment#isDefaultPackage()
*/
public boolean isDefaultPackage() {
return this.names.length == 0;
}
protected final boolean isValidPackageName() {
return this.isValidPackageName;
}
/**
* @see ISourceManipulation#move(IJavaElement, IJavaElement, String, boolean, IProgressMonitor)
*/
public void move(IJavaElement container, IJavaElement sibling, String rename, boolean force, IProgressMonitor monitor) throws JavaModelException {
if (container == null) {
throw new IllegalArgumentException(Messages.operation_nullContainer);
}
IJavaElement[] elements= new IJavaElement[] {this};
IJavaElement[] containers= new IJavaElement[] {container};
IJavaElement[] siblings= null;
if (sibling != null) {
siblings= new IJavaElement[] {sibling};
}
String[] renamings= null;
if (rename != null) {
renamings= new String[] {rename};
}
getJavaModel().move(elements, containers, siblings, renamings, force, monitor);
}
/**
* @see ISourceManipulation#rename(String, boolean, IProgressMonitor)
*/
public void rename(String newName, boolean force, IProgressMonitor monitor) throws JavaModelException {
if (newName == null) {
throw new IllegalArgumentException(Messages.element_nullName);
}
IJavaElement[] elements= new IJavaElement[] {this};
IJavaElement[] dests= new IJavaElement[] {getParent()};
String[] renamings= new String[] {newName};
getJavaModel().rename(elements, dests, renamings, force, monitor);
}
/**
* Debugging purposes
*/
protected void toStringChildren(int tab, StringBuffer buffer, Object info) {
if (tab == 0) {
super.toStringChildren(tab, buffer, info);
}
}
/**
* Debugging purposes
*/
protected void toStringInfo(int tab, StringBuffer buffer, Object info, boolean showResolvedInfo) {
buffer.append(tabString(tab));
if (this.names.length == 0) {
buffer.append("<default>"); //$NON-NLS-1$
} else {
toStringName(buffer);
}
if (info == null) {
buffer.append(" (not open)"); //$NON-NLS-1$
} else {
if (tab > 0) {
buffer.append(" (...)"); //$NON-NLS-1$
}
}
}
/*
* @see IJavaElement#getAttachedJavadoc(IProgressMonitor)
*/
public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException {
PerProjectInfo projectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(getJavaProject().getProject());
String cachedJavadoc = null;
synchronized (projectInfo.javadocCache) {
cachedJavadoc = (String) projectInfo.javadocCache.get(this);
}
if (cachedJavadoc != null) {
return cachedJavadoc;
}
URL baseLocation= getJavadocBaseLocation();
if (baseLocation == null) {
return null;
}
StringBuffer pathBuffer = new StringBuffer(baseLocation.toExternalForm());
if (!(pathBuffer.charAt(pathBuffer.length() - 1) == '/')) {
pathBuffer.append('/');
}
String packPath= getElementName().replace('.', '/');
pathBuffer.append(packPath).append('/').append(JavadocConstants.PACKAGE_FILE_NAME);
if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException();
String contents = getURLContents(baseLocation, String.valueOf(pathBuffer));
if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException();
if (contents == null) return null;
contents = (new JavadocContents(contents)).getPackageDoc();
if (contents == null) contents = ""; //$NON-NLS-1$
synchronized (projectInfo.javadocCache) {
projectInfo.javadocCache.put(this, contents);
}
return contents;
}
protected IStatus validateExistence(IResource underlyingResource) {
// check that the name of the package is valid (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=108456)
if (!isValidPackageName())
return newDoesNotExistStatus();
// check whether this pkg can be opened
if (underlyingResource != null && !resourceExists(underlyingResource))
return newDoesNotExistStatus();
// check that it is not excluded (https://bugs.eclipse.org/bugs/show_bug.cgi?id=138577)
int kind;
try {
kind = getKind();
} catch (JavaModelException e) {
return e.getStatus();
}
if (kind == IPackageFragmentRoot.K_SOURCE && Util.isExcluded(this))
return newDoesNotExistStatus();
return JavaModelStatus.VERIFIED_OK;
}
}