0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.core;
0011:
0012: import java.io.ByteArrayOutputStream;
0013: import java.io.File;
0014: import java.io.OutputStreamWriter;
0015: import java.io.UnsupportedEncodingException;
0016: import java.util.ArrayList;
0017: import java.util.HashMap;
0018: import java.util.HashSet;
0019: import java.util.Map;
0020:
0021: import org.eclipse.core.resources.IProject;
0022: import org.eclipse.core.resources.IResource;
0023: import org.eclipse.core.resources.IWorkspaceRoot;
0024: import org.eclipse.core.resources.ResourcesPlugin;
0025: import org.eclipse.core.runtime.AssertionFailedException;
0026: import org.eclipse.core.runtime.CoreException;
0027: import org.eclipse.core.runtime.IPath;
0028: import org.eclipse.core.runtime.IStatus;
0029: import org.eclipse.core.runtime.Path;
0030: import org.eclipse.jdt.core.IAccessRule;
0031: import org.eclipse.jdt.core.IClasspathAttribute;
0032: import org.eclipse.jdt.core.IClasspathContainer;
0033: import org.eclipse.jdt.core.IClasspathEntry;
0034: import org.eclipse.jdt.core.IJavaModelStatus;
0035: import org.eclipse.jdt.core.IJavaModelStatusConstants;
0036: import org.eclipse.jdt.core.IJavaProject;
0037: import org.eclipse.jdt.core.IPackageFragmentRoot;
0038: import org.eclipse.jdt.core.JavaCore;
0039: import org.eclipse.jdt.core.JavaModelException;
0040: import org.eclipse.jdt.core.compiler.CharOperation;
0041: import org.eclipse.jdt.core.compiler.IProblem;
0042: import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
0043: import org.eclipse.jdt.internal.compiler.env.AccessRule;
0044: import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
0045: import org.eclipse.jdt.internal.core.util.Messages;
0046: import org.eclipse.jdt.internal.core.util.Util;
0047: import org.w3c.dom.DOMException;
0048: import org.w3c.dom.Element;
0049: import org.w3c.dom.NamedNodeMap;
0050: import org.w3c.dom.Node;
0051: import org.w3c.dom.NodeList;
0052: import org.w3c.dom.Text;
0053:
0054: /**
0055: * @see IClasspathEntry
0056: */
0057: public class ClasspathEntry implements IClasspathEntry {
0058:
0059: public static final String TAG_CLASSPATH = "classpath"; //$NON-NLS-1$
0060: public static final String TAG_CLASSPATHENTRY = "classpathentry"; //$NON-NLS-1$
0061: public static final String TAG_OUTPUT = "output"; //$NON-NLS-1$
0062: public static final String TAG_KIND = "kind"; //$NON-NLS-1$
0063: public static final String TAG_PATH = "path"; //$NON-NLS-1$
0064: public static final String TAG_SOURCEPATH = "sourcepath"; //$NON-NLS-1$
0065: public static final String TAG_ROOTPATH = "rootpath"; //$NON-NLS-1$
0066: public static final String TAG_EXPORTED = "exported"; //$NON-NLS-1$
0067: public static final String TAG_INCLUDING = "including"; //$NON-NLS-1$
0068: public static final String TAG_EXCLUDING = "excluding"; //$NON-NLS-1$
0069: public static final String TAG_ATTRIBUTES = "attributes"; //$NON-NLS-1$
0070: public static final String TAG_ATTRIBUTE = "attribute"; //$NON-NLS-1$
0071: public static final String TAG_ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
0072: public static final String TAG_ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$
0073: public static final String TAG_COMBINE_ACCESS_RULES = "combineaccessrules"; //$NON-NLS-1$
0074: public static final String TAG_ACCESS_RULES = "accessrules"; //$NON-NLS-1$
0075: public static final String TAG_ACCESS_RULE = "accessrule"; //$NON-NLS-1$
0076: public static final String TAG_PATTERN = "pattern"; //$NON-NLS-1$
0077: public static final String TAG_ACCESSIBLE = "accessible"; //$NON-NLS-1$
0078: public static final String TAG_NON_ACCESSIBLE = "nonaccessible"; //$NON-NLS-1$
0079: public static final String TAG_DISCOURAGED = "discouraged"; //$NON-NLS-1$
0080: public static final String TAG_IGNORE_IF_BETTER = "ignoreifbetter"; //$NON-NLS-1$
0081:
0082: /**
0083: * Describes the kind of classpath entry - one of
0084: * CPE_PROJECT, CPE_LIBRARY, CPE_SOURCE, CPE_VARIABLE or CPE_CONTAINER
0085: */
0086: public int entryKind;
0087:
0088: /**
0089: * Describes the kind of package fragment roots found on
0090: * this classpath entry - either K_BINARY or K_SOURCE or
0091: * K_OUTPUT.
0092: */
0093: public int contentKind;
0094:
0095: /**
0096: * The meaning of the path of a classpath entry depends on its entry kind:<ul>
0097: * <li>Source code in the current project (<code>CPE_SOURCE</code>) -
0098: * The path associated with this entry is the absolute path to the root folder. </li>
0099: * <li>A binary library in the current project (<code>CPE_LIBRARY</code>) - the path
0100: * associated with this entry is the absolute path to the JAR (or root folder), and
0101: * in case it refers to an external JAR, then there is no associated resource in
0102: * the workbench.
0103: * <li>A required project (<code>CPE_PROJECT</code>) - the path of the entry denotes the
0104: * path to the corresponding project resource.</li>
0105: * <li>A variable entry (<code>CPE_VARIABLE</code>) - the first segment of the path
0106: * is the name of a classpath variable. If this classpath variable
0107: * is bound to the path <it>P</it>, the path of the corresponding classpath entry
0108: * is computed by appending to <it>P</it> the segments of the returned
0109: * path without the variable.</li>
0110: * <li> A container entry (<code>CPE_CONTAINER</code>) - the first segment of the path is denoting
0111: * the unique container identifier (for which a <code>ClasspathContainerInitializer</code> could be
0112: * registered), and the remaining segments are used as additional hints for resolving the container entry to
0113: * an actual <code>IClasspathContainer</code>.</li>
0114: */
0115: public IPath path;
0116:
0117: /**
0118: * Patterns allowing to include/exclude portions of the resource tree denoted by this entry path.
0119: */
0120: private IPath[] inclusionPatterns;
0121: private char[][] fullInclusionPatternChars;
0122: private IPath[] exclusionPatterns;
0123: private char[][] fullExclusionPatternChars;
0124: private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
0125:
0126: private boolean combineAccessRules;
0127:
0128: private String rootID;
0129: private AccessRuleSet accessRuleSet;
0130:
0131: static class UnknownXmlElements {
0132: String[] attributes;
0133: ArrayList children;
0134: }
0135:
0136: /*
0137: * Default inclusion pattern set
0138: */
0139: public final static IPath[] INCLUDE_ALL = {};
0140:
0141: /*
0142: * Default exclusion pattern set
0143: */
0144: public final static IPath[] EXCLUDE_NONE = {};
0145:
0146: /*
0147: * Default extra attributes
0148: */
0149: public final static IClasspathAttribute[] NO_EXTRA_ATTRIBUTES = {};
0150:
0151: /*
0152: * Default access rules
0153: */
0154: public final static IAccessRule[] NO_ACCESS_RULES = {};
0155:
0156: /**
0157: * Describes the path to the source archive associated with this
0158: * classpath entry, or <code>null</code> if this classpath entry has no
0159: * source attachment.
0160: * <p>
0161: * Only library and variable classpath entries may have source attachments.
0162: * For library classpath entries, the result path (if present) locates a source
0163: * archive. For variable classpath entries, the result path (if present) has
0164: * an analogous form and meaning as the variable path, namely the first segment
0165: * is the name of a classpath variable.
0166: */
0167: public IPath sourceAttachmentPath;
0168:
0169: /**
0170: * Describes the path within the source archive where package fragments
0171: * are located. An empty path indicates that packages are located at
0172: * the root of the source archive. Returns a non-<code>null</code> value
0173: * if and only if <code>getSourceAttachmentPath</code> returns
0174: * a non-<code>null</code> value.
0175: */
0176: public IPath sourceAttachmentRootPath;
0177:
0178: /**
0179: * Specific output location (for this source entry)
0180: */
0181: public IPath specificOutputLocation;
0182:
0183: /**
0184: * A constant indicating an output location.
0185: */
0186: public static final int K_OUTPUT = 10;
0187:
0188: /**
0189: * The export flag
0190: */
0191: public boolean isExported;
0192:
0193: /*
0194: * The extra attributes
0195: */
0196: IClasspathAttribute[] extraAttributes;
0197:
0198: /**
0199: * Creates a class path entry of the specified kind with the given path.
0200: */
0201: public ClasspathEntry(int contentKind, int entryKind, IPath path,
0202: IPath[] inclusionPatterns, IPath[] exclusionPatterns,
0203: IPath sourceAttachmentPath, IPath sourceAttachmentRootPath,
0204: IPath specificOutputLocation, boolean isExported,
0205: IAccessRule[] accessRules, boolean combineAccessRules,
0206: IClasspathAttribute[] extraAttributes) {
0207:
0208: this .contentKind = contentKind;
0209: this .entryKind = entryKind;
0210: this .path = path;
0211: this .inclusionPatterns = inclusionPatterns;
0212: this .exclusionPatterns = exclusionPatterns;
0213:
0214: int length;
0215: if (accessRules != null && (length = accessRules.length) > 0) {
0216: AccessRule[] rules = new AccessRule[length];
0217: System.arraycopy(accessRules, 0, rules, 0, length);
0218: this .accessRuleSet = new AccessRuleSet(rules,
0219: getMessageTemplates());
0220: }
0221: // else { -- implicit!
0222: // this.accessRuleSet = null;
0223: // }
0224:
0225: this .combineAccessRules = combineAccessRules;
0226: this .extraAttributes = extraAttributes;
0227:
0228: if (inclusionPatterns != INCLUDE_ALL
0229: && inclusionPatterns.length > 0) {
0230: this .fullInclusionPatternChars = UNINIT_PATTERNS;
0231: }
0232: if (exclusionPatterns.length > 0) {
0233: this .fullExclusionPatternChars = UNINIT_PATTERNS;
0234: }
0235: this .sourceAttachmentPath = sourceAttachmentPath;
0236: this .sourceAttachmentRootPath = sourceAttachmentRootPath;
0237: this .specificOutputLocation = specificOutputLocation;
0238: this .isExported = isExported;
0239: }
0240:
0241: public boolean combineAccessRules() {
0242: return this .combineAccessRules;
0243: }
0244:
0245: /**
0246: * Used to perform export/restriction propagation across referring projects/containers
0247: */
0248: public ClasspathEntry combineWith(ClasspathEntry referringEntry) {
0249: if (referringEntry == null)
0250: return this ;
0251: if (referringEntry.isExported()
0252: || referringEntry.getAccessRuleSet() != null) {
0253: boolean combine = this .entryKind == CPE_SOURCE
0254: || referringEntry.combineAccessRules();
0255: return new ClasspathEntry(getContentKind(), getEntryKind(),
0256: getPath(), this .inclusionPatterns,
0257: this .exclusionPatterns, getSourceAttachmentPath(),
0258: getSourceAttachmentRootPath(),
0259: getOutputLocation(),
0260: referringEntry.isExported() || this .isExported, // duplicate container entry for tagging it as exported
0261: combine(referringEntry.getAccessRules(),
0262: getAccessRules(), combine),
0263: this .combineAccessRules, this .extraAttributes);
0264: }
0265: // no need to clone
0266: return this ;
0267: }
0268:
0269: private IAccessRule[] combine(IAccessRule[] referringRules,
0270: IAccessRule[] rules, boolean combine) {
0271: if (!combine)
0272: return rules;
0273: if (rules == null || rules.length == 0)
0274: return referringRules;
0275:
0276: // concat access rules
0277: int referringRulesLength = referringRules.length;
0278: int accessRulesLength = rules.length;
0279: int rulesLength = referringRulesLength + accessRulesLength;
0280: IAccessRule[] result = new IAccessRule[rulesLength];
0281: System.arraycopy(referringRules, 0, result, 0,
0282: referringRulesLength);
0283: System.arraycopy(rules, 0, result, referringRulesLength,
0284: accessRulesLength);
0285:
0286: return result;
0287: }
0288:
0289: static IClasspathAttribute[] decodeExtraAttributes(
0290: NodeList attributes) {
0291: if (attributes == null)
0292: return NO_EXTRA_ATTRIBUTES;
0293: int length = attributes.getLength();
0294: if (length == 0)
0295: return NO_EXTRA_ATTRIBUTES;
0296: IClasspathAttribute[] result = new IClasspathAttribute[length];
0297: int index = 0;
0298: for (int i = 0; i < length; ++i) {
0299: Node node = attributes.item(i);
0300: if (node.getNodeType() == Node.ELEMENT_NODE) {
0301: Element attribute = (Element) node;
0302: String name = attribute
0303: .getAttribute(TAG_ATTRIBUTE_NAME);
0304: if (name == null)
0305: continue;
0306: String value = attribute
0307: .getAttribute(TAG_ATTRIBUTE_VALUE);
0308: if (value == null)
0309: continue;
0310: result[index++] = new ClasspathAttribute(name, value);
0311: }
0312: }
0313: if (index != length)
0314: System.arraycopy(result, 0,
0315: result = new IClasspathAttribute[index], 0, index);
0316: return result;
0317: }
0318:
0319: static IAccessRule[] decodeAccessRules(NodeList list) {
0320: if (list == null)
0321: return null;
0322: int length = list.getLength();
0323: if (length == 0)
0324: return null;
0325: IAccessRule[] result = new IAccessRule[length];
0326: int index = 0;
0327: for (int i = 0; i < length; i++) {
0328: Node accessRule = list.item(i);
0329: if (accessRule.getNodeType() == Node.ELEMENT_NODE) {
0330: Element elementAccessRule = (Element) accessRule;
0331: String pattern = elementAccessRule
0332: .getAttribute(TAG_PATTERN);
0333: if (pattern == null)
0334: continue;
0335: String tagKind = elementAccessRule
0336: .getAttribute(TAG_KIND);
0337: int kind;
0338: if (TAG_ACCESSIBLE.equals(tagKind))
0339: kind = IAccessRule.K_ACCESSIBLE;
0340: else if (TAG_NON_ACCESSIBLE.equals(tagKind))
0341: kind = IAccessRule.K_NON_ACCESSIBLE;
0342: else if (TAG_DISCOURAGED.equals(tagKind))
0343: kind = IAccessRule.K_DISCOURAGED;
0344: else
0345: continue;
0346: boolean ignoreIfBetter = "true".equals(elementAccessRule.getAttribute(TAG_IGNORE_IF_BETTER)); //$NON-NLS-1$
0347: result[index++] = new ClasspathAccessRule(new Path(
0348: pattern), ignoreIfBetter ? kind
0349: | IAccessRule.IGNORE_IF_BETTER : kind);
0350: }
0351: }
0352: if (index != length)
0353: System.arraycopy(result, 0,
0354: result = new IAccessRule[index], 0, index);
0355: return result;
0356: }
0357:
0358: /**
0359: * Decode some element tag containing a sequence of patterns into IPath[]
0360: */
0361: private static IPath[] decodePatterns(NamedNodeMap nodeMap,
0362: String tag) {
0363: String sequence = removeAttribute(tag, nodeMap);
0364: if (!sequence.equals("")) { //$NON-NLS-1$
0365: char[][] patterns = CharOperation.splitOn('|', sequence
0366: .toCharArray());
0367: int patternCount;
0368: if ((patternCount = patterns.length) > 0) {
0369: IPath[] paths = new IPath[patternCount];
0370: int index = 0;
0371: for (int j = 0; j < patternCount; j++) {
0372: char[] pattern = patterns[j];
0373: if (pattern.length == 0)
0374: continue; // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=105581
0375: paths[index++] = new Path(new String(pattern));
0376: }
0377: if (index < patternCount)
0378: System.arraycopy(paths, 0,
0379: paths = new IPath[index], 0, index);
0380: return paths;
0381: }
0382: }
0383: return null;
0384: }
0385:
0386: private static void decodeUnknownNode(Node node,
0387: StringBuffer buffer, IJavaProject project) {
0388: ByteArrayOutputStream s = new ByteArrayOutputStream();
0389: OutputStreamWriter writer;
0390: try {
0391: writer = new OutputStreamWriter(s, "UTF8"); //$NON-NLS-1$
0392: XMLWriter xmlWriter = new XMLWriter(writer, project, false/*don't print XML version*/);
0393: decodeUnknownNode(node, xmlWriter, true/*insert new line*/);
0394: xmlWriter.flush();
0395: xmlWriter.close();
0396: buffer.append(s.toString("UTF8")); //$NON-NLS-1$
0397: } catch (UnsupportedEncodingException e) {
0398: // ignore (UTF8 is always supported)
0399: }
0400: }
0401:
0402: private static void decodeUnknownNode(Node node,
0403: XMLWriter xmlWriter, boolean insertNewLine) {
0404: switch (node.getNodeType()) {
0405: case Node.ELEMENT_NODE:
0406: NamedNodeMap attributes;
0407: HashMap parameters = null;
0408: if ((attributes = node.getAttributes()) != null) {
0409: int length = attributes.getLength();
0410: if (length > 0) {
0411: parameters = new HashMap();
0412: for (int i = 0; i < length; i++) {
0413: Node attribute = attributes.item(i);
0414: parameters.put(attribute.getNodeName(),
0415: attribute.getNodeValue());
0416: }
0417: }
0418: }
0419: NodeList children = node.getChildNodes();
0420: int childrenLength = children.getLength();
0421: String nodeName = node.getNodeName();
0422: xmlWriter.printTag(nodeName, parameters,
0423: false/*don't insert tab*/,
0424: false/*don't insert new line*/,
0425: childrenLength == 0/*close tag if no children*/);
0426: if (childrenLength > 0) {
0427: for (int i = 0; i < childrenLength; i++) {
0428: decodeUnknownNode(children.item(i), xmlWriter,
0429: false/*don't insert new line*/);
0430: }
0431: xmlWriter.endTag(nodeName, false/*don't insert tab*/,
0432: insertNewLine);
0433: }
0434: break;
0435: case Node.TEXT_NODE:
0436: String data = ((Text) node).getData();
0437: xmlWriter.printString(data, false/*don't insert tab*/,
0438: false/*don't insert new line*/);
0439: break;
0440: }
0441: }
0442:
0443: /*
0444: * Returns a char based representation of the exclusions patterns full path.
0445: */
0446: public char[][] fullExclusionPatternChars() {
0447:
0448: if (this .fullExclusionPatternChars == UNINIT_PATTERNS) {
0449: int length = this .exclusionPatterns.length;
0450: this .fullExclusionPatternChars = new char[length][];
0451: IPath prefixPath = this .path.removeTrailingSeparator();
0452: for (int i = 0; i < length; i++) {
0453: this .fullExclusionPatternChars[i] = prefixPath.append(
0454: this .exclusionPatterns[i]).toString()
0455: .toCharArray();
0456: }
0457: }
0458: return this .fullExclusionPatternChars;
0459: }
0460:
0461: /*
0462: * Returns a char based representation of the exclusions patterns full path.
0463: */
0464: public char[][] fullInclusionPatternChars() {
0465:
0466: if (this .fullInclusionPatternChars == UNINIT_PATTERNS) {
0467: int length = this .inclusionPatterns.length;
0468: this .fullInclusionPatternChars = new char[length][];
0469: IPath prefixPath = this .path.removeTrailingSeparator();
0470: for (int i = 0; i < length; i++) {
0471: this .fullInclusionPatternChars[i] = prefixPath.append(
0472: this .inclusionPatterns[i]).toString()
0473: .toCharArray();
0474: }
0475: }
0476: return this .fullInclusionPatternChars;
0477: }
0478:
0479: /**
0480: * Returns the XML encoding of the class path.
0481: */
0482: public void elementEncode(XMLWriter writer, IPath projectPath,
0483: boolean indent, boolean newLine, Map unknownElements) {
0484: HashMap parameters = new HashMap();
0485:
0486: parameters.put(TAG_KIND, ClasspathEntry
0487: .kindToString(this .entryKind));
0488:
0489: IPath xmlPath = this .path;
0490: if (this .entryKind != IClasspathEntry.CPE_VARIABLE
0491: && this .entryKind != IClasspathEntry.CPE_CONTAINER) {
0492: // translate to project relative from absolute (unless a device path)
0493: if (xmlPath.isAbsolute()) {
0494: if (projectPath != null
0495: && projectPath.isPrefixOf(xmlPath)) {
0496: if (xmlPath.segment(0).equals(
0497: projectPath.segment(0))) {
0498: xmlPath = xmlPath.removeFirstSegments(1);
0499: xmlPath = xmlPath.makeRelative();
0500: } else {
0501: xmlPath = xmlPath.makeAbsolute();
0502: }
0503: }
0504: }
0505: }
0506: parameters.put(TAG_PATH, String.valueOf(xmlPath));
0507:
0508: if (this .sourceAttachmentPath != null) {
0509: xmlPath = this .sourceAttachmentPath;
0510: // translate to project relative from absolute
0511: if (this .entryKind != IClasspathEntry.CPE_VARIABLE
0512: && projectPath != null
0513: && projectPath.isPrefixOf(xmlPath)) {
0514: if (xmlPath.segment(0).equals(projectPath.segment(0))) {
0515: xmlPath = xmlPath.removeFirstSegments(1);
0516: xmlPath = xmlPath.makeRelative();
0517: }
0518: }
0519: parameters.put(TAG_SOURCEPATH, String.valueOf(xmlPath));
0520: }
0521: if (this .sourceAttachmentRootPath != null) {
0522: parameters.put(TAG_ROOTPATH, String
0523: .valueOf(this .sourceAttachmentRootPath));
0524: }
0525: if (this .isExported) {
0526: parameters.put(TAG_EXPORTED, "true");//$NON-NLS-1$
0527: }
0528: encodePatterns(this .inclusionPatterns, TAG_INCLUDING,
0529: parameters);
0530: encodePatterns(this .exclusionPatterns, TAG_EXCLUDING,
0531: parameters);
0532: if (this .entryKind == CPE_PROJECT && !this .combineAccessRules)
0533: parameters.put(TAG_COMBINE_ACCESS_RULES, "false"); //$NON-NLS-1$
0534:
0535: // unknown attributes
0536: UnknownXmlElements unknownXmlElements = unknownElements == null ? null
0537: : (UnknownXmlElements) unknownElements.get(this .path);
0538: String[] unknownAttributes;
0539: if (unknownXmlElements != null
0540: && (unknownAttributes = unknownXmlElements.attributes) != null)
0541: for (int i = 0, length = unknownAttributes.length; i < length; i += 2) {
0542: String tagName = unknownAttributes[i];
0543: String tagValue = unknownAttributes[i + 1];
0544: parameters.put(tagName, tagValue);
0545: }
0546:
0547: if (this .specificOutputLocation != null) {
0548: IPath outputLocation = this .specificOutputLocation
0549: .removeFirstSegments(1);
0550: outputLocation = outputLocation.makeRelative();
0551: parameters.put(TAG_OUTPUT, String.valueOf(outputLocation));
0552: }
0553:
0554: boolean hasExtraAttributes = this .extraAttributes.length != 0;
0555: boolean hasRestrictions = getAccessRuleSet() != null; // access rule set is null if no access rules
0556: ArrayList unknownChildren = unknownXmlElements != null ? unknownXmlElements.children
0557: : null;
0558: boolean hasUnknownChildren = unknownChildren != null;
0559: writer
0560: .printTag(TAG_CLASSPATHENTRY, parameters, indent,
0561: newLine, !hasExtraAttributes
0562: && !hasRestrictions
0563: && !hasUnknownChildren/*close tag if no extra attributes, no restriction and no unknown children*/);
0564:
0565: if (hasExtraAttributes)
0566: encodeExtraAttributes(writer, indent, newLine);
0567:
0568: if (hasRestrictions)
0569: encodeAccessRules(writer, indent, newLine);
0570:
0571: if (hasUnknownChildren)
0572: encodeUnknownChildren(writer, indent, newLine,
0573: unknownChildren);
0574:
0575: if (hasExtraAttributes || hasRestrictions || hasUnknownChildren)
0576: writer
0577: .endTag(TAG_CLASSPATHENTRY, indent, true/*insert new line*/);
0578: }
0579:
0580: void encodeExtraAttributes(XMLWriter writer, boolean indent,
0581: boolean newLine) {
0582: writer.startTag(TAG_ATTRIBUTES, indent);
0583: for (int i = 0; i < this .extraAttributes.length; i++) {
0584: IClasspathAttribute attribute = this .extraAttributes[i];
0585: HashMap parameters = new HashMap();
0586: parameters.put(TAG_ATTRIBUTE_NAME, attribute.getName());
0587: parameters.put(TAG_ATTRIBUTE_VALUE, attribute.getValue());
0588: writer.printTag(TAG_ATTRIBUTE, parameters, indent, newLine,
0589: true);
0590: }
0591: writer.endTag(TAG_ATTRIBUTES, indent, true/*insert new line*/);
0592: }
0593:
0594: void encodeAccessRules(XMLWriter writer, boolean indent,
0595: boolean newLine) {
0596:
0597: writer.startTag(TAG_ACCESS_RULES, indent);
0598: AccessRule[] rules = getAccessRuleSet().getAccessRules();
0599: for (int i = 0, length = rules.length; i < length; i++) {
0600: encodeAccessRule(rules[i], writer, indent, newLine);
0601: }
0602: writer
0603: .endTag(TAG_ACCESS_RULES, indent, true/*insert new line*/);
0604: }
0605:
0606: private void encodeAccessRule(AccessRule accessRule,
0607: XMLWriter writer, boolean indent, boolean newLine) {
0608:
0609: HashMap parameters = new HashMap();
0610: parameters.put(TAG_PATTERN, new String(accessRule.pattern));
0611:
0612: switch (accessRule.getProblemId()) {
0613: case IProblem.ForbiddenReference:
0614: parameters.put(TAG_KIND, TAG_NON_ACCESSIBLE);
0615: break;
0616: case IProblem.DiscouragedReference:
0617: parameters.put(TAG_KIND, TAG_DISCOURAGED);
0618: break;
0619: default:
0620: parameters.put(TAG_KIND, TAG_ACCESSIBLE);
0621: break;
0622: }
0623: if (accessRule.ignoreIfBetter())
0624: parameters.put(TAG_IGNORE_IF_BETTER, "true"); //$NON-NLS-1$
0625:
0626: writer.printTag(TAG_ACCESS_RULE, parameters, indent, newLine,
0627: true);
0628:
0629: }
0630:
0631: private void encodeUnknownChildren(XMLWriter writer,
0632: boolean indent, boolean newLine, ArrayList unknownChildren) {
0633: for (int i = 0, length = unknownChildren.size(); i < length; i++) {
0634: String child = (String) unknownChildren.get(i);
0635: writer
0636: .printString(child, indent, false/*don't insert new line*/);
0637: }
0638: }
0639:
0640: public static IClasspathEntry elementDecode(Element element,
0641: IJavaProject project, Map unknownElements) {
0642:
0643: IPath projectPath = project.getProject().getFullPath();
0644: NamedNodeMap attributes = element.getAttributes();
0645: NodeList children = element.getChildNodes();
0646: boolean[] foundChildren = new boolean[children.getLength()];
0647: String kindAttr = removeAttribute(TAG_KIND, attributes);
0648: String pathAttr = removeAttribute(TAG_PATH, attributes);
0649:
0650: // ensure path is absolute
0651: IPath path = new Path(pathAttr);
0652: int kind = kindFromString(kindAttr);
0653: if (kind != IClasspathEntry.CPE_VARIABLE
0654: && kind != IClasspathEntry.CPE_CONTAINER
0655: && !path.isAbsolute()) {
0656: path = projectPath.append(path);
0657: }
0658: // source attachment info (optional)
0659: IPath sourceAttachmentPath = element
0660: .hasAttribute(TAG_SOURCEPATH) ? new Path(
0661: removeAttribute(TAG_SOURCEPATH, attributes)) : null;
0662: if (kind != IClasspathEntry.CPE_VARIABLE
0663: && sourceAttachmentPath != null
0664: && !sourceAttachmentPath.isAbsolute()) {
0665: sourceAttachmentPath = projectPath
0666: .append(sourceAttachmentPath);
0667: }
0668: IPath sourceAttachmentRootPath = element
0669: .hasAttribute(TAG_ROOTPATH) ? new Path(removeAttribute(
0670: TAG_ROOTPATH, attributes)) : null;
0671:
0672: // exported flag (optional)
0673: boolean isExported = removeAttribute(TAG_EXPORTED, attributes)
0674: .equals("true"); //$NON-NLS-1$
0675:
0676: // inclusion patterns (optional)
0677: IPath[] inclusionPatterns = decodePatterns(attributes,
0678: TAG_INCLUDING);
0679: if (inclusionPatterns == null)
0680: inclusionPatterns = INCLUDE_ALL;
0681:
0682: // exclusion patterns (optional)
0683: IPath[] exclusionPatterns = decodePatterns(attributes,
0684: TAG_EXCLUDING);
0685: if (exclusionPatterns == null)
0686: exclusionPatterns = EXCLUDE_NONE;
0687:
0688: // access rules (optional)
0689: NodeList attributeList = getChildAttributes(TAG_ACCESS_RULES,
0690: children, foundChildren);
0691: IAccessRule[] accessRules = decodeAccessRules(attributeList);
0692:
0693: // backward compatibility
0694: if (accessRules == null) {
0695: accessRules = getAccessRules(inclusionPatterns,
0696: exclusionPatterns);
0697: }
0698:
0699: // combine access rules (optional)
0700: boolean combineAccessRestrictions = !removeAttribute(
0701: TAG_COMBINE_ACCESS_RULES, attributes).equals("false"); //$NON-NLS-1$
0702:
0703: // extra attributes (optional)
0704: attributeList = getChildAttributes(TAG_ATTRIBUTES, children,
0705: foundChildren);
0706: IClasspathAttribute[] extraAttributes = decodeExtraAttributes(attributeList);
0707:
0708: // custom output location
0709: IPath outputLocation = element.hasAttribute(TAG_OUTPUT) ? projectPath
0710: .append(removeAttribute(TAG_OUTPUT, attributes))
0711: : null;
0712:
0713: String[] unknownAttributes = null;
0714: ArrayList unknownChildren = null;
0715:
0716: if (unknownElements != null) {
0717: // unknown attributes
0718: int unknownAttributeLength = attributes.getLength();
0719: if (unknownAttributeLength != 0) {
0720: unknownAttributes = new String[unknownAttributeLength * 2];
0721: for (int i = 0; i < unknownAttributeLength; i++) {
0722: Node attribute = attributes.item(i);
0723: unknownAttributes[i * 2] = attribute.getNodeName();
0724: unknownAttributes[i * 2 + 1] = attribute
0725: .getNodeValue();
0726: }
0727: }
0728:
0729: // unknown children
0730: for (int i = 0, length = foundChildren.length; i < length; i++) {
0731: if (!foundChildren[i]) {
0732: Node node = children.item(i);
0733: if (node.getNodeType() != Node.ELEMENT_NODE)
0734: continue;
0735: if (unknownChildren == null)
0736: unknownChildren = new ArrayList();
0737: StringBuffer buffer = new StringBuffer();
0738: decodeUnknownNode(node, buffer, project);
0739: unknownChildren.add(buffer.toString());
0740: }
0741: }
0742: }
0743:
0744: // recreate the CP entry
0745: IClasspathEntry entry = null;
0746: switch (kind) {
0747:
0748: case IClasspathEntry.CPE_PROJECT:
0749: entry = new ClasspathEntry(IPackageFragmentRoot.K_SOURCE,
0750: IClasspathEntry.CPE_PROJECT,
0751: path,
0752: ClasspathEntry.INCLUDE_ALL, // inclusion patterns
0753: ClasspathEntry.EXCLUDE_NONE, // exclusion patterns
0754: null, // source attachment
0755: null, // source attachment root
0756: null, // specific output folder
0757: isExported, accessRules, combineAccessRestrictions,
0758: extraAttributes);
0759: break;
0760: case IClasspathEntry.CPE_LIBRARY:
0761: entry = JavaCore.newLibraryEntry(path,
0762: sourceAttachmentPath, sourceAttachmentRootPath,
0763: accessRules, extraAttributes, isExported);
0764: break;
0765: case IClasspathEntry.CPE_SOURCE:
0766: // must be an entry in this project or specify another project
0767: String projSegment = path.segment(0);
0768: if (projSegment != null
0769: && projSegment.equals(project.getElementName())) { // this project
0770: entry = JavaCore.newSourceEntry(path,
0771: inclusionPatterns, exclusionPatterns,
0772: outputLocation, extraAttributes);
0773: } else {
0774: if (path.segmentCount() == 1) {
0775: // another project
0776: entry = JavaCore.newProjectEntry(path, accessRules,
0777: combineAccessRestrictions, extraAttributes,
0778: isExported);
0779: } else {
0780: // an invalid source folder
0781: entry = JavaCore.newSourceEntry(path,
0782: inclusionPatterns, exclusionPatterns,
0783: outputLocation, extraAttributes);
0784: }
0785: }
0786: break;
0787: case IClasspathEntry.CPE_VARIABLE:
0788: entry = JavaCore.newVariableEntry(path,
0789: sourceAttachmentPath, sourceAttachmentRootPath,
0790: accessRules, extraAttributes, isExported);
0791: break;
0792: case IClasspathEntry.CPE_CONTAINER:
0793: entry = JavaCore.newContainerEntry(path, accessRules,
0794: extraAttributes, isExported);
0795: break;
0796: case ClasspathEntry.K_OUTPUT:
0797: if (!path.isAbsolute())
0798: return null;
0799: entry = new ClasspathEntry(ClasspathEntry.K_OUTPUT,
0800: IClasspathEntry.CPE_LIBRARY, path, INCLUDE_ALL,
0801: EXCLUDE_NONE, null, // source attachment
0802: null, // source attachment root
0803: null, // custom output location
0804: false, null, // no access rules
0805: false, // no accessible files to combine
0806: NO_EXTRA_ATTRIBUTES);
0807: break;
0808: default:
0809: throw new AssertionFailedException(Messages.bind(
0810: Messages.classpath_unknownKind, kindAttr));
0811: }
0812:
0813: if (unknownAttributes != null || unknownChildren != null) {
0814: UnknownXmlElements unknownXmlElements = new UnknownXmlElements();
0815: unknownXmlElements.attributes = unknownAttributes;
0816: unknownXmlElements.children = unknownChildren;
0817: unknownElements.put(path, unknownXmlElements);
0818: }
0819:
0820: return entry;
0821: }
0822:
0823: public static NodeList getChildAttributes(String childName,
0824: NodeList children, boolean[] foundChildren) {
0825: for (int i = 0, length = foundChildren.length; i < length; i++) {
0826: Node node = children.item(i);
0827: if (childName.equals(node.getNodeName())) {
0828: foundChildren[i] = true;
0829: return node.getChildNodes();
0830: }
0831: }
0832: return null;
0833: }
0834:
0835: private static String removeAttribute(String nodeName,
0836: NamedNodeMap nodeMap) {
0837: Node node = removeNode(nodeName, nodeMap);
0838: if (node == null)
0839: return ""; // //$NON-NLS-1$
0840: return node.getNodeValue();
0841: }
0842:
0843: private static Node removeNode(String nodeName, NamedNodeMap nodeMap) {
0844: try {
0845: return nodeMap.removeNamedItem(nodeName);
0846: } catch (DOMException e) {
0847: if (e.code != DOMException.NOT_FOUND_ERR)
0848: throw e;
0849: return null;
0850: }
0851: }
0852:
0853: /**
0854: * Encode some patterns into XML parameter tag
0855: */
0856: private static void encodePatterns(IPath[] patterns, String tag,
0857: Map parameters) {
0858: if (patterns != null && patterns.length > 0) {
0859: StringBuffer rule = new StringBuffer(10);
0860: for (int i = 0, max = patterns.length; i < max; i++) {
0861: if (i > 0)
0862: rule.append('|');
0863: rule.append(patterns[i]);
0864: }
0865: parameters.put(tag, String.valueOf(rule));
0866: }
0867: }
0868:
0869: /**
0870: * Returns true if the given object is a classpath entry
0871: * with equivalent attributes.
0872: */
0873: public boolean equals(Object object) {
0874: if (this == object)
0875: return true;
0876: if (object instanceof ClasspathEntry) {
0877: ClasspathEntry otherEntry = (ClasspathEntry) object;
0878:
0879: if (this .contentKind != otherEntry.getContentKind())
0880: return false;
0881:
0882: if (this .entryKind != otherEntry.getEntryKind())
0883: return false;
0884:
0885: if (this .isExported != otherEntry.isExported())
0886: return false;
0887:
0888: if (!this .path.equals(otherEntry.getPath()))
0889: return false;
0890:
0891: IPath otherPath = otherEntry.getSourceAttachmentPath();
0892: if (this .sourceAttachmentPath == null) {
0893: if (otherPath != null)
0894: return false;
0895: } else {
0896: if (!this .sourceAttachmentPath.equals(otherPath))
0897: return false;
0898: }
0899:
0900: otherPath = otherEntry.getSourceAttachmentRootPath();
0901: if (this .sourceAttachmentRootPath == null) {
0902: if (otherPath != null)
0903: return false;
0904: } else {
0905: if (!this .sourceAttachmentRootPath.equals(otherPath))
0906: return false;
0907: }
0908:
0909: if (!equalPatterns(this .inclusionPatterns, otherEntry
0910: .getInclusionPatterns()))
0911: return false;
0912: if (!equalPatterns(this .exclusionPatterns, otherEntry
0913: .getExclusionPatterns()))
0914: return false;
0915: AccessRuleSet otherRuleSet = otherEntry.getAccessRuleSet();
0916: if (getAccessRuleSet() != null) {
0917: if (!getAccessRuleSet().equals(otherRuleSet))
0918: return false;
0919: } else if (otherRuleSet != null)
0920: return false;
0921: if (this .combineAccessRules != otherEntry
0922: .combineAccessRules())
0923: return false;
0924: otherPath = otherEntry.getOutputLocation();
0925: if (this .specificOutputLocation == null) {
0926: if (otherPath != null)
0927: return false;
0928: } else {
0929: if (!this .specificOutputLocation.equals(otherPath))
0930: return false;
0931: }
0932: if (!equalAttributes(this .extraAttributes, otherEntry
0933: .getExtraAttributes()))
0934: return false;
0935: return true;
0936: } else {
0937: return false;
0938: }
0939: }
0940:
0941: private static boolean equalAttributes(
0942: IClasspathAttribute[] firstAttributes,
0943: IClasspathAttribute[] secondAttributes) {
0944: if (firstAttributes != secondAttributes) {
0945: if (firstAttributes == null)
0946: return false;
0947: int length = firstAttributes.length;
0948: if (secondAttributes == null
0949: || secondAttributes.length != length)
0950: return false;
0951: for (int i = 0; i < length; i++) {
0952: if (!firstAttributes[i].equals(secondAttributes[i]))
0953: return false;
0954: }
0955: }
0956: return true;
0957: }
0958:
0959: private static boolean equalPatterns(IPath[] firstPatterns,
0960: IPath[] secondPatterns) {
0961: if (firstPatterns != secondPatterns) {
0962: if (firstPatterns == null)
0963: return false;
0964: int length = firstPatterns.length;
0965: if (secondPatterns == null
0966: || secondPatterns.length != length)
0967: return false;
0968: for (int i = 0; i < length; i++) {
0969: // compare toStrings instead of IPaths
0970: // since IPath.equals is specified to ignore trailing separators
0971: if (!firstPatterns[i].toString().equals(
0972: secondPatterns[i].toString()))
0973: return false;
0974: }
0975: }
0976: return true;
0977: }
0978:
0979: /**
0980: * @see IClasspathEntry#getAccessRules()
0981: */
0982: public IAccessRule[] getAccessRules() {
0983: if (this .accessRuleSet == null)
0984: return NO_ACCESS_RULES;
0985: AccessRule[] rules = this .accessRuleSet.getAccessRules();
0986: int length = rules.length;
0987: if (length == 0)
0988: return NO_ACCESS_RULES;
0989: IAccessRule[] result = new IAccessRule[length];
0990: System.arraycopy(rules, 0, result, 0, length);
0991: return result;
0992: }
0993:
0994: public AccessRuleSet getAccessRuleSet() {
0995: return this .accessRuleSet;
0996: }
0997:
0998: /**
0999: * @see IClasspathEntry
1000: */
1001: public int getContentKind() {
1002: return this .contentKind;
1003: }
1004:
1005: /**
1006: * @see IClasspathEntry
1007: */
1008: public int getEntryKind() {
1009: return this .entryKind;
1010: }
1011:
1012: /**
1013: * @see IClasspathEntry#getExclusionPatterns()
1014: */
1015: public IPath[] getExclusionPatterns() {
1016: return this .exclusionPatterns;
1017: }
1018:
1019: public IClasspathAttribute[] getExtraAttributes() {
1020: return this .extraAttributes;
1021: }
1022:
1023: private String[] getMessageTemplates() {
1024: JavaModelManager manager = JavaModelManager
1025: .getJavaModelManager();
1026: String[] result = new String[AccessRuleSet.MESSAGE_TEMPLATES_LENGTH];
1027: if (this .entryKind == CPE_PROJECT
1028: || this .entryKind == CPE_SOURCE) { // can be remote source entry when reconciling
1029: result[0] = manager
1030: .intern(Messages
1031: .bind(
1032: org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_project,
1033: new String[] {
1034: "{0}", getPath().segment(0) })); //$NON-NLS-1$
1035: result[1] = manager
1036: .intern(Messages
1037: .bind(
1038: org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_constructor_project,
1039: new String[] {
1040: "{0}", getPath().segment(0) })); //$NON-NLS-1$
1041: result[2] = manager
1042: .intern(Messages
1043: .bind(
1044: org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_method_project,
1045: new String[] {
1046: "{0}", "{1}", getPath().segment(0) })); //$NON-NLS-1$ //$NON-NLS-2$
1047: result[3] = manager
1048: .intern(Messages
1049: .bind(
1050: org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_field_project,
1051: new String[] {
1052: "{0}", "{1}", getPath().segment(0) })); //$NON-NLS-1$ //$NON-NLS-2$
1053: } else {
1054: IPath libPath = getPath();
1055: Object target = JavaModel.getTarget(ResourcesPlugin
1056: .getWorkspace().getRoot(), libPath, false);
1057: String pathString;
1058: if (target instanceof java.io.File)
1059: pathString = libPath.toOSString();
1060: else
1061: pathString = libPath.makeRelative().toString();
1062: result[0] = manager
1063: .intern(Messages
1064: .bind(
1065: org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_library,
1066: new String[] { "{0}", pathString })); //$NON-NLS-1$
1067: result[1] = manager
1068: .intern(Messages
1069: .bind(
1070: org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_constructor_library,
1071: new String[] { "{0}", pathString })); //$NON-NLS-1$
1072: result[2] = manager
1073: .intern(Messages
1074: .bind(
1075: org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_method_library,
1076: new String[] {
1077: "{0}", "{1}", pathString })); //$NON-NLS-1$ //$NON-NLS-2$
1078: result[3] = manager
1079: .intern(Messages
1080: .bind(
1081: org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_field_library,
1082: new String[] {
1083: "{0}", "{1}", pathString })); //$NON-NLS-1$ //$NON-NLS-2$
1084: }
1085: return result;
1086: }
1087:
1088: /**
1089: * @see IClasspathEntry#getExclusionPatterns()
1090: */
1091: public IPath[] getInclusionPatterns() {
1092: return this .inclusionPatterns;
1093: }
1094:
1095: /**
1096: * @see IClasspathEntry#getOutputLocation()
1097: */
1098: public IPath getOutputLocation() {
1099: return this .specificOutputLocation;
1100: }
1101:
1102: /**
1103: * @see IClasspathEntry
1104: */
1105: public IPath getPath() {
1106: return this .path;
1107: }
1108:
1109: /**
1110: * @see IClasspathEntry
1111: */
1112: public IPath getSourceAttachmentPath() {
1113: return this .sourceAttachmentPath;
1114: }
1115:
1116: /**
1117: * @see IClasspathEntry
1118: */
1119: public IPath getSourceAttachmentRootPath() {
1120: return this .sourceAttachmentRootPath;
1121: }
1122:
1123: /**
1124: * Returns the hash code for this classpath entry
1125: */
1126: public int hashCode() {
1127: return this .path.hashCode();
1128: }
1129:
1130: /**
1131: * @see IClasspathEntry#isExported()
1132: */
1133: public boolean isExported() {
1134: return this .isExported;
1135: }
1136:
1137: public boolean isOptional() {
1138: for (int i = 0, length = this .extraAttributes.length; i < length; i++) {
1139: IClasspathAttribute attribute = this .extraAttributes[i];
1140: if (IClasspathAttribute.OPTIONAL
1141: .equals(attribute.getName())
1142: && "true".equals(attribute.getValue())) //$NON-NLS-1$
1143: return true;
1144: }
1145: return false;
1146: }
1147:
1148: /**
1149: * Returns the kind of a <code>PackageFragmentRoot</code> from its <code>String</code> form.
1150: */
1151: static int kindFromString(String kindStr) {
1152:
1153: if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$
1154: return IClasspathEntry.CPE_PROJECT;
1155: if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$
1156: return IClasspathEntry.CPE_VARIABLE;
1157: if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$
1158: return IClasspathEntry.CPE_CONTAINER;
1159: if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$
1160: return IClasspathEntry.CPE_SOURCE;
1161: if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$
1162: return IClasspathEntry.CPE_LIBRARY;
1163: if (kindStr.equalsIgnoreCase("output")) //$NON-NLS-1$
1164: return ClasspathEntry.K_OUTPUT;
1165: return -1;
1166: }
1167:
1168: /**
1169: * Returns a <code>String</code> for the kind of a class path entry.
1170: */
1171: static String kindToString(int kind) {
1172:
1173: switch (kind) {
1174: case IClasspathEntry.CPE_PROJECT:
1175: return "src"; // backward compatibility //$NON-NLS-1$
1176: case IClasspathEntry.CPE_SOURCE:
1177: return "src"; //$NON-NLS-1$
1178: case IClasspathEntry.CPE_LIBRARY:
1179: return "lib"; //$NON-NLS-1$
1180: case IClasspathEntry.CPE_VARIABLE:
1181: return "var"; //$NON-NLS-1$
1182: case IClasspathEntry.CPE_CONTAINER:
1183: return "con"; //$NON-NLS-1$
1184: case ClasspathEntry.K_OUTPUT:
1185: return "output"; //$NON-NLS-1$
1186: default:
1187: return "unknown"; //$NON-NLS-1$
1188: }
1189: }
1190:
1191: /*
1192: * Backward compatibility: only accessible and non-accessible files are suported.
1193: */
1194: public static IAccessRule[] getAccessRules(IPath[] accessibleFiles,
1195: IPath[] nonAccessibleFiles) {
1196: int accessibleFilesLength = accessibleFiles == null ? 0
1197: : accessibleFiles.length;
1198: int nonAccessibleFilesLength = nonAccessibleFiles == null ? 0
1199: : nonAccessibleFiles.length;
1200: int length = accessibleFilesLength + nonAccessibleFilesLength;
1201: if (length == 0)
1202: return null;
1203: IAccessRule[] accessRules = new IAccessRule[length];
1204: for (int i = 0; i < accessibleFilesLength; i++) {
1205: accessRules[i] = JavaCore.newAccessRule(accessibleFiles[i],
1206: IAccessRule.K_ACCESSIBLE);
1207: }
1208: for (int i = 0; i < nonAccessibleFilesLength; i++) {
1209: accessRules[accessibleFilesLength + i] = JavaCore
1210: .newAccessRule(nonAccessibleFiles[i],
1211: IAccessRule.K_NON_ACCESSIBLE);
1212: }
1213: return accessRules;
1214: }
1215:
1216: /**
1217: * Returns a printable representation of this classpath entry.
1218: */
1219: public String toString() {
1220: StringBuffer buffer = new StringBuffer();
1221: buffer.append(String.valueOf(getPath()));
1222: buffer.append('[');
1223: switch (getEntryKind()) {
1224: case IClasspathEntry.CPE_LIBRARY:
1225: buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
1226: break;
1227: case IClasspathEntry.CPE_PROJECT:
1228: buffer.append("CPE_PROJECT"); //$NON-NLS-1$
1229: break;
1230: case IClasspathEntry.CPE_SOURCE:
1231: buffer.append("CPE_SOURCE"); //$NON-NLS-1$
1232: break;
1233: case IClasspathEntry.CPE_VARIABLE:
1234: buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
1235: break;
1236: case IClasspathEntry.CPE_CONTAINER:
1237: buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
1238: break;
1239: }
1240: buffer.append("]["); //$NON-NLS-1$
1241: switch (getContentKind()) {
1242: case IPackageFragmentRoot.K_BINARY:
1243: buffer.append("K_BINARY"); //$NON-NLS-1$
1244: break;
1245: case IPackageFragmentRoot.K_SOURCE:
1246: buffer.append("K_SOURCE"); //$NON-NLS-1$
1247: break;
1248: case ClasspathEntry.K_OUTPUT:
1249: buffer.append("K_OUTPUT"); //$NON-NLS-1$
1250: break;
1251: }
1252: buffer.append(']');
1253: if (getSourceAttachmentPath() != null) {
1254: buffer.append("[sourcePath:"); //$NON-NLS-1$
1255: buffer.append(getSourceAttachmentPath());
1256: buffer.append(']');
1257: }
1258: if (getSourceAttachmentRootPath() != null) {
1259: buffer.append("[rootPath:"); //$NON-NLS-1$
1260: buffer.append(getSourceAttachmentRootPath());
1261: buffer.append(']');
1262: }
1263: buffer.append("[isExported:"); //$NON-NLS-1$
1264: buffer.append(this .isExported);
1265: buffer.append(']');
1266: IPath[] patterns = this .inclusionPatterns;
1267: int length;
1268: if ((length = patterns == null ? 0 : patterns.length) > 0) {
1269: buffer.append("[including:"); //$NON-NLS-1$
1270: for (int i = 0; i < length; i++) {
1271: buffer.append(patterns[i]);
1272: if (i != length - 1) {
1273: buffer.append('|');
1274: }
1275: }
1276: buffer.append(']');
1277: }
1278: patterns = this .exclusionPatterns;
1279: if ((length = patterns == null ? 0 : patterns.length) > 0) {
1280: buffer.append("[excluding:"); //$NON-NLS-1$
1281: for (int i = 0; i < length; i++) {
1282: buffer.append(patterns[i]);
1283: if (i != length - 1) {
1284: buffer.append('|');
1285: }
1286: }
1287: buffer.append(']');
1288: }
1289: if (this .accessRuleSet != null) {
1290: buffer.append('[');
1291: buffer.append(this .accessRuleSet
1292: .toString(false/*on one line*/));
1293: buffer.append(']');
1294: }
1295: if (this .entryKind == CPE_PROJECT) {
1296: buffer.append("[combine access rules:"); //$NON-NLS-1$
1297: buffer.append(this .combineAccessRules);
1298: buffer.append(']');
1299: }
1300: if (getOutputLocation() != null) {
1301: buffer.append("[output:"); //$NON-NLS-1$
1302: buffer.append(getOutputLocation());
1303: buffer.append(']');
1304: }
1305: if ((length = this .extraAttributes == null ? 0
1306: : this .extraAttributes.length) > 0) {
1307: buffer.append("[attributes:"); //$NON-NLS-1$
1308: for (int i = 0; i < length; i++) {
1309: buffer.append(this .extraAttributes[i]);
1310: if (i != length - 1) {
1311: buffer.append(',');
1312: }
1313: }
1314: buffer.append(']');
1315: }
1316: return buffer.toString();
1317: }
1318:
1319: /**
1320: * Answers an ID which is used to distinguish entries during package
1321: * fragment root computations
1322: */
1323: public String rootID() {
1324:
1325: if (this .rootID == null) {
1326: switch (this .entryKind) {
1327: case IClasspathEntry.CPE_LIBRARY:
1328: this .rootID = "[LIB]" + this .path; //$NON-NLS-1$
1329: break;
1330: case IClasspathEntry.CPE_PROJECT:
1331: this .rootID = "[PRJ]" + this .path; //$NON-NLS-1$
1332: break;
1333: case IClasspathEntry.CPE_SOURCE:
1334: this .rootID = "[SRC]" + this .path; //$NON-NLS-1$
1335: break;
1336: case IClasspathEntry.CPE_VARIABLE:
1337: this .rootID = "[VAR]" + this .path; //$NON-NLS-1$
1338: break;
1339: case IClasspathEntry.CPE_CONTAINER:
1340: this .rootID = "[CON]" + this .path; //$NON-NLS-1$
1341: break;
1342: default:
1343: this .rootID = ""; //$NON-NLS-1$
1344: break;
1345: }
1346: }
1347: return this .rootID;
1348: }
1349:
1350: /**
1351: * @see IClasspathEntry
1352: * @deprecated
1353: */
1354: public IClasspathEntry getResolvedEntry() {
1355:
1356: return JavaCore.getResolvedClasspathEntry(this );
1357: }
1358:
1359: /**
1360: * Validate a given classpath and output location for a project, using the following rules:
1361: * <ul>
1362: * <li> Classpath entries cannot collide with each other; that is, all entry paths must be unique.
1363: * <li> The project output location path cannot be null, must be absolute and located inside the project.
1364: * <li> Specific output locations (specified on source entries) can be null, if not they must be located inside the project,
1365: * <li> A project entry cannot refer to itself directly (that is, a project cannot prerequisite itself).
1366: * <li> Classpath entries or output locations cannot coincidate or be nested in each other, except for the following scenarii listed below:
1367: * <ul><li> A source folder can coincidate with its own output location, in which case this output can then contain library archives.
1368: * However, a specific output location cannot coincidate with any library or a distinct source folder than the one referring to it. </li>
1369: * <li> A source/library folder can be nested in any source folder as long as the nested folder is excluded from the enclosing one. </li>
1370: * <li> An output location can be nested in a source folder, if the source folder coincidates with the project itself, or if the output
1371: * location is excluded from the source folder. </li>
1372: * </ul>
1373: * </ul>
1374: *
1375: * Note that the classpath entries are not validated automatically. Only bound variables or containers are considered
1376: * in the checking process (this allows to perform a consistency check on a classpath which has references to
1377: * yet non existing projects, folders, ...).
1378: * <p>
1379: * This validation is intended to anticipate classpath issues prior to assigning it to a project. In particular, it will automatically
1380: * be performed during the classpath setting operation (if validation fails, the classpath setting will not complete).
1381: * <p>
1382: * @param javaProject the given java project
1383: * @param rawClasspath a given classpath
1384: * @param projectOutputLocation a given output location
1385: * @return a status object with code <code>IStatus.OK</code> if
1386: * the given classpath and output location are compatible, otherwise a status
1387: * object indicating what is wrong with the classpath or output location
1388: */
1389: public static IJavaModelStatus validateClasspath(
1390: IJavaProject javaProject, IClasspathEntry[] rawClasspath,
1391: IPath projectOutputLocation) {
1392:
1393: IProject project = javaProject.getProject();
1394: IPath projectPath = project.getFullPath();
1395: String projectName = javaProject.getElementName();
1396:
1397: /* validate output location */
1398: if (projectOutputLocation == null) {
1399: return new JavaModelStatus(
1400: IJavaModelStatusConstants.NULL_PATH);
1401: }
1402: if (projectOutputLocation.isAbsolute()) {
1403: if (!projectPath.isPrefixOf(projectOutputLocation)) {
1404: return new JavaModelStatus(
1405: IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT,
1406: javaProject, projectOutputLocation.toString());
1407: }
1408: } else {
1409: return new JavaModelStatus(
1410: IJavaModelStatusConstants.RELATIVE_PATH,
1411: projectOutputLocation);
1412: }
1413:
1414: boolean hasSource = false;
1415: boolean hasLibFolder = false;
1416:
1417: // tolerate null path, it will be reset to default
1418: if (rawClasspath == null)
1419: return JavaModelStatus.VERIFIED_OK;
1420:
1421: // retrieve resolved classpath
1422: IClasspathEntry[] classpath;
1423: try {
1424: classpath = ((JavaProject) javaProject)
1425: .resolveClasspath(rawClasspath);
1426: } catch (JavaModelException e) {
1427: return e.getJavaModelStatus();
1428: }
1429: int length = classpath.length;
1430:
1431: int outputCount = 1;
1432: IPath[] outputLocations = new IPath[length + 1];
1433: boolean[] allowNestingInOutputLocations = new boolean[length + 1];
1434: outputLocations[0] = projectOutputLocation;
1435:
1436: // retrieve and check output locations
1437: IPath potentialNestedOutput = null; // for error reporting purpose
1438: int sourceEntryCount = 0;
1439: boolean disableExclusionPatterns = JavaCore.DISABLED
1440: .equals(javaProject
1441: .getOption(
1442: JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS,
1443: true));
1444: boolean disableCustomOutputLocations = JavaCore.DISABLED
1445: .equals(javaProject
1446: .getOption(
1447: JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS,
1448: true));
1449:
1450: for (int i = 0; i < length; i++) {
1451: IClasspathEntry resolvedEntry = classpath[i];
1452: if (disableExclusionPatterns
1453: && ((resolvedEntry.getInclusionPatterns() != null && resolvedEntry
1454: .getInclusionPatterns().length > 0) || (resolvedEntry
1455: .getExclusionPatterns() != null && resolvedEntry
1456: .getExclusionPatterns().length > 0))) {
1457: return new JavaModelStatus(
1458: IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS,
1459: javaProject, resolvedEntry.getPath());
1460: }
1461: switch (resolvedEntry.getEntryKind()) {
1462: case IClasspathEntry.CPE_SOURCE:
1463: sourceEntryCount++;
1464:
1465: IPath customOutput;
1466: if ((customOutput = resolvedEntry.getOutputLocation()) != null) {
1467:
1468: if (disableCustomOutputLocations) {
1469: return new JavaModelStatus(
1470: IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS,
1471: javaProject, resolvedEntry.getPath());
1472: }
1473: // ensure custom output is in project
1474: if (customOutput.isAbsolute()) {
1475: if (!javaProject.getPath().isPrefixOf(
1476: customOutput)) {
1477: return new JavaModelStatus(
1478: IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT,
1479: javaProject, customOutput
1480: .toString());
1481: }
1482: } else {
1483: return new JavaModelStatus(
1484: IJavaModelStatusConstants.RELATIVE_PATH,
1485: customOutput);
1486: }
1487:
1488: // ensure custom output doesn't conflict with other outputs
1489: // check exact match
1490: if (Util.indexOfMatchingPath(customOutput,
1491: outputLocations, outputCount) != -1) {
1492: continue; // already found
1493: }
1494: // accumulate all outputs, will check nesting once all available (to handle ordering issues)
1495: outputLocations[outputCount++] = customOutput;
1496: }
1497: }
1498: }
1499: // check nesting across output locations
1500: for (int i = 1 /*no check for default output*/; i < outputCount; i++) {
1501: IPath customOutput = outputLocations[i];
1502: int index;
1503: // check nesting
1504: if ((index = Util.indexOfEnclosingPath(customOutput,
1505: outputLocations, outputCount)) != -1
1506: && index != i) {
1507: if (index == 0) {
1508: // custom output is nested in project's output: need to check if all source entries have a custom
1509: // output before complaining
1510: if (potentialNestedOutput == null)
1511: potentialNestedOutput = customOutput;
1512: } else {
1513: return new JavaModelStatus(
1514: IJavaModelStatusConstants.INVALID_CLASSPATH,
1515: Messages
1516: .bind(
1517: Messages.classpath_cannotNestOutputInOutput,
1518: new String[] {
1519: customOutput
1520: .makeRelative()
1521: .toString(),
1522: outputLocations[index]
1523: .makeRelative()
1524: .toString() }));
1525: }
1526: }
1527: }
1528: // allow custom output nesting in project's output if all source entries have a custom output
1529: if (sourceEntryCount <= outputCount - 1) {
1530: allowNestingInOutputLocations[0] = true;
1531: } else if (potentialNestedOutput != null) {
1532: return new JavaModelStatus(
1533: IJavaModelStatusConstants.INVALID_CLASSPATH,
1534: Messages
1535: .bind(
1536: Messages.classpath_cannotNestOutputInOutput,
1537: new String[] {
1538: potentialNestedOutput
1539: .makeRelative()
1540: .toString(),
1541: outputLocations[0]
1542: .makeRelative()
1543: .toString() }));
1544: }
1545:
1546: for (int i = 0; i < length; i++) {
1547: IClasspathEntry resolvedEntry = classpath[i];
1548: IPath path = resolvedEntry.getPath();
1549: int index;
1550: switch (resolvedEntry.getEntryKind()) {
1551:
1552: case IClasspathEntry.CPE_SOURCE:
1553: hasSource = true;
1554: if ((index = Util.indexOfMatchingPath(path,
1555: outputLocations, outputCount)) != -1) {
1556: allowNestingInOutputLocations[index] = true;
1557: }
1558: break;
1559:
1560: case IClasspathEntry.CPE_LIBRARY:
1561: hasLibFolder |= !org.eclipse.jdt.internal.compiler.util.Util
1562: .isArchiveFileName(path.lastSegment());
1563: if ((index = Util.indexOfMatchingPath(path,
1564: outputLocations, outputCount)) != -1) {
1565: allowNestingInOutputLocations[index] = true;
1566: }
1567: break;
1568: }
1569: }
1570: if (!hasSource && !hasLibFolder) { // if no source and no lib folder, then allowed
1571: for (int i = 0; i < outputCount; i++)
1572: allowNestingInOutputLocations[i] = true;
1573: }
1574:
1575: HashSet pathes = new HashSet(length);
1576:
1577: // check all entries
1578: for (int i = 0; i < length; i++) {
1579: IClasspathEntry entry = classpath[i];
1580: if (entry == null)
1581: continue;
1582: IPath entryPath = entry.getPath();
1583: int kind = entry.getEntryKind();
1584:
1585: // Build some common strings for status message
1586: boolean isProjectRelative = projectName.equals(entryPath
1587: .segment(0));
1588: String entryPathMsg = isProjectRelative ? entryPath
1589: .removeFirstSegments(1).toString() : entryPath
1590: .makeRelative().toString();
1591:
1592: // complain if duplicate path
1593: if (!pathes.add(entryPath)) {
1594: return new JavaModelStatus(
1595: IJavaModelStatusConstants.NAME_COLLISION,
1596: Messages.bind(
1597: Messages.classpath_duplicateEntryPath,
1598: new String[] { entryPathMsg,
1599: projectName }));
1600: }
1601: // no further check if entry coincidates with project or output location
1602: if (entryPath.equals(projectPath)) {
1603: // complain if self-referring project entry
1604: if (kind == IClasspathEntry.CPE_PROJECT) {
1605: return new JavaModelStatus(
1606: IJavaModelStatusConstants.INVALID_PATH,
1607: Messages
1608: .bind(
1609: Messages.classpath_cannotReferToItself,
1610: entryPath.makeRelative()
1611: .toString()));
1612: }
1613: // tolerate nesting output in src if src==prj
1614: continue;
1615: }
1616:
1617: // allow nesting source entries in each other as long as the outer entry excludes the inner one
1618: if (kind == IClasspathEntry.CPE_SOURCE
1619: || (kind == IClasspathEntry.CPE_LIBRARY && !org.eclipse.jdt.internal.compiler.util.Util
1620: .isArchiveFileName(entryPath.lastSegment()))) {
1621: for (int j = 0; j < classpath.length; j++) {
1622: IClasspathEntry otherEntry = classpath[j];
1623: if (otherEntry == null)
1624: continue;
1625: int otherKind = otherEntry.getEntryKind();
1626: IPath otherPath = otherEntry.getPath();
1627: if (entry != otherEntry
1628: && (otherKind == IClasspathEntry.CPE_SOURCE || (otherKind == IClasspathEntry.CPE_LIBRARY && !org.eclipse.jdt.internal.compiler.util.Util
1629: .isArchiveFileName(otherPath
1630: .lastSegment())))) {
1631: char[][] inclusionPatterns, exclusionPatterns;
1632: if (otherPath.isPrefixOf(entryPath)
1633: && !otherPath.equals(entryPath)
1634: && !Util
1635: .isExcluded(
1636: entryPath.append("*"), inclusionPatterns = ((ClasspathEntry) otherEntry).fullInclusionPatternChars(), exclusionPatterns = ((ClasspathEntry) otherEntry).fullExclusionPatternChars(), false)) { //$NON-NLS-1$
1637: String exclusionPattern = entryPath
1638: .removeFirstSegments(
1639: otherPath.segmentCount())
1640: .segment(0);
1641: if (Util.isExcluded(entryPath,
1642: inclusionPatterns,
1643: exclusionPatterns, false)) {
1644: return new JavaModelStatus(
1645: IJavaModelStatusConstants.INVALID_CLASSPATH,
1646: Messages
1647: .bind(
1648: Messages.classpath_mustEndWithSlash,
1649: new String[] {
1650: exclusionPattern,
1651: entryPath
1652: .makeRelative()
1653: .toString() }));
1654: } else {
1655: if (otherKind == IClasspathEntry.CPE_SOURCE) {
1656: exclusionPattern += '/';
1657: if (!disableExclusionPatterns) {
1658: return new JavaModelStatus(
1659: IJavaModelStatusConstants.INVALID_CLASSPATH,
1660: Messages
1661: .bind(
1662: Messages.classpath_cannotNestEntryInEntry,
1663: new String[] {
1664: entryPath
1665: .makeRelative()
1666: .toString(),
1667: otherEntry
1668: .getPath()
1669: .makeRelative()
1670: .toString(),
1671: exclusionPattern }));
1672: } else {
1673: return new JavaModelStatus(
1674: IJavaModelStatusConstants.INVALID_CLASSPATH,
1675: Messages
1676: .bind(
1677: Messages.classpath_cannotNestEntryInEntryNoExclusion,
1678: new String[] {
1679: entryPath
1680: .makeRelative()
1681: .toString(),
1682: otherEntry
1683: .getPath()
1684: .makeRelative()
1685: .toString(),
1686: exclusionPattern }));
1687: }
1688: } else {
1689: return new JavaModelStatus(
1690: IJavaModelStatusConstants.INVALID_CLASSPATH,
1691: Messages
1692: .bind(
1693: Messages.classpath_cannotNestEntryInLibrary,
1694: new String[] {
1695: entryPath
1696: .makeRelative()
1697: .toString(),
1698: otherEntry
1699: .getPath()
1700: .makeRelative()
1701: .toString() }));
1702: }
1703: }
1704: }
1705: }
1706: }
1707: }
1708:
1709: // prevent nesting output location inside entry unless enclosing is a source entry which explicitly exclude the output location
1710: char[][] inclusionPatterns = ((ClasspathEntry) entry)
1711: .fullInclusionPatternChars();
1712: char[][] exclusionPatterns = ((ClasspathEntry) entry)
1713: .fullExclusionPatternChars();
1714: for (int j = 0; j < outputCount; j++) {
1715: IPath currentOutput = outputLocations[j];
1716: if (entryPath.equals(currentOutput))
1717: continue;
1718: if (entryPath.isPrefixOf(currentOutput)) {
1719: if (kind != IClasspathEntry.CPE_SOURCE
1720: || !Util.isExcluded(currentOutput,
1721: inclusionPatterns,
1722: exclusionPatterns, true)) {
1723: return new JavaModelStatus(
1724: IJavaModelStatusConstants.INVALID_CLASSPATH,
1725: Messages
1726: .bind(
1727: Messages.classpath_cannotNestOutputInEntry,
1728: new String[] {
1729: currentOutput
1730: .makeRelative()
1731: .toString(),
1732: entryPath
1733: .makeRelative()
1734: .toString() }));
1735: }
1736: }
1737: }
1738:
1739: // prevent nesting entry inside output location - when distinct from project or a source folder
1740: for (int j = 0; j < outputCount; j++) {
1741: if (allowNestingInOutputLocations[j])
1742: continue;
1743: IPath currentOutput = outputLocations[j];
1744: if (currentOutput.isPrefixOf(entryPath)) {
1745: return new JavaModelStatus(
1746: IJavaModelStatusConstants.INVALID_CLASSPATH,
1747: Messages
1748: .bind(
1749: Messages.classpath_cannotNestEntryInOutput,
1750: new String[] {
1751: entryPath
1752: .makeRelative()
1753: .toString(),
1754: currentOutput
1755: .makeRelative()
1756: .toString() }));
1757: }
1758: }
1759: }
1760: // ensure that no specific output is coincidating with another source folder (only allowed if matching current source folder)
1761: // 36465 - for 2.0 backward compatibility, only check specific output locations (the default can still coincidate)
1762: // perform one separate iteration so as to not take precedence over previously checked scenarii (in particular should
1763: // diagnose nesting source folder issue before this one, for example, [src]"Project/", [src]"Project/source/" and output="Project/" should
1764: // first complain about missing exclusion pattern
1765: for (int i = 0; i < length; i++) {
1766: IClasspathEntry entry = classpath[i];
1767: if (entry == null)
1768: continue;
1769: IPath entryPath = entry.getPath();
1770: int kind = entry.getEntryKind();
1771:
1772: // Build some common strings for status message
1773: boolean isProjectRelative = projectName.equals(entryPath
1774: .segment(0));
1775: String entryPathMsg = isProjectRelative ? entryPath
1776: .removeFirstSegments(1).toString() : entryPath
1777: .makeRelative().toString();
1778:
1779: if (kind == IClasspathEntry.CPE_SOURCE) {
1780: IPath output = entry.getOutputLocation();
1781: if (output == null)
1782: continue; // 36465 - for 2.0 backward compatibility, only check specific output locations (the default can still coincidate)
1783: // if (output == null) output = projectOutputLocation; // if no specific output, still need to check using default output (this line would check default output)
1784: for (int j = 0; j < length; j++) {
1785: IClasspathEntry otherEntry = classpath[j];
1786: if (otherEntry == entry)
1787: continue;
1788:
1789: // Build some common strings for status message
1790: boolean opStartsWithProject = projectName
1791: .equals(otherEntry.getPath().segment(0));
1792: String otherPathMsg = opStartsWithProject ? otherEntry
1793: .getPath().removeFirstSegments(1)
1794: .toString()
1795: : otherEntry.getPath().makeRelative()
1796: .toString();
1797:
1798: switch (otherEntry.getEntryKind()) {
1799: case IClasspathEntry.CPE_SOURCE:
1800: if (otherEntry.getPath().equals(output)) {
1801: return new JavaModelStatus(
1802: IJavaModelStatusConstants.INVALID_CLASSPATH,
1803: Messages
1804: .bind(
1805: Messages.classpath_cannotUseDistinctSourceFolderAsOutput,
1806: new String[] {
1807: entryPathMsg,
1808: otherPathMsg,
1809: projectName }));
1810: }
1811: break;
1812: case IClasspathEntry.CPE_LIBRARY:
1813: if (otherEntry.getPath().equals(output)) {
1814: return new JavaModelStatus(
1815: IJavaModelStatusConstants.INVALID_CLASSPATH,
1816: Messages
1817: .bind(
1818: Messages.classpath_cannotUseLibraryAsOutput,
1819: new String[] {
1820: entryPathMsg,
1821: otherPathMsg,
1822: projectName }));
1823: }
1824: }
1825: }
1826: }
1827: }
1828: return JavaModelStatus.VERIFIED_OK;
1829: }
1830:
1831: /**
1832: * Returns a Java model status describing the problem related to this classpath entry if any,
1833: * a status object with code <code>IStatus.OK</code> if the entry is fine (that is, if the
1834: * given classpath entry denotes a valid element to be referenced onto a classpath).
1835: *
1836: * @param project the given java project
1837: * @param entry the given classpath entry
1838: * @param checkSourceAttachment a flag to determine if source attachement should be checked
1839: * @param recurseInContainers flag indicating whether validation should be applied to container entries recursively
1840: * @return a java model status describing the problem related to this classpath entry if any, a status object with code <code>IStatus.OK</code> if the entry is fine
1841: */
1842: public static IJavaModelStatus validateClasspathEntry(
1843: IJavaProject project, IClasspathEntry entry,
1844: boolean checkSourceAttachment, boolean recurseInContainers) {
1845:
1846: IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace()
1847: .getRoot();
1848: IPath path = entry.getPath();
1849:
1850: // Build some common strings for status message
1851: String projectName = project.getElementName();
1852: boolean pathStartsWithProject = projectName.equals(path
1853: .segment(0));
1854: String entryPathMsg = pathStartsWithProject ? path
1855: .removeFirstSegments(1).makeRelative().toString()
1856: : path.toString();
1857:
1858: switch (entry.getEntryKind()) {
1859:
1860: // container entry check
1861: case IClasspathEntry.CPE_CONTAINER:
1862: if (path.segmentCount() >= 1) {
1863: try {
1864: IClasspathContainer container = JavaModelManager
1865: .getJavaModelManager()
1866: .getClasspathContainer(path, project);
1867: // container retrieval is performing validation check on container entry kinds.
1868: if (container == null) {
1869: return new JavaModelStatus(
1870: IJavaModelStatusConstants.CP_CONTAINER_PATH_UNBOUND,
1871: project, path);
1872: } else if (container == JavaModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) {
1873: // Validate extra attributes
1874: IClasspathAttribute[] extraAttributes = entry
1875: .getExtraAttributes();
1876: if (extraAttributes != null) {
1877: int length = extraAttributes.length;
1878: HashSet set = new HashSet(length);
1879: for (int i = 0; i < length; i++) {
1880: String attName = extraAttributes[i]
1881: .getName();
1882: if (!set.add(attName)) {
1883: return new JavaModelStatus(
1884: IJavaModelStatusConstants.NAME_COLLISION,
1885: Messages
1886: .bind(
1887: Messages.classpath_duplicateEntryExtraAttribute,
1888: new String[] {
1889: attName,
1890: entryPathMsg,
1891: projectName }));
1892: }
1893: }
1894: }
1895: // don't create a marker if initialization is in progress (case of cp initialization batching)
1896: return JavaModelStatus.VERIFIED_OK;
1897: }
1898: IClasspathEntry[] containerEntries = container
1899: .getClasspathEntries();
1900: if (containerEntries != null) {
1901: for (int i = 0, length = containerEntries.length; i < length; i++) {
1902: IClasspathEntry containerEntry = containerEntries[i];
1903: int kind = containerEntry == null ? 0
1904: : containerEntry.getEntryKind();
1905: if (containerEntry == null
1906: || kind == IClasspathEntry.CPE_SOURCE
1907: || kind == IClasspathEntry.CPE_VARIABLE
1908: || kind == IClasspathEntry.CPE_CONTAINER) {
1909: String description = container
1910: .getDescription();
1911: if (description == null)
1912: description = path.makeRelative()
1913: .toString();
1914: return new JavaModelStatus(
1915: IJavaModelStatusConstants.INVALID_CP_CONTAINER_ENTRY,
1916: project, path);
1917: }
1918: if (recurseInContainers) {
1919: IJavaModelStatus containerEntryStatus = validateClasspathEntry(
1920: project, containerEntry,
1921: checkSourceAttachment,
1922: recurseInContainers);
1923: if (!containerEntryStatus.isOK()) {
1924: return containerEntryStatus;
1925: }
1926: }
1927: }
1928: }
1929: } catch (JavaModelException e) {
1930: return new JavaModelStatus(e);
1931: }
1932: } else {
1933: return new JavaModelStatus(
1934: IJavaModelStatusConstants.INVALID_CLASSPATH,
1935: Messages
1936: .bind(
1937: Messages.classpath_illegalContainerPath,
1938: new String[] { entryPathMsg,
1939: projectName }));
1940: }
1941: break;
1942:
1943: // variable entry check
1944: case IClasspathEntry.CPE_VARIABLE:
1945: if (path.segmentCount() >= 1) {
1946: try {
1947: entry = JavaCore.getResolvedClasspathEntry(entry);
1948: } catch (AssertionFailedException e) {
1949: // Catch the assertion failure and throw java model exception instead
1950: // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=55992
1951: return new JavaModelStatus(
1952: IJavaModelStatusConstants.INVALID_PATH, e
1953: .getMessage());
1954: }
1955: if (entry == null) {
1956: return new JavaModelStatus(
1957: IJavaModelStatusConstants.CP_VARIABLE_PATH_UNBOUND,
1958: project, path);
1959: }
1960:
1961: // get validation status
1962: IJavaModelStatus status = validateClasspathEntry(
1963: project, entry, checkSourceAttachment,
1964: recurseInContainers);
1965: if (!status.isOK())
1966: return status;
1967:
1968: // return deprecation status if any
1969: String variableName = path.segment(0);
1970: String deprecatedMessage = JavaCore
1971: .getClasspathVariableDeprecationMessage(variableName);
1972: if (deprecatedMessage != null) {
1973: return new JavaModelStatus(
1974: IStatus.WARNING,
1975: IJavaModelStatusConstants.DEPRECATED_VARIABLE,
1976: project, path, deprecatedMessage);
1977: }
1978: return status;
1979: } else {
1980: return new JavaModelStatus(
1981: IJavaModelStatusConstants.INVALID_CLASSPATH,
1982: Messages.bind(
1983: Messages.classpath_illegalVariablePath,
1984: new String[] { entryPathMsg,
1985: projectName }));
1986: }
1987:
1988: // library entry check
1989: case IClasspathEntry.CPE_LIBRARY:
1990: if (path.isAbsolute() && !path.isEmpty()) {
1991: IPath sourceAttachment = entry
1992: .getSourceAttachmentPath();
1993: Object target = JavaModel.getTarget(workspaceRoot,
1994: path, true);
1995: if (target != null
1996: && !JavaCore.IGNORE.equals(project.getOption(
1997: JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL,
1998: true))) {
1999: long projectTargetJDK = CompilerOptions
2000: .versionToJdkLevel(project
2001: .getOption(
2002: JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
2003: true));
2004: long libraryJDK = Util.getJdkLevel(target);
2005: if (libraryJDK != 0
2006: && libraryJDK > projectTargetJDK) {
2007: return new JavaModelStatus(
2008: IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL,
2009: project,
2010: path,
2011: CompilerOptions
2012: .versionFromJdkLevel(libraryJDK));
2013: }
2014: }
2015: if (target instanceof IResource) {
2016: IResource resolvedResource = (IResource) target;
2017: switch (resolvedResource.getType()) {
2018: case IResource.FILE:
2019: if (org.eclipse.jdt.internal.compiler.util.Util
2020: .isArchiveFileName(resolvedResource
2021: .getName())) {
2022: if (checkSourceAttachment
2023: && sourceAttachment != null
2024: && !sourceAttachment.isEmpty()
2025: && JavaModel.getTarget(
2026: workspaceRoot,
2027: sourceAttachment, true) == null) {
2028: return new JavaModelStatus(
2029: IJavaModelStatusConstants.INVALID_CLASSPATH,
2030: Messages
2031: .bind(
2032: Messages.classpath_unboundSourceAttachment,
2033: new String[] {
2034: sourceAttachment
2035: .toString(),
2036: path
2037: .toString(),
2038: projectName }));
2039: }
2040: } else {
2041: return new JavaModelStatus(
2042: IJavaModelStatusConstants.INVALID_CLASSPATH,
2043: Messages
2044: .bind(
2045: Messages.classpath_illegalLibraryArchive,
2046: new String[] {
2047: entryPathMsg,
2048: projectName }));
2049: }
2050: break;
2051: case IResource.FOLDER: // internal binary folder
2052: if (checkSourceAttachment
2053: && sourceAttachment != null
2054: && !sourceAttachment.isEmpty()
2055: && JavaModel.getTarget(workspaceRoot,
2056: sourceAttachment, true) == null) {
2057: return new JavaModelStatus(
2058: IJavaModelStatusConstants.INVALID_CLASSPATH,
2059: Messages
2060: .bind(
2061: Messages.classpath_unboundSourceAttachment,
2062: new String[] {
2063: sourceAttachment
2064: .toString(),
2065: path
2066: .toString(),
2067: projectName }));
2068: }
2069: }
2070: } else if (target instanceof File) {
2071: File file = JavaModel.getFile(target);
2072: if (file == null) {
2073: return new JavaModelStatus(
2074: IJavaModelStatusConstants.INVALID_CLASSPATH,
2075: Messages
2076: .bind(
2077: Messages.classpath_illegalExternalFolder,
2078: new String[] {
2079: path
2080: .toOSString(),
2081: projectName }));
2082: } else if (!org.eclipse.jdt.internal.compiler.util.Util
2083: .isArchiveFileName(file.getName())) {
2084: return new JavaModelStatus(
2085: IJavaModelStatusConstants.INVALID_CLASSPATH,
2086: Messages
2087: .bind(
2088: Messages.classpath_illegalLibraryArchive,
2089: (new String[] {
2090: path
2091: .toOSString(),
2092: projectName })));
2093: } else if (checkSourceAttachment
2094: && sourceAttachment != null
2095: && !sourceAttachment.isEmpty()
2096: && JavaModel.getTarget(workspaceRoot,
2097: sourceAttachment, true) == null) {
2098: return new JavaModelStatus(
2099: IJavaModelStatusConstants.INVALID_CLASSPATH,
2100: Messages
2101: .bind(
2102: Messages.classpath_unboundSourceAttachment,
2103: new String[] {
2104: sourceAttachment
2105: .toString(),
2106: path
2107: .toOSString(),
2108: projectName }));
2109: }
2110: } else {
2111: boolean isExternal = path.getDevice() != null
2112: || !workspaceRoot.getProject(
2113: path.segment(0)).exists();
2114: if (isExternal) {
2115: return new JavaModelStatus(
2116: IJavaModelStatusConstants.INVALID_CLASSPATH,
2117: Messages
2118: .bind(
2119: Messages.classpath_unboundLibrary,
2120: new String[] {
2121: path
2122: .toOSString(),
2123: projectName }));
2124: } else {
2125: return new JavaModelStatus(
2126: IJavaModelStatusConstants.INVALID_CLASSPATH,
2127: Messages
2128: .bind(
2129: Messages.classpath_unboundLibrary,
2130: new String[] {
2131: entryPathMsg,
2132: projectName }));
2133: }
2134: }
2135: } else {
2136: return new JavaModelStatus(
2137: IJavaModelStatusConstants.INVALID_CLASSPATH,
2138: Messages.bind(
2139: Messages.classpath_illegalLibraryPath,
2140: new String[] { entryPathMsg,
2141: projectName }));
2142: }
2143: break;
2144:
2145: // project entry check
2146: case IClasspathEntry.CPE_PROJECT:
2147: if (path.isAbsolute() && path.segmentCount() == 1) {
2148: IProject prereqProjectRsc = workspaceRoot
2149: .getProject(path.segment(0));
2150: IJavaProject prereqProject = JavaCore
2151: .create(prereqProjectRsc);
2152: try {
2153: if (!prereqProjectRsc.exists()
2154: || !prereqProjectRsc
2155: .hasNature(JavaCore.NATURE_ID)) {
2156: return new JavaModelStatus(
2157: IJavaModelStatusConstants.INVALID_CLASSPATH,
2158: Messages
2159: .bind(
2160: Messages.classpath_unboundProject,
2161: new String[] {
2162: path.segment(0),
2163: projectName }));
2164: }
2165: if (!prereqProjectRsc.isOpen()) {
2166: return new JavaModelStatus(
2167: IJavaModelStatusConstants.INVALID_CLASSPATH,
2168: Messages
2169: .bind(
2170: Messages.classpath_closedProject,
2171: new String[] { path
2172: .segment(0) }));
2173: }
2174: if (!JavaCore.IGNORE
2175: .equals(project
2176: .getOption(
2177: JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL,
2178: true))) {
2179: long projectTargetJDK = CompilerOptions
2180: .versionToJdkLevel(project
2181: .getOption(
2182: JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
2183: true));
2184: long prereqProjectTargetJDK = CompilerOptions
2185: .versionToJdkLevel(prereqProject
2186: .getOption(
2187: JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM,
2188: true));
2189: if (prereqProjectTargetJDK > projectTargetJDK) {
2190: return new JavaModelStatus(
2191: IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL,
2192: project,
2193: path,
2194: CompilerOptions
2195: .versionFromJdkLevel(prereqProjectTargetJDK));
2196: }
2197: }
2198: } catch (CoreException e) {
2199: return new JavaModelStatus(
2200: IJavaModelStatusConstants.INVALID_CLASSPATH,
2201: Messages.bind(
2202: Messages.classpath_unboundProject,
2203: new String[] { path.segment(0),
2204: projectName }));
2205: }
2206: } else {
2207: return new JavaModelStatus(
2208: IJavaModelStatusConstants.INVALID_CLASSPATH,
2209: Messages.bind(
2210: Messages.classpath_illegalProjectPath,
2211: new String[] { path.toString(),
2212: projectName }));
2213: }
2214: break;
2215:
2216: // project source folder
2217: case IClasspathEntry.CPE_SOURCE:
2218: if (((entry.getInclusionPatterns() != null && entry
2219: .getInclusionPatterns().length > 0) || (entry
2220: .getExclusionPatterns() != null && entry
2221: .getExclusionPatterns().length > 0))
2222: && JavaCore.DISABLED
2223: .equals(project
2224: .getOption(
2225: JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS,
2226: true))) {
2227: return new JavaModelStatus(
2228: IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS,
2229: project, path);
2230: }
2231: if (entry.getOutputLocation() != null
2232: && JavaCore.DISABLED
2233: .equals(project
2234: .getOption(
2235: JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS,
2236: true))) {
2237: return new JavaModelStatus(
2238: IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS,
2239: project, path);
2240: }
2241: if (path.isAbsolute() && !path.isEmpty()) {
2242: IPath projectPath = project.getProject().getFullPath();
2243: if (!projectPath.isPrefixOf(path)
2244: || JavaModel.getTarget(workspaceRoot, path,
2245: true) == null) {
2246: return new JavaModelStatus(
2247: IJavaModelStatusConstants.INVALID_CLASSPATH,
2248: Messages
2249: .bind(
2250: Messages.classpath_unboundSourceFolder,
2251: new String[] {
2252: entryPathMsg,
2253: projectName }));
2254: }
2255: } else {
2256: return new JavaModelStatus(
2257: IJavaModelStatusConstants.INVALID_CLASSPATH,
2258: Messages
2259: .bind(
2260: Messages.classpath_illegalSourceFolderPath,
2261: new String[] { entryPathMsg,
2262: projectName }));
2263: }
2264: break;
2265: }
2266:
2267: // Validate extra attributes
2268: IClasspathAttribute[] extraAttributes = entry
2269: .getExtraAttributes();
2270: if (extraAttributes != null) {
2271: int length = extraAttributes.length;
2272: HashSet set = new HashSet(length);
2273: for (int i = 0; i < length; i++) {
2274: String attName = extraAttributes[i].getName();
2275: if (!set.add(attName)) {
2276: return new JavaModelStatus(
2277: IJavaModelStatusConstants.NAME_COLLISION,
2278: Messages
2279: .bind(
2280: Messages.classpath_duplicateEntryExtraAttribute,
2281: new String[] { attName,
2282: entryPathMsg,
2283: projectName }));
2284: }
2285: }
2286: }
2287:
2288: return JavaModelStatus.VERIFIED_OK;
2289: }
2290: }
|