Package org.aspectj.org.eclipse.jdt.internal.core

Source Code of org.aspectj.org.eclipse.jdt.internal.core.ClasspathEntry

/*******************************************************************************
* Copyright (c) 2000, 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
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.aspectj.org.eclipse.jdt.internal.core;

import java.io.ByteArrayOutputStream;
import java.io.File;
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.AssertionFailedException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.aspectj.org.eclipse.jdt.core.IAccessRule;
import org.aspectj.org.eclipse.jdt.core.IClasspathAttribute;
import org.aspectj.org.eclipse.jdt.core.IClasspathContainer;
import org.aspectj.org.eclipse.jdt.core.IClasspathEntry;
import org.aspectj.org.eclipse.jdt.core.IJavaModelStatus;
import org.aspectj.org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.aspectj.org.eclipse.jdt.core.IJavaProject;
import org.aspectj.org.eclipse.jdt.core.IPackageFragmentRoot;
import org.aspectj.org.eclipse.jdt.core.JavaCore;
import org.aspectj.org.eclipse.jdt.core.JavaModelException;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.core.compiler.IProblem;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.AccessRule;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.aspectj.org.eclipse.jdt.internal.core.util.Messages;
import org.aspectj.org.eclipse.jdt.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;

/**
* @see IClasspathEntry
*/
public class ClasspathEntry implements IClasspathEntry {

  public static final String TAG_CLASSPATH = "classpath"; //$NON-NLS-1$
  public static final String TAG_CLASSPATHENTRY = "classpathentry"; //$NON-NLS-1$
  public static final String TAG_OUTPUT = "output"; //$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_INCLUDING = "including"; //$NON-NLS-1$
  public static final String TAG_EXCLUDING = "excluding"; //$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_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_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 classpath entry - one of
   * CPE_PROJECT, CPE_LIBRARY, CPE_SOURCE, CPE_VARIABLE or CPE_CONTAINER
   */
  public int entryKind;

  /**
   * Describes the kind of package fragment roots found on
   * this classpath entry - either K_BINARY or K_SOURCE or
   * K_OUTPUT.
   */
  public int contentKind;

  /**
   * The meaning of the path of a classpath entry depends on its entry kind:<ul>
   <li>Source code in the current project (<code>CPE_SOURCE</code>) -
   *      The path associated with this entry is the absolute path to the root folder. </li>
   <li>A binary library in the current project (<code>CPE_LIBRARY</code>) - the path
   *    associated with this entry is the absolute path to the JAR (or root folder), and
   *    in case it refers to an external JAR, then there is no associated resource in
   *    the workbench.
   *  <li>A required project (<code>CPE_PROJECT</code>) - the path of the entry denotes the
   *    path to the corresponding project resource.</li>
   <li>A variable entry (<code>CPE_VARIABLE</code>) - the first segment of the path
   *      is the name of a classpath variable. If this classpath variable
   *    is bound to the path <it>P</it>, the path of the corresponding classpath entry
   *    is computed by appending to <it>P</it> the segments of the returned
   *    path without the variable.</li>
   <li> A container entry (<code>CPE_CONTAINER</code>) - the first segment of the path is denoting
   *     the unique container identifier (for which a <code>ClasspathContainerInitializer</code> could be
   *   registered), and the remaining segments are used as additional hints for resolving the container entry to
   *   an actual <code>IClasspathContainer</code>.</li>
   */
  public 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$

  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 = {};

  /*
   * Default extra attributes
   */
  public final static IClasspathAttribute[] NO_EXTRA_ATTRIBUTES = {};

  /*
   * Default access rules
   */
  public final static IAccessRule[] NO_ACCESS_RULES = {};

  /**
   * Describes the path to the source archive associated with this
   * classpath entry, or <code>null</code> if this classpath entry has no
   * source attachment.
   * <p>
   * Only library and variable classpath entries may have source attachments.
   * For library classpath 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;

  /**
   * Specific output location (for this source entry)
   */
  public IPath specificOutputLocation;

  /**
   * A constant indicating an output location.
   */
  public static final int K_OUTPUT = 10;

  /**
   * The export flag
   */
  public boolean isExported;

  /*
   * The extra attributes
   */
  IClasspathAttribute[] extraAttributes;

  /**
   * Creates a class path entry of the specified kind with the given path.
   */
  public ClasspathEntry(
    int contentKind,
    int entryKind,
    IPath path,
    IPath[] inclusionPatterns,
    IPath[] exclusionPatterns,
    IPath sourceAttachmentPath,
    IPath sourceAttachmentRootPath,
    IPath specificOutputLocation,
    boolean isExported,
    IAccessRule[] accessRules,
    boolean combineAccessRules,
    IClasspathAttribute[] extraAttributes) {

    this.contentKind = contentKind;
    this.entryKind = entryKind;
    this.path = path;
    this.inclusionPatterns = inclusionPatterns;
    this.exclusionPatterns = exclusionPatterns;

    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());
    }
//    else { -- implicit!
//      this.accessRuleSet = null;
//    }

    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;
      }
    this.sourceAttachmentPath = sourceAttachmentPath;
    this.sourceAttachmentRootPath = sourceAttachmentRootPath;
    this.specificOutputLocation = specificOutputLocation;
    this.isExported = isExported;
  }

  public boolean combineAccessRules() {
    return this.combineAccessRules;
  }

  /**
   * Used to perform export/restriction propagation across referring projects/containers
   */
  public ClasspathEntry combineWith(ClasspathEntry referringEntry) {
    if (referringEntry == null) return this;
    if (referringEntry.isExported() || referringEntry.getAccessRuleSet() != null ) {
      boolean combine = this.entryKind == CPE_SOURCE || referringEntry.combineAccessRules();
      return new ClasspathEntry(
                getContentKind(),
                getEntryKind(),
                getPath(),
                this.inclusionPatterns,
                this.exclusionPatterns,
                getSourceAttachmentPath(),
                getSourceAttachmentRootPath(),
                getOutputLocation(),
                referringEntry.isExported() || this.isExported, // duplicate container entry for tagging it as exported
                combine(referringEntry.getAccessRules(), getAccessRules(), combine),
                this.combineAccessRules,
                this.extraAttributes);
    }
    // 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;
  }

  static IClasspathAttribute[] decodeExtraAttributes(NodeList attributes) {
    if (attributes == null) return NO_EXTRA_ATTRIBUTES;
    int length = attributes.getLength();
    if (length == 0) return NO_EXTRA_ATTRIBUTES;
    IClasspathAttribute[] result = new IClasspathAttribute[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 ClasspathAttribute(name, value);
      }
    }
    if (index != length)
      System.arraycopy(result, 0, result = new IClasspathAttribute[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 ClasspathAccessRule(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, IJavaProject 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;
      HashMap parameters = null;
      if ((attributes = node.getAttributes()) != null) {
        int length = attributes.getLength();
        if (length > 0) {
          parameters = new HashMap();
          for (int i = 0; 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;
  }

  /**
   * Returns the XML encoding of the class path.
   */
  public void elementEncode(XMLWriter writer, IPath projectPath, boolean indent, boolean newLine, Map unknownElements) {
    HashMap parameters = new HashMap();

    parameters.put(TAG_KIND, ClasspathEntry.kindToString(this.entryKind));

    IPath xmlPath = this.path;
    if (this.entryKind != IClasspathEntry.CPE_VARIABLE && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
      // translate to project relative from absolute (unless a device 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.sourceAttachmentPath != null) {
      xmlPath = this.sourceAttachmentPath;
      // translate to project relative from absolute
      if (this.entryKind != IClasspathEntry.CPE_VARIABLE && projectPath != null && projectPath.isPrefixOf(xmlPath)) {
        if (xmlPath.segment(0).equals(projectPath.segment(0))) {
          xmlPath = xmlPath.removeFirstSegments(1);
          xmlPath = xmlPath.makeRelative();
        }
      }
      parameters.put(TAG_SOURCEPATH, String.valueOf(xmlPath));
    }
    if (this.sourceAttachmentRootPath != null) {
      parameters.put(TAG_ROOTPATH, String.valueOf(this.sourceAttachmentRootPath));
    }
    if (this.isExported) {
      parameters.put(TAG_EXPORTED, "true");//$NON-NLS-1$
    }
    encodePatterns(this.inclusionPatterns, TAG_INCLUDING, parameters);
    encodePatterns(this.exclusionPatterns, TAG_EXCLUDING, parameters);
    if (this.entryKind == CPE_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);
      }

    if (this.specificOutputLocation != null) {
      IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1);
      outputLocation = outputLocation.makeRelative();
      parameters.put(TAG_OUTPUT, String.valueOf(outputLocation));
    }

    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_CLASSPATHENTRY,
      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 (hasExtraAttributes || hasRestrictions || hasUnknownChildren)
      writer.endTag(TAG_CLASSPATHENTRY, 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++) {
      IClasspathAttribute 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 IProblem.ForbiddenReference:
        parameters.put(TAG_KIND, TAG_NON_ACCESSIBLE);
        break;
      case 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);

  }

  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*/);
    }
  }

  public static IClasspathEntry elementDecode(Element element, IJavaProject 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 != IClasspathEntry.CPE_VARIABLE && kind != IClasspathEntry.CPE_CONTAINER && !path.isAbsolute()) {
      path = projectPath.append(path);
    }
    // source attachment info (optional)
    IPath sourceAttachmentPath =
      element.hasAttribute(TAG_SOURCEPATH)
      ? new Path(removeAttribute(TAG_SOURCEPATH, attributes))
      : null;
    if (kind != IClasspathEntry.CPE_VARIABLE && sourceAttachmentPath != null && !sourceAttachmentPath.isAbsolute()) {
      sourceAttachmentPath = projectPath.append(sourceAttachmentPath);
    }
    IPath sourceAttachmentRootPath =
      element.hasAttribute(TAG_ROOTPATH)
      ? new Path(removeAttribute(TAG_ROOTPATH, attributes))
      : null;

    // exported flag (optional)
    boolean isExported = removeAttribute(TAG_EXPORTED, 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);

    // backward compatibility
    if (accessRules == null) {
      accessRules = getAccessRules(inclusionPatterns, exclusionPatterns);
    }

    // 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);
    IClasspathAttribute[] extraAttributes = decodeExtraAttributes(attributeList);

    // custom output location
    IPath outputLocation = element.hasAttribute(TAG_OUTPUT) ? projectPath.append(removeAttribute(TAG_OUTPUT, attributes)) : null;

    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 CP entry
    IClasspathEntry entry = null;
    switch (kind) {

      case IClasspathEntry.CPE_PROJECT :
        entry = new ClasspathEntry(
        IPackageFragmentRoot.K_SOURCE,
        IClasspathEntry.CPE_PROJECT,
        path,
        ClasspathEntry.INCLUDE_ALL, // inclusion patterns
        ClasspathEntry.EXCLUDE_NONE, // exclusion patterns
        null, // source attachment
        null, // source attachment root
        null, // specific output folder
        isExported,
        accessRules,
        combineAccessRestrictions,
        extraAttributes);
        break;
      case IClasspathEntry.CPE_LIBRARY :
        entry = JavaCore.newLibraryEntry(
                        path,
                        sourceAttachmentPath,
                        sourceAttachmentRootPath,
                        accessRules,
                        extraAttributes,
                        isExported);
        break;
      case IClasspathEntry.CPE_SOURCE :
        // must be an entry in this project or specify another project
        String projSegment = path.segment(0);
        if (projSegment != null && projSegment.equals(project.getElementName())) { // this project
          entry = JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes);
        } else {
          if (path.segmentCount() == 1) {
            // another project
            entry = JavaCore.newProjectEntry(
                        path,
                        accessRules,
                        combineAccessRestrictions,
                        extraAttributes,
                        isExported);
          } else {
            // an invalid source folder
            entry = JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes);
          }
        }
        break;
      case IClasspathEntry.CPE_VARIABLE :
        entry = JavaCore.newVariableEntry(
            path,
            sourceAttachmentPath,
            sourceAttachmentRootPath,
            accessRules,
            extraAttributes,
            isExported);
        break;
      case IClasspathEntry.CPE_CONTAINER :
        entry = JavaCore.newContainerEntry(
            path,
            accessRules,
            extraAttributes,
            isExported);
        break;
      case ClasspathEntry.K_OUTPUT :
        if (!path.isAbsolute()) return null;
        entry = new ClasspathEntry(
            ClasspathEntry.K_OUTPUT,
            IClasspathEntry.CPE_LIBRARY,
            path,
            INCLUDE_ALL,
            EXCLUDE_NONE,
            null, // source attachment
            null, // source attachment root
            null, // custom output location
            false,
            null, // no access rules
            false, // no accessible files to combine
            NO_EXTRA_ATTRIBUTES);
        break;
      default :
        throw new AssertionFailedException(Messages.bind(Messages.classpath_unknownKind, kindAttr));
    }

    if (unknownAttributes != null || unknownChildren != null) {
      UnknownXmlElements unknownXmlElements = new UnknownXmlElements();
      unknownXmlElements.attributes = unknownAttributes;
      unknownXmlElements.children = unknownChildren;
      unknownElements.put(path, unknownXmlElements);
    }

    return entry;
  }

  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;
    }
  }

  /**
   * 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 classpath entry
   * with equivalent attributes.
   */
  public boolean equals(Object object) {
    if (this == object)
      return true;
    if (object instanceof ClasspathEntry) {
      ClasspathEntry otherEntry = (ClasspathEntry) object;

      if (this.contentKind != otherEntry.getContentKind())
        return false;

      if (this.entryKind != otherEntry.getEntryKind())
        return false;

      if (this.isExported != otherEntry.isExported())
        return false;

      if (!this.path.equals(otherEntry.getPath()))
        return false;

      IPath otherPath = otherEntry.getSourceAttachmentPath();
      if (this.sourceAttachmentPath == null) {
        if (otherPath != null)
          return false;
      } else {
        if (!this.sourceAttachmentPath.equals(otherPath))
          return false;
      }

      otherPath = otherEntry.getSourceAttachmentRootPath();
      if (this.sourceAttachmentRootPath == null) {
        if (otherPath != null)
          return false;
      } else {
        if (!this.sourceAttachmentRootPath.equals(otherPath))
          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;
      otherPath = otherEntry.getOutputLocation();
      if (this.specificOutputLocation == null) {
        if (otherPath != null)
          return false;
      } else {
        if (!this.specificOutputLocation.equals(otherPath))
          return false;
      }
      if (!equalAttributes(this.extraAttributes, otherEntry.getExtraAttributes()))
        return false;
      return true;
    } else {
      return false;
    }
  }

  private static boolean equalAttributes(IClasspathAttribute[] firstAttributes, IClasspathAttribute[] 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;
  }

  /**
   * @see IClasspathEntry#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 AccessRuleSet getAccessRuleSet() {
    return this.accessRuleSet;
  }

  /**
   * @see IClasspathEntry
   */
  public int getContentKind() {
    return this.contentKind;
  }

  /**
   * @see IClasspathEntry
   */
  public int getEntryKind() {
    return this.entryKind;
  }

  /**
   * @see IClasspathEntry#getExclusionPatterns()
   */
  public IPath[] getExclusionPatterns() {
    return this.exclusionPatterns;
  }

  public IClasspathAttribute[] getExtraAttributes() {
    return this.extraAttributes;
  }

  private String[] getMessageTemplates() {
    JavaModelManager manager = JavaModelManager.getJavaModelManager();
    String [] result = new String[AccessRuleSet.MESSAGE_TEMPLATES_LENGTH];
    if (this.entryKind == CPE_PROJECT || this.entryKind == CPE_SOURCE) { // can be remote source entry when reconciling
      result[0] = manager.intern(Messages.bind(
        org.aspectj.org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_project,
        new String[] {"{0}", getPath().segment(0)}))//$NON-NLS-1$
      result[1] = manager.intern(Messages.bind(
          org.aspectj.org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_constructor_project,
          new String[] {"{0}", getPath().segment(0)}))//$NON-NLS-1$
      result[2] = manager.intern(Messages.bind(
          org.aspectj.org.eclipse.jdt.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.aspectj.org.eclipse.jdt.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 = JavaModel.getTarget(ResourcesPlugin.getWorkspace().getRoot(), libPath, false);
      String pathString;
      if (target instanceof java.io.File)
        pathString = libPath.toOSString();
      else
        pathString = libPath.makeRelative().toString();
      result[0] = manager.intern(Messages.bind(
        org.aspectj.org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_library,
        new String[] {"{0}", pathString})); //$NON-NLS-1$
      result[1] = manager.intern(Messages.bind(
          org.aspectj.org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_constructor_library,
          new String[] {"{0}", pathString})); //$NON-NLS-1$
      result[2] = manager.intern(Messages.bind(
          org.aspectj.org.eclipse.jdt.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.aspectj.org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_field_library,
          new String[] {"{0}", "{1}", pathString})); //$NON-NLS-1$ //$NON-NLS-2$
    }
    return result;
  }

  /**
   * @see IClasspathEntry#getExclusionPatterns()
   */
  public IPath[] getInclusionPatterns() {
    return this.inclusionPatterns;
  }

  /**
   * @see IClasspathEntry#getOutputLocation()
   */
  public IPath getOutputLocation() {
    return this.specificOutputLocation;
  }

  /**
   * @see IClasspathEntry
   */
  public IPath getPath() {
    return this.path;
  }

  /**
   * @see IClasspathEntry
   */
  public IPath getSourceAttachmentPath() {
    return this.sourceAttachmentPath;
  }

  /**
   * @see IClasspathEntry
   */
  public IPath getSourceAttachmentRootPath() {
    return this.sourceAttachmentRootPath;
  }

  /**
   * Returns the hash code for this classpath entry
   */
  public int hashCode() {
    return this.path.hashCode();
  }

  /**
   * @see IClasspathEntry#isExported()
   */
  public boolean isExported() {
    return this.isExported;
  }

  public boolean isOptional() {
    for (int i = 0, length = this.extraAttributes.length; i < length; i++) {
      IClasspathAttribute attribute = this.extraAttributes[i];
      if (IClasspathAttribute.OPTIONAL.equals(attribute.getName()) && "true".equals(attribute.getValue())) //$NON-NLS-1$
        return true;
    }
    return false;
  }

  /**
   * Returns the kind of a <code>PackageFragmentRoot</code> from its <code>String</code> form.
   */
  static int kindFromString(String kindStr) {

    if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$
      return IClasspathEntry.CPE_PROJECT;
    if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$
      return IClasspathEntry.CPE_VARIABLE;
    if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$
      return IClasspathEntry.CPE_CONTAINER;
    if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$
      return IClasspathEntry.CPE_SOURCE;
    if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$
      return IClasspathEntry.CPE_LIBRARY;
    if (kindStr.equalsIgnoreCase("output")) //$NON-NLS-1$
      return ClasspathEntry.K_OUTPUT;
    return -1;
  }

  /**
   * Returns a <code>String</code> for the kind of a class path entry.
   */
  static String kindToString(int kind) {

    switch (kind) {
      case IClasspathEntry.CPE_PROJECT :
        return "src"; // backward compatibility //$NON-NLS-1$
      case IClasspathEntry.CPE_SOURCE :
        return "src"; //$NON-NLS-1$
      case IClasspathEntry.CPE_LIBRARY :
        return "lib"; //$NON-NLS-1$
      case IClasspathEntry.CPE_VARIABLE :
        return "var"; //$NON-NLS-1$
      case IClasspathEntry.CPE_CONTAINER :
        return "con"; //$NON-NLS-1$
      case ClasspathEntry.K_OUTPUT :
        return "output"; //$NON-NLS-1$
      default :
        return "unknown"; //$NON-NLS-1$
    }
  }

  /*
   * Backward compatibility: only accessible and non-accessible files are suported.
   */
  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] = JavaCore.newAccessRule(accessibleFiles[i], IAccessRule.K_ACCESSIBLE);
    }
    for (int i = 0; i < nonAccessibleFilesLength; i++) {
      accessRules[accessibleFilesLength + i] = JavaCore.newAccessRule(nonAccessibleFiles[i], IAccessRule.K_NON_ACCESSIBLE);
    }
    return accessRules;
  }

  /**
   * Returns a printable representation of this classpath entry.
   */
  public String toString() {
    StringBuffer buffer = new StringBuffer();
    buffer.append(String.valueOf(getPath()));
    buffer.append('[');
    switch (getEntryKind()) {
      case IClasspathEntry.CPE_LIBRARY :
        buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
        break;
      case IClasspathEntry.CPE_PROJECT :
        buffer.append("CPE_PROJECT"); //$NON-NLS-1$
        break;
      case IClasspathEntry.CPE_SOURCE :
        buffer.append("CPE_SOURCE"); //$NON-NLS-1$
        break;
      case IClasspathEntry.CPE_VARIABLE :
        buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
        break;
      case IClasspathEntry.CPE_CONTAINER :
        buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
        break;
    }
    buffer.append("]["); //$NON-NLS-1$
    switch (getContentKind()) {
      case IPackageFragmentRoot.K_BINARY :
        buffer.append("K_BINARY"); //$NON-NLS-1$
        break;
      case IPackageFragmentRoot.K_SOURCE :
        buffer.append("K_SOURCE"); //$NON-NLS-1$
        break;
      case ClasspathEntry.K_OUTPUT :
        buffer.append("K_OUTPUT"); //$NON-NLS-1$
        break;
    }
    buffer.append(']');
    if (getSourceAttachmentPath() != null) {
      buffer.append("[sourcePath:"); //$NON-NLS-1$
      buffer.append(getSourceAttachmentPath());
      buffer.append(']');
    }
    if (getSourceAttachmentRootPath() != null) {
      buffer.append("[rootPath:"); //$NON-NLS-1$
      buffer.append(getSourceAttachmentRootPath());
      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 == CPE_PROJECT) {
      buffer.append("[combine access rules:"); //$NON-NLS-1$
      buffer.append(this.combineAccessRules);
      buffer.append(']');
    }
    if (getOutputLocation() != null) {
      buffer.append("[output:"); //$NON-NLS-1$
      buffer.append(getOutputLocation());
      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();
  }

  /**
   * 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 IClasspathEntry.CPE_LIBRARY :
          this.rootID = "[LIB]"+this.path;  //$NON-NLS-1$
          break;
        case IClasspathEntry.CPE_PROJECT :
          this.rootID = "[PRJ]"+this.path;  //$NON-NLS-1$
          break;
        case IClasspathEntry.CPE_SOURCE :
          this.rootID = "[SRC]"+this.path;  //$NON-NLS-1$
          break;
        case IClasspathEntry.CPE_VARIABLE :
          this.rootID = "[VAR]"+this.path;  //$NON-NLS-1$
          break;
        case IClasspathEntry.CPE_CONTAINER :
          this.rootID = "[CON]"+this.path;  //$NON-NLS-1$
          break;
        default :
          this.rootID = ""//$NON-NLS-1$
          break;
      }
    }
    return this.rootID;
  }

  /**
   * @see IClasspathEntry
   * @deprecated
   */
  public IClasspathEntry getResolvedEntry() {

    return JavaCore.getResolvedClasspathEntry(this);
  }

  /**
   * Validate a given classpath and output location for a project, using the following rules:
   * <ul>
   *   <li> Classpath entries cannot collide with each other; that is, all entry paths must be unique.
   *   <li> The project output location path cannot be null, must be absolute and located inside the project.
   *   <li> Specific output locations (specified on source entries) can be null, if not they must be located inside the project,
   *   <li> A project entry cannot refer to itself directly (that is, a project cannot prerequisite itself).
     *   <li> Classpath entries or output locations cannot coincidate or be nested in each other, except for the following scenarii listed below:
   *      <ul><li> A source folder can coincidate with its own output location, in which case this output can then contain library archives.
   *                     However, a specific output location cannot coincidate with any library or a distinct source folder than the one referring to it. </li>
   *              <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>
   *       <li> An output location can be nested in a source folder, if the source folder coincidates with the project itself, or if the output
   *           location is excluded from the source folder. </li>
   *      </ul>
   * </ul>
   *
   *  Note that the classpath 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 classpath which has references to
   *  yet non existing projects, folders, ...).
   *  <p>
   *  This validation is intended to anticipate classpath issues prior to assigning it to a project. In particular, it will automatically
   *  be performed during the classpath setting operation (if validation fails, the classpath setting will not complete).
   *  <p>
   * @param javaProject the given java project
   * @param rawClasspath a given classpath
   * @param projectOutputLocation a given output location
   * @return a status object with code <code>IStatus.OK</code> if
   *    the given classpath and output location are compatible, otherwise a status
   *    object indicating what is wrong with the classpath or output location
   */
  public static IJavaModelStatus validateClasspath(IJavaProject javaProject, IClasspathEntry[] rawClasspath, IPath projectOutputLocation) {

    IProject project = javaProject.getProject();
    IPath projectPath= project.getFullPath();
    String projectName = javaProject.getElementName();

    /* validate output location */
    if (projectOutputLocation == null) {
      return new JavaModelStatus(IJavaModelStatusConstants.NULL_PATH);
    }
    if (projectOutputLocation.isAbsolute()) {
      if (!projectPath.isPrefixOf(projectOutputLocation)) {
        return new JavaModelStatus(IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT, javaProject, projectOutputLocation.toString());
      }
    } else {
      return new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH, projectOutputLocation);
    }

    boolean hasSource = false;
    boolean hasLibFolder = false;


    // tolerate null path, it will be reset to default
    if (rawClasspath == null)
      return JavaModelStatus.VERIFIED_OK;

    // retrieve resolved classpath
    IClasspathEntry[] classpath;
    try {
      classpath = ((JavaProject)javaProject).resolveClasspath(rawClasspath);
    } catch(JavaModelException e){
      return e.getJavaModelStatus();
    }
    int length = classpath.length;

    int outputCount = 1;
    IPath[] outputLocations  = new IPath[length+1];
    boolean[] allowNestingInOutputLocations = new boolean[length+1];
    outputLocations[0] = projectOutputLocation;

    // retrieve and check output locations
    IPath potentialNestedOutput = null; // for error reporting purpose
    int sourceEntryCount = 0;
    boolean disableExclusionPatterns = JavaCore.DISABLED.equals(javaProject.getOption(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, true));
    boolean disableCustomOutputLocations = JavaCore.DISABLED.equals(javaProject.getOption(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, true));

    for (int i = 0 ; i < length; i++) {
      IClasspathEntry resolvedEntry = classpath[i];
      if (disableExclusionPatterns &&
              ((resolvedEntry.getInclusionPatterns() != null && resolvedEntry.getInclusionPatterns().length > 0)
              || (resolvedEntry.getExclusionPatterns() != null && resolvedEntry.getExclusionPatterns().length > 0))) {
        return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS, javaProject, resolvedEntry.getPath());
      }
      switch(resolvedEntry.getEntryKind()){
        case IClasspathEntry.CPE_SOURCE :
          sourceEntryCount++;

          IPath customOutput;
          if ((customOutput = resolvedEntry.getOutputLocation()) != null) {

            if (disableCustomOutputLocations) {
              return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS, javaProject, resolvedEntry.getPath());
            }
            // ensure custom output is in project
            if (customOutput.isAbsolute()) {
              if (!javaProject.getPath().isPrefixOf(customOutput)) {
                return new JavaModelStatus(IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT, javaProject, customOutput.toString());
              }
            } else {
              return new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH, customOutput);
            }

            // ensure custom output doesn't conflict with other outputs
            // check exact match
            if (Util.indexOfMatchingPath(customOutput, outputLocations, outputCount) != -1) {
              continue; // already found
            }
            // accumulate all outputs, will check nesting once all available (to handle ordering issues)
            outputLocations[outputCount++] = customOutput;
          }
      }
    }
    // check nesting across output locations
    for (int i = 1 /*no check for default output*/ ; i < outputCount; i++) {
        IPath customOutput = outputLocations[i];
        int index;
      // check nesting
      if ((index = Util.indexOfEnclosingPath(customOutput, outputLocations, outputCount)) != -1 && index != i) {
        if (index == 0) {
          // custom output is nested in project's output: need to check if all source entries have a custom
          // output before complaining
          if (potentialNestedOutput == null) potentialNestedOutput = customOutput;
        } else {
          return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestOutputInOutput, new String[] {customOutput.makeRelative().toString(), outputLocations[index].makeRelative().toString()}));
        }
      }
    }
    // allow custom output nesting in project's output if all source entries have a custom output
    if (sourceEntryCount <= outputCount-1) {
        allowNestingInOutputLocations[0] = true;
    } else if (potentialNestedOutput != null) {
      return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestOutputInOutput, new String[] {potentialNestedOutput.makeRelative().toString(), outputLocations[0].makeRelative().toString()}));
    }

    for (int i = 0 ; i < length; i++) {
      IClasspathEntry resolvedEntry = classpath[i];
      IPath path = resolvedEntry.getPath();
      int index;
      switch(resolvedEntry.getEntryKind()){

        case IClasspathEntry.CPE_SOURCE :
          hasSource = true;
          if ((index = Util.indexOfMatchingPath(path, outputLocations, outputCount)) != -1){
            allowNestingInOutputLocations[index] = true;
          }
          break;

        case IClasspathEntry.CPE_LIBRARY:
          hasLibFolder |= !org.aspectj.org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(path.lastSegment());
          if ((index = Util.indexOfMatchingPath(path, outputLocations, outputCount)) != -1){
            allowNestingInOutputLocations[index] = true;
          }
          break;
      }
    }
    if (!hasSource && !hasLibFolder) { // if no source and no lib folder, then allowed
      for (int i = 0; i < outputCount; i++) allowNestingInOutputLocations[i] = true;
    }

    HashSet pathes = new HashSet(length);

    // check all entries
    for (int i = 0 ; i < length; i++) {
      IClasspathEntry entry = classpath[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() : entryPath.makeRelative().toString();

      // complain if duplicate path
      if (!pathes.add(entryPath)){
        return new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, Messages.bind(Messages.classpath_duplicateEntryPath, new String[] {entryPathMsg, projectName}));
      }
      // no further check if entry coincidates with project or output location
      if (entryPath.equals(projectPath)){
        // complain if self-referring project entry
        if (kind == IClasspathEntry.CPE_PROJECT){
          return new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, Messages.bind(Messages.classpath_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
      if (kind == IClasspathEntry.CPE_SOURCE
          || (kind == IClasspathEntry.CPE_LIBRARY && !org.aspectj.org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(entryPath.lastSegment()))){
        for (int j = 0; j < classpath.length; j++){
          IClasspathEntry otherEntry = classpath[j];
          if (otherEntry == null) continue;
          int otherKind = otherEntry.getEntryKind();
          IPath otherPath = otherEntry.getPath();
          if (entry != otherEntry
            && (otherKind == IClasspathEntry.CPE_SOURCE
                || (otherKind == IClasspathEntry.CPE_LIBRARY
                    && !org.aspectj.org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(otherPath.lastSegment())))){
            char[][] inclusionPatterns, exclusionPatterns;
            if (otherPath.isPrefixOf(entryPath)
                && !otherPath.equals(entryPath)
                && !Util.isExcluded(entryPath.append("*"), inclusionPatterns = ((ClasspathEntry)otherEntry).fullInclusionPatternChars(), exclusionPatterns = ((ClasspathEntry)otherEntry).fullExclusionPatternChars(), false)) { //$NON-NLS-1$
              String exclusionPattern = entryPath.removeFirstSegments(otherPath.segmentCount()).segment(0);
              if (Util.isExcluded(entryPath, inclusionPatterns, exclusionPatterns, false)) {
                return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_mustEndWithSlash, new String[] {exclusionPattern, entryPath.makeRelative().toString()}));
              } else {
                if (otherKind == IClasspathEntry.CPE_SOURCE) {
                  exclusionPattern += '/';
                  if (!disableExclusionPatterns) {
                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestEntryInEntry, new String[] {entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString(), exclusionPattern}));
                  } else {
                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestEntryInEntryNoExclusion, new String[] {entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString(), exclusionPattern}));
                  }
                } else {
                  return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestEntryInLibrary, new String[] {entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString()}));
                }
              }
            }
          }
        }
      }

      // prevent nesting output location inside entry unless enclosing is a source entry which explicitly exclude the output location
        char[][] inclusionPatterns = ((ClasspathEntry)entry).fullInclusionPatternChars();
        char[][] exclusionPatterns = ((ClasspathEntry)entry).fullExclusionPatternChars();
        for (int j = 0; j < outputCount; j++){
            IPath currentOutput = outputLocations[j];
          if (entryPath.equals(currentOutput)) continue;
        if (entryPath.isPrefixOf(currentOutput)) {
            if (kind != IClasspathEntry.CPE_SOURCE || !Util.isExcluded(currentOutput, inclusionPatterns, exclusionPatterns, true)) {
            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestOutputInEntry, new String[] {currentOutput.makeRelative().toString(), entryPath.makeRelative().toString()}));
            }
        }
        }

        // prevent nesting entry inside output location - when distinct from project or a source folder
        for (int j = 0; j < outputCount; j++){
            if (allowNestingInOutputLocations[j]) continue;
            IPath currentOutput = outputLocations[j];
        if (currentOutput.isPrefixOf(entryPath)) {
          return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestEntryInOutput, new String[] {entryPath.makeRelative().toString(), currentOutput.makeRelative().toString()}));
        }
        }
    }
    // ensure that no specific output is coincidating with another source folder (only allowed if matching current source folder)
    // 36465 - for 2.0 backward compatibility, only check specific output locations (the default can still coincidate)
    // perform one separate iteration so as to not take precedence over previously checked scenarii (in particular should
    // diagnose nesting source folder issue before this one, for example, [src]"Project/", [src]"Project/source/" and output="Project/" should
    // first complain about missing exclusion pattern
    for (int i = 0 ; i < length; i++) {
      IClasspathEntry entry = classpath[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() : entryPath.makeRelative().toString();

      if (kind == IClasspathEntry.CPE_SOURCE) {
        IPath output = entry.getOutputLocation();
        if (output == null) continue; // 36465 - for 2.0 backward compatibility, only check specific output locations (the default can still coincidate)
        // if (output == null) output = projectOutputLocation; // if no specific output, still need to check using default output (this line would check default output)
        for (int j = 0; j < length; j++) {
          IClasspathEntry otherEntry = classpath[j];
          if (otherEntry == entry) continue;

          // Build some common strings for status message
          boolean opStartsWithProject = projectName.equals(otherEntry.getPath().segment(0));
          String otherPathMsg = opStartsWithProject ? otherEntry.getPath().removeFirstSegments(1).toString() : otherEntry.getPath().makeRelative().toString();

          switch (otherEntry.getEntryKind()) {
            case IClasspathEntry.CPE_SOURCE :
              if (otherEntry.getPath().equals(output)) {
                return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotUseDistinctSourceFolderAsOutput, new String[] {entryPathMsg, otherPathMsg, projectName}));
              }
              break;
            case IClasspathEntry.CPE_LIBRARY :
              if (otherEntry.getPath().equals(output)) {
                return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotUseLibraryAsOutput, new String[] {entryPathMsg, otherPathMsg, projectName}));
              }
          }
        }
      }
    }
    return JavaModelStatus.VERIFIED_OK;
  }

  /**
   * Returns 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 (that is, if the
   * given classpath entry denotes a valid element to be referenced onto a classpath).
   *
   * @param project the given java project
   * @param entry the given classpath 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 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
   */
  public static IJavaModelStatus validateClasspathEntry(IJavaProject project, IClasspathEntry entry, boolean checkSourceAttachment, 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() : path.toString();

    switch(entry.getEntryKind()){

      // container entry check
      case IClasspathEntry.CPE_CONTAINER :
        if (path.segmentCount() >= 1){
          try {
            IClasspathContainer container = JavaModelManager.getJavaModelManager().getClasspathContainer(path, project);
            // container retrieval is performing validation check on container entry kinds.
            if (container == null){
              return new JavaModelStatus(IJavaModelStatusConstants.CP_CONTAINER_PATH_UNBOUND, project, path);
            } else if (container == JavaModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) {
              // Validate extra attributes
              IClasspathAttribute[] 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 JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, Messages.bind(Messages.classpath_duplicateEntryExtraAttribute, new String[] {attName, entryPathMsg, projectName}));
                  }
                }
              }
              // don't create a marker if initialization is in progress (case of cp initialization batching)
              return JavaModelStatus.VERIFIED_OK;
            }
            IClasspathEntry[] containerEntries = container.getClasspathEntries();
            if (containerEntries != null){
              for (int i = 0, length = containerEntries.length; i < length; i++){
                IClasspathEntry containerEntry = containerEntries[i];
                int kind = containerEntry == null ? 0 : containerEntry.getEntryKind();
                if (containerEntry == null
                  || kind == IClasspathEntry.CPE_SOURCE
                  || kind == IClasspathEntry.CPE_VARIABLE
                  || kind == IClasspathEntry.CPE_CONTAINER){
                    String description = container.getDescription();
                    if (description == null) description = path.makeRelative().toString();
                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CP_CONTAINER_ENTRY, project, path);
                }
                if (recurseInContainers) {
                  IJavaModelStatus containerEntryStatus = validateClasspathEntry(project, containerEntry, checkSourceAttachment, recurseInContainers);
                  if (!containerEntryStatus.isOK()){
                    return containerEntryStatus;
                  }
                }
              }
            }
          } catch(JavaModelException e){
            return new JavaModelStatus(e);
          }
        } else {
          return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalContainerPath, new String[] {entryPathMsg, projectName}));
        }
        break;

      // variable entry check
      case IClasspathEntry.CPE_VARIABLE :
        if (path.segmentCount() >= 1){
          try {
            entry = JavaCore.getResolvedClasspathEntry(entry);
          } catch (AssertionFailedException e) {
            // Catch the assertion failure and throw java model exception instead
            // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=55992
            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, e.getMessage());
          }
          if (entry == null){
            return new JavaModelStatus(IJavaModelStatusConstants.CP_VARIABLE_PATH_UNBOUND, project, path);
          }

          // get validation status
          IJavaModelStatus status = validateClasspathEntry(project, entry, checkSourceAttachment, recurseInContainers);
          if (!status.isOK()) return status;

          // return deprecation status if any
          String variableName = path.segment(0);
          String deprecatedMessage = JavaCore.getClasspathVariableDeprecationMessage(variableName);
          if (deprecatedMessage != null) {
            return new JavaModelStatus(IStatus.WARNING, IJavaModelStatusConstants.DEPRECATED_VARIABLE, project, path, deprecatedMessage);
          }
          return status;
        } else {
          return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalVariablePath, new String[] {entryPathMsg, projectName}));
        }

      // library entry check
      case IClasspathEntry.CPE_LIBRARY :
        if (path.isAbsolute() && !path.isEmpty()) {
          IPath sourceAttachment = entry.getSourceAttachmentPath();
          Object target = JavaModel.getTarget(workspaceRoot, path, true);
          if (target != null && !JavaCore.IGNORE.equals(project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true))) {
            long projectTargetJDK = CompilerOptions.versionToJdkLevel(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
            long libraryJDK = Util.getJdkLevel(target);
            if (libraryJDK != 0 && libraryJDK > projectTargetJDK) {
              return new JavaModelStatus(IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL, project, path, CompilerOptions.versionFromJdkLevel(libraryJDK));
            }
          }
          if (target instanceof IResource){
            IResource resolvedResource = (IResource) target;
            switch(resolvedResource.getType()){
              case IResource.FILE :
                if (org.aspectj.org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(resolvedResource.getName())) {
                  if (checkSourceAttachment
                    && sourceAttachment != null
                    && !sourceAttachment.isEmpty()
                    && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toString(), projectName}));
                  }
                } else {
                  return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalLibraryArchive, new String[] {entryPathMsg, projectName}));
                }
                break;
              case IResource.FOLDER :  // internal binary folder
                if (checkSourceAttachment
                  && sourceAttachment != null
                  && !sourceAttachment.isEmpty()
                  && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
                  return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toString(), projectName}));
                }
            }
          } else if (target instanceof File){
            File file = JavaModel.getFile(target);
              if (file == null) {
              return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalExternalFolder, new String[] {path.toOSString(), projectName}));
              } else if (!org.aspectj.org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())) {
              return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalLibraryArchive, (new String[] {path.toOSString(), projectName})));
              } else if (checkSourceAttachment
                && sourceAttachment != null
                && !sourceAttachment.isEmpty()
                && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
                return  new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String [] {sourceAttachment.toString(), path.toOSString(), projectName}));
              }
          } else {
            boolean isExternal = path.getDevice() != null || !workspaceRoot.getProject(path.segment(0)).exists();
            if (isExternal) {
              return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundLibrary, new String[] {path.toOSString(), projectName}));
            } else {
              return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundLibrary, new String[] {entryPathMsg, projectName}));
            }
          }
        } else {
          return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalLibraryPath, new String[] {entryPathMsg, projectName}));
        }
        break;

      // project entry check
      case IClasspathEntry.CPE_PROJECT :
        if (path.isAbsolute() && path.segmentCount() == 1) {
          IProject prereqProjectRsc = workspaceRoot.getProject(path.segment(0));
          IJavaProject prereqProject = JavaCore.create(prereqProjectRsc);
          try {
            if (!prereqProjectRsc.exists() || !prereqProjectRsc.hasNature(JavaCore.NATURE_ID)){
              return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundProject, new String[] {path.segment(0), projectName}));
            }
            if (!prereqProjectRsc.isOpen()){
              return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_closedProject, new String[] {path.segment(0)}));
            }
            if (!JavaCore.IGNORE.equals(project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true))) {
              long projectTargetJDK = CompilerOptions.versionToJdkLevel(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
              long prereqProjectTargetJDK = CompilerOptions.versionToJdkLevel(prereqProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
              if (prereqProjectTargetJDK > projectTargetJDK) {
                return new JavaModelStatus(IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL, project, path, CompilerOptions.versionFromJdkLevel(prereqProjectTargetJDK));
              }
            }
          } catch (CoreException e){
            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundProject, new String[] {path.segment(0), projectName}));
          }
        } else {
          return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalProjectPath, new String[] {path.toString(), projectName}));
        }
        break;

      // project source folder
      case IClasspathEntry.CPE_SOURCE :
        if (((entry.getInclusionPatterns() != null && entry.getInclusionPatterns().length > 0)
                  || (entry.getExclusionPatterns() != null && entry.getExclusionPatterns().length > 0))
            && JavaCore.DISABLED.equals(project.getOption(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, true))) {
          return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS, project, path);
        }
        if (entry.getOutputLocation() != null && JavaCore.DISABLED.equals(project.getOption(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, true))) {
          return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS, project, path);
        }
        if (path.isAbsolute() && !path.isEmpty()) {
          IPath projectPath= project.getProject().getFullPath();
          if (!projectPath.isPrefixOf(path) || JavaModel.getTarget(workspaceRoot, path, true) == null){
            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceFolder, new String[] {entryPathMsg, projectName}));
          }
        } else {
          return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalSourceFolderPath, new String[] {entryPathMsg, projectName}));
        }
        break;
    }

    // Validate extra attributes
    IClasspathAttribute[] 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 JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, Messages.bind(Messages.classpath_duplicateEntryExtraAttribute, new String[] {attName, entryPathMsg, projectName}));
        }
      }
    }

    return JavaModelStatus.VERIFIED_OK;
  }
}
TOP

Related Classes of org.aspectj.org.eclipse.jdt.internal.core.ClasspathEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.