1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2003 IBM Corporation and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v1.0
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v10.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.core;
 
  13 import net.sourceforge.phpdt.core.IClasspathEntry;
 
  14 import net.sourceforge.phpdt.core.IJavaProject;
 
  15 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
 
  16 import net.sourceforge.phpdt.core.JavaModelException;
 
  17 import net.sourceforge.phpdt.core.compiler.CharOperation;
 
  18 import net.sourceforge.phpdt.internal.corext.Assert;
 
  19 import net.sourceforge.phpeclipse.PHPCore;
 
  21 import org.eclipse.core.runtime.IPath;
 
  22 import org.eclipse.core.runtime.Path;
 
  23 import org.w3c.dom.Document;
 
  24 import org.w3c.dom.Element;
 
  27  * @see IClasspathEntry
 
  29 public class ClasspathEntry implements IClasspathEntry {
 
  32          * Describes the kind of classpath entry - one of 
 
  33          * CPE_PROJECT, CPE_LIBRARY, CPE_SOURCE, CPE_VARIABLE or CPE_CONTAINER
 
  38          * Describes the kind of package fragment roots found on
 
  39          * this classpath entry - either K_BINARY or K_SOURCE or
 
  42         public int contentKind;
 
  45          * The meaning of the path of a classpath entry depends on its entry kind:<ul>
 
  46          *      <li>Source code in the current project (<code>CPE_SOURCE</code>) -  
 
  47          *      The path associated with this entry is the absolute path to the root folder. </li>
 
  48          *      <li>A binary library in the current project (<code>CPE_LIBRARY</code>) - the path
 
  49          *              associated with this entry is the absolute path to the JAR (or root folder), and 
 
  50          *              in case it refers to an external JAR, then there is no associated resource in 
 
  52          *      <li>A required project (<code>CPE_PROJECT</code>) - the path of the entry denotes the
 
  53          *              path to the corresponding project resource.</li>
 
  54          *  <li>A variable entry (<code>CPE_VARIABLE</code>) - the first segment of the path 
 
  55          *      is the name of a classpath variable. If this classpath variable
 
  56          *              is bound to the path <it>P</it>, the path of the corresponding classpath entry
 
  57          *              is computed by appending to <it>P</it> the segments of the returned
 
  58          *              path without the variable.</li>
 
  59          *  <li> A container entry (<code>CPE_CONTAINER</code>) - the first segment of the path is denoting
 
  60          *     the unique container identifier (for which a <code>ClasspathContainerInitializer</code> could be
 
  61          *      registered), and the remaining segments are used as additional hints for resolving the container entry to
 
  62          *      an actual <code>IClasspathContainer</code>.</li>
 
  67          * Patterns allowing to exclude portions of the resource tree denoted by this entry path.
 
  70         public IPath[] exclusionPatterns;
 
  71         private char[][] fullCharExclusionPatterns;
 
  72         private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
 
  74         private String rootID;
 
  77          * Default exclusion pattern set
 
  79         public final static IPath[] NO_EXCLUSION_PATTERNS = {};
 
  82          * Describes the path to the source archive associated with this
 
  83          * classpath entry, or <code>null</code> if this classpath entry has no
 
  86          * Only library and variable classpath entries may have source attachments.
 
  87          * For library classpath entries, the result path (if present) locates a source
 
  88          * archive. For variable classpath entries, the result path (if present) has
 
  89          * an analogous form and meaning as the variable path, namely the first segment 
 
  90          * is the name of a classpath variable.
 
  92         public IPath sourceAttachmentPath;
 
  95          * Describes the path within the source archive where package fragments
 
  96          * are located. An empty path indicates that packages are located at
 
  97          * the root of the source archive. Returns a non-<code>null</code> value
 
  98          * if and only if <code>getSourceAttachmentPath</code> returns 
 
  99          * a non-<code>null</code> value.
 
 101         public IPath sourceAttachmentRootPath;
 
 104          * Specific output location (for this source entry)
 
 106         public IPath specificOutputLocation;
 
 109          * A constant indicating an output location.
 
 111         public static final int K_OUTPUT = 10;
 
 116         public boolean isExported;
 
 119          * Creates a class path entry of the specified kind with the given path.
 
 121         public ClasspathEntry(
 
 125                 IPath[] exclusionPatterns,
 
 126                 IPath sourceAttachmentPath,
 
 127                 IPath sourceAttachmentRootPath,
 
 128                 IPath specificOutputLocation,
 
 129                 boolean isExported) {
 
 131                 this.contentKind = contentKind;
 
 132                 this.entryKind = entryKind;
 
 134                 this.exclusionPatterns = exclusionPatterns;
 
 135                 if (exclusionPatterns.length > 0) {
 
 136                         this.fullCharExclusionPatterns = UNINIT_PATTERNS;
 
 138                 this.sourceAttachmentPath = sourceAttachmentPath;
 
 139                 this.sourceAttachmentRootPath = sourceAttachmentRootPath;
 
 140                 this.specificOutputLocation = specificOutputLocation;
 
 141                 this.isExported = isExported;
 
 145          * Returns a char based representation of the exclusions patterns full path.
 
 147         public char[][] fullExclusionPatternChars() {
 
 149                 if (this.fullCharExclusionPatterns == UNINIT_PATTERNS) {
 
 150                         int length = this.exclusionPatterns.length;
 
 151                         this.fullCharExclusionPatterns = new char[length][];
 
 152                         IPath prefixPath = path.removeTrailingSeparator();
 
 153                         for (int i = 0; i < length; i++) {
 
 154                                 this.fullCharExclusionPatterns[i] = 
 
 155                                         prefixPath.append(this.exclusionPatterns[i]).toString().toCharArray();
 
 158                 return this.fullCharExclusionPatterns;
 
 162          * Returns the XML encoding of the class path.
 
 164         public Element elementEncode(
 
 167                 throws JavaModelException {
 
 169                 Element element = document.createElement("classpathentry"); //$NON-NLS-1$
 
 170                 element.setAttribute("kind", kindToString(this.entryKind));     //$NON-NLS-1$
 
 171                 IPath xmlPath = this.path;
 
 172                 if (this.entryKind != IClasspathEntry.CPE_VARIABLE && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
 
 173                         // translate to project relative from absolute (unless a device path)
 
 174                         if (xmlPath.isAbsolute()) {
 
 175                                 if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
 
 176                                         if (xmlPath.segment(0).equals(projectPath.segment(0))) {
 
 177                                                 xmlPath = xmlPath.removeFirstSegments(1);
 
 178                                                 xmlPath = xmlPath.makeRelative();
 
 180                                                 xmlPath = xmlPath.makeAbsolute();
 
 185                 element.setAttribute("path", xmlPath.toString()); //$NON-NLS-1$
 
 186                 if (this.sourceAttachmentPath != null) {
 
 187                         element.setAttribute("sourcepath", this.sourceAttachmentPath.toString()); //$NON-NLS-1$
 
 189                 if (this.sourceAttachmentRootPath != null) {
 
 190                         element.setAttribute("rootpath", this.sourceAttachmentRootPath.toString()); //$NON-NLS-1$
 
 192                 if (this.isExported) {
 
 193                         element.setAttribute("exported", "true"); //$NON-NLS-1$ //$NON-NLS-2$
 
 196                 if (this.exclusionPatterns.length > 0) {
 
 197                         StringBuffer excludeRule = new StringBuffer(10);
 
 198                         for (int i = 0, max = this.exclusionPatterns.length; i < max; i++){
 
 199                                 if (i > 0) excludeRule.append('|');
 
 200                                 excludeRule.append(this.exclusionPatterns[i]);
 
 202                         element.setAttribute("excluding", excludeRule.toString());  //$NON-NLS-1$
 
 205                 if (this.specificOutputLocation != null) {
 
 206                         IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1);
 
 207                         outputLocation = outputLocation.makeRelative();
 
 208                         element.setAttribute("output", outputLocation.toString()); //$NON-NLS-1$ 
 
 213         public static IClasspathEntry elementDecode(Element element, IJavaProject project) {
 
 215                 IPath projectPath = project.getProject().getFullPath();
 
 216                 String kindAttr = element.getAttribute("kind"); //$NON-NLS-1$
 
 217                 String pathAttr = element.getAttribute("path"); //$NON-NLS-1$
 
 219                 // ensure path is absolute
 
 220                 IPath path = new Path(pathAttr);                
 
 221                 int kind = kindFromString(kindAttr);
 
 222                 if (kind != IClasspathEntry.CPE_VARIABLE && kind != IClasspathEntry.CPE_CONTAINER && !path.isAbsolute()) {
 
 223                         path = projectPath.append(path);
 
 225                 // source attachment info (optional)
 
 226                 IPath sourceAttachmentPath = 
 
 227                         element.hasAttribute("sourcepath")      //$NON-NLS-1$
 
 228                         ? new Path(element.getAttribute("sourcepath")) //$NON-NLS-1$
 
 230                 IPath sourceAttachmentRootPath = 
 
 231                         element.hasAttribute("rootpath") //$NON-NLS-1$
 
 232                         ? new Path(element.getAttribute("rootpath")) //$NON-NLS-1$
 
 235                 // exported flag (optional)
 
 236                 boolean isExported = element.getAttribute("exported").equals("true"); //$NON-NLS-1$ //$NON-NLS-2$
 
 238                 // exclusion patterns (optional)
 
 239                 String exclusion = element.getAttribute("excluding"); //$NON-NLS-1$ 
 
 240                 IPath[] exclusionPatterns = ClasspathEntry.NO_EXCLUSION_PATTERNS;
 
 241                 if (!exclusion.equals("")) { //$NON-NLS-1$ 
 
 242                         char[][] patterns = CharOperation.splitOn('|', exclusion.toCharArray());
 
 244                         if ((patternCount  = patterns.length) > 0) {
 
 245                                 exclusionPatterns = new IPath[patternCount];
 
 246                                 for (int j = 0; j < patterns.length; j++){
 
 247                                         exclusionPatterns[j] = new Path(new String(patterns[j]));
 
 252                 // custom output location
 
 253                 IPath outputLocation = element.hasAttribute("output") ? projectPath.append(element.getAttribute("output")) : null; //$NON-NLS-1$ //$NON-NLS-2$
 
 255                 // recreate the CP entry
 
 258                         case IClasspathEntry.CPE_PROJECT :
 
 259                                 return PHPCore.newProjectEntry(path, isExported);
 
 261 //                      case IClasspathEntry.CPE_LIBRARY :
 
 262 //                              return JavaCore.newLibraryEntry(
 
 264 //                                                                                              sourceAttachmentPath,
 
 265 //                                                                                              sourceAttachmentRootPath,
 
 268                         case IClasspathEntry.CPE_SOURCE :
 
 269                                 // must be an entry in this project or specify another project
 
 270                                 String projSegment = path.segment(0);
 
 271                                 if (projSegment != null && projSegment.equals(project.getElementName())) { // this project
 
 272                                         return PHPCore.newSourceEntry(path, exclusionPatterns, outputLocation);
 
 273                                 } else { // another project
 
 274                                         return PHPCore.newProjectEntry(path, isExported);
 
 277 //                      case IClasspathEntry.CPE_VARIABLE :
 
 278 //                              return PHPCore.newVariableEntry(
 
 280 //                                              sourceAttachmentPath,
 
 281 //                                              sourceAttachmentRootPath, 
 
 284                         case IClasspathEntry.CPE_CONTAINER :
 
 285                                 return PHPCore.newContainerEntry(
 
 289                         case ClasspathEntry.K_OUTPUT :
 
 290                                 if (!path.isAbsolute()) return null;
 
 291                                 return new ClasspathEntry(
 
 292                                                 ClasspathEntry.K_OUTPUT,
 
 293                                                 IClasspathEntry.CPE_LIBRARY,
 
 295                                                 ClasspathEntry.NO_EXCLUSION_PATTERNS, 
 
 296                                                 null, // source attachment
 
 297                                                 null, // source attachment root
 
 298                                                 null, // custom output location
 
 301                                 throw new Assert.AssertionFailedException(Util.bind("classpath.unknownKind", kindAttr)); //$NON-NLS-1$
 
 306          * Returns true if the given object is a classpath entry
 
 307          * with equivalent attributes.
 
 309         public boolean equals(Object object) {
 
 312                 if (object instanceof IClasspathEntry) {
 
 313                         IClasspathEntry otherEntry = (IClasspathEntry) object;
 
 315                         if (this.contentKind != otherEntry.getContentKind())
 
 318                         if (this.entryKind != otherEntry.getEntryKind())
 
 321                         if (this.isExported != otherEntry.isExported())
 
 324                         if (!this.path.equals(otherEntry.getPath()))
 
 327                         IPath otherPath = otherEntry.getSourceAttachmentPath();
 
 328                         if (this.sourceAttachmentPath == null) {
 
 329                                 if (otherPath != null)
 
 332                                 if (!this.sourceAttachmentPath.equals(otherPath))
 
 336                         otherPath = otherEntry.getSourceAttachmentRootPath();
 
 337                         if (this.sourceAttachmentRootPath == null) {
 
 338                                 if (otherPath != null)
 
 341                                 if (!this.sourceAttachmentRootPath.equals(otherPath))
 
 345                         IPath[] otherExcludes = otherEntry.getExclusionPatterns();
 
 346                         if (this.exclusionPatterns != otherExcludes){
 
 347                                 int excludeLength = this.exclusionPatterns.length;
 
 348                                 if (otherExcludes.length != excludeLength) 
 
 350                                 for (int i = 0; i < excludeLength; i++) {
 
 351                                         // compare toStrings instead of IPaths 
 
 352                                         // since IPath.equals is specified to ignore trailing separators
 
 353                                         if (!this.exclusionPatterns[i].toString().equals(otherExcludes[i].toString()))
 
 358                         otherPath = otherEntry.getOutputLocation();
 
 359                         if (this.specificOutputLocation == null) {
 
 360                                 if (otherPath != null)
 
 363                                 if (!this.specificOutputLocation.equals(otherPath))
 
 373          * @see IClasspathEntry
 
 375         public int getContentKind() {
 
 376                 return this.contentKind;
 
 380          * @see IClasspathEntry
 
 382         public int getEntryKind() {
 
 383                 return this.entryKind;
 
 387          * @see IClasspathEntry#getExclusionPatterns()
 
 389         public IPath[] getExclusionPatterns() {
 
 390                 return this.exclusionPatterns;
 
 394          * @see IClasspathEntry#getOutputLocation()
 
 396         public IPath getOutputLocation() {
 
 397                 return this.specificOutputLocation;
 
 401          * @see IClasspathEntry
 
 403         public IPath getPath() {
 
 408          * @see IClasspathEntry
 
 410         public IPath getSourceAttachmentPath() {
 
 411                 return this.sourceAttachmentPath;
 
 415          * @see IClasspathEntry
 
 417         public IPath getSourceAttachmentRootPath() {
 
 418                 return this.sourceAttachmentRootPath;
 
 422          * Returns the hash code for this classpath entry
 
 424         public int hashCode() {
 
 425                 return this.path.hashCode();
 
 429          * @see IClasspathEntry#isExported()
 
 431         public boolean isExported() {
 
 432                 return this.isExported;
 
 436          * Returns the kind of a <code>PackageFragmentRoot</code> from its <code>String</code> form.
 
 438         static int kindFromString(String kindStr) {
 
 440                 if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$
 
 441                         return IClasspathEntry.CPE_PROJECT;
 
 442                 if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$
 
 443                         return IClasspathEntry.CPE_VARIABLE;
 
 444                 if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$
 
 445                         return IClasspathEntry.CPE_CONTAINER;
 
 446                 if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$
 
 447                         return IClasspathEntry.CPE_SOURCE;
 
 448                 if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$
 
 449                         return IClasspathEntry.CPE_LIBRARY;
 
 450                 if (kindStr.equalsIgnoreCase("output")) //$NON-NLS-1$
 
 451                         return ClasspathEntry.K_OUTPUT;
 
 456          * Returns a <code>String</code> for the kind of a class path entry.
 
 458         static String kindToString(int kind) {
 
 461                         case IClasspathEntry.CPE_PROJECT :
 
 462                                 return "src"; // backward compatibility //$NON-NLS-1$
 
 463                         case IClasspathEntry.CPE_SOURCE :
 
 464                                 return "src"; //$NON-NLS-1$
 
 465                         case IClasspathEntry.CPE_LIBRARY :
 
 466                                 return "lib"; //$NON-NLS-1$
 
 467                         case IClasspathEntry.CPE_VARIABLE :
 
 468                                 return "var"; //$NON-NLS-1$
 
 469                         case IClasspathEntry.CPE_CONTAINER :
 
 470                                 return "con"; //$NON-NLS-1$
 
 471                         case ClasspathEntry.K_OUTPUT :
 
 472                                 return "output"; //$NON-NLS-1$
 
 474                                 return "unknown"; //$NON-NLS-1$
 
 479          * Returns a printable representation of this classpath entry.
 
 481         public String toString() {
 
 482                 StringBuffer buffer = new StringBuffer();
 
 483                 buffer.append(getPath().toString());
 
 485                 switch (getEntryKind()) {
 
 486                         case IClasspathEntry.CPE_LIBRARY :
 
 487                                 buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
 
 489                         case IClasspathEntry.CPE_PROJECT :
 
 490                                 buffer.append("CPE_PROJECT"); //$NON-NLS-1$
 
 492                         case IClasspathEntry.CPE_SOURCE :
 
 493                                 buffer.append("CPE_SOURCE"); //$NON-NLS-1$
 
 495                         case IClasspathEntry.CPE_VARIABLE :
 
 496                                 buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
 
 498                         case IClasspathEntry.CPE_CONTAINER :
 
 499                                 buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
 
 502                 buffer.append("]["); //$NON-NLS-1$
 
 503                 switch (getContentKind()) {
 
 504                         case IPackageFragmentRoot.K_BINARY :
 
 505                                 buffer.append("K_BINARY"); //$NON-NLS-1$
 
 507                         case IPackageFragmentRoot.K_SOURCE :
 
 508                                 buffer.append("K_SOURCE"); //$NON-NLS-1$
 
 510                         case ClasspathEntry.K_OUTPUT :
 
 511                                 buffer.append("K_OUTPUT"); //$NON-NLS-1$
 
 515                 if (getSourceAttachmentPath() != null) {
 
 516                         buffer.append("[sourcePath:"); //$NON-NLS-1$
 
 517                         buffer.append(getSourceAttachmentPath());
 
 520                 if (getSourceAttachmentRootPath() != null) {
 
 521                         buffer.append("[rootPath:"); //$NON-NLS-1$
 
 522                         buffer.append(getSourceAttachmentRootPath());
 
 525                 buffer.append("[isExported:"); //$NON-NLS-1$
 
 526                 buffer.append(this.isExported);
 
 528                 IPath[] patterns = getExclusionPatterns();
 
 530                 if ((length = patterns.length) > 0) {
 
 531                         buffer.append("[excluding:"); //$NON-NLS-1$
 
 532                         for (int i = 0; i < length; i++) {
 
 533                                 buffer.append(patterns[i]);
 
 540                 if (getOutputLocation() != null) {
 
 541                         buffer.append("[output:"); //$NON-NLS-1$
 
 542                         buffer.append(getOutputLocation());
 
 545                 return buffer.toString();
 
 549          * Answers an ID which is used to distinguish entries during package
 
 550          * fragment root computations
 
 552         public String rootID(){
 
 554                 if (this.rootID == null) {
 
 555                         switch(this.entryKind){
 
 556                                 case IClasspathEntry.CPE_LIBRARY :
 
 557                                         this.rootID = "[LIB]"+this.path;  //$NON-NLS-1$
 
 559                                 case IClasspathEntry.CPE_PROJECT :
 
 560                                         this.rootID = "[PRJ]"+this.path;  //$NON-NLS-1$
 
 562                                 case IClasspathEntry.CPE_SOURCE :
 
 563                                         this.rootID = "[SRC]"+this.path;  //$NON-NLS-1$
 
 565                                 case IClasspathEntry.CPE_VARIABLE :
 
 566                                         this.rootID = "[VAR]"+this.path;  //$NON-NLS-1$
 
 568                                 case IClasspathEntry.CPE_CONTAINER :
 
 569                                         this.rootID = "[CON]"+this.path;  //$NON-NLS-1$
 
 572                                         this.rootID = "";  //$NON-NLS-1$
 
 580          * @see IClasspathEntry
 
 583         public IClasspathEntry getResolvedEntry() {
 
 585                 return PHPCore.getResolvedClasspathEntry(this);