| /******************************************************************************* |
| * Copyright (c) 2000, 2017 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 |
| * Frits Jalvingh <jal@etc.to> - Contribution for Bug 459831 - [launching] Support attaching external annotations to a JRE container |
| * Stephan Herrmann - Contribution for Bug 465293 - External annotation path per container and project |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.wizards.buildpaths; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.stream.Stream; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| |
| import org.eclipse.jdt.core.ClasspathContainerInitializer; |
| import org.eclipse.jdt.core.IAccessRule; |
| import org.eclipse.jdt.core.IClasspathAttribute; |
| import org.eclipse.jdt.core.IClasspathContainer; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IModuleDescription; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| |
| |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.jdt.internal.ui.wizards.buildpaths.ModuleEncapsulationDetail.LimitModules; |
| import org.eclipse.jdt.internal.ui.wizards.buildpaths.ModuleEncapsulationDetail.ModuleAddExport; |
| import org.eclipse.jdt.internal.ui.wizards.buildpaths.ModuleEncapsulationDetail.ModuleAddReads; |
| import org.eclipse.jdt.internal.ui.wizards.buildpaths.ModuleEncapsulationDetail.ModulePatch; |
| |
| import org.eclipse.jdt.launching.JavaRuntime; |
| |
| import org.eclipse.jdt.ui.JavaUI; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| |
| public class CPListElement { |
| |
| public static final String SOURCEATTACHMENT= "sourcepath"; //$NON-NLS-1$ |
| public static final String OUTPUT= "output"; //$NON-NLS-1$ |
| public static final String EXCLUSION= "exclusion"; //$NON-NLS-1$ |
| public static final String INCLUSION= "inclusion"; //$NON-NLS-1$ |
| |
| public static final String ACCESSRULES= "accessrules"; //$NON-NLS-1$ |
| public static final String COMBINE_ACCESSRULES= "combineaccessrules"; //$NON-NLS-1$ |
| |
| public static final String JAVADOC= IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME; |
| public static final String SOURCE_ATTACHMENT_ENCODING= IClasspathAttribute.SOURCE_ATTACHMENT_ENCODING; |
| public static final String IGNORE_OPTIONAL_PROBLEMS= IClasspathAttribute.IGNORE_OPTIONAL_PROBLEMS; |
| public static final String NATIVE_LIB_PATH= JavaRuntime.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY; |
| public static final String MODULE= IClasspathAttribute.MODULE; |
| |
| private IJavaProject fProject; |
| |
| private int fEntryKind; |
| private IPath fPath, fOrginalPath; |
| private IResource fResource; |
| private boolean fIsExported; |
| private boolean fIsMissing; |
| |
| private Object fParentContainer; |
| |
| private IClasspathEntry fCachedEntry; |
| /** |
| * List of {@link CPListElement} and {@link CPListElementAttribute}. |
| */ |
| protected ArrayList<Object> fChildren; |
| private IPath fLinkTarget, fOrginalLinkTarget; |
| private IModuleDescription fModule; |
| |
| private CPListElement() {} |
| |
| public CPListElement(IJavaProject project, int entryKind, IPath path, IResource res) { |
| this(null, project, entryKind, path, res); |
| } |
| |
| public CPListElement(Object parent, IJavaProject project, int entryKind, IPath path, IResource res) { |
| this(parent, project, entryKind, path, res, null); |
| } |
| |
| public CPListElement(IJavaProject project, int entryKind) { |
| this(null, project, entryKind, null, null); |
| } |
| |
| public CPListElement(Object parent, IJavaProject project, int entryKind, IPath path, IResource res, IPath linkTarget) { |
| this(parent, project, entryKind, path, false, res, linkTarget); |
| } |
| |
| public CPListElement(Object parent, IJavaProject project, int entryKind, IPath path, boolean newElement, IResource res, IPath linkTarget) { |
| this(parent, project, null, entryKind, path, null, newElement, res, linkTarget); |
| } |
| |
| public CPListElement(Object parent, IJavaProject project, IClasspathEntry entry, int entryKind, IPath path, IModuleDescription module, boolean newElement, IResource res, IPath linkTarget) { |
| fProject= project; |
| |
| fEntryKind= entryKind; |
| fPath= path; |
| fOrginalPath= newElement ? null : path; |
| fLinkTarget= linkTarget; |
| fOrginalLinkTarget= linkTarget; |
| fChildren= new ArrayList<>(); |
| fResource= res; |
| fIsExported= false; |
| fModule= module; |
| |
| fIsMissing= false; |
| fCachedEntry= null; |
| fParentContainer= parent; |
| |
| switch (entryKind) { |
| case IClasspathEntry.CPE_SOURCE: |
| createAttributeElement(OUTPUT, null, true); |
| createAttributeElement(INCLUSION, new Path[0], true); |
| createAttributeElement(EXCLUSION, new Path[0], true); |
| createAttributeElement(NATIVE_LIB_PATH, null, false); |
| createAttributeElement(IGNORE_OPTIONAL_PROBLEMS, null, false); |
| break; |
| case IClasspathEntry.CPE_LIBRARY: |
| case IClasspathEntry.CPE_VARIABLE: |
| createAttributeElement(SOURCEATTACHMENT, null, true); |
| createAttributeElement(JAVADOC, null, false); |
| createAttributeElement(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, null, false); |
| createAttributeElement(MODULE, (module != null ? new ModuleEncapsulationDetail[0] : null), true); |
| createAttributeElement(SOURCE_ATTACHMENT_ENCODING, null, false); |
| createAttributeElement(NATIVE_LIB_PATH, null, false); |
| createAttributeElement(ACCESSRULES, new IAccessRule[0], true); |
| break; |
| case IClasspathEntry.CPE_PROJECT: |
| createAttributeElement(MODULE, null, true); |
| createAttributeElement(ACCESSRULES, new IAccessRule[0], true); |
| createAttributeElement(COMBINE_ACCESSRULES, Boolean.FALSE, true); // not rendered |
| createAttributeElement(NATIVE_LIB_PATH, null, false); |
| break; |
| case IClasspathEntry.CPE_CONTAINER: |
| createAttributeElement(ACCESSRULES, new IAccessRule[0], true); |
| createAttributeElement(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, null, false); |
| createAttributeElement(NATIVE_LIB_PATH, null, false); |
| try { |
| IClasspathContainer container= JavaCore.getClasspathContainer(fPath, fProject); |
| if (container != null) { |
| IClasspathEntry[] entries= container.getClasspathEntries(); |
| if (entries != null) { // catch invalid container implementation |
| if (entries.length == 1) { |
| // redirect the Java 9 JRT pseudo entry to the list of contained modules: |
| int beforeModules= fChildren.size(); |
| boolean modulesAdded= addModuleNodes(entry, entries[0]); |
| if (modulesAdded) { |
| fChildren.add(beforeModules, new CPListElementAttribute(this, MODULE, new ModuleEncapsulationDetail[0], true)); |
| return; |
| } |
| } |
| for (int i= 0; i < entries.length; i++) { |
| IClasspathEntry currEntry= entries[i]; |
| if (currEntry != null) { |
| CPListElement curr= createFromExisting(this, currEntry, fProject); |
| fChildren.add(curr); |
| } else { |
| JavaPlugin.logErrorMessage("Null entry in container '" + fPath + "'"); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| } |
| } else { |
| JavaPlugin.logErrorMessage("container returns null as entries: '" + fPath + "'"); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| } |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| break; |
| default: |
| } |
| } |
| |
| private boolean addModuleNodes(IClasspathEntry containerEntry, IClasspathEntry pseudoEntry) { |
| boolean modulesAdded= false; |
| if (containerEntry != null) { |
| IPackageFragmentRoot[] fragmentRoots= fProject.findPackageFragmentRoots(containerEntry); |
| if (fragmentRoots != null && fragmentRoots.length > 1) { |
| for (IPackageFragmentRoot fragmentRoot : fragmentRoots) { |
| IModuleDescription currModule= fragmentRoot.getModuleDescription(); |
| if (currModule != null) { |
| CPListElement curr= create(this, pseudoEntry, currModule, true, fProject); |
| fChildren.add(curr); |
| modulesAdded= true; |
| } |
| } |
| } |
| } else { |
| JavaPlugin.logErrorMessage("Null entry for container '" + fPath + "'"); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| return modulesAdded; |
| } |
| |
| public IClasspathEntry getClasspathEntry() { |
| if (fCachedEntry == null) { |
| fCachedEntry= newClasspathEntry(); |
| } |
| return fCachedEntry; |
| } |
| |
| |
| private IClasspathAttribute[] getClasspathAttributes() { |
| ArrayList<IClasspathAttribute> res= new ArrayList<>(); |
| for (int i= 0; i < fChildren.size(); i++) { |
| Object curr= fChildren.get(i); |
| if (curr instanceof CPListElementAttribute) { |
| CPListElementAttribute elem= (CPListElementAttribute) curr; |
| if (!elem.isBuiltIn()) { |
| if (elem.getValue() != null) { |
| res.add(elem.getClasspathAttribute()); |
| } |
| } else if (elem.getValue() instanceof ModuleEncapsulationDetail[]) { |
| res.add(JavaCore.newClasspathAttribute(IClasspathAttribute.MODULE, "true")); //$NON-NLS-1$ |
| |
| ModuleEncapsulationDetail[] detailValue= (ModuleEncapsulationDetail[]) elem.getValue(); |
| String encodedPatch= ModuleEncapsulationDetail.encodeFiltered(detailValue, ModulePatch.class); |
| if (!encodedPatch.isEmpty()) { |
| res.add(JavaCore.newClasspathAttribute(IClasspathAttribute.PATCH_MODULE, encodedPatch)); |
| } |
| String encodedExports= ModuleEncapsulationDetail.encodeFiltered(detailValue, ModuleAddExport.class); |
| if (!encodedExports.isEmpty()) { |
| res.add(JavaCore.newClasspathAttribute(IClasspathAttribute.ADD_EXPORTS, encodedExports)); |
| } |
| String encodedReads= ModuleEncapsulationDetail.encodeFiltered(detailValue, ModuleAddReads.class); |
| if (!encodedReads.isEmpty()) { |
| res.add(JavaCore.newClasspathAttribute(IClasspathAttribute.ADD_READS, encodedReads)); |
| } |
| String encodedLimits= ModuleEncapsulationDetail.encodeFiltered(detailValue, LimitModules.class); |
| if (!encodedLimits.isEmpty()) { |
| res.add(JavaCore.newClasspathAttribute(IClasspathAttribute.LIMIT_MODULES, encodedLimits)); |
| } |
| } |
| } |
| } |
| return res.toArray(new IClasspathAttribute[res.size()]); |
| } |
| |
| private IClasspathEntry newClasspathEntry() { |
| |
| IClasspathAttribute[] extraAttributes= getClasspathAttributes(); |
| switch (fEntryKind) { |
| case IClasspathEntry.CPE_SOURCE: |
| IPath[] inclusionPattern= (IPath[]) getAttribute(INCLUSION); |
| IPath[] exclusionPattern= (IPath[]) getAttribute(EXCLUSION); |
| IPath outputLocation= (IPath) getAttribute(OUTPUT); |
| return JavaCore.newSourceEntry(fPath, inclusionPattern, exclusionPattern, outputLocation, extraAttributes); |
| case IClasspathEntry.CPE_LIBRARY: { |
| IPath attach= (IPath) getAttribute(SOURCEATTACHMENT); |
| IAccessRule[] accesRules= (IAccessRule[]) getAttribute(ACCESSRULES); |
| return JavaCore.newLibraryEntry(fPath, attach, null, accesRules, extraAttributes, isExported()); |
| } |
| case IClasspathEntry.CPE_PROJECT: { |
| IAccessRule[] accesRules= (IAccessRule[]) getAttribute(ACCESSRULES); |
| boolean combineAccessRules= ((Boolean) getAttribute(COMBINE_ACCESSRULES)).booleanValue(); |
| return JavaCore.newProjectEntry(fPath, accesRules, combineAccessRules, extraAttributes, isExported()); |
| } |
| case IClasspathEntry.CPE_CONTAINER: { |
| IAccessRule[] accesRules= (IAccessRule[]) getAttribute(ACCESSRULES); |
| return JavaCore.newContainerEntry(fPath, accesRules, extraAttributes, isExported()); |
| } |
| case IClasspathEntry.CPE_VARIABLE: { |
| IPath varAttach= (IPath) getAttribute(SOURCEATTACHMENT); |
| IAccessRule[] accesRules= (IAccessRule[]) getAttribute(ACCESSRULES); |
| return JavaCore.newVariableEntry(fPath, varAttach, null, accesRules, extraAttributes, isExported()); |
| } |
| default: |
| return null; |
| } |
| } |
| |
| /** |
| * Gets the class path entry path. |
| * @return returns the path |
| * @see IClasspathEntry#getPath() |
| */ |
| public IPath getPath() { |
| return fPath; |
| } |
| |
| /** |
| * Gets the class path entry kind. |
| * @return the entry kind |
| * @see IClasspathEntry#getEntryKind() |
| */ |
| public int getEntryKind() { |
| return fEntryKind; |
| } |
| |
| /** |
| * Entries without resource are either non existing or a variable entry |
| * External jars do not have a resource |
| * @return returns the resource |
| */ |
| public IResource getResource() { |
| return fResource; |
| } |
| |
| /** |
| * Entries inside a JRT container represent one module each. |
| * @return the module represented by this element. |
| */ |
| public IModuleDescription getModule() { |
| return fModule; |
| } |
| |
| public CPListElementAttribute setAttribute(String key, Object value) { |
| CPListElementAttribute attribute= findAttributeElement(key); |
| if (attribute == null) { |
| return null; |
| } |
| if (key.equals(EXCLUSION) || key.equals(INCLUSION)) { |
| Assert.isTrue(value != null || fEntryKind != IClasspathEntry.CPE_SOURCE); |
| } |
| |
| if (key.equals(ACCESSRULES)) { |
| Assert.isTrue(value != null || fEntryKind == IClasspathEntry.CPE_SOURCE); |
| } |
| if (key.equals(COMBINE_ACCESSRULES)) { |
| Assert.isTrue(value instanceof Boolean); |
| } |
| |
| attribute.setValue(value); |
| return attribute; |
| } |
| |
| public boolean addToExclusions(IPath path) { |
| String key= CPListElement.EXCLUSION; |
| return addFilter(path, key); |
| } |
| |
| public boolean addToInclusion(IPath path) { |
| String key= CPListElement.INCLUSION; |
| return addFilter(path, key); |
| } |
| |
| public boolean removeFromExclusions(IPath path) { |
| String key= CPListElement.EXCLUSION; |
| return removeFilter(path, key); |
| } |
| |
| public boolean removeFromInclusion(IPath path) { |
| String key= CPListElement.INCLUSION; |
| return removeFilter(path, key); |
| } |
| |
| private boolean addFilter(IPath path, String key) { |
| IPath[] filters= (IPath[]) getAttribute(key); |
| if (filters == null) |
| return false; |
| |
| if (!JavaModelUtil.isExcludedPath(path, filters)) { |
| IPath toAdd= path.removeFirstSegments(getPath().segmentCount()).addTrailingSeparator(); |
| IPath[] newFilters= new IPath[filters.length + 1]; |
| System.arraycopy(filters, 0, newFilters, 0, filters.length); |
| newFilters[filters.length]= toAdd; |
| setAttribute(key, newFilters); |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean removeFilter(IPath path, String key) { |
| IPath[] filters= (IPath[]) getAttribute(key); |
| if (filters == null) |
| return false; |
| |
| IPath toRemove= path.removeFirstSegments(getPath().segmentCount()).addTrailingSeparator(); |
| if (JavaModelUtil.isExcludedPath(toRemove, filters)) { |
| List<IPath> l= new ArrayList<>(Arrays.asList(filters)); |
| l.remove(toRemove); |
| IPath[] newFilters= l.toArray(new IPath[l.size()]); |
| setAttribute(key, newFilters); |
| return true; |
| } |
| return false; |
| } |
| |
| public CPListElementAttribute findAttributeElement(String key) { |
| for (int i= 0; i < fChildren.size(); i++) { |
| Object curr= fChildren.get(i); |
| if (curr instanceof CPListElementAttribute) { |
| CPListElementAttribute elem= (CPListElementAttribute) curr; |
| if (key.equals(elem.getKey())) { |
| return elem; |
| } |
| } |
| } |
| return null; |
| } |
| |
| |
| public Object getAttribute(String key) { |
| CPListElementAttribute attrib= findAttributeElement(key); |
| if (attrib != null) { |
| return attrib.getValue(); |
| } |
| return null; |
| } |
| |
| public CPListElementAttribute[] getAllAttributes() { |
| ArrayList<Object> res= new ArrayList<>(); |
| for (int i= 0; i < fChildren.size(); i++) { |
| Object curr= fChildren.get(i); |
| if (curr instanceof CPListElementAttribute) { |
| res.add(curr); |
| } |
| } |
| return res.toArray(new CPListElementAttribute[res.size()]); |
| } |
| |
| public <T extends ModuleEncapsulationDetail> List<T> getModuleEncapsulationDetails(Class<T> clazz) { |
| Object moduleDetails= getAttribute(CPListElement.MODULE); |
| if (moduleDetails instanceof ModuleEncapsulationDetail[]) { |
| ModuleEncapsulationDetail[] details= (ModuleEncapsulationDetail[]) moduleDetails; |
| List<T> elements= new ArrayList<>(details.length); |
| for (int i= 0; i < details.length; i++) { |
| if (clazz.isInstance(details[i])) |
| elements.add(clazz.cast(details[i])); |
| } |
| return elements; |
| } else { |
| return Collections.emptyList(); |
| } |
| } |
| |
| private void createAttributeElement(String key, Object value, boolean builtIn) { |
| fChildren.add(new CPListElementAttribute(this, key, value, builtIn)); |
| } |
| |
| private static boolean isFiltered(Object entry, String[] filteredKeys) { |
| if (entry instanceof CPListElementAttribute) { |
| CPListElementAttribute curr= (CPListElementAttribute) entry; |
| String key= curr.getKey(); |
| for (int i= 0; i < filteredKeys.length; i++) { |
| if (key.equals(filteredKeys[i])) { |
| return true; |
| } |
| } |
| if (curr.isNotSupported()) { |
| return true; |
| } |
| if (!curr.isBuiltIn() && !key.equals(CPListElement.JAVADOC) && !key.equals(CPListElement.NATIVE_LIB_PATH) && !key.equals(CPListElement.IGNORE_OPTIONAL_PROBLEMS)) { |
| return !JavaPlugin.getDefault().getClasspathAttributeConfigurationDescriptors().containsKey(key); |
| } |
| } |
| return false; |
| } |
| |
| private Object[] getFilteredChildren(String[] filteredKeys) { |
| int nChildren= fChildren.size(); |
| ArrayList<Object> res= new ArrayList<>(nChildren); |
| |
| for (int i= 0; i < nChildren; i++) { |
| Object curr= fChildren.get(i); |
| if (!isFiltered(curr, filteredKeys)) { |
| res.add(curr); |
| } |
| } |
| return res.toArray(); |
| } |
| |
| public Object[] getChildren(boolean hideOutputFolder) { |
| if (hideOutputFolder && fEntryKind == IClasspathEntry.CPE_SOURCE) { |
| return getFilteredChildren(new String[] { OUTPUT }); |
| } |
| /*if (isInContainer(JavaRuntime.JRE_CONTAINER)) { |
| return getFilteredChildren(new String[] { COMBINE_ACCESSRULES, NATIVE_LIB_PATH }); |
| }*/ |
| if (fEntryKind == IClasspathEntry.CPE_PROJECT) { |
| return getFilteredChildren(new String[] { COMBINE_ACCESSRULES }); |
| } |
| return getFilteredChildren(new String[0]); |
| } |
| |
| public Object getParentContainer() { |
| return fParentContainer; |
| } |
| |
| /** |
| * Sets the parent container. |
| * |
| * @param parent the parent container |
| * @since 3.7 |
| */ |
| void setParentContainer(CPUserLibraryElement parent) { |
| fParentContainer= parent; |
| } |
| |
| /** |
| * Notifies that an attribute has changed |
| * |
| * @param key the changed key |
| */ |
| protected void attributeChanged(String key) { |
| fCachedEntry= null; |
| } |
| |
| private IStatus evaluateContainerChildStatus(CPListElementAttribute attrib) { |
| if (fProject != null) { |
| ClasspathContainerInitializer initializer= JavaCore.getClasspathContainerInitializer(fPath.segment(0)); |
| if (initializer != null && initializer.canUpdateClasspathContainer(fPath, fProject)) { |
| if (attrib.isBuiltIn()) { |
| if (CPListElement.SOURCEATTACHMENT.equals(attrib.getKey())) { |
| return initializer.getSourceAttachmentStatus(fPath, fProject); |
| } else if (CPListElement.ACCESSRULES.equals(attrib.getKey())) { |
| return initializer.getAccessRulesStatus(fPath, fProject); |
| } |
| } else { |
| return initializer.getAttributeStatus(fPath, fProject, attrib.getKey()); |
| } |
| } |
| return new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, ClasspathContainerInitializer.ATTRIBUTE_READ_ONLY, "", null); //$NON-NLS-1$ |
| } |
| return null; |
| } |
| |
| public IStatus getContainerChildStatus(CPListElementAttribute attrib) { |
| if (fParentContainer instanceof CPListElement) { |
| CPListElement parent= (CPListElement) fParentContainer; |
| if (parent.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { |
| return parent.evaluateContainerChildStatus(attrib); |
| } |
| return ((CPListElement) fParentContainer).getContainerChildStatus(attrib); |
| } |
| return null; |
| } |
| |
| public boolean isInContainer(String containerName) { |
| if (fParentContainer instanceof CPListElement) { |
| CPListElement elem= (CPListElement) fParentContainer; |
| return new Path(containerName).isPrefixOf(elem.getPath()); |
| } |
| return false; |
| } |
| |
| public boolean isDeprecated() { |
| if (fEntryKind != IClasspathEntry.CPE_VARIABLE) { |
| return false; |
| } |
| if (fPath.segmentCount() > 0) { |
| return JavaCore.getClasspathVariableDeprecationMessage(fPath.segment(0)) != null; |
| } |
| return false; |
| } |
| |
| public String getDeprecationMessage() { |
| if (fEntryKind != IClasspathEntry.CPE_VARIABLE) { |
| return null; |
| } |
| if (fPath.segmentCount() > 0) { |
| String varName= fPath.segment(0); |
| return BuildPathSupport.getDeprecationMessage(varName); |
| } |
| return null; |
| } |
| |
| /* |
| * @see Object#equals(java.lang.Object) |
| */ |
| @Override |
| public boolean equals(Object other) { |
| if (other != null && other.getClass().equals(getClass())) { |
| CPListElement elem= (CPListElement) other; |
| return getClasspathEntry().equals(elem.getClasspathEntry()); |
| } |
| return false; |
| } |
| |
| /* |
| * @see Object#hashCode() |
| */ |
| @Override |
| public int hashCode() { |
| if(fPath==null) |
| return super.hashCode(); |
| return fPath.hashCode() + fEntryKind; |
| } |
| |
| @Override |
| public String toString() { |
| return getClasspathEntry().toString(); |
| } |
| |
| /** |
| * Returns if a entry is missing. |
| * @return Returns a boolean |
| */ |
| public boolean isMissing() { |
| return fIsMissing; |
| } |
| |
| /** |
| * Returns if a entry has children that are missing |
| * @return Returns a boolean |
| */ |
| public boolean hasMissingChildren() { |
| for (int i= 0; i < fChildren.size(); i++) { |
| Object curr= fChildren.get(i); |
| if (curr instanceof CPListElement && ((CPListElement) curr).isMissing()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Sets the 'missing' state of the entry. |
| * @param isMissing the new state |
| */ |
| public void setIsMissing(boolean isMissing) { |
| fIsMissing= isMissing; |
| } |
| |
| /** |
| * Returns if a entry is exported (only applies to libraries) |
| * @return Returns a boolean |
| */ |
| public boolean isExported() { |
| return fIsExported; |
| } |
| |
| /** |
| * Sets the export state of the entry. |
| * @param isExported the new state |
| */ |
| public void setExported(boolean isExported) { |
| if (isExported != fIsExported) { |
| fIsExported = isExported; |
| |
| attributeChanged(null); |
| } |
| } |
| |
| /** |
| * Gets the project. |
| * @return Returns a IJavaProject |
| */ |
| public IJavaProject getJavaProject() { |
| return fProject; |
| } |
| |
| public static CPListElement createFromExisting(IClasspathEntry curr, IJavaProject project) { |
| //Note: Some old clients of this method could actually mean create(curr, true, project) |
| return create(curr, false, project); |
| } |
| |
| public static CPListElement createFromExisting(Object parent, IClasspathEntry curr, IJavaProject project) { |
| //Note: Some old clients of this method could actually mean create(parent, curr, true, project) |
| return create(parent, curr, null, false, project); |
| } |
| |
| public static CPListElement create(IClasspathEntry curr, boolean newElement, IJavaProject project) { |
| return create(null, curr, null, newElement, project); |
| } |
| |
| public static CPListElement create(Object parent, IClasspathEntry curr, IModuleDescription module, boolean newElement, IJavaProject project) { |
| IPath path= curr.getPath(); |
| IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); |
| |
| // get the resource |
| IResource res= null; |
| boolean isMissing= false; |
| IPath linkTarget= null; |
| |
| switch (curr.getEntryKind()) { |
| case IClasspathEntry.CPE_CONTAINER: |
| try { |
| isMissing= project != null && (JavaCore.getClasspathContainer(path, project) == null); |
| } catch (JavaModelException e) { |
| isMissing= true; |
| } |
| break; |
| case IClasspathEntry.CPE_VARIABLE: |
| IPath resolvedPath= JavaCore.getResolvedVariablePath(path); |
| isMissing= root.findMember(resolvedPath) == null && !resolvedPath.toFile().exists(); |
| break; |
| case IClasspathEntry.CPE_LIBRARY: |
| res= root.findMember(path); |
| if (res == null) { |
| if (!ArchiveFileFilter.isArchivePath(path, true)) { |
| if (root.getWorkspace().validatePath(path.toString(), IResource.FOLDER).isOK() |
| && root.getProject(path.segment(0)).exists()) { |
| res= root.getFolder(path); |
| } |
| } |
| |
| IPath rawPath= path; |
| if (project != null) { |
| IPackageFragmentRoot[] roots= project.findPackageFragmentRoots(curr); |
| if (roots.length == 1) |
| rawPath= roots[0].getPath(); |
| } |
| isMissing= !rawPath.toFile().exists(); // look for external JARs and folders |
| } else if (res.isLinked()) { |
| linkTarget= res.getLocation(); |
| } |
| break; |
| case IClasspathEntry.CPE_SOURCE: |
| path= path.removeTrailingSeparator(); |
| res= root.findMember(path); |
| if (res == null) { |
| if (root.getWorkspace().validatePath(path.toString(), IResource.FOLDER).isOK()) { |
| res= root.getFolder(path); |
| } |
| isMissing= true; |
| } else if (res.isLinked()) { |
| linkTarget= res.getLocation(); |
| } |
| break; |
| case IClasspathEntry.CPE_PROJECT: |
| res= root.findMember(path); |
| isMissing= (res == null); |
| break; |
| } |
| CPListElement elem= new CPListElement(parent, project, curr, curr.getEntryKind(), path, module, newElement, res, linkTarget); |
| elem.setExported(curr.isExported()); |
| elem.setAttribute(SOURCEATTACHMENT, curr.getSourceAttachmentPath()); |
| elem.setAttribute(OUTPUT, curr.getOutputLocation()); |
| elem.setAttribute(EXCLUSION, curr.getExclusionPatterns()); |
| elem.setAttribute(INCLUSION, curr.getInclusionPatterns()); |
| elem.setAttribute(ACCESSRULES, curr.getAccessRules()); |
| elem.setAttribute(COMBINE_ACCESSRULES, Boolean.valueOf(curr.combineAccessRules())); |
| |
| IClasspathAttribute[] extraAttributes= curr.getExtraAttributes(); |
| for (int i= 0; i < extraAttributes.length; i++) { |
| IClasspathAttribute attrib= extraAttributes[i]; |
| CPListElementAttribute attribElem= elem.findAttributeElement(attrib.getName()); |
| if (attribElem == null) { |
| if (!isModuleAttribute(attrib.getName())) { |
| elem.createAttributeElement(attrib.getName(), attrib.getValue(), false); |
| } else if (attrib.getName().equals(MODULE)) { |
| attribElem = new CPListElementAttribute(elem, MODULE, null, true); |
| attribElem.setValue(getModuleAttributeValue(attribElem, attrib, extraAttributes)); |
| elem.fChildren.add(attribElem); |
| } |
| } else { |
| Object value= attrib.getValue(); |
| if (attrib.getName().equals(MODULE)) { |
| value= getModuleAttributeValue(attribElem, attrib, extraAttributes); |
| } |
| attribElem.setValue(value); |
| } |
| } |
| |
| elem.setIsMissing(isMissing); |
| return elem; |
| } |
| |
| private static Object getModuleAttributeValue(CPListElementAttribute attribElem, IClasspathAttribute attrib, IClasspathAttribute[] extraAttributes) { |
| if (ModuleAttributeConfiguration.TRUE.equals(attrib.getValue())) { |
| List<ModuleEncapsulationDetail> details= new ArrayList<>(); |
| for (int j= 0; j < extraAttributes.length; j++) { |
| IClasspathAttribute otherAttrib= extraAttributes[j]; |
| if (IClasspathAttribute.PATCH_MODULE.equals(otherAttrib.getName())) { |
| details.add(ModulePatch.fromString(attribElem, otherAttrib.getValue())); |
| } else if (IClasspathAttribute.ADD_EXPORTS.equals(otherAttrib.getName())) { |
| details.addAll(ModuleAddExport.fromMultiString(attribElem, otherAttrib.getValue())); |
| } else if (IClasspathAttribute.ADD_READS.equals(otherAttrib.getName())) { |
| details.addAll(ModuleAddReads.fromMultiString(attribElem, otherAttrib.getValue())); |
| } else if (IClasspathAttribute.LIMIT_MODULES.equals(otherAttrib.getName())) { |
| details.add(LimitModules.fromString(attribElem, otherAttrib.getValue())); |
| } |
| } |
| return details.toArray(new ModuleEncapsulationDetail[details.size()]); |
| } else { |
| return null; |
| } |
| } |
| |
| private static boolean isModuleAttribute(String attributeName) { |
| return Stream.of(IClasspathAttribute.MODULE, IClasspathAttribute.LIMIT_MODULES, |
| IClasspathAttribute.PATCH_MODULE, IClasspathAttribute.ADD_EXPORTS, IClasspathAttribute.ADD_READS) |
| .anyMatch(attributeName::equals); |
| } |
| |
| public static StringBuffer appendEncodePath(IPath path, StringBuffer buf) { |
| if (path != null) { |
| String str= path.toString(); |
| buf.append('[').append(str.length()).append(']').append(str); |
| } else { |
| buf.append('[').append(']'); |
| } |
| return buf; |
| } |
| |
| public static StringBuffer appendEncodedString(String str, StringBuffer buf) { |
| if (str != null) { |
| buf.append('[').append(str.length()).append(']').append(str); |
| } else { |
| buf.append('[').append(']'); |
| } |
| return buf; |
| } |
| |
| public static StringBuffer appendEncodedFilter(IPath[] filters, StringBuffer buf) { |
| if (filters != null) { |
| buf.append('[').append(filters.length).append(']'); |
| for (int i= 0; i < filters.length; i++) { |
| appendEncodePath(filters[i], buf).append(';'); |
| } |
| } else { |
| buf.append('[').append(']'); |
| } |
| return buf; |
| } |
| |
| public static StringBuffer appendEncodedAccessRules(IAccessRule[] rules, StringBuffer buf) { |
| if (rules != null) { |
| buf.append('[').append(rules.length).append(']'); |
| for (int i= 0; i < rules.length; i++) { |
| appendEncodePath(rules[i].getPattern(), buf).append(';'); |
| buf.append(rules[i].getKind()).append(';'); |
| } |
| } else { |
| buf.append('[').append(']'); |
| } |
| return buf; |
| } |
| |
| |
| public StringBuffer appendEncodedSettings(StringBuffer buf) { |
| buf.append(fEntryKind).append(';'); |
| if (getLinkTarget() == null) { |
| appendEncodePath(fPath, buf).append(';'); |
| } else { |
| appendEncodePath(fPath, buf).append('-').append('>'); |
| appendEncodePath(getLinkTarget(), buf).append(';'); |
| } |
| buf.append(Boolean.valueOf(fIsExported)).append(';'); |
| for (int i= 0; i < fChildren.size(); i++) { |
| Object curr= fChildren.get(i); |
| if (curr instanceof CPListElementAttribute) { |
| CPListElementAttribute elem= (CPListElementAttribute) curr; |
| if (elem.isBuiltIn()) { |
| String key= elem.getKey(); |
| if (OUTPUT.equals(key) || SOURCEATTACHMENT.equals(key)) { |
| appendEncodePath((IPath) elem.getValue(), buf).append(';'); |
| } else if (EXCLUSION.equals(key) || INCLUSION.equals(key)) { |
| appendEncodedFilter((IPath[]) elem.getValue(), buf).append(';'); |
| } else if (ACCESSRULES.equals(key)) { |
| appendEncodedAccessRules((IAccessRule[]) elem.getValue(), buf).append(';'); |
| } else if (COMBINE_ACCESSRULES.equals(key)) { |
| buf.append(((Boolean) elem.getValue()).booleanValue()).append(';'); |
| } else if (MODULE.equals(key)) { |
| Object value= elem.getValue(); |
| if (value instanceof ModuleEncapsulationDetail[]) { |
| buf.append(MODULE+"=true;"); //$NON-NLS-1$ |
| for (ModuleEncapsulationDetail detail : ((ModuleEncapsulationDetail[]) value)) { |
| if (detail instanceof ModulePatch) |
| buf.append(IClasspathAttribute.PATCH_MODULE+':'+detail.toString()).append(';'); |
| if (detail instanceof ModuleAddExport) |
| buf.append(IClasspathAttribute.ADD_EXPORTS+':'+detail.toString()).append(';'); |
| if (detail instanceof ModuleAddReads) |
| buf.append(IClasspathAttribute.ADD_READS+':'+detail.toString()).append(';'); |
| if (detail instanceof LimitModules) |
| buf.append(IClasspathAttribute.LIMIT_MODULES+':'+detail.toString()).append(';'); |
| } |
| } else { |
| buf.append(MODULE+"=false;"); //$NON-NLS-1$ |
| } |
| } |
| } else { |
| appendEncodedString((String) elem.getValue(), buf); |
| } |
| } |
| } |
| return buf; |
| } |
| |
| public IPath getLinkTarget() { |
| return fLinkTarget; |
| } |
| |
| public void setPath(IPath path) { |
| fCachedEntry= null; |
| fPath= path; |
| } |
| |
| public void setLinkTarget(IPath linkTarget) { |
| fCachedEntry= null; |
| fLinkTarget= linkTarget; |
| } |
| |
| public static void insert(CPListElement element, List<CPListElement> cpList) { |
| int length= cpList.size(); |
| CPListElement[] elements= cpList.toArray(new CPListElement[length]); |
| int i= 0; |
| while (i < length && elements[i].getEntryKind() != element.getEntryKind()) { |
| i++; |
| } |
| if (i < length) { |
| i++; |
| while (i < length && elements[i].getEntryKind() == element.getEntryKind()) { |
| i++; |
| } |
| cpList.add(i, element); |
| return; |
| } |
| |
| switch (element.getEntryKind()) { |
| case IClasspathEntry.CPE_SOURCE: |
| cpList.add(0, element); |
| break; |
| case IClasspathEntry.CPE_CONTAINER: |
| case IClasspathEntry.CPE_LIBRARY: |
| case IClasspathEntry.CPE_PROJECT: |
| case IClasspathEntry.CPE_VARIABLE: |
| default: |
| cpList.add(element); |
| break; |
| } |
| } |
| |
| public static IClasspathEntry[] convertToClasspathEntries(List<CPListElement> cpList) { |
| IClasspathEntry[] result= new IClasspathEntry[cpList.size()]; |
| int i= 0; |
| for (Iterator<CPListElement> iter= cpList.iterator(); iter.hasNext();) { |
| CPListElement cur= iter.next(); |
| result[i]= cur.getClasspathEntry(); |
| i++; |
| } |
| return result; |
| } |
| |
| public static CPListElement[] createFromExisting(IJavaProject project) throws JavaModelException { |
| IClasspathEntry[] rawClasspath= project.getRawClasspath(); |
| CPListElement[] result= new CPListElement[rawClasspath.length]; |
| for (int i= 0; i < rawClasspath.length; i++) { |
| result[i]= CPListElement.createFromExisting(rawClasspath[i], project); |
| } |
| return result; |
| } |
| |
| public static boolean isProjectSourceFolder(CPListElement[] existing, IJavaProject project) { |
| IPath projPath= project.getProject().getFullPath(); |
| for (int i= 0; i < existing.length; i++) { |
| IClasspathEntry curr= existing[i].getClasspathEntry(); |
| if (curr.getEntryKind() == IClasspathEntry.CPE_SOURCE) { |
| if (projPath.equals(curr.getPath())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public IPath getOrginalPath() { |
| return fOrginalPath; |
| } |
| |
| public IPath getOrginalLinkTarget() { |
| return fOrginalLinkTarget; |
| } |
| |
| |
| public CPListElement copy() { |
| CPListElement result= new CPListElement(); |
| result.fProject= fProject; |
| result.fEntryKind= fEntryKind; |
| result.fPath= fPath; |
| result.fOrginalPath= fOrginalPath; |
| result.fResource= fResource; |
| result.fIsExported= fIsExported; |
| result.fIsMissing= fIsMissing; |
| result.fParentContainer= fParentContainer; |
| result.fCachedEntry= null; |
| result.fChildren= new ArrayList<>(fChildren.size()); |
| for (Iterator<Object> iterator= fChildren.iterator(); iterator.hasNext();) { |
| Object child= iterator.next(); |
| if (child instanceof CPListElement) { |
| result.fChildren.add(((CPListElement)child).copy()); |
| } else { |
| result.fChildren.add(((CPListElementAttribute)child).copy()); |
| } |
| } |
| result.fLinkTarget= fLinkTarget; |
| result.fOrginalLinkTarget= fOrginalLinkTarget; |
| return result; |
| } |
| |
| public void setAttributesFromExisting(CPListElement existing) { |
| Assert.isTrue(existing.getEntryKind() == getEntryKind()); |
| CPListElementAttribute[] attributes= existing.getAllAttributes(); |
| for (int i= 0; i < attributes.length; i++) { |
| CPListElementAttribute curr= attributes[i]; |
| CPListElementAttribute elem= findAttributeElement(curr.getKey()); |
| if (elem == null) { |
| createAttributeElement(curr.getKey(), curr.getValue(), curr.isBuiltIn()); |
| } else { |
| elem.setValue(curr.getValue()); |
| } |
| } |
| } |
| |
| void setModuleAttributeIf9OrHigher(IJavaProject project) { |
| if (!JavaModelUtil.is9OrHigher(project)) { |
| return; |
| } |
| CPListElementAttribute moduleAttribute= findAttributeElement(MODULE); |
| if (moduleAttribute == null) { |
| createAttributeElement(MODULE, new ModuleEncapsulationDetail[0], true); |
| } else { |
| moduleAttribute.setValue(new ModuleEncapsulationDetail[0]); |
| } |
| } |
| |
| public void updateExtraAttributeOfClasspathEntry() { |
| this.createAttributeElement(MODULE, new ModuleEncapsulationDetail[0], true); |
| //refresh the cached classpath entry |
| fCachedEntry= null; |
| fCachedEntry= this.getClasspathEntry(); |
| } |
| |
| boolean isRootNodeForPath() { |
| return false; |
| } |
| |
| boolean isModulePathRootNode() { |
| return false; |
| } |
| |
| boolean isClassPathRootNode() { |
| return false; |
| } |
| |
| |
| } |