| /******************************************************************************* |
| * Copyright (c) 2005, 2018 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.core; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.AssertionFailedException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.dltk.compiler.CharOperation; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.DLTKLanguageManager; |
| import org.eclipse.dltk.core.IAccessRule; |
| import org.eclipse.dltk.core.IBuildpathAttribute; |
| import org.eclipse.dltk.core.IBuildpathContainer; |
| import org.eclipse.dltk.core.IBuildpathContainerExtension2; |
| import org.eclipse.dltk.core.IBuildpathEntry; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IModelStatus; |
| import org.eclipse.dltk.core.IModelStatusConstants; |
| import org.eclipse.dltk.core.IProjectFragment; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.environment.EnvironmentManager; |
| import org.eclipse.dltk.core.environment.EnvironmentPathUtils; |
| import org.eclipse.dltk.core.environment.IFileHandle; |
| import org.eclipse.dltk.internal.compiler.env.AccessRule; |
| import org.eclipse.dltk.internal.compiler.env.AccessRuleSet; |
| import org.eclipse.dltk.internal.core.util.Messages; |
| import org.eclipse.dltk.internal.core.util.Util; |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.Text; |
| |
| public class BuildpathEntry implements IBuildpathEntry { |
| public static final String TAG_BUILDPATH = "buildpath"; //$NON-NLS-1$ |
| public static final String TAG_BUILDPATHENTRY = "buildpathentry"; //$NON-NLS-1$ |
| public static final String TAG_KIND = "kind"; //$NON-NLS-1$ |
| public static final String TAG_PATH = "path"; //$NON-NLS-1$ |
| public static final String TAG_SOURCEPATH = "sourcepath"; //$NON-NLS-1$ |
| public static final String TAG_ROOTPATH = "rootpath"; //$NON-NLS-1$ |
| public static final String TAG_EXPORTED = "exported"; //$NON-NLS-1$ |
| public static final String TAG_EXTERNAL = "external"; //$NON-NLS-1$ |
| public static final String TAG_INCLUDING = "including"; //$NON-NLS-1$ |
| public static final String TAG_EXCLUDING = "excluding"; //$NON-NLS-1$ |
| public static final String TAG_ACCESS_RULES = "accessrules"; //$NON-NLS-1$ |
| public static final String TAG_ACCESS_RULE = "accessrule"; //$NON-NLS-1$ |
| public static final String TAG_PATTERN = "pattern"; //$NON-NLS-1$ |
| public static final String TAG_ATTRIBUTES = "attributes"; //$NON-NLS-1$ |
| public static final String TAG_ATTRIBUTE = "attribute"; //$NON-NLS-1$ |
| public static final String TAG_ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ |
| public static final String TAG_ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$ |
| public static final String TAG_COMBINE_ACCESS_RULES = "combineaccessrules"; //$NON-NLS-1$ |
| public static final String TAG_ACCESSIBLE = "accessible"; //$NON-NLS-1$ |
| public static final String TAG_NON_ACCESSIBLE = "nonaccessible"; //$NON-NLS-1$ |
| public static final String TAG_DISCOURAGED = "discouraged"; //$NON-NLS-1$ |
| public static final String TAG_IGNORE_IF_BETTER = "ignoreifbetter"; //$NON-NLS-1$ |
| /** |
| * Describes the kind of project fragment found on this buildpath entry - |
| * either K_BINARY or K_SOURCE |
| */ |
| private int contentKind; |
| /** |
| * Describes the kind of buildpath entry - one of BPE_PROJECT, BPE_SOURCE or |
| * BPE_CONTAINER |
| */ |
| private int entryKind; |
| /** |
| * The meaning of the path of a buildpath entry depends on its entry kind: |
| * <ul> |
| * <li>Source code in the current project (<code>BPE_SOURCE</code>) - The |
| * path associated with this entry is the absolute path to the root |
| * folder.</li> |
| * <li>A required project (<code>BPE_PROJECT</code>) - the path of the entry |
| * denotes the path to the corresponding project resource.</li> |
| * <li>A container entry (<code>BPE_CONTAINER</code>) - the first segment of |
| * the path is denoting the unique container identifier (for which a |
| * <code>BuildpathContainerInitializer</code> could be registered), and the |
| * remaining segments are used as additional hints for resolving the |
| * container entry to an actual <code>IBuildpathContainer</code>.</li> |
| */ |
| private IPath path; |
| /** |
| * Patterns allowing to include/exclude portions of the resource tree |
| * denoted by this entry path. |
| */ |
| private IPath[] inclusionPatterns; |
| private char[][] fullInclusionPatternChars; |
| private IPath[] exclusionPatterns; |
| private char[][] fullExclusionPatternChars; |
| private final static char[][] UNINIT_PATTERNS = new char[][] { |
| "Non-initialized yet".toCharArray() }; //$NON-NLS-1$ |
| public static final BuildpathEntry[] NO_ENTRIES = new BuildpathEntry[0]; |
| private boolean combineAccessRules; |
| private String rootID; |
| private AccessRuleSet accessRuleSet; |
| |
| static class UnknownXmlElements { |
| String[] attributes; |
| ArrayList children; |
| } |
| |
| /* |
| * Default inclusion pattern set |
| */ |
| public final static IPath[] INCLUDE_ALL = {}; |
| /* |
| * Default exclusion pattern set |
| */ |
| public final static IPath[] EXCLUDE_NONE = {}; |
| /** |
| * The export flag |
| */ |
| boolean isExported; |
| |
| /** |
| * Describes the path to the source archive/folder associated with this |
| * classpath entry, or <code>null</code> if this buildpath entry has no |
| * source attachment. |
| * <p> |
| * Only library and variable buildpath entries may have source attachments. |
| * For library buildpath entries, the result path (if present) locates a |
| * source archive. For variable classpath entries, the result path (if |
| * present) has an analogous form and meaning as the variable path, namely |
| * the first segment is the name of a classpath variable. |
| */ |
| public IPath sourceAttachmentPath; |
| |
| /** |
| * Describes the path within the source archive where package fragments are |
| * located. An empty path indicates that packages are located at the root of |
| * the source archive. Returns a non-<code>null</code> value if and only if |
| * <code>getSourceAttachmentPath</code> returns a non-<code>null</code> |
| * value. |
| */ |
| public IPath sourceAttachmentRootPath; |
| /* |
| * The extra attributes |
| */ |
| IBuildpathAttribute[] extraAttributes; |
| |
| /** |
| * External library extry. |
| */ |
| private boolean isExternal; |
| |
| /** |
| * Creates a build path entry of the specified kind with the given path. |
| * |
| * @param externalLib |
| * TODO |
| */ |
| public BuildpathEntry(int contentKind, int entryKind, IPath path, |
| boolean isExported, IPath[] inclusionPatterns, |
| IPath[] exclusionPatterns, |
| org.eclipse.dltk.core.IAccessRule[] accessRules, |
| boolean combineAccessRules, IBuildpathAttribute[] extraAttributes, |
| boolean externalLib) { |
| // if this is external entry, path should be full |
| Assert.isLegal(!externalLib || EnvironmentPathUtils.isFull(path) || path |
| .segment(0).startsWith(IBuildpathEntry.BUILDPATH_SPECIAL)); |
| |
| this.contentKind = contentKind; |
| this.entryKind = entryKind; |
| this.path = path; |
| this.isExported = isExported; |
| this.inclusionPatterns = inclusionPatterns; |
| this.exclusionPatterns = exclusionPatterns; |
| this.isExternal = externalLib; |
| int length; |
| if (accessRules != null && (length = accessRules.length) > 0) { |
| AccessRule[] rules = new AccessRule[length]; |
| System.arraycopy(accessRules, 0, rules, 0, length); |
| this.accessRuleSet = new AccessRuleSet(rules, |
| getMessageTemplates()); |
| } |
| this.combineAccessRules = combineAccessRules; |
| this.extraAttributes = extraAttributes; |
| if (inclusionPatterns != INCLUDE_ALL && inclusionPatterns.length > 0) { |
| this.fullInclusionPatternChars = UNINIT_PATTERNS; |
| } |
| if (exclusionPatterns.length > 0) { |
| this.fullExclusionPatternChars = UNINIT_PATTERNS; |
| } |
| } |
| |
| @Override |
| public IPath getSourceAttachmentPath() { |
| return sourceAttachmentPath; |
| } |
| |
| @Override |
| public IPath getSourceAttachmentRootPath() { |
| return sourceAttachmentRootPath; |
| } |
| |
| @Override |
| public void setSourceAttachmentPath(IPath sourceAttachmentPath) { |
| this.sourceAttachmentPath = sourceAttachmentPath; |
| } |
| |
| @Override |
| public void setSourceAttachmentRootPath(IPath sourceAttachmentRootPath) { |
| this.sourceAttachmentRootPath = sourceAttachmentRootPath; |
| } |
| |
| @Override |
| public int getEntryKind() { |
| return entryKind; |
| } |
| |
| @Override |
| public IPath[] getExclusionPatterns() { |
| return this.exclusionPatterns; |
| } |
| |
| @Override |
| public IBuildpathAttribute[] getExtraAttributes() { |
| return this.extraAttributes; |
| } |
| |
| @Override |
| public String getExtraAttribute(String name) { |
| if (name != null) { |
| for (IBuildpathAttribute attribute : extraAttributes) { |
| if (name.equals(attribute.getName())) { |
| return attribute.getValue(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| private String[] getMessageTemplates() { |
| ModelManager manager = ModelManager.getModelManager(); |
| String[] result = new String[AccessRuleSet.MESSAGE_TEMPLATES_LENGTH]; |
| if (this.entryKind == BPE_PROJECT || this.entryKind == BPE_SOURCE) { |
| /* |
| * can be remote source entry when reconciling |
| */ |
| result[0] = manager.intern(Messages.bind( |
| org.eclipse.dltk.internal.core.util.Messages.restrictedAccess_project, |
| new String[] { "{0}", getPath().segment(0) })); //$NON-NLS-1$ |
| result[1] = manager.intern(Messages.bind( |
| org.eclipse.dltk.internal.core.util.Messages.restrictedAccess_constructor_project, |
| new String[] { "{0}", getPath().segment(0) })); //$NON-NLS-1$ |
| result[2] = manager.intern(Messages.bind( |
| org.eclipse.dltk.internal.core.util.Messages.restrictedAccess_method_project, |
| new String[] { "{0}", "{1}", getPath().segment(0) })); //$NON-NLS-1$ //$NON-NLS-2$ |
| result[3] = manager.intern(Messages.bind( |
| org.eclipse.dltk.internal.core.util.Messages.restrictedAccess_field_project, |
| new String[] { "{0}", "{1}", getPath().segment(0) })); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| IPath libPath = getPath(); |
| Object target = Model.getTarget( |
| ResourcesPlugin.getWorkspace().getRoot(), libPath, false); |
| String pathString; |
| if (target instanceof IFileHandle) { |
| pathString = target.toString(); |
| } else { |
| pathString = libPath.makeRelative().toString(); |
| } |
| |
| result[0] = manager.intern(Messages.bind( |
| org.eclipse.dltk.internal.core.util.Messages.restrictedAccess_library, |
| new String[] { "{0}", pathString })); //$NON-NLS-1$ |
| result[1] = manager.intern(Messages.bind( |
| org.eclipse.dltk.internal.core.util.Messages.restrictedAccess_constructor_library, |
| new String[] { "{0}", pathString })); //$NON-NLS-1$ |
| result[2] = manager.intern(Messages.bind( |
| org.eclipse.dltk.internal.core.util.Messages.restrictedAccess_method_library, |
| new String[] { "{0}", "{1}", pathString })); //$NON-NLS-1$ //$NON-NLS-2$ |
| result[3] = manager.intern(Messages.bind( |
| org.eclipse.dltk.internal.core.util.Messages.restrictedAccess_field_library, |
| new String[] { "{0}", "{1}", pathString })); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return result; |
| } |
| |
| /** |
| * @see IBuildpathEntry#getExclusionPatterns() |
| */ |
| @Override |
| public IPath[] getInclusionPatterns() { |
| return this.inclusionPatterns; |
| } |
| |
| @Override |
| public IPath getPath() { |
| return path; |
| } |
| |
| /** |
| * @see IBuildpathEntry#isExported() |
| */ |
| @Override |
| public boolean isExported() { |
| return this.isExported; |
| } |
| |
| public boolean isOptional() { |
| for (int i = 0, length = this.extraAttributes.length; i < length; i++) { |
| IBuildpathAttribute attribute = this.extraAttributes[i]; |
| if (IBuildpathAttribute.OPTIONAL.equals(attribute.getName()) |
| && "true".equals(attribute.getValue())) //$NON-NLS-1$ |
| return true; |
| } |
| return false; |
| } |
| |
| public BuildpathEntry resolvedDotDot(IPath reference) { |
| // TODO (alex) port the implementation |
| return this; |
| } |
| |
| public BuildpathEntry[] resolvedChainedLibraries() { |
| // TODO (alex) port the implementation |
| return NO_ENTRIES; |
| } |
| |
| /** |
| * Answers an ID which is used to distinguish entries during package |
| * fragment root computations |
| */ |
| public String rootID() { |
| if (this.rootID == null) { |
| switch (this.entryKind) { |
| case IBuildpathEntry.BPE_PROJECT: |
| this.rootID = "[PRJ]" + this.path; //$NON-NLS-1$ |
| break; |
| case IBuildpathEntry.BPE_LIBRARY: |
| this.rootID = "[LIB]" + this.path; //$NON-NLS-1$ |
| break; |
| case IBuildpathEntry.BPE_SOURCE: |
| this.rootID = "[SRC]" + this.path; //$NON-NLS-1$ |
| break; |
| case IBuildpathEntry.BPE_CONTAINER: |
| this.rootID = "[CON]" + this.path; //$NON-NLS-1$ |
| break; |
| default: |
| this.rootID = ""; //$NON-NLS-1$ |
| break; |
| } |
| } |
| return this.rootID; |
| } |
| |
| @Override |
| public boolean combineAccessRules() { |
| return this.combineAccessRules; |
| } |
| |
| /** |
| * Used to perform export/restriction propagation across referring |
| * projects/containers |
| */ |
| public BuildpathEntry combineWith(BuildpathEntry referringEntry) { |
| if (referringEntry == null) |
| return this; |
| if (referringEntry.isExported() |
| || referringEntry.getAccessRuleSet() != null) { |
| boolean combine = this.entryKind == BPE_SOURCE |
| || referringEntry.combineAccessRules(); |
| BuildpathEntry newEntry = new BuildpathEntry(getContentKind(), |
| getEntryKind(), getPath(), |
| referringEntry.isExported() || this.isExported, // duplicate |
| // container |
| // entry for |
| // tagging |
| // it as |
| // exported |
| this.inclusionPatterns, this.exclusionPatterns, |
| combine(referringEntry.getAccessRules(), getAccessRules(), |
| combine), |
| this.combineAccessRules, this.extraAttributes, |
| this.isExternal); |
| newEntry.setSourceAttachmentPath(getSourceAttachmentPath()); |
| newEntry.setSourceAttachmentRootPath(getSourceAttachmentRootPath()); |
| return newEntry; |
| } |
| // no need to clone |
| return this; |
| } |
| |
| private IAccessRule[] combine(IAccessRule[] referringRules, |
| IAccessRule[] rules, boolean combine) { |
| if (!combine) |
| return rules; |
| if (rules == null || rules.length == 0) |
| return referringRules; |
| // concat access rules |
| int referringRulesLength = referringRules.length; |
| int accessRulesLength = rules.length; |
| int rulesLength = referringRulesLength + accessRulesLength; |
| IAccessRule[] result = new IAccessRule[rulesLength]; |
| System.arraycopy(referringRules, 0, result, 0, referringRulesLength); |
| System.arraycopy(rules, 0, result, referringRulesLength, |
| accessRulesLength); |
| return result; |
| } |
| |
| /** |
| * @see IBuildpathEntry#getAccessRules() |
| */ |
| @Override |
| public IAccessRule[] getAccessRules() { |
| if (this.accessRuleSet == null) |
| return NO_ACCESS_RULES; |
| AccessRule[] rules = this.accessRuleSet.getAccessRules(); |
| int length = rules.length; |
| if (length == 0) |
| return NO_ACCESS_RULES; |
| IAccessRule[] result = new IAccessRule[length]; |
| System.arraycopy(rules, 0, result, 0, length); |
| return result; |
| } |
| |
| public static IAccessRule[] getAccessRules(IPath[] accessibleFiles, |
| IPath[] nonAccessibleFiles) { |
| int accessibleFilesLength = accessibleFiles == null ? 0 |
| : accessibleFiles.length; |
| int nonAccessibleFilesLength = nonAccessibleFiles == null ? 0 |
| : nonAccessibleFiles.length; |
| int length = accessibleFilesLength + nonAccessibleFilesLength; |
| if (length == 0) |
| return null; |
| IAccessRule[] accessRules = new IAccessRule[length]; |
| for (int i = 0; i < accessibleFilesLength; i++) { |
| accessRules[i] = DLTKCore.newAccessRule(accessibleFiles[i], |
| IAccessRule.K_ACCESSIBLE); |
| } |
| for (int i = 0; i < nonAccessibleFilesLength; i++) { |
| accessRules[accessibleFilesLength + i] = DLTKCore.newAccessRule( |
| nonAccessibleFiles[i], IAccessRule.K_NON_ACCESSIBLE); |
| } |
| return accessRules; |
| } |
| |
| /** |
| * Returns a printable representation of this buildpath entry. |
| */ |
| @Override |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append(getPath().toString()); |
| buffer.append('['); |
| switch (getEntryKind()) { |
| case IBuildpathEntry.BPE_LIBRARY: |
| buffer.append("BPE_LIBRARY"); //$NON-NLS-1$ |
| break; |
| case IBuildpathEntry.BPE_PROJECT: |
| buffer.append("BPE_PROJECT"); //$NON-NLS-1$ |
| break; |
| case IBuildpathEntry.BPE_SOURCE: |
| buffer.append("BPE_SOURCE"); //$NON-NLS-1$ |
| break; |
| case IBuildpathEntry.BPE_VARIABLE: |
| buffer.append("BPE_VARIABLE"); //$NON-NLS-1$ |
| break; |
| case IBuildpathEntry.BPE_CONTAINER: |
| buffer.append("BPE_CONTAINER"); //$NON-NLS-1$ |
| break; |
| } |
| buffer.append("]["); //$NON-NLS-1$ |
| switch (getContentKind()) { |
| case IProjectFragment.K_BINARY: |
| buffer.append("K_BINARY"); //$NON-NLS-1$ |
| break; |
| case IProjectFragment.K_SOURCE: |
| buffer.append("K_SOURCE"); //$NON-NLS-1$ |
| break; |
| } |
| buffer.append(']'); |
| buffer.append("[isExported:"); //$NON-NLS-1$ |
| buffer.append(this.isExported); |
| buffer.append(']'); |
| IPath[] patterns = this.inclusionPatterns; |
| int length; |
| if ((length = patterns == null ? 0 : patterns.length) > 0) { |
| buffer.append("[including:"); //$NON-NLS-1$ |
| for (int i = 0; i < length; i++) { |
| buffer.append(patterns[i]); |
| if (i != length - 1) { |
| buffer.append('|'); |
| } |
| } |
| buffer.append(']'); |
| } |
| patterns = this.exclusionPatterns; |
| if ((length = patterns == null ? 0 : patterns.length) > 0) { |
| buffer.append("[excluding:"); //$NON-NLS-1$ |
| for (int i = 0; i < length; i++) { |
| buffer.append(patterns[i]); |
| if (i != length - 1) { |
| buffer.append('|'); |
| } |
| } |
| buffer.append(']'); |
| } |
| if (this.accessRuleSet != null) { |
| buffer.append('['); |
| buffer.append(this.accessRuleSet.toString(false/* on one line */)); |
| buffer.append(']'); |
| } |
| if (this.entryKind == BPE_PROJECT) { |
| buffer.append("[combine access rules:"); //$NON-NLS-1$ |
| buffer.append(this.combineAccessRules); |
| buffer.append(']'); |
| } |
| if ((length = this.extraAttributes == null ? 0 |
| : this.extraAttributes.length) > 0) { |
| buffer.append("[attributes:"); //$NON-NLS-1$ |
| for (int i = 0; i < length; i++) { |
| buffer.append(this.extraAttributes[i]); |
| if (i != length - 1) { |
| buffer.append(','); |
| } |
| } |
| buffer.append(']'); |
| } |
| return buffer.toString(); |
| } |
| |
| public AccessRuleSet getAccessRuleSet() { |
| return this.accessRuleSet; |
| } |
| |
| @Override |
| public int getContentKind() { |
| return contentKind; |
| } |
| |
| public static IBuildpathEntry elementDecode(Element element, |
| IScriptProject project, Map unknownElements) { |
| IPath projectPath = project.getProject().getFullPath(); |
| NamedNodeMap attributes = element.getAttributes(); |
| NodeList children = element.getChildNodes(); |
| boolean[] foundChildren = new boolean[children.getLength()]; |
| String kindAttr = removeAttribute(TAG_KIND, attributes); |
| String pathAttr = removeAttribute(TAG_PATH, attributes); |
| // ensure path is absolute |
| IPath path = new Path(pathAttr); |
| int kind = kindFromString(kindAttr); |
| if (kind != IBuildpathEntry.BPE_VARIABLE |
| && kind != IBuildpathEntry.BPE_CONTAINER |
| && !path.isAbsolute()) { |
| path = projectPath.append(path); |
| } |
| // exported flag (optional) |
| boolean isExported = removeAttribute(TAG_EXPORTED, attributes) |
| .equals("true"); //$NON-NLS-1$ |
| boolean isExternal = removeAttribute(TAG_EXTERNAL, attributes) |
| .equals("true"); //$NON-NLS-1$ |
| // inclusion patterns (optional) |
| IPath[] inclusionPatterns = decodePatterns(attributes, TAG_INCLUDING); |
| if (inclusionPatterns == null) |
| inclusionPatterns = INCLUDE_ALL; |
| // exclusion patterns (optional) |
| IPath[] exclusionPatterns = decodePatterns(attributes, TAG_EXCLUDING); |
| if (exclusionPatterns == null) |
| exclusionPatterns = EXCLUDE_NONE; |
| // access rules (optional) |
| NodeList attributeList = getChildAttributes(TAG_ACCESS_RULES, children, |
| foundChildren); |
| IAccessRule[] accessRules = decodeAccessRules(attributeList); |
| // combine access rules (optional) |
| boolean combineAccessRestrictions = !removeAttribute( |
| TAG_COMBINE_ACCESS_RULES, attributes).equals("false"); //$NON-NLS-1$ |
| // extra attributes (optional) |
| attributeList = getChildAttributes(TAG_ATTRIBUTES, children, |
| foundChildren); |
| IBuildpathAttribute[] extraAttributes = decodeExtraAttributes( |
| attributeList); |
| String[] unknownAttributes = null; |
| ArrayList unknownChildren = null; |
| if (unknownElements != null) { |
| // unknown attributes |
| int unknownAttributeLength = attributes.getLength(); |
| if (unknownAttributeLength != 0) { |
| unknownAttributes = new String[unknownAttributeLength * 2]; |
| for (int i = 0; i < unknownAttributeLength; i++) { |
| Node attribute = attributes.item(i); |
| unknownAttributes[i * 2] = attribute.getNodeName(); |
| unknownAttributes[i * 2 + 1] = attribute.getNodeValue(); |
| } |
| } |
| // unknown children |
| for (int i = 0, length = foundChildren.length; i < length; i++) { |
| if (!foundChildren[i]) { |
| Node node = children.item(i); |
| if (node.getNodeType() != Node.ELEMENT_NODE) |
| continue; |
| if (unknownChildren == null) |
| unknownChildren = new ArrayList(); |
| StringBuffer buffer = new StringBuffer(); |
| decodeUnknownNode(node, buffer, project); |
| unknownChildren.add(buffer.toString()); |
| } |
| } |
| } |
| // recreate the BP entry |
| IBuildpathEntry entry = null; |
| switch (kind) { |
| case IBuildpathEntry.BPE_PROJECT: |
| entry = new BuildpathEntry(IProjectFragment.K_SOURCE, |
| IBuildpathEntry.BPE_PROJECT, path, isExported, |
| BuildpathEntry.INCLUDE_ALL, // inclusion patterns |
| BuildpathEntry.EXCLUDE_NONE, // exclusion patterns |
| accessRules, combineAccessRestrictions, extraAttributes, |
| false); |
| break; |
| case IBuildpathEntry.BPE_LIBRARY: |
| if (isExternal) { |
| IPath fullPath = Path.fromPortableString(pathAttr); |
| if (!EnvironmentPathUtils.isFull(fullPath)) { |
| String environmentId = EnvironmentManager |
| .getEnvironmentId(project.getProject()); |
| if (environmentId != null) { |
| path = EnvironmentPathUtils.getFullPath(environmentId, |
| path); |
| } |
| } else { |
| path = fullPath; |
| } |
| } |
| entry = DLTKCore.newLibraryEntry(path, accessRules, extraAttributes, |
| inclusionPatterns, exclusionPatterns, isExported, |
| isExternal); |
| |
| break; |
| case IBuildpathEntry.BPE_SOURCE: |
| // entry = DLTKCore.newSourceEntry(path, inclusionPatterns, |
| // exclusionPatterns, extraAttributes); |
| String projSegment = path.segment(0); |
| if (projSegment != null |
| && projSegment.equals(project.getElementName())) { // this |
| // project |
| entry = DLTKCore.newSourceEntry(path, inclusionPatterns, |
| exclusionPatterns, extraAttributes); |
| } else { |
| if (path.segmentCount() == 1) { |
| // another project |
| entry = DLTKCore.newProjectEntry(path, accessRules, |
| combineAccessRestrictions, extraAttributes, |
| isExported); |
| } else { |
| // an invalid source folder |
| entry = DLTKCore.newSourceEntry(path, inclusionPatterns, |
| exclusionPatterns, extraAttributes); |
| } |
| } |
| break; |
| case IBuildpathEntry.BPE_VARIABLE: |
| entry = DLTKCore.newVariableEntry(path, accessRules, |
| extraAttributes, isExported); |
| break; |
| case IBuildpathEntry.BPE_CONTAINER: |
| entry = DLTKCore.newContainerEntry(path, accessRules, |
| extraAttributes, isExported); |
| break; |
| default: |
| throw new AssertionFailedException( |
| Messages.bind(Messages.buildpath_unknownKind, kindAttr)); |
| } |
| |
| String sourcePath = removeAttribute(TAG_SOURCEPATH, attributes); |
| String rootPath = removeAttribute(TAG_ROOTPATH, attributes); |
| if (sourcePath.length() > 0) { |
| entry.setSourceAttachmentPath(new Path(sourcePath)); |
| entry.setSourceAttachmentRootPath(new Path(rootPath)); |
| } |
| if (unknownAttributes != null || unknownChildren != null) { |
| UnknownXmlElements unknownXmlElements = new UnknownXmlElements(); |
| unknownXmlElements.attributes = unknownAttributes; |
| unknownXmlElements.children = unknownChildren; |
| unknownElements.put(path, unknownXmlElements); |
| } |
| return entry; |
| } |
| |
| static IBuildpathAttribute[] decodeExtraAttributes(NodeList attributes) { |
| if (attributes == null) |
| return NO_EXTRA_ATTRIBUTES; |
| int length = attributes.getLength(); |
| if (length == 0) |
| return NO_EXTRA_ATTRIBUTES; |
| IBuildpathAttribute[] result = new IBuildpathAttribute[length]; |
| int index = 0; |
| for (int i = 0; i < length; ++i) { |
| Node node = attributes.item(i); |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| Element attribute = (Element) node; |
| String name = attribute.getAttribute(TAG_ATTRIBUTE_NAME); |
| if (name == null) |
| continue; |
| String value = attribute.getAttribute(TAG_ATTRIBUTE_VALUE); |
| if (value == null) |
| continue; |
| result[index++] = new BuildpathAttribute(name, value); |
| } |
| } |
| if (index != length) |
| System.arraycopy(result, 0, result = new IBuildpathAttribute[index], |
| 0, index); |
| return result; |
| } |
| |
| static IAccessRule[] decodeAccessRules(NodeList list) { |
| if (list == null) |
| return null; |
| int length = list.getLength(); |
| if (length == 0) |
| return null; |
| IAccessRule[] result = new IAccessRule[length]; |
| int index = 0; |
| for (int i = 0; i < length; i++) { |
| Node accessRule = list.item(i); |
| if (accessRule.getNodeType() == Node.ELEMENT_NODE) { |
| Element elementAccessRule = (Element) accessRule; |
| String pattern = elementAccessRule.getAttribute(TAG_PATTERN); |
| if (pattern == null) |
| continue; |
| String tagKind = elementAccessRule.getAttribute(TAG_KIND); |
| int kind; |
| if (TAG_ACCESSIBLE.equals(tagKind)) |
| kind = IAccessRule.K_ACCESSIBLE; |
| else if (TAG_NON_ACCESSIBLE.equals(tagKind)) |
| kind = IAccessRule.K_NON_ACCESSIBLE; |
| else if (TAG_DISCOURAGED.equals(tagKind)) |
| kind = IAccessRule.K_DISCOURAGED; |
| else |
| continue; |
| boolean ignoreIfBetter = "true".equals( //$NON-NLS-1$ |
| elementAccessRule.getAttribute(TAG_IGNORE_IF_BETTER)); |
| result[index++] = new BuildpathAccessRule(new Path(pattern), |
| ignoreIfBetter ? kind | IAccessRule.IGNORE_IF_BETTER |
| : kind); |
| } |
| } |
| if (index != length) |
| System.arraycopy(result, 0, result = new IAccessRule[index], 0, |
| index); |
| return result; |
| } |
| |
| /** |
| * Decode some element tag containing a sequence of patterns into IPath[] |
| */ |
| private static IPath[] decodePatterns(NamedNodeMap nodeMap, String tag) { |
| String sequence = removeAttribute(tag, nodeMap); |
| if (!sequence.equals("")) { //$NON-NLS-1$ |
| char[][] patterns = CharOperation.splitOn('|', |
| sequence.toCharArray()); |
| int patternCount; |
| if ((patternCount = patterns.length) > 0) { |
| IPath[] paths = new IPath[patternCount]; |
| int index = 0; |
| for (int j = 0; j < patternCount; j++) { |
| char[] pattern = patterns[j]; |
| if (pattern.length == 0) |
| continue; // see |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=105581 |
| paths[index++] = new Path(new String(pattern)); |
| } |
| if (index < patternCount) |
| System.arraycopy(paths, 0, paths = new IPath[index], 0, |
| index); |
| return paths; |
| } |
| } |
| return null; |
| } |
| |
| private static void decodeUnknownNode(Node node, StringBuffer buffer, |
| IScriptProject project) { |
| ByteArrayOutputStream s = new ByteArrayOutputStream(); |
| OutputStreamWriter writer; |
| try { |
| writer = new OutputStreamWriter(s, StandardCharsets.UTF_8); // $NON-NLS-1$ |
| XMLWriter xmlWriter = new XMLWriter(writer, project, |
| false/* |
| * don't print XML version |
| */); |
| decodeUnknownNode(node, xmlWriter, true/* insert new line */); |
| xmlWriter.flush(); |
| xmlWriter.close(); |
| buffer.append(s.toString("UTF8")); //$NON-NLS-1$ |
| } catch (UnsupportedEncodingException e) { |
| // ignore (UTF8 is always supported) |
| } |
| } |
| |
| private static void decodeUnknownNode(Node node, XMLWriter xmlWriter, |
| boolean insertNewLine) { |
| switch (node.getNodeType()) { |
| case Node.ELEMENT_NODE: |
| NamedNodeMap attributes = node.getAttributes(); |
| HashMap parameters = new HashMap(); |
| for (int i = 0, length = attributes == null ? 0 |
| : attributes.getLength(); i < length; i++) { |
| Node attribute = attributes.item(i); |
| parameters.put(attribute.getNodeName(), |
| attribute.getNodeValue()); |
| } |
| NodeList children = node.getChildNodes(); |
| int childrenLength = children.getLength(); |
| String nodeName = node.getNodeName(); |
| xmlWriter.printTag(nodeName, parameters, |
| false/* don't insert tab */, false/* |
| * don't insert new line |
| */, |
| childrenLength == 0/* |
| * close tag if no children |
| */); |
| if (childrenLength > 0) { |
| for (int i = 0; i < childrenLength; i++) { |
| decodeUnknownNode(children.item(i), xmlWriter, |
| false/* |
| * don't insert new line |
| */); |
| } |
| xmlWriter.endTag(nodeName, false/* don't insert tab */, |
| insertNewLine); |
| } |
| break; |
| case Node.TEXT_NODE: |
| String data = ((Text) node).getData(); |
| xmlWriter.printString(data, false/* don't insert tab */, |
| false/* |
| * don't insert new line |
| */); |
| break; |
| } |
| } |
| |
| /* |
| * Returns a char based representation of the exclusions patterns full path. |
| */ |
| public char[][] fullExclusionPatternChars() { |
| if (this.fullExclusionPatternChars == UNINIT_PATTERNS) { |
| int length = this.exclusionPatterns.length; |
| this.fullExclusionPatternChars = new char[length][]; |
| IPath prefixPath = this.path.removeTrailingSeparator(); |
| for (int i = 0; i < length; i++) { |
| this.fullExclusionPatternChars[i] = prefixPath |
| .append(this.exclusionPatterns[i]).toString() |
| .toCharArray(); |
| } |
| } |
| return this.fullExclusionPatternChars; |
| } |
| |
| /* |
| * Returns a char based representation of the exclusions patterns full path. |
| */ |
| public char[][] fullInclusionPatternChars() { |
| if (this.fullInclusionPatternChars == UNINIT_PATTERNS) { |
| int length = this.inclusionPatterns.length; |
| this.fullInclusionPatternChars = new char[length][]; |
| IPath prefixPath = this.path.removeTrailingSeparator(); |
| for (int i = 0; i < length; i++) { |
| this.fullInclusionPatternChars[i] = prefixPath |
| .append(this.inclusionPatterns[i]).toString() |
| .toCharArray(); |
| } |
| } |
| return this.fullInclusionPatternChars; |
| } |
| |
| public static NodeList getChildAttributes(String childName, |
| NodeList children, boolean[] foundChildren) { |
| for (int i = 0, length = foundChildren.length; i < length; i++) { |
| Node node = children.item(i); |
| if (childName.equals(node.getNodeName())) { |
| foundChildren[i] = true; |
| return node.getChildNodes(); |
| } |
| } |
| return null; |
| } |
| |
| private static String removeAttribute(String nodeName, |
| NamedNodeMap nodeMap) { |
| Node node = removeNode(nodeName, nodeMap); |
| if (node == null) |
| return ""; // //$NON-NLS-1$ |
| return node.getNodeValue(); |
| } |
| |
| private static Node removeNode(String nodeName, NamedNodeMap nodeMap) { |
| try { |
| return nodeMap.removeNamedItem(nodeName); |
| } catch (DOMException e) { |
| if (e.code != DOMException.NOT_FOUND_ERR) |
| throw e; |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the kind of a <code>ProjectFragment</code> from its |
| * <code>String</code> form. |
| */ |
| static int kindFromString(String kindStr) { |
| if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$ |
| return IBuildpathEntry.BPE_PROJECT; |
| if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$ |
| return IBuildpathEntry.BPE_VARIABLE; |
| if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$ |
| return IBuildpathEntry.BPE_CONTAINER; |
| if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$ |
| return IBuildpathEntry.BPE_SOURCE; |
| if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$ |
| return IBuildpathEntry.BPE_LIBRARY; |
| return -1; |
| } |
| |
| /** |
| * Returns the XML encoding of the class path. |
| */ |
| public void elementEncode(XMLWriter writer, IPath projectPath, |
| boolean indent, boolean newLine, Map unknownElements) { |
| Map<String, String> parameters = new HashMap<>(); |
| parameters.put(TAG_KIND, kindToString(this.entryKind)); |
| IPath xmlPath = this.path; |
| if (this.entryKind != IBuildpathEntry.BPE_CONTAINER |
| && this.entryKind != IBuildpathEntry.BPE_CONTAINER) { |
| // translate to project relative from absolute (unless a device |
| // path) |
| if (EnvironmentPathUtils.isFull(xmlPath)) { |
| xmlPath = EnvironmentPathUtils.getLocalPath(path); |
| } |
| if (xmlPath.isAbsolute()) { |
| if (projectPath != null && projectPath.isPrefixOf(xmlPath)) { |
| if (xmlPath.segment(0).equals(projectPath.segment(0))) { |
| xmlPath = xmlPath.removeFirstSegments(1); |
| xmlPath = xmlPath.makeRelative(); |
| } else { |
| xmlPath = xmlPath.makeAbsolute(); |
| } |
| } |
| } |
| } |
| parameters.put(TAG_PATH, String.valueOf(xmlPath)); |
| if (this.isExported) { |
| parameters.put(TAG_EXPORTED, "true");//$NON-NLS-1$ |
| } |
| if (this.isExternal) { |
| parameters.put(TAG_EXTERNAL, "true");//$NON-NLS-1$ |
| } |
| if (this.sourceAttachmentPath != null) { |
| parameters.put(TAG_SOURCEPATH, |
| this.sourceAttachmentPath.toString()); |
| } |
| if (this.sourceAttachmentRootPath != null) { |
| parameters.put(TAG_ROOTPATH, |
| this.sourceAttachmentRootPath.toString()); |
| } |
| encodePatterns(this.inclusionPatterns, TAG_INCLUDING, parameters); |
| encodePatterns(this.exclusionPatterns, TAG_EXCLUDING, parameters); |
| if (this.entryKind == BPE_PROJECT && !this.combineAccessRules) { |
| parameters.put(TAG_COMBINE_ACCESS_RULES, "false"); //$NON-NLS-1$ |
| } |
| // unknown attributes |
| UnknownXmlElements unknownXmlElements = unknownElements == null ? null |
| : (UnknownXmlElements) unknownElements.get(this.path); |
| String[] unknownAttributes; |
| if (unknownXmlElements != null |
| && (unknownAttributes = unknownXmlElements.attributes) != null) { |
| for (int i = 0, length = unknownAttributes.length; i < length; i += 2) { |
| String tagName = unknownAttributes[i]; |
| String tagValue = unknownAttributes[i + 1]; |
| parameters.put(tagName, tagValue); |
| } |
| } |
| boolean hasExtraAttributes = this.extraAttributes.length != 0; |
| boolean hasRestrictions = getAccessRuleSet() != null; // access rule |
| // set is null if no access rules |
| ArrayList unknownChildren = unknownXmlElements != null |
| ? unknownXmlElements.children |
| : null; |
| boolean hasUnknownChildren = unknownChildren != null; |
| writer.printTag(TAG_BUILDPATHENTRY, parameters, indent, newLine, |
| !hasExtraAttributes && !hasRestrictions |
| && !hasUnknownChildren/* |
| * close tag if no extra |
| * attributes , no restriction |
| * and no unknown children |
| */); |
| if (hasExtraAttributes) |
| encodeExtraAttributes(writer, indent, newLine); |
| if (hasRestrictions) |
| encodeAccessRules(writer, indent, newLine); |
| if (hasUnknownChildren) |
| encodeUnknownChildren(writer, indent, newLine, unknownChildren); |
| if (hasUnknownChildren) |
| writer.endTag(TAG_BUILDPATHENTRY, indent, |
| true/* insert new line */); |
| if (hasExtraAttributes || hasRestrictions || hasUnknownChildren) |
| writer.endTag(TAG_BUILDPATHENTRY, indent, |
| true/* insert new line */); |
| } |
| |
| void encodeExtraAttributes(XMLWriter writer, boolean indent, |
| boolean newLine) { |
| writer.startTag(TAG_ATTRIBUTES, indent); |
| for (int i = 0; i < this.extraAttributes.length; i++) { |
| IBuildpathAttribute attribute = this.extraAttributes[i]; |
| HashMap parameters = new HashMap(); |
| parameters.put(TAG_ATTRIBUTE_NAME, attribute.getName()); |
| parameters.put(TAG_ATTRIBUTE_VALUE, attribute.getValue()); |
| writer.printTag(TAG_ATTRIBUTE, parameters, indent, newLine, true); |
| } |
| writer.endTag(TAG_ATTRIBUTES, indent, true/* insert new line */); |
| } |
| |
| void encodeAccessRules(XMLWriter writer, boolean indent, boolean newLine) { |
| writer.startTag(TAG_ACCESS_RULES, indent); |
| AccessRule[] rules = getAccessRuleSet().getAccessRules(); |
| for (int i = 0, length = rules.length; i < length; i++) { |
| encodeAccessRule(rules[i], writer, indent, newLine); |
| } |
| writer.endTag(TAG_ACCESS_RULES, indent, true/* insert new line */); |
| } |
| |
| private void encodeAccessRule(AccessRule accessRule, XMLWriter writer, |
| boolean indent, boolean newLine) { |
| HashMap parameters = new HashMap(); |
| parameters.put(TAG_PATTERN, new String(accessRule.pattern)); |
| switch (accessRule.getProblemId()) { |
| case org.eclipse.dltk.compiler.problem.IProblem.ForbiddenReference: |
| parameters.put(TAG_KIND, TAG_NON_ACCESSIBLE); |
| break; |
| case org.eclipse.dltk.compiler.problem.IProblem.DiscouragedReference: |
| parameters.put(TAG_KIND, TAG_DISCOURAGED); |
| break; |
| default: |
| parameters.put(TAG_KIND, TAG_ACCESSIBLE); |
| break; |
| } |
| if (accessRule.ignoreIfBetter()) |
| parameters.put(TAG_IGNORE_IF_BETTER, "true"); //$NON-NLS-1$ |
| writer.printTag(TAG_ACCESS_RULE, parameters, indent, newLine, true); |
| } |
| |
| /** |
| * Returns a <code>String</code> for the kind of a class path entry. |
| */ |
| static String kindToString(int kind) { |
| switch (kind) { |
| case IBuildpathEntry.BPE_PROJECT: |
| return "prj"; // backward compatibility //$NON-NLS-1$ |
| case IBuildpathEntry.BPE_SOURCE: |
| return "src"; //$NON-NLS-1$ |
| case IBuildpathEntry.BPE_LIBRARY: |
| return "lib"; //$NON-NLS-1$ |
| case IBuildpathEntry.BPE_VARIABLE: |
| return "var"; //$NON-NLS-1$ |
| case IBuildpathEntry.BPE_CONTAINER: |
| return "con"; //$NON-NLS-1$ |
| default: |
| return "unknown"; //$NON-NLS-1$ |
| } |
| } |
| |
| private void encodeUnknownChildren(XMLWriter writer, boolean indent, |
| boolean newLine, ArrayList unknownChildren) { |
| for (int i = 0, length = unknownChildren.size(); i < length; i++) { |
| String child = (String) unknownChildren.get(i); |
| writer.printString(child, indent, false/* don't insert new line */); |
| } |
| } |
| |
| /** |
| * Validate a given buildpath for a project, using the following rules: |
| * <ul> |
| * <li>Buildpath entries cannot collide with each other; that is, all entry |
| * paths must be unique. * |
| * <li>A project entry cannot refer to itself directly (that is, a project |
| * cannot prerequisite itself). |
| * <li>Buildpath entries cannot coincidate or be nested in each other. |
| * </ul> |
| * |
| * Note that the buildpath entries are not validated automatically. Only |
| * bound variables or containers are considered in the checking process |
| * (this allows to perform a consistency check on a buildpath which has |
| * references to yet non existing projects, folders, ...). |
| * <p> |
| * This validation is intended to anticipate buildpath issues prior to |
| * assigning it to a project. In particular, it will automatically be |
| * performed during the buildpath setting operation (if validation fails, |
| * the buildpath setting will not complete). |
| * <p> |
| * |
| * @param scriptProject |
| * the given script project |
| * @param rawBuildpath |
| * a given buildpath |
| * @return a status object with code <code>ModelStatus.VERIFIED_OK</code> if |
| * the given buildpath are correct, otherwise a status object |
| * indicating what is wrong with the buildpath |
| */ |
| public static IModelStatus validateBuildpath(IScriptProject scriptProject, |
| IBuildpathEntry[] rawBuildpath) { |
| // TODO implement |
| IProject project = scriptProject.getProject(); |
| IPath projectPath = project.getFullPath(); |
| String projectName = scriptProject.getElementName(); |
| // tolerate null path, it will be reset to default |
| if (rawBuildpath == null) { |
| return ModelStatus.VERIFIED_OK; |
| } |
| int interpreterCount = 0; |
| for (int i = 0; i < rawBuildpath.length; ++i) { |
| final IBuildpathEntry entry = rawBuildpath[i]; |
| if (entry.getEntryKind() == BPE_CONTAINER |
| && BuiltinProjectFragment.INTERPRETER_CONTAINER |
| .equals(entry.getPath().segment(0))) { |
| ++interpreterCount; |
| if (interpreterCount > 1) { |
| return new ModelStatus(IModelStatusConstants.NAME_COLLISION, |
| Messages.buildpath_multipleInterpreters); |
| } |
| } |
| } |
| final HashSet<IPath> paths = new HashSet<>(rawBuildpath.length); |
| for (IBuildpathEntry entry : rawBuildpath) { |
| final IPath entryPath = entry.getPath(); |
| if (!paths.add(entryPath)) { |
| String entryPathMsg = projectName.equals(entryPath.segment(0)) |
| ? entryPath.removeFirstSegments(1).toString() |
| : EnvironmentPathUtils.getLocalPath(entryPath) |
| .makeRelative().toString(); |
| return new ModelStatus(IModelStatusConstants.NAME_COLLISION, |
| Messages.bind(Messages.buildpath_duplicateEntryPath, |
| new String[] { entryPathMsg, projectName })); |
| } |
| } |
| // retrieve resolved buildpath |
| IBuildpathEntry[] buildpath; |
| try { |
| buildpath = ((ScriptProject) scriptProject).resolveBuildpath( |
| rawBuildpath, false/* |
| * don't use previous session |
| */, |
| false/* |
| * don't resolve chained libraries |
| */).resolvedClasspath; |
| } catch (ModelException e) { |
| return e.getModelStatus(); |
| } |
| int length = buildpath.length; |
| // int sourceEntryCount = 0; |
| boolean disableExclusionPatterns = DLTKCore.DISABLED |
| .equals(scriptProject.getOption( |
| DLTKCore.CORE_ENABLE_BUILDPATH_EXCLUSION_PATTERNS, |
| true)); |
| for (int i = 0; i < length; i++) { |
| IBuildpathEntry resolvedEntry = buildpath[i]; |
| if (disableExclusionPatterns && ((resolvedEntry |
| .getInclusionPatterns() != null |
| && resolvedEntry.getInclusionPatterns().length > 0) |
| || (resolvedEntry.getExclusionPatterns() != null |
| && resolvedEntry |
| .getExclusionPatterns().length > 0))) { |
| return new ModelStatus( |
| IModelStatusConstants.DISABLED_BP_EXCLUSION_PATTERNS, |
| scriptProject, resolvedEntry.getPath()); |
| } |
| } |
| // check all entries |
| for (int i = 0; i < length; i++) { |
| IBuildpathEntry entry = buildpath[i]; |
| if (entry == null) |
| continue; |
| IPath entryPath = entry.getPath(); |
| int kind = entry.getEntryKind(); |
| // Build some common strings for status message |
| boolean isProjectRelative = projectName |
| .equals(entryPath.segment(0)); |
| String entryPathMsg = isProjectRelative |
| ? entryPath.removeFirstSegments(1).toString() |
| : EnvironmentPathUtils.getLocalPath(entryPath) |
| .makeRelative().toString(); |
| // boolean external = entry.isExternal(); |
| // no further check if entry coincidates with project or output |
| // location |
| if (entryPath.equals(projectPath)) { |
| // complain if self-referring project entry |
| if (kind == IBuildpathEntry.BPE_PROJECT) { |
| return new ModelStatus(IModelStatusConstants.INVALID_PATH, |
| Messages.bind( |
| Messages.buildpath_cannotReferToItself, |
| entryPath.makeRelative().toString())); |
| } |
| // tolerate nesting output in src if src==prj |
| continue; |
| } |
| // allow nesting source entries in each other as long as the outer |
| // entry excludes the inner one |
| IDLTKLanguageToolkit toolkit = DLTKLanguageManager |
| .getLanguageToolkit(scriptProject); |
| if (kind == IBuildpathEntry.BPE_SOURCE |
| || (kind == IBuildpathEntry.BPE_LIBRARY |
| && !org.eclipse.dltk.compiler.util.Util |
| .isArchiveFileName(toolkit, |
| entryPath.lastSegment()))) { |
| for (int j = 0; j < buildpath.length; j++) { |
| IBuildpathEntry otherEntry = buildpath[j]; |
| if (otherEntry == null) |
| continue; |
| int otherKind = otherEntry.getEntryKind(); |
| IPath otherPath = otherEntry.getPath(); |
| if (entry != otherEntry |
| && (otherKind == IBuildpathEntry.BPE_SOURCE |
| || (otherKind == IBuildpathEntry.BPE_LIBRARY |
| && !org.eclipse.dltk.compiler.util.Util |
| .isArchiveFileName(toolkit, |
| otherPath |
| .lastSegment())))) { |
| char[][] inclusionPatterns, exclusionPatterns; |
| if (otherPath.isPrefixOf(entryPath) |
| && !otherPath.equals(entryPath) |
| && !Util.isExcluded(entryPath.append("*"), //$NON-NLS-1$ |
| inclusionPatterns = ((BuildpathEntry) otherEntry) |
| .fullInclusionPatternChars(), |
| exclusionPatterns = ((BuildpathEntry) otherEntry) |
| .fullExclusionPatternChars(), |
| false)) { |
| String exclusionPattern = entryPath |
| .removeFirstSegments( |
| otherPath.segmentCount()) |
| .segment(0); |
| if (Util.isExcluded(entryPath, inclusionPatterns, |
| exclusionPatterns, false)) { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind( |
| Messages.buildpath_mustEndWithSlash, |
| new String[] { exclusionPattern, |
| EnvironmentPathUtils |
| .getLocalPath( |
| entryPath) |
| .makeRelative() |
| .toString() })); |
| } else { |
| if (otherKind == IBuildpathEntry.BPE_SOURCE) { |
| exclusionPattern += '/'; |
| if (!disableExclusionPatterns) { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind( |
| Messages.buildpath_cannotNestEntryInEntry, |
| new String[] { |
| EnvironmentPathUtils |
| .getLocalPath( |
| entryPath) |
| .makeRelative() |
| .toString(), |
| EnvironmentPathUtils |
| .getLocalPath( |
| otherEntry |
| .getPath()) |
| .makeRelative() |
| .toString(), |
| exclusionPattern })); |
| } else { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind( |
| Messages.buildpath_cannotNestEntryInEntryNoExclusion, |
| new String[] { |
| EnvironmentPathUtils |
| .getLocalPath( |
| entryPath) |
| .makeRelative() |
| .toString(), |
| EnvironmentPathUtils |
| .getLocalPath( |
| otherEntry |
| .getPath()) |
| .makeRelative() |
| .toString(), |
| exclusionPattern })); |
| } |
| } else { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind( |
| Messages.buildpath_cannotNestEntryInLibrary, |
| new String[] { |
| EnvironmentPathUtils |
| .getLocalPath( |
| entryPath) |
| .makeRelative() |
| .toString(), |
| EnvironmentPathUtils |
| .getLocalPath( |
| otherEntry |
| .getPath()) |
| .makeRelative() |
| .toString() })); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return ModelStatus.VERIFIED_OK; |
| } |
| |
| /** |
| * Returns a model status describing the problem related to this buildpath |
| * entry if any, a status object with code <code>IStatus.OK</code> if the |
| * entry is fine (that is, if the given buildpath entry denotes a valid |
| * element to be referenced onto a buildpath). |
| * |
| * @param project |
| * the given script project |
| * @param entry |
| * the given buildpath entry |
| * @param checkSourceAttachment |
| * a flag to determine if source attachement should be checked |
| * @param recurseInContainers |
| * flag indicating whether validation should be applied to |
| * container entries recursively |
| * @return a model status describing the problem related to this buildpath |
| * entry if any, a status object with code <code>IStatus.OK</code> |
| * if the entry is fine |
| */ |
| public static IModelStatus validateBuildpathEntry(IScriptProject project, |
| IBuildpathEntry entry, boolean recurseInContainers) { |
| IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| IPath path = entry.getPath(); |
| // Build some common strings for status message |
| String projectName = project.getElementName(); |
| boolean pathStartsWithProject = projectName.equals(path.segment(0)); |
| String entryPathMsg = pathStartsWithProject |
| ? path.removeFirstSegments(1).makeRelative().toString() |
| : EnvironmentPathUtils.getLocalPath(path).toString(); |
| switch (entry.getEntryKind()) { |
| // container entry check |
| case IBuildpathEntry.BPE_CONTAINER: |
| if (path != null && path.segmentCount() >= 1) { |
| try { |
| IBuildpathContainer container = ModelManager |
| .getModelManager() |
| .getBuildpathContainer(path, project); |
| // container retrieval is performing validation check on |
| // container entry kinds. |
| if (container == null) { |
| return new ModelStatus( |
| IModelStatusConstants.BP_CONTAINER_PATH_UNBOUND, |
| project, path); |
| } else if (container == ModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) { |
| // Validate extra attributes |
| IBuildpathAttribute[] extraAttributes = entry |
| .getExtraAttributes(); |
| if (extraAttributes != null) { |
| int length = extraAttributes.length; |
| HashSet set = new HashSet(length); |
| for (int i = 0; i < length; i++) { |
| String attName = extraAttributes[i].getName(); |
| if (!set.add(attName)) { |
| return new ModelStatus( |
| IModelStatusConstants.NAME_COLLISION, |
| Messages.bind( |
| Messages.buildpath_duplicateEntryExtraAttribute, |
| new String[] { attName, |
| entryPathMsg, |
| projectName })); |
| } |
| } |
| } |
| // don't create a marker if initialization is in |
| // progress (case of cp initialization batching) |
| return ModelStatus.VERIFIED_OK; |
| } else if (container instanceof IBuildpathContainerExtension2) { |
| final IModelStatus status = ((IBuildpathContainerExtension2) container) |
| .validate(); |
| if (status != null && !status.isOK()) { |
| return status; |
| } |
| } |
| IBuildpathEntry[] containerEntries = container |
| .getBuildpathEntries(); |
| if (containerEntries != null) { |
| for (int i = 0, length = containerEntries.length; i < length; i++) { |
| IBuildpathEntry containerEntry = containerEntries[i]; |
| int kind = containerEntry == null ? 0 |
| : containerEntry.getEntryKind(); |
| if (containerEntry == null |
| || kind == IBuildpathEntry.BPE_SOURCE |
| || kind == IBuildpathEntry.BPE_CONTAINER) { |
| // String description = |
| // container.getDescription(project); |
| // if (description == null) |
| // description = path.makeRelative() |
| // .toString(); |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BP_CONTAINER_ENTRY, |
| project, path); |
| } |
| if (recurseInContainers) { |
| IModelStatus containerEntryStatus = validateBuildpathEntry( |
| project, containerEntry, |
| recurseInContainers); |
| if (!containerEntryStatus.isOK()) { |
| return containerEntryStatus; |
| } |
| } |
| } |
| } |
| } catch (ModelException e) { |
| return new ModelStatus(e); |
| } |
| } else { |
| return new ModelStatus(IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind(Messages.buildpath_illegalContainerPath, |
| new String[] { entryPathMsg, projectName })); |
| } |
| break; |
| // variable entry check |
| case IBuildpathEntry.BPE_VARIABLE: |
| if (path.segmentCount() >= 1) { |
| try { |
| entry = DLTKCore.getResolvedBuildpathEntry(entry); |
| } catch (AssertionFailedException e) { |
| // Catch the assertion failure and throw java model |
| // exception instead |
| return new ModelStatus(IModelStatusConstants.INVALID_PATH, |
| e.getMessage()); |
| } |
| if (entry == null) { |
| return new ModelStatus( |
| IModelStatusConstants.BP_VARIABLE_PATH_UNBOUND, |
| project, path); |
| } |
| |
| // get validation status |
| IModelStatus status = validateBuildpathEntry(project, entry, |
| /* checkSourceAttachment, */recurseInContainers); |
| if (!status.isOK()) |
| return status; |
| } else { |
| return new ModelStatus(IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind(Messages.buildpath_illegalVariablePath, |
| new String[] { entryPathMsg, projectName })); |
| } |
| break; |
| // library entry check |
| case IBuildpathEntry.BPE_LIBRARY: |
| if (path.segmentCount() >= 1 |
| && path.segment(0) |
| .startsWith(IBuildpathEntry.BUILDPATH_SPECIAL) |
| && !path.isAbsolute()) { |
| break; |
| } |
| if (path != null && path.isAbsolute() && !path.isEmpty()) { |
| Object target = Model.getTarget(workspaceRoot, path, true); |
| // TODO: Add here some library version cheking |
| if (!entry.isExternal()) { |
| if (target instanceof IResource) { |
| IResource resolvedResource = (IResource) target; |
| switch (resolvedResource.getType()) { |
| case IResource.FILE: |
| break; |
| case IResource.FOLDER: // internal binary folder |
| break; |
| } |
| } else if (target instanceof IFileHandle) { |
| IFileHandle file = (IFileHandle) target; |
| if (!file.exists()) { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind( |
| Messages.buildpath_illegalLibraryArchive, |
| new String[] { |
| EnvironmentPathUtils |
| .getLocalPath(path) |
| .toString(), |
| projectName })); |
| } |
| } |
| } else { |
| IFileHandle file = EnvironmentPathUtils |
| .getFile(entry.getPath()); |
| if (file == null || !file.exists()) { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind( |
| Messages.buildpath_illegalExternalFolder, |
| new String[] { EnvironmentPathUtils |
| .getLocalPath(path).toString(), |
| projectName })); |
| } |
| } |
| } else { |
| return new ModelStatus(IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind(Messages.buildpath_illegalLibraryPath, |
| new String[] { entryPathMsg, projectName })); |
| } |
| break; |
| // project entry check |
| case IBuildpathEntry.BPE_PROJECT: |
| if (path != null && path.isAbsolute() && path.segmentCount() == 1) { |
| IProject prereqProjectRsc = workspaceRoot |
| .getProject(path.segment(0)); |
| if (!prereqProjectRsc.exists() || !DLTKLanguageManager |
| .hasScriptNature(prereqProjectRsc)) { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind(Messages.buildpath_unboundProject, |
| new String[] { path.segment(0), |
| projectName })); |
| } |
| if (!prereqProjectRsc.isOpen()) { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind(Messages.buildpath_closedProject, |
| new String[] { path.segment(0) })); |
| } |
| } else { |
| return new ModelStatus(IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind(Messages.buildpath_illegalProjectPath, |
| new String[] { path.toString(), projectName })); |
| } |
| break; |
| // project source folder |
| case IBuildpathEntry.BPE_SOURCE: |
| if (((entry.getInclusionPatterns() != null |
| && entry.getInclusionPatterns().length > 0) |
| || (entry.getExclusionPatterns() != null |
| && entry.getExclusionPatterns().length > 0)) |
| && DLTKCore.DISABLED.equals(project.getOption( |
| DLTKCore.CORE_ENABLE_BUILDPATH_EXCLUSION_PATTERNS, |
| true))) { |
| return new ModelStatus( |
| IModelStatusConstants.DISABLED_BP_EXCLUSION_PATTERNS, |
| project, path); |
| } |
| if (path != null && path.isAbsolute() && !path.isEmpty()) { |
| IPath projectPath = project.getProject().getFullPath(); |
| if (!projectPath.isPrefixOf(path) |
| || Model.getTarget(workspaceRoot, path, true) == null) { |
| return new ModelStatus( |
| IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind( |
| Messages.buildpath_unboundSourceFolder, |
| new String[] { entryPathMsg, |
| projectName })); |
| } |
| } else { |
| return new ModelStatus(IModelStatusConstants.INVALID_BUILDPATH, |
| Messages.bind( |
| Messages.buildpath_illegalSourceFolderPath, |
| new String[] { entryPathMsg, projectName })); |
| } |
| break; |
| } |
| // Validate extra attributes |
| IBuildpathAttribute[] extraAttributes = entry.getExtraAttributes(); |
| if (extraAttributes != null) { |
| int length = extraAttributes.length; |
| HashSet set = new HashSet(length); |
| for (int i = 0; i < length; i++) { |
| String attName = extraAttributes[i].getName(); |
| if (!set.add(attName)) { |
| return new ModelStatus(IModelStatusConstants.NAME_COLLISION, |
| Messages.bind( |
| Messages.buildpath_duplicateEntryExtraAttribute, |
| new String[] { attName, entryPathMsg, |
| projectName })); |
| } |
| } |
| } |
| return ModelStatus.VERIFIED_OK; |
| } |
| |
| public static IPath resolveDotDot(IPath reference, IPath path) { |
| // TODO (alex) port the implementation |
| return path; |
| } |
| |
| /** |
| * Encode some patterns into XML parameter tag |
| */ |
| private static void encodePatterns(IPath[] patterns, String tag, |
| Map parameters) { |
| if (patterns != null && patterns.length > 0) { |
| StringBuffer rule = new StringBuffer(10); |
| for (int i = 0, max = patterns.length; i < max; i++) { |
| if (i > 0) |
| rule.append('|'); |
| rule.append(patterns[i]); |
| } |
| parameters.put(tag, String.valueOf(rule)); |
| } |
| } |
| |
| /** |
| * Returns true if the given object is a equivalent buildpath entry. |
| */ |
| @Override |
| public boolean equals(Object object) { |
| if (this == object) |
| return true; |
| if (object instanceof BuildpathEntry) { |
| BuildpathEntry otherEntry = (BuildpathEntry) object; |
| if (this.contentKind != otherEntry.getContentKind()) |
| return false; |
| if (this.entryKind != otherEntry.getEntryKind()) |
| return false; |
| if (this.isExported != otherEntry.isExported()) |
| return false; |
| if (this.isExternal != otherEntry.isExternal()) |
| return false; |
| if (!this.path.equals(otherEntry.getPath())) |
| return false; |
| if (!equalPatterns(this.inclusionPatterns, |
| otherEntry.getInclusionPatterns())) |
| return false; |
| if (!equalPatterns(this.exclusionPatterns, |
| otherEntry.getExclusionPatterns())) |
| return false; |
| |
| AccessRuleSet otherRuleSet = otherEntry.getAccessRuleSet(); |
| if (getAccessRuleSet() != null) { |
| if (!getAccessRuleSet().equals(otherRuleSet)) |
| return false; |
| } else if (otherRuleSet != null) |
| return false; |
| if (this.combineAccessRules != otherEntry.combineAccessRules()) |
| return false; |
| if (!equalAttributes(this.extraAttributes, |
| otherEntry.getExtraAttributes())) |
| return false; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| private static boolean equalAttributes( |
| IBuildpathAttribute[] firstAttributes, |
| IBuildpathAttribute[] secondAttributes) { |
| if (firstAttributes != secondAttributes) { |
| if (firstAttributes == null) |
| return false; |
| int length = firstAttributes.length; |
| if (secondAttributes == null || secondAttributes.length != length) |
| return false; |
| for (int i = 0; i < length; i++) { |
| if (!firstAttributes[i].equals(secondAttributes[i])) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean equalPatterns(IPath[] firstPatterns, |
| IPath[] secondPatterns) { |
| if (firstPatterns != secondPatterns) { |
| if (firstPatterns == null) |
| return false; |
| int length = firstPatterns.length; |
| if (secondPatterns == null || secondPatterns.length != length) |
| return false; |
| for (int i = 0; i < length; i++) { |
| // compare toStrings instead of IPaths |
| // since IPath.equals is specified to ignore trailing separators |
| if (!firstPatterns[i].toString() |
| .equals(secondPatterns[i].toString())) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean isExternal() { |
| return isExternal; |
| } |
| |
| /** |
| * Returns the hash code for this buildpath entry |
| */ |
| @Override |
| public int hashCode() { |
| return this.path.hashCode(); |
| } |
| } |