new version with WorkingCopy Management
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / ClasspathEntry.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core;
12
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;
20
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;
25
26 /**
27  * @see IClasspathEntry
28  */
29 public class ClasspathEntry implements IClasspathEntry {
30
31         /**
32          * Describes the kind of classpath entry - one of 
33          * CPE_PROJECT, CPE_LIBRARY, CPE_SOURCE, CPE_VARIABLE or CPE_CONTAINER
34          */
35         public int entryKind;
36
37         /**
38          * Describes the kind of package fragment roots found on
39          * this classpath entry - either K_BINARY or K_SOURCE or
40          * K_OUTPUT.
41          */
42         public int contentKind;
43
44         /**
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 
51          *              the workbench.
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>
63          */
64         public IPath path;
65
66         /**
67          * Patterns allowing to exclude portions of the resource tree denoted by this entry path.
68          */
69         
70         public IPath[] exclusionPatterns;
71         private char[][] fullCharExclusionPatterns;
72         private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
73
74         private String rootID;
75         
76         /**
77          * Default exclusion pattern set
78          */
79         public final static IPath[] NO_EXCLUSION_PATTERNS = {};
80                                 
81         /**
82          * Describes the path to the source archive associated with this
83          * classpath entry, or <code>null</code> if this classpath entry has no
84          * source attachment.
85          * <p>
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.
91          */
92         public IPath sourceAttachmentPath;
93
94         /**
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.
100          */
101         public IPath sourceAttachmentRootPath;
102
103         /**
104          * Specific output location (for this source entry)
105          */
106         public IPath specificOutputLocation;
107         
108         /**
109          * A constant indicating an output location.
110          */
111         public static final int K_OUTPUT = 10;
112
113         /**
114          * The export flag
115          */
116         public boolean isExported;
117
118         /**
119          * Creates a class path entry of the specified kind with the given path.
120          */
121         public ClasspathEntry(
122                 int contentKind,
123                 int entryKind,
124                 IPath path,
125                 IPath[] exclusionPatterns,
126                 IPath sourceAttachmentPath,
127                 IPath sourceAttachmentRootPath,
128                 IPath specificOutputLocation,
129                 boolean isExported) {
130
131                 this.contentKind = contentKind;
132                 this.entryKind = entryKind;
133                 this.path = path;
134                 this.exclusionPatterns = exclusionPatterns;
135                 if (exclusionPatterns.length > 0) {
136                         this.fullCharExclusionPatterns = UNINIT_PATTERNS;
137                 }
138                 this.sourceAttachmentPath = sourceAttachmentPath;
139                 this.sourceAttachmentRootPath = sourceAttachmentRootPath;
140                 this.specificOutputLocation = specificOutputLocation;
141                 this.isExported = isExported;
142         }
143         
144         /*
145          * Returns a char based representation of the exclusions patterns full path.
146          */
147         public char[][] fullExclusionPatternChars() {
148
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();
156                         }
157                 }
158                 return this.fullCharExclusionPatterns;
159         }
160         
161         /**
162          * Returns the XML encoding of the class path.
163          */
164         public Element elementEncode(
165                 Document document,
166                 IPath projectPath)
167                 throws JavaModelException {
168
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();
179                                         } else {
180                                                 xmlPath = xmlPath.makeAbsolute();
181                                         }
182                                 }
183                         }
184                 }
185                 element.setAttribute("path", xmlPath.toString()); //$NON-NLS-1$
186                 if (this.sourceAttachmentPath != null) {
187                         element.setAttribute("sourcepath", this.sourceAttachmentPath.toString()); //$NON-NLS-1$
188                 }
189                 if (this.sourceAttachmentRootPath != null) {
190                         element.setAttribute("rootpath", this.sourceAttachmentRootPath.toString()); //$NON-NLS-1$
191                 }
192                 if (this.isExported) {
193                         element.setAttribute("exported", "true"); //$NON-NLS-1$ //$NON-NLS-2$
194                 }
195                 
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]);
201                         }
202                         element.setAttribute("excluding", excludeRule.toString());  //$NON-NLS-1$
203                 }
204                 
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$ 
209                 }
210                 return element;
211         }
212         
213         public static IClasspathEntry elementDecode(Element element, IJavaProject project) {
214         
215                 IPath projectPath = project.getProject().getFullPath();
216                 String kindAttr = element.getAttribute("kind"); //$NON-NLS-1$
217                 String pathAttr = element.getAttribute("path"); //$NON-NLS-1$
218
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);
224                 }
225                 // source attachment info (optional)
226                 IPath sourceAttachmentPath = 
227                         element.hasAttribute("sourcepath")      //$NON-NLS-1$
228                         ? new Path(element.getAttribute("sourcepath")) //$NON-NLS-1$
229                         : null;
230                 IPath sourceAttachmentRootPath = 
231                         element.hasAttribute("rootpath") //$NON-NLS-1$
232                         ? new Path(element.getAttribute("rootpath")) //$NON-NLS-1$
233                         : null;
234                 
235                 // exported flag (optional)
236                 boolean isExported = element.getAttribute("exported").equals("true"); //$NON-NLS-1$ //$NON-NLS-2$
237
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());
243                         int patternCount;
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]));
248                                 }
249                         }
250                 }
251
252                 // custom output location
253                 IPath outputLocation = element.hasAttribute("output") ? projectPath.append(element.getAttribute("output")) : null; //$NON-NLS-1$ //$NON-NLS-2$
254                 
255                 // recreate the CP entry
256                 switch (kind) {
257
258                         case IClasspathEntry.CPE_PROJECT :
259                                 return PHPCore.newProjectEntry(path, isExported);
260                                 
261 //                      case IClasspathEntry.CPE_LIBRARY :
262 //                              return JavaCore.newLibraryEntry(
263 //                                                                                              path,
264 //                                                                                              sourceAttachmentPath,
265 //                                                                                              sourceAttachmentRootPath,
266 //                                                                                              isExported);
267                                 
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);
275                                 }
276
277 //                      case IClasspathEntry.CPE_VARIABLE :
278 //                              return PHPCore.newVariableEntry(
279 //                                              path,
280 //                                              sourceAttachmentPath,
281 //                                              sourceAttachmentRootPath, 
282 //                                              isExported);
283                                 
284                         case IClasspathEntry.CPE_CONTAINER :
285                                 return PHPCore.newContainerEntry(
286                                                 path,
287                                                 isExported);
288
289                         case ClasspathEntry.K_OUTPUT :
290                                 if (!path.isAbsolute()) return null;
291                                 return new ClasspathEntry(
292                                                 ClasspathEntry.K_OUTPUT,
293                                                 IClasspathEntry.CPE_LIBRARY,
294                                                 path,
295                                                 ClasspathEntry.NO_EXCLUSION_PATTERNS, 
296                                                 null, // source attachment
297                                                 null, // source attachment root
298                                                 null, // custom output location
299                                                 false);
300                         default :
301                                 throw new Assert.AssertionFailedException(Util.bind("classpath.unknownKind", kindAttr)); //$NON-NLS-1$
302                 }
303         }
304
305         /**
306          * Returns true if the given object is a classpath entry
307          * with equivalent attributes.
308          */
309         public boolean equals(Object object) {
310                 if (this == object)
311                         return true;
312                 if (object instanceof IClasspathEntry) {
313                         IClasspathEntry otherEntry = (IClasspathEntry) object;
314
315                         if (this.contentKind != otherEntry.getContentKind())
316                                 return false;
317
318                         if (this.entryKind != otherEntry.getEntryKind())
319                                 return false;
320
321                         if (this.isExported != otherEntry.isExported())
322                                 return false;
323
324                         if (!this.path.equals(otherEntry.getPath()))
325                                 return false;
326
327                         IPath otherPath = otherEntry.getSourceAttachmentPath();
328                         if (this.sourceAttachmentPath == null) {
329                                 if (otherPath != null)
330                                         return false;
331                         } else {
332                                 if (!this.sourceAttachmentPath.equals(otherPath))
333                                         return false;
334                         }
335
336                         otherPath = otherEntry.getSourceAttachmentRootPath();
337                         if (this.sourceAttachmentRootPath == null) {
338                                 if (otherPath != null)
339                                         return false;
340                         } else {
341                                 if (!this.sourceAttachmentRootPath.equals(otherPath))
342                                         return false;
343                         }
344
345                         IPath[] otherExcludes = otherEntry.getExclusionPatterns();
346                         if (this.exclusionPatterns != otherExcludes){
347                                 int excludeLength = this.exclusionPatterns.length;
348                                 if (otherExcludes.length != excludeLength) 
349                                         return false;
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()))
354                                                 return false;
355                                 }
356                         }
357                         
358                         otherPath = otherEntry.getOutputLocation();
359                         if (this.specificOutputLocation == null) {
360                                 if (otherPath != null)
361                                         return false;
362                         } else {
363                                 if (!this.specificOutputLocation.equals(otherPath))
364                                         return false;
365                         }
366                         return true;
367                 } else {
368                         return false;
369                 }
370         }
371
372         /**
373          * @see IClasspathEntry
374          */
375         public int getContentKind() {
376                 return this.contentKind;
377         }
378
379         /**
380          * @see IClasspathEntry
381          */
382         public int getEntryKind() {
383                 return this.entryKind;
384         }
385
386         /**
387          * @see IClasspathEntry#getExclusionPatterns()
388          */
389         public IPath[] getExclusionPatterns() {
390                 return this.exclusionPatterns;
391         }
392
393         /**
394          * @see IClasspathEntry#getOutputLocation()
395          */
396         public IPath getOutputLocation() {
397                 return this.specificOutputLocation;
398         }
399
400         /**
401          * @see IClasspathEntry
402          */
403         public IPath getPath() {
404                 return this.path;
405         }
406
407         /**
408          * @see IClasspathEntry
409          */
410         public IPath getSourceAttachmentPath() {
411                 return this.sourceAttachmentPath;
412         }
413
414         /**
415          * @see IClasspathEntry
416          */
417         public IPath getSourceAttachmentRootPath() {
418                 return this.sourceAttachmentRootPath;
419         }
420
421         /**
422          * Returns the hash code for this classpath entry
423          */
424         public int hashCode() {
425                 return this.path.hashCode();
426         }
427
428         /**
429          * @see IClasspathEntry#isExported()
430          */
431         public boolean isExported() {
432                 return this.isExported;
433         }
434
435         /**
436          * Returns the kind of a <code>PackageFragmentRoot</code> from its <code>String</code> form.
437          */
438         static int kindFromString(String kindStr) {
439
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;
452                 return -1;
453         }
454
455         /**
456          * Returns a <code>String</code> for the kind of a class path entry.
457          */
458         static String kindToString(int kind) {
459
460                 switch (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$
473                         default :
474                                 return "unknown"; //$NON-NLS-1$
475                 }
476         }
477
478         /**
479          * Returns a printable representation of this classpath entry.
480          */
481         public String toString() {
482                 StringBuffer buffer = new StringBuffer();
483                 buffer.append(getPath().toString());
484                 buffer.append('[');
485                 switch (getEntryKind()) {
486                         case IClasspathEntry.CPE_LIBRARY :
487                                 buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
488                                 break;
489                         case IClasspathEntry.CPE_PROJECT :
490                                 buffer.append("CPE_PROJECT"); //$NON-NLS-1$
491                                 break;
492                         case IClasspathEntry.CPE_SOURCE :
493                                 buffer.append("CPE_SOURCE"); //$NON-NLS-1$
494                                 break;
495                         case IClasspathEntry.CPE_VARIABLE :
496                                 buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
497                                 break;
498                         case IClasspathEntry.CPE_CONTAINER :
499                                 buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
500                                 break;
501                 }
502                 buffer.append("]["); //$NON-NLS-1$
503                 switch (getContentKind()) {
504                         case IPackageFragmentRoot.K_BINARY :
505                                 buffer.append("K_BINARY"); //$NON-NLS-1$
506                                 break;
507                         case IPackageFragmentRoot.K_SOURCE :
508                                 buffer.append("K_SOURCE"); //$NON-NLS-1$
509                                 break;
510                         case ClasspathEntry.K_OUTPUT :
511                                 buffer.append("K_OUTPUT"); //$NON-NLS-1$
512                                 break;
513                 }
514                 buffer.append(']');
515                 if (getSourceAttachmentPath() != null) {
516                         buffer.append("[sourcePath:"); //$NON-NLS-1$
517                         buffer.append(getSourceAttachmentPath());
518                         buffer.append(']');
519                 }
520                 if (getSourceAttachmentRootPath() != null) {
521                         buffer.append("[rootPath:"); //$NON-NLS-1$
522                         buffer.append(getSourceAttachmentRootPath());
523                         buffer.append(']');
524                 }
525                 buffer.append("[isExported:"); //$NON-NLS-1$
526                 buffer.append(this.isExported);
527                 buffer.append(']');
528                 IPath[] patterns = getExclusionPatterns();
529                 int length;
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]);
534                                 if (i != length-1) {
535                                         buffer.append('|');
536                                 }
537                         }
538                         buffer.append(']');
539                 }
540                 if (getOutputLocation() != null) {
541                         buffer.append("[output:"); //$NON-NLS-1$
542                         buffer.append(getOutputLocation());
543                         buffer.append(']');
544                 }
545                 return buffer.toString();
546         }
547         
548         /**
549          * Answers an ID which is used to distinguish entries during package
550          * fragment root computations
551          */
552         public String rootID(){
553
554                 if (this.rootID == null) {
555                         switch(this.entryKind){
556                                 case IClasspathEntry.CPE_LIBRARY :
557                                         this.rootID = "[LIB]"+this.path;  //$NON-NLS-1$
558                                         break;
559                                 case IClasspathEntry.CPE_PROJECT :
560                                         this.rootID = "[PRJ]"+this.path;  //$NON-NLS-1$
561                                         break;
562                                 case IClasspathEntry.CPE_SOURCE :
563                                         this.rootID = "[SRC]"+this.path;  //$NON-NLS-1$
564                                         break;
565                                 case IClasspathEntry.CPE_VARIABLE :
566                                         this.rootID = "[VAR]"+this.path;  //$NON-NLS-1$
567                                         break;
568                                 case IClasspathEntry.CPE_CONTAINER :
569                                         this.rootID = "[CON]"+this.path;  //$NON-NLS-1$
570                                         break;
571                                 default :
572                                         this.rootID = "";  //$NON-NLS-1$
573                                         break;
574                         }
575                 }
576                 return this.rootID;
577         }
578         
579         /**
580          * @see IClasspathEntry
581          * @deprecated
582          */
583         public IClasspathEntry getResolvedEntry() {
584         
585                 return PHPCore.getResolvedClasspathEntry(this);
586         }
587 }