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 net.sourceforge.phpdt.internal.core.XMLWriter;
27 import org.w3c.dom.Document;
28 import org.w3c.dom.Element;
31 * @see IClasspathEntry
33 public class ClasspathEntry implements IClasspathEntry {
36 * Describes the kind of classpath entry - one of
37 * CPE_PROJECT, CPE_LIBRARY, CPE_SOURCE, CPE_VARIABLE or CPE_CONTAINER
42 * Describes the kind of package fragment roots found on
43 * this classpath entry - either K_BINARY or K_SOURCE or
46 public int contentKind;
49 * The meaning of the path of a classpath entry depends on its entry kind:<ul>
50 * <li>Source code in the current project (<code>CPE_SOURCE</code>) -
51 * The path associated with this entry is the absolute path to the root folder. </li>
52 * <li>A binary library in the current project (<code>CPE_LIBRARY</code>) - the path
53 * associated with this entry is the absolute path to the JAR (or root folder), and
54 * in case it refers to an external JAR, then there is no associated resource in
56 * <li>A required project (<code>CPE_PROJECT</code>) - the path of the entry denotes the
57 * path to the corresponding project resource.</li>
58 * <li>A variable entry (<code>CPE_VARIABLE</code>) - the first segment of the path
59 * is the name of a classpath variable. If this classpath variable
60 * is bound to the path <it>P</it>, the path of the corresponding classpath entry
61 * is computed by appending to <it>P</it> the segments of the returned
62 * path without the variable.</li>
63 * <li> A container entry (<code>CPE_CONTAINER</code>) - the first segment of the path is denoting
64 * the unique container identifier (for which a <code>ClasspathContainerInitializer</code> could be
65 * registered), and the remaining segments are used as additional hints for resolving the container entry to
66 * an actual <code>IClasspathContainer</code>.</li>
71 * Patterns allowing to exclude portions of the resource tree denoted by this entry path.
74 public IPath[] exclusionPatterns;
75 private char[][] fullCharExclusionPatterns;
76 private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
78 private String rootID;
81 * Default exclusion pattern set
83 public final static IPath[] NO_EXCLUSION_PATTERNS = {};
86 * Describes the path to the source archive associated with this
87 * classpath entry, or <code>null</code> if this classpath entry has no
90 * Only library and variable classpath entries may have source attachments.
91 * For library classpath entries, the result path (if present) locates a source
92 * archive. For variable classpath entries, the result path (if present) has
93 * an analogous form and meaning as the variable path, namely the first segment
94 * is the name of a classpath variable.
96 public IPath sourceAttachmentPath;
99 * Describes the path within the source archive where package fragments
100 * are located. An empty path indicates that packages are located at
101 * the root of the source archive. Returns a non-<code>null</code> value
102 * if and only if <code>getSourceAttachmentPath</code> returns
103 * a non-<code>null</code> value.
105 public IPath sourceAttachmentRootPath;
108 * Specific output location (for this source entry)
110 public IPath specificOutputLocation;
113 * A constant indicating an output location.
115 public static final int K_OUTPUT = 10;
120 public boolean isExported;
123 * Creates a class path entry of the specified kind with the given path.
125 public ClasspathEntry(
129 IPath[] exclusionPatterns,
130 IPath sourceAttachmentPath,
131 IPath sourceAttachmentRootPath,
132 IPath specificOutputLocation,
133 boolean isExported) {
135 this.contentKind = contentKind;
136 this.entryKind = entryKind;
138 this.exclusionPatterns = exclusionPatterns;
139 if (exclusionPatterns.length > 0) {
140 this.fullCharExclusionPatterns = UNINIT_PATTERNS;
142 this.sourceAttachmentPath = sourceAttachmentPath;
143 this.sourceAttachmentRootPath = sourceAttachmentRootPath;
144 this.specificOutputLocation = specificOutputLocation;
145 this.isExported = isExported;
149 * Returns a char based representation of the exclusions patterns full path.
151 public char[][] fullExclusionPatternChars() {
153 if (this.fullCharExclusionPatterns == UNINIT_PATTERNS) {
154 int length = this.exclusionPatterns.length;
155 this.fullCharExclusionPatterns = new char[length][];
156 IPath prefixPath = path.removeTrailingSeparator();
157 for (int i = 0; i < length; i++) {
158 this.fullCharExclusionPatterns[i] =
159 prefixPath.append(this.exclusionPatterns[i]).toString().toCharArray();
162 return this.fullCharExclusionPatterns;
166 * Returns the XML encoding of the class path.
168 public Element elementEncode(
171 throws JavaModelException {
173 Element element = document.createElement("classpathentry"); //$NON-NLS-1$
174 element.setAttribute("kind", kindToString(this.entryKind)); //$NON-NLS-1$
175 IPath xmlPath = this.path;
176 if (this.entryKind != IClasspathEntry.CPE_VARIABLE && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
177 // translate to project relative from absolute (unless a device path)
178 if (xmlPath.isAbsolute()) {
179 if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
180 if (xmlPath.segment(0).equals(projectPath.segment(0))) {
181 xmlPath = xmlPath.removeFirstSegments(1);
182 xmlPath = xmlPath.makeRelative();
184 xmlPath = xmlPath.makeAbsolute();
189 element.setAttribute("path", xmlPath.toString()); //$NON-NLS-1$
190 if (this.sourceAttachmentPath != null) {
191 element.setAttribute("sourcepath", this.sourceAttachmentPath.toString()); //$NON-NLS-1$
193 if (this.sourceAttachmentRootPath != null) {
194 element.setAttribute("rootpath", this.sourceAttachmentRootPath.toString()); //$NON-NLS-1$
196 if (this.isExported) {
197 element.setAttribute("exported", "true"); //$NON-NLS-1$ //$NON-NLS-2$
200 if (this.exclusionPatterns.length > 0) {
201 StringBuffer excludeRule = new StringBuffer(10);
202 for (int i = 0, max = this.exclusionPatterns.length; i < max; i++){
203 if (i > 0) excludeRule.append('|');
204 excludeRule.append(this.exclusionPatterns[i]);
206 element.setAttribute("excluding", excludeRule.toString()); //$NON-NLS-1$
209 if (this.specificOutputLocation != null) {
210 IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1);
211 outputLocation = outputLocation.makeRelative();
212 element.setAttribute("output", outputLocation.toString()); //$NON-NLS-1$
217 public static IClasspathEntry elementDecode(Element element, IJavaProject project) {
219 IPath projectPath = project.getProject().getFullPath();
220 String kindAttr = element.getAttribute("kind"); //$NON-NLS-1$
221 String pathAttr = element.getAttribute("path"); //$NON-NLS-1$
223 // ensure path is absolute
224 IPath path = new Path(pathAttr);
225 int kind = kindFromString(kindAttr);
226 if (kind != IClasspathEntry.CPE_VARIABLE && kind != IClasspathEntry.CPE_CONTAINER && !path.isAbsolute()) {
227 path = projectPath.append(path);
229 // source attachment info (optional)
230 IPath sourceAttachmentPath =
231 element.hasAttribute("sourcepath") //$NON-NLS-1$
232 ? new Path(element.getAttribute("sourcepath")) //$NON-NLS-1$
234 IPath sourceAttachmentRootPath =
235 element.hasAttribute("rootpath") //$NON-NLS-1$
236 ? new Path(element.getAttribute("rootpath")) //$NON-NLS-1$
239 // exported flag (optional)
240 boolean isExported = element.getAttribute("exported").equals("true"); //$NON-NLS-1$ //$NON-NLS-2$
242 // exclusion patterns (optional)
243 String exclusion = element.getAttribute("excluding"); //$NON-NLS-1$
244 IPath[] exclusionPatterns = ClasspathEntry.NO_EXCLUSION_PATTERNS;
245 if (!exclusion.equals("")) { //$NON-NLS-1$
246 char[][] patterns = CharOperation.splitOn('|', exclusion.toCharArray());
248 if ((patternCount = patterns.length) > 0) {
249 exclusionPatterns = new IPath[patternCount];
250 for (int j = 0; j < patterns.length; j++){
251 exclusionPatterns[j] = new Path(new String(patterns[j]));
256 // custom output location
257 IPath outputLocation = element.hasAttribute("output") ? projectPath.append(element.getAttribute("output")) : null; //$NON-NLS-1$ //$NON-NLS-2$
259 // recreate the CP entry
262 case IClasspathEntry.CPE_PROJECT :
263 return JavaCore.newProjectEntry(path, isExported);
265 // case IClasspathEntry.CPE_LIBRARY :
266 // return JavaCore.newLibraryEntry(
268 // sourceAttachmentPath,
269 // sourceAttachmentRootPath,
272 case IClasspathEntry.CPE_SOURCE :
273 // must be an entry in this project or specify another project
274 String projSegment = path.segment(0);
275 if (projSegment != null && projSegment.equals(project.getElementName())) { // this project
276 return JavaCore.newSourceEntry(path, exclusionPatterns, outputLocation);
277 } else { // another project
278 return JavaCore.newProjectEntry(path, isExported);
281 // case IClasspathEntry.CPE_VARIABLE :
282 // return PHPCore.newVariableEntry(
284 // sourceAttachmentPath,
285 // sourceAttachmentRootPath,
288 case IClasspathEntry.CPE_CONTAINER :
289 return JavaCore.newContainerEntry(
293 case ClasspathEntry.K_OUTPUT :
294 if (!path.isAbsolute()) return null;
295 return new ClasspathEntry(
296 ClasspathEntry.K_OUTPUT,
297 IClasspathEntry.CPE_LIBRARY,
299 ClasspathEntry.NO_EXCLUSION_PATTERNS,
300 null, // source attachment
301 null, // source attachment root
302 null, // custom output location
305 throw new Assert.AssertionFailedException(Util.bind("classpath.unknownKind", kindAttr)); //$NON-NLS-1$
310 * Returns true if the given object is a classpath entry
311 * with equivalent attributes.
313 public boolean equals(Object object) {
316 if (object instanceof IClasspathEntry) {
317 IClasspathEntry otherEntry = (IClasspathEntry) object;
319 if (this.contentKind != otherEntry.getContentKind())
322 if (this.entryKind != otherEntry.getEntryKind())
325 if (this.isExported != otherEntry.isExported())
328 if (!this.path.equals(otherEntry.getPath()))
331 IPath otherPath = otherEntry.getSourceAttachmentPath();
332 if (this.sourceAttachmentPath == null) {
333 if (otherPath != null)
336 if (!this.sourceAttachmentPath.equals(otherPath))
340 otherPath = otherEntry.getSourceAttachmentRootPath();
341 if (this.sourceAttachmentRootPath == null) {
342 if (otherPath != null)
345 if (!this.sourceAttachmentRootPath.equals(otherPath))
349 IPath[] otherExcludes = otherEntry.getExclusionPatterns();
350 if (this.exclusionPatterns != otherExcludes){
351 int excludeLength = this.exclusionPatterns.length;
352 if (otherExcludes.length != excludeLength)
354 for (int i = 0; i < excludeLength; i++) {
355 // compare toStrings instead of IPaths
356 // since IPath.equals is specified to ignore trailing separators
357 if (!this.exclusionPatterns[i].toString().equals(otherExcludes[i].toString()))
362 otherPath = otherEntry.getOutputLocation();
363 if (this.specificOutputLocation == null) {
364 if (otherPath != null)
367 if (!this.specificOutputLocation.equals(otherPath))
377 * @see IClasspathEntry
379 public int getContentKind() {
380 return this.contentKind;
384 * @see IClasspathEntry
386 public int getEntryKind() {
387 return this.entryKind;
391 * @see IClasspathEntry#getExclusionPatterns()
393 public IPath[] getExclusionPatterns() {
394 return this.exclusionPatterns;
398 * @see IClasspathEntry#getOutputLocation()
400 public IPath getOutputLocation() {
401 return this.specificOutputLocation;
405 * @see IClasspathEntry
407 public IPath getPath() {
412 * @see IClasspathEntry
414 public IPath getSourceAttachmentPath() {
415 return this.sourceAttachmentPath;
419 * @see IClasspathEntry
421 public IPath getSourceAttachmentRootPath() {
422 return this.sourceAttachmentRootPath;
426 * Returns the hash code for this classpath entry
428 public int hashCode() {
429 return this.path.hashCode();
433 * @see IClasspathEntry#isExported()
435 public boolean isExported() {
436 return this.isExported;
440 * Returns the kind of a <code>PackageFragmentRoot</code> from its <code>String</code> form.
442 static int kindFromString(String kindStr) {
444 if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$
445 return IClasspathEntry.CPE_PROJECT;
446 if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$
447 return IClasspathEntry.CPE_VARIABLE;
448 if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$
449 return IClasspathEntry.CPE_CONTAINER;
450 if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$
451 return IClasspathEntry.CPE_SOURCE;
452 if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$
453 return IClasspathEntry.CPE_LIBRARY;
454 if (kindStr.equalsIgnoreCase("output")) //$NON-NLS-1$
455 return ClasspathEntry.K_OUTPUT;
460 * Returns a <code>String</code> for the kind of a class path entry.
462 static String kindToString(int kind) {
465 case IClasspathEntry.CPE_PROJECT :
466 return "src"; // backward compatibility //$NON-NLS-1$
467 case IClasspathEntry.CPE_SOURCE :
468 return "src"; //$NON-NLS-1$
469 case IClasspathEntry.CPE_LIBRARY :
470 return "lib"; //$NON-NLS-1$
471 case IClasspathEntry.CPE_VARIABLE :
472 return "var"; //$NON-NLS-1$
473 case IClasspathEntry.CPE_CONTAINER :
474 return "con"; //$NON-NLS-1$
475 case ClasspathEntry.K_OUTPUT :
476 return "output"; //$NON-NLS-1$
478 return "unknown"; //$NON-NLS-1$
483 * Returns a printable representation of this classpath entry.
485 public String toString() {
486 StringBuffer buffer = new StringBuffer();
487 buffer.append(getPath().toString());
489 switch (getEntryKind()) {
490 case IClasspathEntry.CPE_LIBRARY :
491 buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
493 case IClasspathEntry.CPE_PROJECT :
494 buffer.append("CPE_PROJECT"); //$NON-NLS-1$
496 case IClasspathEntry.CPE_SOURCE :
497 buffer.append("CPE_SOURCE"); //$NON-NLS-1$
499 case IClasspathEntry.CPE_VARIABLE :
500 buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
502 case IClasspathEntry.CPE_CONTAINER :
503 buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
506 buffer.append("]["); //$NON-NLS-1$
507 switch (getContentKind()) {
508 case IPackageFragmentRoot.K_BINARY :
509 buffer.append("K_BINARY"); //$NON-NLS-1$
511 case IPackageFragmentRoot.K_SOURCE :
512 buffer.append("K_SOURCE"); //$NON-NLS-1$
514 case ClasspathEntry.K_OUTPUT :
515 buffer.append("K_OUTPUT"); //$NON-NLS-1$
519 if (getSourceAttachmentPath() != null) {
520 buffer.append("[sourcePath:"); //$NON-NLS-1$
521 buffer.append(getSourceAttachmentPath());
524 if (getSourceAttachmentRootPath() != null) {
525 buffer.append("[rootPath:"); //$NON-NLS-1$
526 buffer.append(getSourceAttachmentRootPath());
529 buffer.append("[isExported:"); //$NON-NLS-1$
530 buffer.append(this.isExported);
532 IPath[] patterns = getExclusionPatterns();
534 if ((length = patterns.length) > 0) {
535 buffer.append("[excluding:"); //$NON-NLS-1$
536 for (int i = 0; i < length; i++) {
537 buffer.append(patterns[i]);
544 if (getOutputLocation() != null) {
545 buffer.append("[output:"); //$NON-NLS-1$
546 buffer.append(getOutputLocation());
549 return buffer.toString();
553 * Answers an ID which is used to distinguish entries during package
554 * fragment root computations
556 public String rootID(){
558 if (this.rootID == null) {
559 switch(this.entryKind){
560 case IClasspathEntry.CPE_LIBRARY :
561 this.rootID = "[LIB]"+this.path; //$NON-NLS-1$
563 case IClasspathEntry.CPE_PROJECT :
564 this.rootID = "[PRJ]"+this.path; //$NON-NLS-1$
566 case IClasspathEntry.CPE_SOURCE :
567 this.rootID = "[SRC]"+this.path; //$NON-NLS-1$
569 case IClasspathEntry.CPE_VARIABLE :
570 this.rootID = "[VAR]"+this.path; //$NON-NLS-1$
572 case IClasspathEntry.CPE_CONTAINER :
573 this.rootID = "[CON]"+this.path; //$NON-NLS-1$
576 this.rootID = ""; //$NON-NLS-1$
584 * @see IClasspathEntry
587 public IClasspathEntry getResolvedEntry() {
589 return JavaCore.getResolvedClasspathEntry(this);
592 * Returns the XML encoding of the class path.
594 public void elementEncode(XMLWriter writer, IPath projectPath, boolean indent, boolean newLine) {
595 HashMap parameters = new HashMap();
597 parameters.put("kind", ClasspathEntry.kindToString(this.entryKind));//$NON-NLS-1$
599 IPath xmlPath = this.path;
600 if (this.entryKind != IClasspathEntry.CPE_VARIABLE && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
601 // translate to project relative from absolute (unless a device path)
602 if (xmlPath.isAbsolute()) {
603 if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
604 if (xmlPath.segment(0).equals(projectPath.segment(0))) {
605 xmlPath = xmlPath.removeFirstSegments(1);
606 xmlPath = xmlPath.makeRelative();
608 xmlPath = xmlPath.makeAbsolute();
613 parameters.put("path", String.valueOf(xmlPath));//$NON-NLS-1$
615 if (this.sourceAttachmentPath != null) {
616 xmlPath = this.sourceAttachmentPath;
617 // translate to project relative from absolute
618 if (this.entryKind != IClasspathEntry.CPE_VARIABLE && projectPath != null && projectPath.isPrefixOf(xmlPath)) {
619 if (xmlPath.segment(0).equals(projectPath.segment(0))) {
620 xmlPath = xmlPath.removeFirstSegments(1);
621 xmlPath = xmlPath.makeRelative();
624 parameters.put("sourcepath", String.valueOf(xmlPath));//$NON-NLS-1$
626 if (this.sourceAttachmentRootPath != null) {
627 parameters.put("rootpath", String.valueOf(this.sourceAttachmentRootPath));//$NON-NLS-1$
629 if (this.isExported) {
630 parameters.put("exported", "true");//$NON-NLS-1$//$NON-NLS-2$
632 // if (this.inclusionPatterns != null && this.inclusionPatterns.length > 0) {
633 // StringBuffer includeRule = new StringBuffer(10);
634 // for (int i = 0, max = this.inclusionPatterns.length; i < max; i++){
635 // if (i > 0) includeRule.append('|');
636 // includeRule.append(this.inclusionPatterns[i]);
638 // parameters.put("including", String.valueOf(includeRule));//$NON-NLS-1$
640 if (this.exclusionPatterns != null && this.exclusionPatterns.length > 0) {
641 StringBuffer excludeRule = new StringBuffer(10);
642 for (int i = 0, max = this.exclusionPatterns.length; i < max; i++){
643 if (i > 0) excludeRule.append('|');
644 excludeRule.append(this.exclusionPatterns[i]);
646 parameters.put("excluding", String.valueOf(excludeRule));//$NON-NLS-1$
649 if (this.specificOutputLocation != null) {
650 IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1);
651 outputLocation = outputLocation.makeRelative();
652 parameters.put("output", String.valueOf(outputLocation));//$NON-NLS-1$
655 writer.printTag("classpathentry", parameters, indent, newLine, true);//$NON-NLS-1$