/******************************************************************************* * Copyright (c) 2005, 2007 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 * *******************************************************************************/ package org.eclipse.dltk.internal.core; import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; 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; } } public IPath getSourceAttachmentPath() { return sourceAttachmentPath; } public IPath getSourceAttachmentRootPath() { return sourceAttachmentRootPath; } public void setSourceAttachmentPath(IPath sourceAttachmentPath) { this.sourceAttachmentPath = sourceAttachmentPath; } public void setSourceAttachmentRootPath(IPath sourceAttachmentRootPath) { this.sourceAttachmentRootPath = sourceAttachmentRootPath; } public int getEntryKind() { return entryKind; } public IPath[] getExclusionPatterns() { return this.exclusionPatterns; } public IBuildpathAttribute[] getExtraAttributes() { return this.extraAttributes; } 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() */ public IPath[] getInclusionPatterns() { return this.inclusionPatterns; } public IPath getPath() { return path; } /** * @see IBuildpathEntry#isExported() */ 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; } 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() */ 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. */ 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; } 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) { String environmentId = EnvironmentManager .getEnvironmentId(project.getProject()); if (environmentId != null) { path = EnvironmentPathUtils .getFullPath(environmentId, path); } } 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(elementAccessRule.getAttribute(TAG_IGNORE_IF_BETTER)); //$NON-NLS-1$ 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, "UTF8"); //$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<String, String>(); 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<IPath>(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("*"), inclusionPatterns = ((BuildpathEntry) otherEntry).fullInclusionPatternChars(), exclusionPatterns = ((BuildpathEntry) otherEntry).fullExclusionPatternChars(), false)) { //$NON-NLS-1$ 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. */ 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; } public boolean isExternal() { return isExternal; } /** * Returns the hash code for this buildpath entry */ public int hashCode() { return this.path.hashCode(); } }