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 java.util.HashMap;
15 import net.sourceforge.phpdt.core.IClasspathEntry;
16 import net.sourceforge.phpdt.core.IJavaProject;
17 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
18 import net.sourceforge.phpdt.core.JavaCore;
19 import net.sourceforge.phpdt.core.JavaModelException;
20 import net.sourceforge.phpdt.core.compiler.CharOperation;
21 import net.sourceforge.phpdt.internal.core.util.Util;
22 import net.sourceforge.phpdt.internal.corext.Assert;
24 import org.eclipse.core.runtime.IPath;
25 import org.eclipse.core.runtime.Path;
26 import org.w3c.dom.Document;
27 import org.w3c.dom.Element;
30 * @see IClasspathEntry
32 public class ClasspathEntry implements IClasspathEntry {
35 * Describes the kind of classpath entry - one of
36 * CPE_PROJECT, CPE_LIBRARY, CPE_SOURCE, CPE_VARIABLE or CPE_CONTAINER
41 * Describes the kind of package fragment roots found on
42 * this classpath entry - either K_BINARY or K_SOURCE or
45 public int contentKind;
48 * The meaning of the path of a classpath entry depends on its entry kind:<ul>
49 * <li>Source code in the current project (<code>CPE_SOURCE</code>) -
50 * The path associated with this entry is the absolute path to the root folder. </li>
51 * <li>A binary library in the current project (<code>CPE_LIBRARY</code>) - the path
52 * associated with this entry is the absolute path to the JAR (or root folder), and
53 * in case it refers to an external JAR, then there is no associated resource in
55 * <li>A required project (<code>CPE_PROJECT</code>) - the path of the entry denotes the
56 * path to the corresponding project resource.</li>
57 * <li>A variable entry (<code>CPE_VARIABLE</code>) - the first segment of the path
58 * is the name of a classpath variable. If this classpath variable
59 * is bound to the path <it>P</it>, the path of the corresponding classpath entry
60 * is computed by appending to <it>P</it> the segments of the returned
61 * path without the variable.</li>
62 * <li> A container entry (<code>CPE_CONTAINER</code>) - the first segment of the path is denoting
63 * the unique container identifier (for which a <code>ClasspathContainerInitializer</code> could be
64 * registered), and the remaining segments are used as additional hints for resolving the container entry to
65 * an actual <code>IClasspathContainer</code>.</li>
70 * Patterns allowing to exclude portions of the resource tree denoted by this entry path.
73 public IPath[] exclusionPatterns;
74 private char[][] fullCharExclusionPatterns;
75 private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
77 private String rootID;
80 * Default exclusion pattern set
82 public final static IPath[] NO_EXCLUSION_PATTERNS = {};
85 * Describes the path to the source archive associated with this
86 * classpath entry, or <code>null</code> if this classpath entry has no
89 * Only library and variable classpath entries may have source attachments.
90 * For library classpath entries, the result path (if present) locates a source
91 * archive. For variable classpath entries, the result path (if present) has
92 * an analogous form and meaning as the variable path, namely the first segment
93 * is the name of a classpath variable.
95 public IPath sourceAttachmentPath;
98 * Describes the path within the source archive where package fragments
99 * are located. An empty path indicates that packages are located at
100 * the root of the source archive. Returns a non-<code>null</code> value
101 * if and only if <code>getSourceAttachmentPath</code> returns
102 * a non-<code>null</code> value.
104 public IPath sourceAttachmentRootPath;
107 * Specific output location (for this source entry)
109 public IPath specificOutputLocation;
112 * A constant indicating an output location.
114 public static final int K_OUTPUT = 10;
119 public boolean isExported;
122 * Creates a class path entry of the specified kind with the given path.
124 public ClasspathEntry(
128 IPath[] exclusionPatterns,
129 IPath sourceAttachmentPath,
130 IPath sourceAttachmentRootPath,
131 IPath specificOutputLocation,
132 boolean isExported) {
134 this.contentKind = contentKind;
135 this.entryKind = entryKind;
137 this.exclusionPatterns = exclusionPatterns;
138 if (exclusionPatterns.length > 0) {
139 this.fullCharExclusionPatterns = UNINIT_PATTERNS;
141 this.sourceAttachmentPath = sourceAttachmentPath;
142 this.sourceAttachmentRootPath = sourceAttachmentRootPath;
143 this.specificOutputLocation = specificOutputLocation;
144 this.isExported = isExported;
148 * Returns a char based representation of the exclusions patterns full path.
150 public char[][] fullExclusionPatternChars() {
152 if (this.fullCharExclusionPatterns == UNINIT_PATTERNS) {
153 int length = this.exclusionPatterns.length;
154 this.fullCharExclusionPatterns = new char[length][];
155 IPath prefixPath = path.removeTrailingSeparator();
156 for (int i = 0; i < length; i++) {
157 this.fullCharExclusionPatterns[i] =
158 prefixPath.append(this.exclusionPatterns[i]).toString().toCharArray();
161 return this.fullCharExclusionPatterns;
165 * Returns the XML encoding of the class path.
167 public Element elementEncode(
170 throws JavaModelException {
172 Element element = document.createElement("classpathentry"); //$NON-NLS-1$
173 element.setAttribute("kind", kindToString(this.entryKind)); //$NON-NLS-1$
174 IPath xmlPath = this.path;
175 if (this.entryKind != IClasspathEntry.CPE_VARIABLE && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
176 // translate to project relative from absolute (unless a device path)
177 if (xmlPath.isAbsolute()) {
178 if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
179 if (xmlPath.segment(0).equals(projectPath.segment(0))) {
180 xmlPath = xmlPath.removeFirstSegments(1);
181 xmlPath = xmlPath.makeRelative();
183 xmlPath = xmlPath.makeAbsolute();
188 element.setAttribute("path", xmlPath.toString()); //$NON-NLS-1$
189 if (this.sourceAttachmentPath != null) {
190 element.setAttribute("sourcepath", this.sourceAttachmentPath.toString()); //$NON-NLS-1$
192 if (this.sourceAttachmentRootPath != null) {
193 element.setAttribute("rootpath", this.sourceAttachmentRootPath.toString()); //$NON-NLS-1$
195 if (this.isExported) {
196 element.setAttribute("exported", "true"); //$NON-NLS-1$ //$NON-NLS-2$
199 if (this.exclusionPatterns.length > 0) {
200 StringBuffer excludeRule = new StringBuffer(10);
201 for (int i = 0, max = this.exclusionPatterns.length; i < max; i++){
202 if (i > 0) excludeRule.append('|');
203 excludeRule.append(this.exclusionPatterns[i]);
205 element.setAttribute("excluding", excludeRule.toString()); //$NON-NLS-1$
208 if (this.specificOutputLocation != null) {
209 IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1);
210 outputLocation = outputLocation.makeRelative();
211 element.setAttribute("output", outputLocation.toString()); //$NON-NLS-1$
216 public static IClasspathEntry elementDecode(Element element, IJavaProject project) {
218 IPath projectPath = project.getProject().getFullPath();
219 String kindAttr = element.getAttribute("kind"); //$NON-NLS-1$
220 String pathAttr = element.getAttribute("path"); //$NON-NLS-1$
222 // ensure path is absolute
223 IPath path = new Path(pathAttr);
224 int kind = kindFromString(kindAttr);
225 if (kind != IClasspathEntry.CPE_VARIABLE && kind != IClasspathEntry.CPE_CONTAINER && !path.isAbsolute()) {
226 path = projectPath.append(path);
228 // source attachment info (optional)
229 IPath sourceAttachmentPath =
230 element.hasAttribute("sourcepath") //$NON-NLS-1$
231 ? new Path(element.getAttribute("sourcepath")) //$NON-NLS-1$
233 IPath sourceAttachmentRootPath =
234 element.hasAttribute("rootpath") //$NON-NLS-1$
235 ? new Path(element.getAttribute("rootpath")) //$NON-NLS-1$
238 // exported flag (optional)
239 boolean isExported = element.getAttribute("exported").equals("true"); //$NON-NLS-1$ //$NON-NLS-2$
241 // exclusion patterns (optional)
242 String exclusion = element.getAttribute("excluding"); //$NON-NLS-1$
243 IPath[] exclusionPatterns = ClasspathEntry.NO_EXCLUSION_PATTERNS;
244 if (!exclusion.equals("")) { //$NON-NLS-1$
245 char[][] patterns = CharOperation.splitOn('|', exclusion.toCharArray());
247 if ((patternCount = patterns.length) > 0) {
248 exclusionPatterns = new IPath[patternCount];
249 for (int j = 0; j < patterns.length; j++){
250 exclusionPatterns[j] = new Path(new String(patterns[j]));
255 // custom output location
256 IPath outputLocation = element.hasAttribute("output") ? projectPath.append(element.getAttribute("output")) : null; //$NON-NLS-1$ //$NON-NLS-2$
258 // recreate the CP entry
261 case IClasspathEntry.CPE_PROJECT :
262 return JavaCore.newProjectEntry(path, isExported);
264 // case IClasspathEntry.CPE_LIBRARY :
265 // return JavaCore.newLibraryEntry(
267 // sourceAttachmentPath,
268 // sourceAttachmentRootPath,
271 case IClasspathEntry.CPE_SOURCE :
272 // must be an entry in this project or specify another project
273 String projSegment = path.segment(0);
274 if (projSegment != null && projSegment.equals(project.getElementName())) { // this project
275 return JavaCore.newSourceEntry(path, exclusionPatterns, outputLocation);
276 } else { // another project
277 return JavaCore.newProjectEntry(path, isExported);
280 // case IClasspathEntry.CPE_VARIABLE :
281 // return PHPCore.newVariableEntry(
283 // sourceAttachmentPath,
284 // sourceAttachmentRootPath,
287 case IClasspathEntry.CPE_CONTAINER :
288 return JavaCore.newContainerEntry(
292 case ClasspathEntry.K_OUTPUT :
293 if (!path.isAbsolute()) return null;
294 return new ClasspathEntry(
295 ClasspathEntry.K_OUTPUT,
296 IClasspathEntry.CPE_LIBRARY,
298 ClasspathEntry.NO_EXCLUSION_PATTERNS,
299 null, // source attachment
300 null, // source attachment root
301 null, // custom output location
304 throw new Assert.AssertionFailedException(Util.bind("classpath.unknownKind", kindAttr)); //$NON-NLS-1$
309 * Returns true if the given object is a classpath entry
310 * with equivalent attributes.
312 public boolean equals(Object object) {
315 if (object instanceof IClasspathEntry) {
316 IClasspathEntry otherEntry = (IClasspathEntry) object;
318 if (this.contentKind != otherEntry.getContentKind())
321 if (this.entryKind != otherEntry.getEntryKind())
324 if (this.isExported != otherEntry.isExported())
327 if (!this.path.equals(otherEntry.getPath()))
330 IPath otherPath = otherEntry.getSourceAttachmentPath();
331 if (this.sourceAttachmentPath == null) {
332 if (otherPath != null)
335 if (!this.sourceAttachmentPath.equals(otherPath))
339 otherPath = otherEntry.getSourceAttachmentRootPath();
340 if (this.sourceAttachmentRootPath == null) {
341 if (otherPath != null)
344 if (!this.sourceAttachmentRootPath.equals(otherPath))
348 IPath[] otherExcludes = otherEntry.getExclusionPatterns();
349 if (this.exclusionPatterns != otherExcludes){
350 int excludeLength = this.exclusionPatterns.length;
351 if (otherExcludes.length != excludeLength)
353 for (int i = 0; i < excludeLength; i++) {
354 // compare toStrings instead of IPaths
355 // since IPath.equals is specified to ignore trailing separators
356 if (!this.exclusionPatterns[i].toString().equals(otherExcludes[i].toString()))
361 otherPath = otherEntry.getOutputLocation();
362 if (this.specificOutputLocation == null) {
363 if (otherPath != null)
366 if (!this.specificOutputLocation.equals(otherPath))
376 * @see IClasspathEntry
378 public int getContentKind() {
379 return this.contentKind;
383 * @see IClasspathEntry
385 public int getEntryKind() {
386 return this.entryKind;
390 * @see IClasspathEntry#getExclusionPatterns()
392 public IPath[] getExclusionPatterns() {
393 return this.exclusionPatterns;
397 * @see IClasspathEntry#getOutputLocation()
399 public IPath getOutputLocation() {
400 return this.specificOutputLocation;
404 * @see IClasspathEntry
406 public IPath getPath() {
411 * @see IClasspathEntry
413 public IPath getSourceAttachmentPath() {
414 return this.sourceAttachmentPath;
418 * @see IClasspathEntry
420 public IPath getSourceAttachmentRootPath() {
421 return this.sourceAttachmentRootPath;
425 * Returns the hash code for this classpath entry
427 public int hashCode() {
428 return this.path.hashCode();
432 * @see IClasspathEntry#isExported()
434 public boolean isExported() {
435 return this.isExported;
439 * Returns the kind of a <code>PackageFragmentRoot</code> from its <code>String</code> form.
441 static int kindFromString(String kindStr) {
443 if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$
444 return IClasspathEntry.CPE_PROJECT;
445 if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$
446 return IClasspathEntry.CPE_VARIABLE;
447 if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$
448 return IClasspathEntry.CPE_CONTAINER;
449 if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$
450 return IClasspathEntry.CPE_SOURCE;
451 if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$
452 return IClasspathEntry.CPE_LIBRARY;
453 if (kindStr.equalsIgnoreCase("output")) //$NON-NLS-1$
454 return ClasspathEntry.K_OUTPUT;
459 * Returns a <code>String</code> for the kind of a class path entry.
461 static String kindToString(int kind) {
464 case IClasspathEntry.CPE_PROJECT :
465 return "src"; // backward compatibility //$NON-NLS-1$
466 case IClasspathEntry.CPE_SOURCE :
467 return "src"; //$NON-NLS-1$
468 case IClasspathEntry.CPE_LIBRARY :
469 return "lib"; //$NON-NLS-1$
470 case IClasspathEntry.CPE_VARIABLE :
471 return "var"; //$NON-NLS-1$
472 case IClasspathEntry.CPE_CONTAINER :
473 return "con"; //$NON-NLS-1$
474 case ClasspathEntry.K_OUTPUT :
475 return "output"; //$NON-NLS-1$
477 return "unknown"; //$NON-NLS-1$
482 * Returns a printable representation of this classpath entry.
484 public String toString() {
485 StringBuffer buffer = new StringBuffer();
486 buffer.append(getPath().toString());
488 switch (getEntryKind()) {
489 case IClasspathEntry.CPE_LIBRARY :
490 buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
492 case IClasspathEntry.CPE_PROJECT :
493 buffer.append("CPE_PROJECT"); //$NON-NLS-1$
495 case IClasspathEntry.CPE_SOURCE :
496 buffer.append("CPE_SOURCE"); //$NON-NLS-1$
498 case IClasspathEntry.CPE_VARIABLE :
499 buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
501 case IClasspathEntry.CPE_CONTAINER :
502 buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
505 buffer.append("]["); //$NON-NLS-1$
506 switch (getContentKind()) {
507 case IPackageFragmentRoot.K_BINARY :
508 buffer.append("K_BINARY"); //$NON-NLS-1$
510 case IPackageFragmentRoot.K_SOURCE :
511 buffer.append("K_SOURCE"); //$NON-NLS-1$
513 case ClasspathEntry.K_OUTPUT :
514 buffer.append("K_OUTPUT"); //$NON-NLS-1$
518 if (getSourceAttachmentPath() != null) {
519 buffer.append("[sourcePath:"); //$NON-NLS-1$
520 buffer.append(getSourceAttachmentPath());
523 if (getSourceAttachmentRootPath() != null) {
524 buffer.append("[rootPath:"); //$NON-NLS-1$
525 buffer.append(getSourceAttachmentRootPath());
528 buffer.append("[isExported:"); //$NON-NLS-1$
529 buffer.append(this.isExported);
531 IPath[] patterns = getExclusionPatterns();
533 if ((length = patterns.length) > 0) {
534 buffer.append("[excluding:"); //$NON-NLS-1$
535 for (int i = 0; i < length; i++) {
536 buffer.append(patterns[i]);
543 if (getOutputLocation() != null) {
544 buffer.append("[output:"); //$NON-NLS-1$
545 buffer.append(getOutputLocation());
548 return buffer.toString();
552 * Answers an ID which is used to distinguish entries during package
553 * fragment root computations
555 public String rootID(){
557 if (this.rootID == null) {
558 switch(this.entryKind){
559 case IClasspathEntry.CPE_LIBRARY :
560 this.rootID = "[LIB]"+this.path; //$NON-NLS-1$
562 case IClasspathEntry.CPE_PROJECT :
563 this.rootID = "[PRJ]"+this.path; //$NON-NLS-1$
565 case IClasspathEntry.CPE_SOURCE :
566 this.rootID = "[SRC]"+this.path; //$NON-NLS-1$
568 case IClasspathEntry.CPE_VARIABLE :
569 this.rootID = "[VAR]"+this.path; //$NON-NLS-1$
571 case IClasspathEntry.CPE_CONTAINER :
572 this.rootID = "[CON]"+this.path; //$NON-NLS-1$
575 this.rootID = ""; //$NON-NLS-1$
583 * @see IClasspathEntry
586 public IClasspathEntry getResolvedEntry() {
588 return JavaCore.getResolvedClasspathEntry(this);
591 * Returns the XML encoding of the class path.
593 public void elementEncode(XMLWriter writer, IPath projectPath, boolean indent, boolean newLine) {
594 HashMap parameters = new HashMap();
596 parameters.put("kind", ClasspathEntry.kindToString(this.entryKind));//$NON-NLS-1$
598 IPath xmlPath = this.path;
599 if (this.entryKind != IClasspathEntry.CPE_VARIABLE && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
600 // translate to project relative from absolute (unless a device path)
601 if (xmlPath.isAbsolute()) {
602 if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
603 if (xmlPath.segment(0).equals(projectPath.segment(0))) {
604 xmlPath = xmlPath.removeFirstSegments(1);
605 xmlPath = xmlPath.makeRelative();
607 xmlPath = xmlPath.makeAbsolute();
612 parameters.put("path", String.valueOf(xmlPath));//$NON-NLS-1$
614 if (this.sourceAttachmentPath != null) {
615 xmlPath = this.sourceAttachmentPath;
616 // translate to project relative from absolute
617 if (this.entryKind != IClasspathEntry.CPE_VARIABLE && projectPath != null && projectPath.isPrefixOf(xmlPath)) {
618 if (xmlPath.segment(0).equals(projectPath.segment(0))) {
619 xmlPath = xmlPath.removeFirstSegments(1);
620 xmlPath = xmlPath.makeRelative();
623 parameters.put("sourcepath", String.valueOf(xmlPath));//$NON-NLS-1$
625 if (this.sourceAttachmentRootPath != null) {
626 parameters.put("rootpath", String.valueOf(this.sourceAttachmentRootPath));//$NON-NLS-1$
628 if (this.isExported) {
629 parameters.put("exported", "true");//$NON-NLS-1$//$NON-NLS-2$
631 // if (this.inclusionPatterns != null && this.inclusionPatterns.length > 0) {
632 // StringBuffer includeRule = new StringBuffer(10);
633 // for (int i = 0, max = this.inclusionPatterns.length; i < max; i++){
634 // if (i > 0) includeRule.append('|');
635 // includeRule.append(this.inclusionPatterns[i]);
637 // parameters.put("including", String.valueOf(includeRule));//$NON-NLS-1$
639 if (this.exclusionPatterns != null && this.exclusionPatterns.length > 0) {
640 StringBuffer excludeRule = new StringBuffer(10);
641 for (int i = 0, max = this.exclusionPatterns.length; i < max; i++){
642 if (i > 0) excludeRule.append('|');
643 excludeRule.append(this.exclusionPatterns[i]);
645 parameters.put("excluding", String.valueOf(excludeRule));//$NON-NLS-1$
648 if (this.specificOutputLocation != null) {
649 IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1);
650 outputLocation = outputLocation.makeRelative();
651 parameters.put("output", String.valueOf(outputLocation));//$NON-NLS-1$
654 writer.printTag("classpathentry", parameters, indent, newLine, true);//$NON-NLS-1$