KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > core > ClasspathEntry


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.core;
12
13 import java.io.ByteArrayOutputStream JavaDoc;
14 import java.io.File JavaDoc;
15 import java.io.OutputStreamWriter JavaDoc;
16 import java.io.UnsupportedEncodingException JavaDoc;
17 import java.util.ArrayList JavaDoc;
18 import java.util.HashMap JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.eclipse.core.resources.IProject;
23 import org.eclipse.core.resources.IResource;
24 import org.eclipse.core.resources.IWorkspaceRoot;
25 import org.eclipse.core.resources.ResourcesPlugin;
26 import org.eclipse.core.runtime.AssertionFailedException;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.runtime.IPath;
29 import org.eclipse.core.runtime.IStatus;
30 import org.eclipse.core.runtime.Path;
31 import org.eclipse.jdt.core.IAccessRule;
32 import org.eclipse.jdt.core.IClasspathAttribute;
33 import org.eclipse.jdt.core.IClasspathContainer;
34 import org.eclipse.jdt.core.IClasspathEntry;
35 import org.eclipse.jdt.core.IJavaModelStatus;
36 import org.eclipse.jdt.core.IJavaModelStatusConstants;
37 import org.eclipse.jdt.core.IJavaProject;
38 import org.eclipse.jdt.core.IPackageFragmentRoot;
39 import org.eclipse.jdt.core.JavaCore;
40 import org.eclipse.jdt.core.JavaModelException;
41 import org.eclipse.jdt.core.compiler.CharOperation;
42 import org.eclipse.jdt.core.compiler.IProblem;
43 import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
44 import org.eclipse.jdt.internal.compiler.env.AccessRule;
45 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
46 import org.eclipse.jdt.internal.core.util.Messages;
47 import org.eclipse.jdt.internal.core.util.Util;
48 import org.w3c.dom.DOMException JavaDoc;
49 import org.w3c.dom.Element JavaDoc;
50 import org.w3c.dom.NamedNodeMap JavaDoc;
51 import org.w3c.dom.Node JavaDoc;
52 import org.w3c.dom.NodeList JavaDoc;
53 import org.w3c.dom.Text JavaDoc;
54
55 /**
56  * @see IClasspathEntry
57  */

58 public class ClasspathEntry implements IClasspathEntry {
59
60     public static final String JavaDoc TAG_CLASSPATH = "classpath"; //$NON-NLS-1$
61
public static final String JavaDoc TAG_CLASSPATHENTRY = "classpathentry"; //$NON-NLS-1$
62
public static final String JavaDoc TAG_OUTPUT = "output"; //$NON-NLS-1$
63
public static final String JavaDoc TAG_KIND = "kind"; //$NON-NLS-1$
64
public static final String JavaDoc TAG_PATH = "path"; //$NON-NLS-1$
65
public static final String JavaDoc TAG_SOURCEPATH = "sourcepath"; //$NON-NLS-1$
66
public static final String JavaDoc TAG_ROOTPATH = "rootpath"; //$NON-NLS-1$
67
public static final String JavaDoc TAG_EXPORTED = "exported"; //$NON-NLS-1$
68
public static final String JavaDoc TAG_INCLUDING = "including"; //$NON-NLS-1$
69
public static final String JavaDoc TAG_EXCLUDING = "excluding"; //$NON-NLS-1$
70
public static final String JavaDoc TAG_ATTRIBUTES = "attributes"; //$NON-NLS-1$
71
public static final String JavaDoc TAG_ATTRIBUTE = "attribute"; //$NON-NLS-1$
72
public static final String JavaDoc TAG_ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
73
public static final String JavaDoc TAG_ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$
74
public static final String JavaDoc TAG_COMBINE_ACCESS_RULES = "combineaccessrules"; //$NON-NLS-1$
75
public static final String JavaDoc TAG_ACCESS_RULES = "accessrules"; //$NON-NLS-1$
76
public static final String JavaDoc TAG_ACCESS_RULE = "accessrule"; //$NON-NLS-1$
77
public static final String JavaDoc TAG_PATTERN = "pattern"; //$NON-NLS-1$
78
public static final String JavaDoc TAG_ACCESSIBLE = "accessible"; //$NON-NLS-1$
79
public static final String JavaDoc TAG_NON_ACCESSIBLE = "nonaccessible"; //$NON-NLS-1$
80
public static final String JavaDoc TAG_DISCOURAGED = "discouraged"; //$NON-NLS-1$
81
public static final String JavaDoc TAG_IGNORE_IF_BETTER = "ignoreifbetter"; //$NON-NLS-1$
82

83     /**
84      * Describes the kind of classpath entry - one of
85      * CPE_PROJECT, CPE_LIBRARY, CPE_SOURCE, CPE_VARIABLE or CPE_CONTAINER
86      */

87     public int entryKind;
88
89     /**
90      * Describes the kind of package fragment roots found on
91      * this classpath entry - either K_BINARY or K_SOURCE or
92      * K_OUTPUT.
93      */

94     public int contentKind;
95
96     /**
97      * The meaning of the path of a classpath entry depends on its entry kind:<ul>
98      * <li>Source code in the current project (<code>CPE_SOURCE</code>) -
99      * The path associated with this entry is the absolute path to the root folder. </li>
100      * <li>A binary library in the current project (<code>CPE_LIBRARY</code>) - the path
101      * associated with this entry is the absolute path to the JAR (or root folder), and
102      * in case it refers to an external JAR, then there is no associated resource in
103      * the workbench.
104      * <li>A required project (<code>CPE_PROJECT</code>) - the path of the entry denotes the
105      * path to the corresponding project resource.</li>
106      * <li>A variable entry (<code>CPE_VARIABLE</code>) - the first segment of the path
107      * is the name of a classpath variable. If this classpath variable
108      * is bound to the path <it>P</it>, the path of the corresponding classpath entry
109      * is computed by appending to <it>P</it> the segments of the returned
110      * path without the variable.</li>
111      * <li> A container entry (<code>CPE_CONTAINER</code>) - the first segment of the path is denoting
112      * the unique container identifier (for which a <code>ClasspathContainerInitializer</code> could be
113      * registered), and the remaining segments are used as additional hints for resolving the container entry to
114      * an actual <code>IClasspathContainer</code>.</li>
115      */

116     public IPath path;
117
118     /**
119      * Patterns allowing to include/exclude portions of the resource tree denoted by this entry path.
120      */

121     private IPath[] inclusionPatterns;
122     private char[][] fullInclusionPatternChars;
123     private IPath[] exclusionPatterns;
124     private char[][] fullExclusionPatternChars;
125     private final static char[][] UNINIT_PATTERNS = new char[][] { "Non-initialized yet".toCharArray() }; //$NON-NLS-1$
126

127     private boolean combineAccessRules;
128
129     private String JavaDoc rootID;
130     private AccessRuleSet accessRuleSet;
131
132
133     static class UnknownXmlElements {
134         String JavaDoc[] attributes;
135         ArrayList JavaDoc children;
136     }
137
138     /*
139      * Default inclusion pattern set
140      */

141     public final static IPath[] INCLUDE_ALL = {};
142
143     /*
144      * Default exclusion pattern set
145      */

146     public final static IPath[] EXCLUDE_NONE = {};
147
148     /*
149      * Default extra attributes
150      */

151     public final static IClasspathAttribute[] NO_EXTRA_ATTRIBUTES = {};
152
153     /*
154      * Default access rules
155      */

156     public final static IAccessRule[] NO_ACCESS_RULES = {};
157
158     /**
159      * Describes the path to the source archive associated with this
160      * classpath entry, or <code>null</code> if this classpath entry has no
161      * source attachment.
162      * <p>
163      * Only library and variable classpath entries may have source attachments.
164      * For library classpath entries, the result path (if present) locates a source
165      * archive. For variable classpath entries, the result path (if present) has
166      * an analogous form and meaning as the variable path, namely the first segment
167      * is the name of a classpath variable.
168      */

169     public IPath sourceAttachmentPath;
170
171     /**
172      * Describes the path within the source archive where package fragments
173      * are located. An empty path indicates that packages are located at
174      * the root of the source archive. Returns a non-<code>null</code> value
175      * if and only if <code>getSourceAttachmentPath</code> returns
176      * a non-<code>null</code> value.
177      */

178     public IPath sourceAttachmentRootPath;
179
180     /**
181      * Specific output location (for this source entry)
182      */

183     public IPath specificOutputLocation;
184
185     /**
186      * A constant indicating an output location.
187      */

188     public static final int K_OUTPUT = 10;
189
190     /**
191      * The export flag
192      */

193     public boolean isExported;
194
195     /*
196      * The extra attributes
197      */

198     IClasspathAttribute[] extraAttributes;
199
200     /**
201      * Creates a class path entry of the specified kind with the given path.
202      */

203     public ClasspathEntry(
204         int contentKind,
205         int entryKind,
206         IPath path,
207         IPath[] inclusionPatterns,
208         IPath[] exclusionPatterns,
209         IPath sourceAttachmentPath,
210         IPath sourceAttachmentRootPath,
211         IPath specificOutputLocation,
212         boolean isExported,
213         IAccessRule[] accessRules,
214         boolean combineAccessRules,
215         IClasspathAttribute[] extraAttributes) {
216
217         this.contentKind = contentKind;
218         this.entryKind = entryKind;
219         this.path = path;
220         this.inclusionPatterns = inclusionPatterns;
221         this.exclusionPatterns = exclusionPatterns;
222
223         int length;
224         if (accessRules != null && (length = accessRules.length) > 0) {
225             AccessRule[] rules = new AccessRule[length];
226             System.arraycopy(accessRules, 0, rules, 0, length);
227             this.accessRuleSet = new AccessRuleSet(rules, getMessageTemplates());
228         }
229 // else { -- implicit!
230
// this.accessRuleSet = null;
231
// }
232

233         this.combineAccessRules = combineAccessRules;
234         this.extraAttributes = extraAttributes;
235
236         if (inclusionPatterns != INCLUDE_ALL && inclusionPatterns.length > 0) {
237             this.fullInclusionPatternChars = UNINIT_PATTERNS;
238         }
239         if (exclusionPatterns.length > 0) {
240             this.fullExclusionPatternChars = UNINIT_PATTERNS;
241         }
242         this.sourceAttachmentPath = sourceAttachmentPath;
243         this.sourceAttachmentRootPath = sourceAttachmentRootPath;
244         this.specificOutputLocation = specificOutputLocation;
245         this.isExported = isExported;
246     }
247
248     public boolean combineAccessRules() {
249         return this.combineAccessRules;
250     }
251
252     /**
253      * Used to perform export/restriction propagation across referring projects/containers
254      */

255     public ClasspathEntry combineWith(ClasspathEntry referringEntry) {
256         if (referringEntry == null) return this;
257         if (referringEntry.isExported() || referringEntry.getAccessRuleSet() != null ) {
258             boolean combine = this.entryKind == CPE_SOURCE || referringEntry.combineAccessRules();
259             return new ClasspathEntry(
260                                 getContentKind(),
261                                 getEntryKind(),
262                                 getPath(),
263                                 this.inclusionPatterns,
264                                 this.exclusionPatterns,
265                                 getSourceAttachmentPath(),
266                                 getSourceAttachmentRootPath(),
267                                 getOutputLocation(),
268                                 referringEntry.isExported() || this.isExported, // duplicate container entry for tagging it as exported
269
combine(referringEntry.getAccessRules(), getAccessRules(), combine),
270                                 this.combineAccessRules,
271                                 this.extraAttributes);
272         }
273         // no need to clone
274
return this;
275     }
276
277     private IAccessRule[] combine(IAccessRule[] referringRules, IAccessRule[] rules, boolean combine) {
278         if (!combine) return rules;
279         if (rules == null || rules.length == 0) return referringRules;
280
281         // concat access rules
282
int referringRulesLength = referringRules.length;
283         int accessRulesLength = rules.length;
284         int rulesLength = referringRulesLength + accessRulesLength;
285         IAccessRule[] result = new IAccessRule[rulesLength];
286         System.arraycopy(referringRules, 0, result, 0, referringRulesLength);
287         System.arraycopy(rules, 0, result, referringRulesLength, accessRulesLength);
288
289         return result;
290     }
291
292     static IClasspathAttribute[] decodeExtraAttributes(NodeList JavaDoc attributes) {
293         if (attributes == null) return NO_EXTRA_ATTRIBUTES;
294         int length = attributes.getLength();
295         if (length == 0) return NO_EXTRA_ATTRIBUTES;
296         IClasspathAttribute[] result = new IClasspathAttribute[length];
297         int index = 0;
298         for (int i = 0; i < length; ++i) {
299             Node JavaDoc node = attributes.item(i);
300             if (node.getNodeType() == Node.ELEMENT_NODE) {
301                 Element JavaDoc attribute = (Element JavaDoc)node;
302                 String JavaDoc name = attribute.getAttribute(TAG_ATTRIBUTE_NAME);
303                 if (name == null) continue;
304                 String JavaDoc value = attribute.getAttribute(TAG_ATTRIBUTE_VALUE);
305                 if (value == null) continue;
306                 result[index++] = new ClasspathAttribute(name, value);
307             }
308         }
309         if (index != length)
310             System.arraycopy(result, 0, result = new IClasspathAttribute[index], 0, index);
311         return result;
312     }
313
314     static IAccessRule[] decodeAccessRules(NodeList JavaDoc list) {
315         if (list == null) return null;
316         int length = list.getLength();
317         if (length == 0) return null;
318         IAccessRule[] result = new IAccessRule[length];
319         int index = 0;
320         for (int i = 0; i < length; i++) {
321             Node JavaDoc accessRule = list.item(i);
322             if (accessRule.getNodeType() == Node.ELEMENT_NODE) {
323                 Element JavaDoc elementAccessRule = (Element JavaDoc) accessRule;
324                 String JavaDoc pattern = elementAccessRule.getAttribute(TAG_PATTERN);
325                 if (pattern == null) continue;
326                 String JavaDoc tagKind = elementAccessRule.getAttribute(TAG_KIND);
327                 int kind;
328                 if (TAG_ACCESSIBLE.equals(tagKind))
329                     kind = IAccessRule.K_ACCESSIBLE;
330                 else if (TAG_NON_ACCESSIBLE.equals(tagKind))
331                     kind = IAccessRule.K_NON_ACCESSIBLE;
332                 else if (TAG_DISCOURAGED.equals(tagKind))
333                     kind = IAccessRule.K_DISCOURAGED;
334                 else
335                     continue;
336                 boolean ignoreIfBetter = "true".equals(elementAccessRule.getAttribute(TAG_IGNORE_IF_BETTER)); //$NON-NLS-1$
337
result[index++] = new ClasspathAccessRule(new Path(pattern), ignoreIfBetter ? kind | IAccessRule.IGNORE_IF_BETTER : kind);
338             }
339         }
340         if (index != length)
341             System.arraycopy(result, 0, result = new IAccessRule[index], 0, index);
342         return result;
343     }
344
345     /**
346      * Decode some element tag containing a sequence of patterns into IPath[]
347      */

348     private static IPath[] decodePatterns(NamedNodeMap JavaDoc nodeMap, String JavaDoc tag) {
349         String JavaDoc sequence = removeAttribute(tag, nodeMap);
350         if (!sequence.equals("")) { //$NON-NLS-1$
351
char[][] patterns = CharOperation.splitOn('|', sequence.toCharArray());
352             int patternCount;
353             if ((patternCount = patterns.length) > 0) {
354                 IPath[] paths = new IPath[patternCount];
355                 int index = 0;
356                 for (int j = 0; j < patternCount; j++) {
357                     char[] pattern = patterns[j];
358                     if (pattern.length == 0) continue; // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=105581
359
paths[index++] = new Path(new String JavaDoc(pattern));
360                 }
361                 if (index < patternCount)
362                     System.arraycopy(paths, 0, paths = new IPath[index], 0, index);
363                 return paths;
364             }
365         }
366         return null;
367     }
368
369     private static void decodeUnknownNode(Node JavaDoc node, StringBuffer JavaDoc buffer, IJavaProject project) {
370         ByteArrayOutputStream JavaDoc s = new ByteArrayOutputStream JavaDoc();
371         OutputStreamWriter JavaDoc writer;
372         try {
373             writer = new OutputStreamWriter JavaDoc(s, "UTF8"); //$NON-NLS-1$
374
XMLWriter xmlWriter = new XMLWriter(writer, project, false/*don't print XML version*/);
375             decodeUnknownNode(node, xmlWriter, true/*insert new line*/);
376             xmlWriter.flush();
377             xmlWriter.close();
378             buffer.append(s.toString("UTF8")); //$NON-NLS-1$
379
} catch (UnsupportedEncodingException JavaDoc e) {
380             // ignore (UTF8 is always supported)
381
}
382     }
383
384     private static void decodeUnknownNode(Node JavaDoc node, XMLWriter xmlWriter, boolean insertNewLine) {
385         switch (node.getNodeType()) {
386         case Node.ELEMENT_NODE:
387             NamedNodeMap JavaDoc attributes;
388             HashMap JavaDoc parameters = null;
389             if ((attributes = node.getAttributes()) != null) {
390                 int length = attributes.getLength();
391                 if (length > 0) {
392                     parameters = new HashMap JavaDoc();
393                     for (int i = 0; i < length; i++) {
394                         Node JavaDoc attribute = attributes.item(i);
395                         parameters.put(attribute.getNodeName(), attribute.getNodeValue());
396                     }
397                 }
398             }
399             NodeList JavaDoc children = node.getChildNodes();
400             int childrenLength = children.getLength();
401             String JavaDoc nodeName = node.getNodeName();
402             xmlWriter.printTag(nodeName, parameters, false/*don't insert tab*/, false/*don't insert new line*/, childrenLength == 0/*close tag if no children*/);
403             if (childrenLength > 0) {
404                 for (int i = 0; i < childrenLength; i++) {
405                     decodeUnknownNode(children.item(i), xmlWriter, false/*don't insert new line*/);
406                 }
407                 xmlWriter.endTag(nodeName, false/*don't insert tab*/, insertNewLine);
408             }
409             break;
410         case Node.TEXT_NODE:
411             String JavaDoc data = ((Text JavaDoc) node).getData();
412             xmlWriter.printString(data, false/*don't insert tab*/, false/*don't insert new line*/);
413             break;
414         }
415     }
416
417     /*
418      * Returns a char based representation of the exclusions patterns full path.
419      */

420     public char[][] fullExclusionPatternChars() {
421
422         if (this.fullExclusionPatternChars == UNINIT_PATTERNS) {
423             int length = this.exclusionPatterns.length;
424             this.fullExclusionPatternChars = new char[length][];
425             IPath prefixPath = this.path.removeTrailingSeparator();
426             for (int i = 0; i < length; i++) {
427                 this.fullExclusionPatternChars[i] =
428                     prefixPath.append(this.exclusionPatterns[i]).toString().toCharArray();
429             }
430         }
431         return this.fullExclusionPatternChars;
432     }
433
434     /*
435      * Returns a char based representation of the exclusions patterns full path.
436      */

437     public char[][] fullInclusionPatternChars() {
438
439         if (this.fullInclusionPatternChars == UNINIT_PATTERNS) {
440             int length = this.inclusionPatterns.length;
441             this.fullInclusionPatternChars = new char[length][];
442             IPath prefixPath = this.path.removeTrailingSeparator();
443             for (int i = 0; i < length; i++) {
444                 this.fullInclusionPatternChars[i] =
445                     prefixPath.append(this.inclusionPatterns[i]).toString().toCharArray();
446             }
447         }
448         return this.fullInclusionPatternChars;
449     }
450
451     /**
452      * Returns the XML encoding of the class path.
453      */

454     public void elementEncode(XMLWriter writer, IPath projectPath, boolean indent, boolean newLine, Map JavaDoc unknownElements) {
455         HashMap JavaDoc parameters = new HashMap JavaDoc();
456
457         parameters.put(TAG_KIND, ClasspathEntry.kindToString(this.entryKind));
458
459         IPath xmlPath = this.path;
460         if (this.entryKind != IClasspathEntry.CPE_VARIABLE && this.entryKind != IClasspathEntry.CPE_CONTAINER) {
461             // translate to project relative from absolute (unless a device path)
462
if (xmlPath.isAbsolute()) {
463                 if (projectPath != null && projectPath.isPrefixOf(xmlPath)) {
464                     if (xmlPath.segment(0).equals(projectPath.segment(0))) {
465                         xmlPath = xmlPath.removeFirstSegments(1);
466                         xmlPath = xmlPath.makeRelative();
467                     } else {
468                         xmlPath = xmlPath.makeAbsolute();
469                     }
470                 }
471             }
472         }
473         parameters.put(TAG_PATH, String.valueOf(xmlPath));
474
475         if (this.sourceAttachmentPath != null) {
476             xmlPath = this.sourceAttachmentPath;
477             // translate to project relative from absolute
478
if (this.entryKind != IClasspathEntry.CPE_VARIABLE && projectPath != null && projectPath.isPrefixOf(xmlPath)) {
479                 if (xmlPath.segment(0).equals(projectPath.segment(0))) {
480                     xmlPath = xmlPath.removeFirstSegments(1);
481                     xmlPath = xmlPath.makeRelative();
482                 }
483             }
484             parameters.put(TAG_SOURCEPATH, String.valueOf(xmlPath));
485         }
486         if (this.sourceAttachmentRootPath != null) {
487             parameters.put(TAG_ROOTPATH, String.valueOf(this.sourceAttachmentRootPath));
488         }
489         if (this.isExported) {
490             parameters.put(TAG_EXPORTED, "true");//$NON-NLS-1$
491
}
492         encodePatterns(this.inclusionPatterns, TAG_INCLUDING, parameters);
493         encodePatterns(this.exclusionPatterns, TAG_EXCLUDING, parameters);
494         if (this.entryKind == CPE_PROJECT && !this.combineAccessRules)
495             parameters.put(TAG_COMBINE_ACCESS_RULES, "false"); //$NON-NLS-1$
496

497
498         // unknown attributes
499
UnknownXmlElements unknownXmlElements = unknownElements == null ? null : (UnknownXmlElements) unknownElements.get(this.path);
500         String JavaDoc[] unknownAttributes;
501         if (unknownXmlElements != null && (unknownAttributes = unknownXmlElements.attributes) != null)
502             for (int i = 0, length = unknownAttributes.length; i < length; i+=2) {
503                 String JavaDoc tagName = unknownAttributes[i];
504                 String JavaDoc tagValue = unknownAttributes[i+1];
505                 parameters.put(tagName, tagValue);
506             }
507
508         if (this.specificOutputLocation != null) {
509             IPath outputLocation = this.specificOutputLocation.removeFirstSegments(1);
510             outputLocation = outputLocation.makeRelative();
511             parameters.put(TAG_OUTPUT, String.valueOf(outputLocation));
512         }
513
514         boolean hasExtraAttributes = this.extraAttributes.length != 0;
515         boolean hasRestrictions = getAccessRuleSet() != null; // access rule set is null if no access rules
516
ArrayList JavaDoc unknownChildren = unknownXmlElements != null ? unknownXmlElements.children : null;
517         boolean hasUnknownChildren = unknownChildren != null;
518         writer.printTag(
519             TAG_CLASSPATHENTRY,
520             parameters,
521             indent,
522             newLine,
523             !hasExtraAttributes && !hasRestrictions && !hasUnknownChildren/*close tag if no extra attributes, no restriction and no unknown children*/);
524
525         if (hasExtraAttributes)
526             encodeExtraAttributes(writer, indent, newLine);
527
528         if (hasRestrictions)
529             encodeAccessRules(writer, indent, newLine);
530
531         if (hasUnknownChildren)
532             encodeUnknownChildren(writer, indent, newLine, unknownChildren);
533
534         if (hasExtraAttributes || hasRestrictions || hasUnknownChildren)
535             writer.endTag(TAG_CLASSPATHENTRY, indent, true/*insert new line*/);
536     }
537
538     void encodeExtraAttributes(XMLWriter writer, boolean indent, boolean newLine) {
539         writer.startTag(TAG_ATTRIBUTES, indent);
540         for (int i = 0; i < this.extraAttributes.length; i++) {
541             IClasspathAttribute attribute = this.extraAttributes[i];
542             HashMap JavaDoc parameters = new HashMap JavaDoc();
543             parameters.put(TAG_ATTRIBUTE_NAME, attribute.getName());
544             parameters.put(TAG_ATTRIBUTE_VALUE, attribute.getValue());
545             writer.printTag(TAG_ATTRIBUTE, parameters, indent, newLine, true);
546         }
547         writer.endTag(TAG_ATTRIBUTES, indent, true/*insert new line*/);
548     }
549
550     void encodeAccessRules(XMLWriter writer, boolean indent, boolean newLine) {
551
552         writer.startTag(TAG_ACCESS_RULES, indent);
553         AccessRule[] rules = getAccessRuleSet().getAccessRules();
554         for (int i = 0, length = rules.length; i < length; i++) {
555             encodeAccessRule(rules[i], writer, indent, newLine);
556         }
557         writer.endTag(TAG_ACCESS_RULES, indent, true/*insert new line*/);
558     }
559
560     private void encodeAccessRule(AccessRule accessRule, XMLWriter writer, boolean indent, boolean newLine) {
561
562         HashMap JavaDoc parameters = new HashMap JavaDoc();
563         parameters.put(TAG_PATTERN, new String JavaDoc(accessRule.pattern));
564
565         switch (accessRule.getProblemId()) {
566             case IProblem.ForbiddenReference:
567                 parameters.put(TAG_KIND, TAG_NON_ACCESSIBLE);
568                 break;
569             case IProblem.DiscouragedReference:
570                 parameters.put(TAG_KIND, TAG_DISCOURAGED);
571                 break;
572             default:
573                 parameters.put(TAG_KIND, TAG_ACCESSIBLE);
574                 break;
575         }
576         if (accessRule.ignoreIfBetter())
577             parameters.put(TAG_IGNORE_IF_BETTER, "true"); //$NON-NLS-1$
578

579         writer.printTag(TAG_ACCESS_RULE, parameters, indent, newLine, true);
580
581     }
582
583     private void encodeUnknownChildren(XMLWriter writer, boolean indent, boolean newLine, ArrayList JavaDoc unknownChildren) {
584         for (int i = 0, length = unknownChildren.size(); i < length; i++) {
585             String JavaDoc child = (String JavaDoc) unknownChildren.get(i);
586             writer.printString(child, indent, false/*don't insert new line*/);
587         }
588     }
589
590     public static IClasspathEntry elementDecode(Element JavaDoc element, IJavaProject project, Map JavaDoc unknownElements) {
591
592         IPath projectPath = project.getProject().getFullPath();
593         NamedNodeMap JavaDoc attributes = element.getAttributes();
594         NodeList JavaDoc children = element.getChildNodes();
595         boolean[] foundChildren = new boolean[children.getLength()];
596         String JavaDoc kindAttr = removeAttribute(TAG_KIND, attributes);
597         String JavaDoc pathAttr = removeAttribute(TAG_PATH, attributes);
598
599         // ensure path is absolute
600
IPath path = new Path(pathAttr);
601         int kind = kindFromString(kindAttr);
602         if (kind != IClasspathEntry.CPE_VARIABLE && kind != IClasspathEntry.CPE_CONTAINER && !path.isAbsolute()) {
603             path = projectPath.append(path);
604         }
605         // source attachment info (optional)
606
IPath sourceAttachmentPath =
607             element.hasAttribute(TAG_SOURCEPATH)
608             ? new Path(removeAttribute(TAG_SOURCEPATH, attributes))
609             : null;
610         if (kind != IClasspathEntry.CPE_VARIABLE && sourceAttachmentPath != null && !sourceAttachmentPath.isAbsolute()) {
611             sourceAttachmentPath = projectPath.append(sourceAttachmentPath);
612         }
613         IPath sourceAttachmentRootPath =
614             element.hasAttribute(TAG_ROOTPATH)
615             ? new Path(removeAttribute(TAG_ROOTPATH, attributes))
616             : null;
617
618         // exported flag (optional)
619
boolean isExported = removeAttribute(TAG_EXPORTED, attributes).equals("true"); //$NON-NLS-1$
620

621         // inclusion patterns (optional)
622
IPath[] inclusionPatterns = decodePatterns(attributes, TAG_INCLUDING);
623         if (inclusionPatterns == null) inclusionPatterns = INCLUDE_ALL;
624
625         // exclusion patterns (optional)
626
IPath[] exclusionPatterns = decodePatterns(attributes, TAG_EXCLUDING);
627         if (exclusionPatterns == null) exclusionPatterns = EXCLUDE_NONE;
628
629         // access rules (optional)
630
NodeList JavaDoc attributeList = getChildAttributes(TAG_ACCESS_RULES, children, foundChildren);
631         IAccessRule[] accessRules = decodeAccessRules(attributeList);
632
633         // backward compatibility
634
if (accessRules == null) {
635             accessRules = getAccessRules(inclusionPatterns, exclusionPatterns);
636         }
637
638         // combine access rules (optional)
639
boolean combineAccessRestrictions = !removeAttribute(TAG_COMBINE_ACCESS_RULES, attributes).equals("false"); //$NON-NLS-1$
640

641         // extra attributes (optional)
642
attributeList = getChildAttributes(TAG_ATTRIBUTES, children, foundChildren);
643         IClasspathAttribute[] extraAttributes = decodeExtraAttributes(attributeList);
644
645         // custom output location
646
IPath outputLocation = element.hasAttribute(TAG_OUTPUT) ? projectPath.append(removeAttribute(TAG_OUTPUT, attributes)) : null;
647
648         String JavaDoc[] unknownAttributes = null;
649         ArrayList JavaDoc unknownChildren = null;
650
651         if (unknownElements != null) {
652             // unknown attributes
653
int unknownAttributeLength = attributes.getLength();
654             if (unknownAttributeLength != 0) {
655                 unknownAttributes = new String JavaDoc[unknownAttributeLength*2];
656                 for (int i = 0; i < unknownAttributeLength; i++) {
657                     Node JavaDoc attribute = attributes.item(i);
658                     unknownAttributes[i*2] = attribute.getNodeName();
659                     unknownAttributes[i*2 + 1] = attribute.getNodeValue();
660                 }
661             }
662
663             // unknown children
664
for (int i = 0, length = foundChildren.length; i < length; i++) {
665                 if (!foundChildren[i]) {
666                     Node JavaDoc node = children.item(i);
667                     if (node.getNodeType() != Node.ELEMENT_NODE) continue;
668                     if (unknownChildren == null)
669                         unknownChildren = new ArrayList JavaDoc();
670                     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
671                     decodeUnknownNode(node, buffer, project);
672                     unknownChildren.add(buffer.toString());
673                 }
674             }
675         }
676
677         // recreate the CP entry
678
IClasspathEntry entry = null;
679         switch (kind) {
680
681             case IClasspathEntry.CPE_PROJECT :
682                 entry = new ClasspathEntry(
683                 IPackageFragmentRoot.K_SOURCE,
684                 IClasspathEntry.CPE_PROJECT,
685                 path,
686                 ClasspathEntry.INCLUDE_ALL, // inclusion patterns
687
ClasspathEntry.EXCLUDE_NONE, // exclusion patterns
688
null, // source attachment
689
null, // source attachment root
690
null, // specific output folder
691
isExported,
692                 accessRules,
693                 combineAccessRestrictions,
694                 extraAttributes);
695                 break;
696             case IClasspathEntry.CPE_LIBRARY :
697                 entry = JavaCore.newLibraryEntry(
698                                                 path,
699                                                 sourceAttachmentPath,
700                                                 sourceAttachmentRootPath,
701                                                 accessRules,
702                                                 extraAttributes,
703                                                 isExported);
704                 break;
705             case IClasspathEntry.CPE_SOURCE :
706                 // must be an entry in this project or specify another project
707
String JavaDoc projSegment = path.segment(0);
708                 if (projSegment != null && projSegment.equals(project.getElementName())) { // this project
709
entry = JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes);
710                 } else {
711                     if (path.segmentCount() == 1) {
712                         // another project
713
entry = JavaCore.newProjectEntry(
714                                                 path,
715                                                 accessRules,
716                                                 combineAccessRestrictions,
717                                                 extraAttributes,
718                                                 isExported);
719                     } else {
720                         // an invalid source folder
721
entry = JavaCore.newSourceEntry(path, inclusionPatterns, exclusionPatterns, outputLocation, extraAttributes);
722                     }
723                 }
724                 break;
725             case IClasspathEntry.CPE_VARIABLE :
726                 entry = JavaCore.newVariableEntry(
727                         path,
728                         sourceAttachmentPath,
729                         sourceAttachmentRootPath,
730                         accessRules,
731                         extraAttributes,
732                         isExported);
733                 break;
734             case IClasspathEntry.CPE_CONTAINER :
735                 entry = JavaCore.newContainerEntry(
736                         path,
737                         accessRules,
738                         extraAttributes,
739                         isExported);
740                 break;
741             case ClasspathEntry.K_OUTPUT :
742                 if (!path.isAbsolute()) return null;
743                 entry = new ClasspathEntry(
744                         ClasspathEntry.K_OUTPUT,
745                         IClasspathEntry.CPE_LIBRARY,
746                         path,
747                         INCLUDE_ALL,
748                         EXCLUDE_NONE,
749                         null, // source attachment
750
null, // source attachment root
751
null, // custom output location
752
false,
753                         null, // no access rules
754
false, // no accessible files to combine
755
NO_EXTRA_ATTRIBUTES);
756                 break;
757             default :
758                 throw new AssertionFailedException(Messages.bind(Messages.classpath_unknownKind, kindAttr));
759         }
760
761         if (unknownAttributes != null || unknownChildren != null) {
762             UnknownXmlElements unknownXmlElements = new UnknownXmlElements();
763             unknownXmlElements.attributes = unknownAttributes;
764             unknownXmlElements.children = unknownChildren;
765             unknownElements.put(path, unknownXmlElements);
766         }
767
768         return entry;
769     }
770
771     public static NodeList JavaDoc getChildAttributes(String JavaDoc childName, NodeList JavaDoc children, boolean[] foundChildren) {
772         for (int i = 0, length = foundChildren.length; i < length; i++) {
773             Node JavaDoc node = children.item(i);
774             if (childName.equals(node.getNodeName())) {
775                 foundChildren[i] = true;
776                 return node.getChildNodes();
777             }
778         }
779         return null;
780     }
781
782
783     private static String JavaDoc removeAttribute(String JavaDoc nodeName, NamedNodeMap JavaDoc nodeMap) {
784         Node JavaDoc node = removeNode(nodeName, nodeMap);
785         if (node == null)
786             return ""; // //$NON-NLS-1$
787
return node.getNodeValue();
788     }
789
790     private static Node JavaDoc removeNode(String JavaDoc nodeName, NamedNodeMap JavaDoc nodeMap) {
791         try {
792             return nodeMap.removeNamedItem(nodeName);
793         } catch (DOMException JavaDoc e) {
794             if (e.code != DOMException.NOT_FOUND_ERR)
795                 throw e;
796             return null;
797         }
798     }
799
800     /**
801      * Encode some patterns into XML parameter tag
802      */

803     private static void encodePatterns(IPath[] patterns, String JavaDoc tag, Map JavaDoc parameters) {
804         if (patterns != null && patterns.length > 0) {
805             StringBuffer JavaDoc rule = new StringBuffer JavaDoc(10);
806             for (int i = 0, max = patterns.length; i < max; i++){
807                 if (i > 0) rule.append('|');
808                 rule.append(patterns[i]);
809             }
810             parameters.put(tag, String.valueOf(rule));
811         }
812     }
813
814     /**
815      * Returns true if the given object is a classpath entry
816      * with equivalent attributes.
817      */

818     public boolean equals(Object JavaDoc object) {
819         if (this == object)
820             return true;
821         if (object instanceof ClasspathEntry) {
822             ClasspathEntry otherEntry = (ClasspathEntry) object;
823
824             if (this.contentKind != otherEntry.getContentKind())
825                 return false;
826
827             if (this.entryKind != otherEntry.getEntryKind())
828                 return false;
829
830             if (this.isExported != otherEntry.isExported())
831                 return false;
832
833             if (!this.path.equals(otherEntry.getPath()))
834                 return false;
835
836             IPath otherPath = otherEntry.getSourceAttachmentPath();
837             if (this.sourceAttachmentPath == null) {
838                 if (otherPath != null)
839                     return false;
840             } else {
841                 if (!this.sourceAttachmentPath.equals(otherPath))
842                     return false;
843             }
844
845             otherPath = otherEntry.getSourceAttachmentRootPath();
846             if (this.sourceAttachmentRootPath == null) {
847                 if (otherPath != null)
848                     return false;
849             } else {
850                 if (!this.sourceAttachmentRootPath.equals(otherPath))
851                     return false;
852             }
853
854             if (!equalPatterns(this.inclusionPatterns, otherEntry.getInclusionPatterns()))
855                 return false;
856             if (!equalPatterns(this.exclusionPatterns, otherEntry.getExclusionPatterns()))
857                 return false;
858             AccessRuleSet otherRuleSet = otherEntry.getAccessRuleSet();
859             if (getAccessRuleSet() != null) {
860                 if (!getAccessRuleSet().equals(otherRuleSet))
861                     return false;
862             } else if (otherRuleSet != null)
863                 return false;
864             if (this.combineAccessRules != otherEntry.combineAccessRules())
865                 return false;
866             otherPath = otherEntry.getOutputLocation();
867             if (this.specificOutputLocation == null) {
868                 if (otherPath != null)
869                     return false;
870             } else {
871                 if (!this.specificOutputLocation.equals(otherPath))
872                     return false;
873             }
874             if (!equalAttributes(this.extraAttributes, otherEntry.getExtraAttributes()))
875                 return false;
876             return true;
877         } else {
878             return false;
879         }
880     }
881
882     private static boolean equalAttributes(IClasspathAttribute[] firstAttributes, IClasspathAttribute[] secondAttributes) {
883         if (firstAttributes != secondAttributes){
884             if (firstAttributes == null) return false;
885             int length = firstAttributes.length;
886             if (secondAttributes == null || secondAttributes.length != length)
887                 return false;
888             for (int i = 0; i < length; i++) {
889                 if (!firstAttributes[i].equals(secondAttributes[i]))
890                     return false;
891             }
892         }
893         return true;
894     }
895
896     private static boolean equalPatterns(IPath[] firstPatterns, IPath[] secondPatterns) {
897         if (firstPatterns != secondPatterns){
898             if (firstPatterns == null) return false;
899             int length = firstPatterns.length;
900             if (secondPatterns == null || secondPatterns.length != length)
901                 return false;
902             for (int i = 0; i < length; i++) {
903                 // compare toStrings instead of IPaths
904
// since IPath.equals is specified to ignore trailing separators
905
if (!firstPatterns[i].toString().equals(secondPatterns[i].toString()))
906                     return false;
907             }
908         }
909         return true;
910     }
911
912     /**
913      * @see IClasspathEntry#getAccessRules()
914      */

915     public IAccessRule[] getAccessRules() {
916         if (this.accessRuleSet == null) return NO_ACCESS_RULES;
917         AccessRule[] rules = this.accessRuleSet.getAccessRules();
918         int length = rules.length;
919         if (length == 0) return NO_ACCESS_RULES;
920         IAccessRule[] result = new IAccessRule[length];
921         System.arraycopy(rules, 0, result, 0, length);
922         return result;
923     }
924
925     public AccessRuleSet getAccessRuleSet() {
926         return this.accessRuleSet;
927     }
928
929     /**
930      * @see IClasspathEntry
931      */

932     public int getContentKind() {
933         return this.contentKind;
934     }
935
936     /**
937      * @see IClasspathEntry
938      */

939     public int getEntryKind() {
940         return this.entryKind;
941     }
942
943     /**
944      * @see IClasspathEntry#getExclusionPatterns()
945      */

946     public IPath[] getExclusionPatterns() {
947         return this.exclusionPatterns;
948     }
949
950     public IClasspathAttribute[] getExtraAttributes() {
951         return this.extraAttributes;
952     }
953
954     private String JavaDoc[] getMessageTemplates() {
955         JavaModelManager manager = JavaModelManager.getJavaModelManager();
956         String JavaDoc [] result = new String JavaDoc[AccessRuleSet.MESSAGE_TEMPLATES_LENGTH];
957         if (this.entryKind == CPE_PROJECT || this.entryKind == CPE_SOURCE) { // can be remote source entry when reconciling
958
result[0] = manager.intern(Messages.bind(
959                 org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_project,
960                 new String JavaDoc[] {"{0}", getPath().segment(0)})); //$NON-NLS-1$
961
result[1] = manager.intern(Messages.bind(
962                     org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_constructor_project,
963                     new String JavaDoc[] {"{0}", getPath().segment(0)})); //$NON-NLS-1$
964
result[2] = manager.intern(Messages.bind(
965                     org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_method_project,
966                     new String JavaDoc[] {"{0}", "{1}", getPath().segment(0)})); //$NON-NLS-1$ //$NON-NLS-2$
967
result[3] = manager.intern(Messages.bind(
968                     org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_field_project,
969                     new String JavaDoc[] {"{0}", "{1}", getPath().segment(0)})); //$NON-NLS-1$ //$NON-NLS-2$
970
} else {
971             IPath libPath = getPath();
972             Object JavaDoc target = JavaModel.getTarget(ResourcesPlugin.getWorkspace().getRoot(), libPath, false);
973             String JavaDoc pathString;
974             if (target instanceof java.io.File JavaDoc)
975                 pathString = libPath.toOSString();
976             else
977                 pathString = libPath.makeRelative().toString();
978             result[0] = manager.intern(Messages.bind(
979                 org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_library,
980                 new String JavaDoc[] {"{0}", pathString})); //$NON-NLS-1$
981
result[1] = manager.intern(Messages.bind(
982                     org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_constructor_library,
983                     new String JavaDoc[] {"{0}", pathString})); //$NON-NLS-1$
984
result[2] = manager.intern(Messages.bind(
985                     org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_method_library,
986                     new String JavaDoc[] {"{0}", "{1}", pathString})); //$NON-NLS-1$ //$NON-NLS-2$
987
result[3] = manager.intern(Messages.bind(
988                     org.eclipse.jdt.internal.core.util.Messages.restrictedAccess_field_library,
989                     new String JavaDoc[] {"{0}", "{1}", pathString})); //$NON-NLS-1$ //$NON-NLS-2$
990
}
991         return result;
992     }
993
994     /**
995      * @see IClasspathEntry#getExclusionPatterns()
996      */

997     public IPath[] getInclusionPatterns() {
998         return this.inclusionPatterns;
999     }
1000
1001    /**
1002     * @see IClasspathEntry#getOutputLocation()
1003     */

1004    public IPath getOutputLocation() {
1005        return this.specificOutputLocation;
1006    }
1007
1008    /**
1009     * @see IClasspathEntry
1010     */

1011    public IPath getPath() {
1012        return this.path;
1013    }
1014
1015    /**
1016     * @see IClasspathEntry
1017     */

1018    public IPath getSourceAttachmentPath() {
1019        return this.sourceAttachmentPath;
1020    }
1021
1022    /**
1023     * @see IClasspathEntry
1024     */

1025    public IPath getSourceAttachmentRootPath() {
1026        return this.sourceAttachmentRootPath;
1027    }
1028
1029    /**
1030     * Returns the hash code for this classpath entry
1031     */

1032    public int hashCode() {
1033        return this.path.hashCode();
1034    }
1035
1036    /**
1037     * @see IClasspathEntry#isExported()
1038     */

1039    public boolean isExported() {
1040        return this.isExported;
1041    }
1042
1043    public boolean isOptional() {
1044        for (int i = 0, length = this.extraAttributes.length; i < length; i++) {
1045            IClasspathAttribute attribute = this.extraAttributes[i];
1046            if (IClasspathAttribute.OPTIONAL.equals(attribute.getName()) && "true".equals(attribute.getValue())) //$NON-NLS-1$
1047
return true;
1048        }
1049        return false;
1050    }
1051
1052    /**
1053     * Returns the kind of a <code>PackageFragmentRoot</code> from its <code>String</code> form.
1054     */

1055    static int kindFromString(String JavaDoc kindStr) {
1056
1057        if (kindStr.equalsIgnoreCase("prj")) //$NON-NLS-1$
1058
return IClasspathEntry.CPE_PROJECT;
1059        if (kindStr.equalsIgnoreCase("var")) //$NON-NLS-1$
1060
return IClasspathEntry.CPE_VARIABLE;
1061        if (kindStr.equalsIgnoreCase("con")) //$NON-NLS-1$
1062
return IClasspathEntry.CPE_CONTAINER;
1063        if (kindStr.equalsIgnoreCase("src")) //$NON-NLS-1$
1064
return IClasspathEntry.CPE_SOURCE;
1065        if (kindStr.equalsIgnoreCase("lib")) //$NON-NLS-1$
1066
return IClasspathEntry.CPE_LIBRARY;
1067        if (kindStr.equalsIgnoreCase("output")) //$NON-NLS-1$
1068
return ClasspathEntry.K_OUTPUT;
1069        return -1;
1070    }
1071
1072    /**
1073     * Returns a <code>String</code> for the kind of a class path entry.
1074     */

1075    static String JavaDoc kindToString(int kind) {
1076
1077        switch (kind) {
1078            case IClasspathEntry.CPE_PROJECT :
1079                return "src"; // backward compatibility //$NON-NLS-1$
1080
case IClasspathEntry.CPE_SOURCE :
1081                return "src"; //$NON-NLS-1$
1082
case IClasspathEntry.CPE_LIBRARY :
1083                return "lib"; //$NON-NLS-1$
1084
case IClasspathEntry.CPE_VARIABLE :
1085                return "var"; //$NON-NLS-1$
1086
case IClasspathEntry.CPE_CONTAINER :
1087                return "con"; //$NON-NLS-1$
1088
case ClasspathEntry.K_OUTPUT :
1089                return "output"; //$NON-NLS-1$
1090
default :
1091                return "unknown"; //$NON-NLS-1$
1092
}
1093    }
1094
1095    /*
1096     * Backward compatibility: only accessible and non-accessible files are suported.
1097     */

1098    public static IAccessRule[] getAccessRules(IPath[] accessibleFiles, IPath[] nonAccessibleFiles) {
1099        int accessibleFilesLength = accessibleFiles == null ? 0 : accessibleFiles.length;
1100        int nonAccessibleFilesLength = nonAccessibleFiles == null ? 0 : nonAccessibleFiles.length;
1101        int length = accessibleFilesLength + nonAccessibleFilesLength;
1102        if (length == 0) return null;
1103        IAccessRule[] accessRules = new IAccessRule[length];
1104        for (int i = 0; i < accessibleFilesLength; i++) {
1105            accessRules[i] = JavaCore.newAccessRule(accessibleFiles[i], IAccessRule.K_ACCESSIBLE);
1106        }
1107        for (int i = 0; i < nonAccessibleFilesLength; i++) {
1108            accessRules[accessibleFilesLength + i] = JavaCore.newAccessRule(nonAccessibleFiles[i], IAccessRule.K_NON_ACCESSIBLE);
1109        }
1110        return accessRules;
1111    }
1112
1113    /**
1114     * Returns a printable representation of this classpath entry.
1115     */

1116    public String JavaDoc toString() {
1117        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1118        buffer.append(String.valueOf(getPath()));
1119        buffer.append('[');
1120        switch (getEntryKind()) {
1121            case IClasspathEntry.CPE_LIBRARY :
1122                buffer.append("CPE_LIBRARY"); //$NON-NLS-1$
1123
break;
1124            case IClasspathEntry.CPE_PROJECT :
1125                buffer.append("CPE_PROJECT"); //$NON-NLS-1$
1126
break;
1127            case IClasspathEntry.CPE_SOURCE :
1128                buffer.append("CPE_SOURCE"); //$NON-NLS-1$
1129
break;
1130            case IClasspathEntry.CPE_VARIABLE :
1131                buffer.append("CPE_VARIABLE"); //$NON-NLS-1$
1132
break;
1133            case IClasspathEntry.CPE_CONTAINER :
1134                buffer.append("CPE_CONTAINER"); //$NON-NLS-1$
1135
break;
1136        }
1137        buffer.append("]["); //$NON-NLS-1$
1138
switch (getContentKind()) {
1139            case IPackageFragmentRoot.K_BINARY :
1140                buffer.append("K_BINARY"); //$NON-NLS-1$
1141
break;
1142            case IPackageFragmentRoot.K_SOURCE :
1143                buffer.append("K_SOURCE"); //$NON-NLS-1$
1144
break;
1145            case ClasspathEntry.K_OUTPUT :
1146                buffer.append("K_OUTPUT"); //$NON-NLS-1$
1147
break;
1148        }
1149        buffer.append(']');
1150        if (getSourceAttachmentPath() != null) {
1151            buffer.append("[sourcePath:"); //$NON-NLS-1$
1152
buffer.append(getSourceAttachmentPath());
1153            buffer.append(']');
1154        }
1155        if (getSourceAttachmentRootPath() != null) {
1156            buffer.append("[rootPath:"); //$NON-NLS-1$
1157
buffer.append(getSourceAttachmentRootPath());
1158            buffer.append(']');
1159        }
1160        buffer.append("[isExported:"); //$NON-NLS-1$
1161
buffer.append(this.isExported);
1162        buffer.append(']');
1163        IPath[] patterns = this.inclusionPatterns;
1164        int length;
1165        if ((length = patterns == null ? 0 : patterns.length) > 0) {
1166            buffer.append("[including:"); //$NON-NLS-1$
1167
for (int i = 0; i < length; i++) {
1168                buffer.append(patterns[i]);
1169                if (i != length-1) {
1170                    buffer.append('|');
1171                }
1172            }
1173            buffer.append(']');
1174        }
1175        patterns = this.exclusionPatterns;
1176        if ((length = patterns == null ? 0 : patterns.length) > 0) {
1177            buffer.append("[excluding:"); //$NON-NLS-1$
1178
for (int i = 0; i < length; i++) {
1179                buffer.append(patterns[i]);
1180                if (i != length-1) {
1181                    buffer.append('|');
1182                }
1183            }
1184            buffer.append(']');
1185        }
1186        if (this.accessRuleSet != null) {
1187            buffer.append('[');
1188            buffer.append(this.accessRuleSet.toString(false/*on one line*/));
1189            buffer.append(']');
1190        }
1191        if (this.entryKind == CPE_PROJECT) {
1192            buffer.append("[combine access rules:"); //$NON-NLS-1$
1193
buffer.append(this.combineAccessRules);
1194            buffer.append(']');
1195        }
1196        if (getOutputLocation() != null) {
1197            buffer.append("[output:"); //$NON-NLS-1$
1198
buffer.append(getOutputLocation());
1199            buffer.append(']');
1200        }
1201        if ((length = this.extraAttributes == null ? 0 : this.extraAttributes.length) > 0) {
1202            buffer.append("[attributes:"); //$NON-NLS-1$
1203
for (int i = 0; i < length; i++) {
1204                buffer.append(this.extraAttributes[i]);
1205                if (i != length-1) {
1206                    buffer.append(',');
1207                }
1208            }
1209            buffer.append(']');
1210        }
1211        return buffer.toString();
1212    }
1213
1214    /**
1215     * Answers an ID which is used to distinguish entries during package
1216     * fragment root computations
1217     */

1218    public String JavaDoc rootID(){
1219
1220        if (this.rootID == null) {
1221            switch(this.entryKind){
1222                case IClasspathEntry.CPE_LIBRARY :
1223                    this.rootID = "[LIB]"+this.path; //$NON-NLS-1$
1224
break;
1225                case IClasspathEntry.CPE_PROJECT :
1226                    this.rootID = "[PRJ]"+this.path; //$NON-NLS-1$
1227
break;
1228                case IClasspathEntry.CPE_SOURCE :
1229                    this.rootID = "[SRC]"+this.path; //$NON-NLS-1$
1230
break;
1231                case IClasspathEntry.CPE_VARIABLE :
1232                    this.rootID = "[VAR]"+this.path; //$NON-NLS-1$
1233
break;
1234                case IClasspathEntry.CPE_CONTAINER :
1235                    this.rootID = "[CON]"+this.path; //$NON-NLS-1$
1236
break;
1237                default :
1238                    this.rootID = ""; //$NON-NLS-1$
1239
break;
1240            }
1241        }
1242        return this.rootID;
1243    }
1244
1245    /**
1246     * @see IClasspathEntry
1247     * @deprecated
1248     */

1249    public IClasspathEntry getResolvedEntry() {
1250
1251        return JavaCore.getResolvedClasspathEntry(this);
1252    }
1253
1254    /**
1255     * Validate a given classpath and output location for a project, using the following rules:
1256     * <ul>
1257     * <li> Classpath entries cannot collide with each other; that is, all entry paths must be unique.
1258     * <li> The project output location path cannot be null, must be absolute and located inside the project.
1259     * <li> Specific output locations (specified on source entries) can be null, if not they must be located inside the project,
1260     * <li> A project entry cannot refer to itself directly (that is, a project cannot prerequisite itself).
1261     * <li> Classpath entries or output locations cannot coincidate or be nested in each other, except for the following scenarii listed below:
1262     * <ul><li> A source folder can coincidate with its own output location, in which case this output can then contain library archives.
1263     * However, a specific output location cannot coincidate with any library or a distinct source folder than the one referring to it. </li>
1264     * <li> A source/library folder can be nested in any source folder as long as the nested folder is excluded from the enclosing one. </li>
1265     * <li> An output location can be nested in a source folder, if the source folder coincidates with the project itself, or if the output
1266     * location is excluded from the source folder. </li>
1267     * </ul>
1268     * </ul>
1269     *
1270     * Note that the classpath entries are not validated automatically. Only bound variables or containers are considered
1271     * in the checking process (this allows to perform a consistency check on a classpath which has references to
1272     * yet non existing projects, folders, ...).
1273     * <p>
1274     * This validation is intended to anticipate classpath issues prior to assigning it to a project. In particular, it will automatically
1275     * be performed during the classpath setting operation (if validation fails, the classpath setting will not complete).
1276     * <p>
1277     * @param javaProject the given java project
1278     * @param rawClasspath a given classpath
1279     * @param projectOutputLocation a given output location
1280     * @return a status object with code <code>IStatus.OK</code> if
1281     * the given classpath and output location are compatible, otherwise a status
1282     * object indicating what is wrong with the classpath or output location
1283     */

1284    public static IJavaModelStatus validateClasspath(IJavaProject javaProject, IClasspathEntry[] rawClasspath, IPath projectOutputLocation) {
1285
1286        IProject project = javaProject.getProject();
1287        IPath projectPath= project.getFullPath();
1288        String JavaDoc projectName = javaProject.getElementName();
1289
1290        /* validate output location */
1291        if (projectOutputLocation == null) {
1292            return new JavaModelStatus(IJavaModelStatusConstants.NULL_PATH);
1293        }
1294        if (projectOutputLocation.isAbsolute()) {
1295            if (!projectPath.isPrefixOf(projectOutputLocation)) {
1296                return new JavaModelStatus(IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT, javaProject, projectOutputLocation.toString());
1297            }
1298        } else {
1299            return new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH, projectOutputLocation);
1300        }
1301
1302        boolean hasSource = false;
1303        boolean hasLibFolder = false;
1304
1305
1306        // tolerate null path, it will be reset to default
1307
if (rawClasspath == null)
1308            return JavaModelStatus.VERIFIED_OK;
1309
1310        // retrieve resolved classpath
1311
IClasspathEntry[] classpath;
1312        try {
1313            classpath = ((JavaProject)javaProject).resolveClasspath(rawClasspath);
1314        } catch(JavaModelException e){
1315            return e.getJavaModelStatus();
1316        }
1317        int length = classpath.length;
1318
1319        int outputCount = 1;
1320        IPath[] outputLocations = new IPath[length+1];
1321        boolean[] allowNestingInOutputLocations = new boolean[length+1];
1322        outputLocations[0] = projectOutputLocation;
1323
1324        // retrieve and check output locations
1325
IPath potentialNestedOutput = null; // for error reporting purpose
1326
int sourceEntryCount = 0;
1327        boolean disableExclusionPatterns = JavaCore.DISABLED.equals(javaProject.getOption(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, true));
1328        boolean disableCustomOutputLocations = JavaCore.DISABLED.equals(javaProject.getOption(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, true));
1329
1330        for (int i = 0 ; i < length; i++) {
1331            IClasspathEntry resolvedEntry = classpath[i];
1332            if (disableExclusionPatterns &&
1333                    ((resolvedEntry.getInclusionPatterns() != null && resolvedEntry.getInclusionPatterns().length > 0)
1334                    || (resolvedEntry.getExclusionPatterns() != null && resolvedEntry.getExclusionPatterns().length > 0))) {
1335                return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS, javaProject, resolvedEntry.getPath());
1336            }
1337            switch(resolvedEntry.getEntryKind()){
1338                case IClasspathEntry.CPE_SOURCE :
1339                    sourceEntryCount++;
1340
1341                    IPath customOutput;
1342                    if ((customOutput = resolvedEntry.getOutputLocation()) != null) {
1343
1344                        if (disableCustomOutputLocations) {
1345                            return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS, javaProject, resolvedEntry.getPath());
1346                        }
1347                        // ensure custom output is in project
1348
if (customOutput.isAbsolute()) {
1349                            if (!javaProject.getPath().isPrefixOf(customOutput)) {
1350                                return new JavaModelStatus(IJavaModelStatusConstants.PATH_OUTSIDE_PROJECT, javaProject, customOutput.toString());
1351                            }
1352                        } else {
1353                            return new JavaModelStatus(IJavaModelStatusConstants.RELATIVE_PATH, customOutput);
1354                        }
1355
1356                        // ensure custom output doesn't conflict with other outputs
1357
// check exact match
1358
if (Util.indexOfMatchingPath(customOutput, outputLocations, outputCount) != -1) {
1359                            continue; // already found
1360
}
1361                        // accumulate all outputs, will check nesting once all available (to handle ordering issues)
1362
outputLocations[outputCount++] = customOutput;
1363                    }
1364            }
1365        }
1366        // check nesting across output locations
1367
for (int i = 1 /*no check for default output*/ ; i < outputCount; i++) {
1368            IPath customOutput = outputLocations[i];
1369            int index;
1370            // check nesting
1371
if ((index = Util.indexOfEnclosingPath(customOutput, outputLocations, outputCount)) != -1 && index != i) {
1372                if (index == 0) {
1373                    // custom output is nested in project's output: need to check if all source entries have a custom
1374
// output before complaining
1375
if (potentialNestedOutput == null) potentialNestedOutput = customOutput;
1376                } else {
1377                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestOutputInOutput, new String JavaDoc[] {customOutput.makeRelative().toString(), outputLocations[index].makeRelative().toString()}));
1378                }
1379            }
1380        }
1381        // allow custom output nesting in project's output if all source entries have a custom output
1382
if (sourceEntryCount <= outputCount-1) {
1383            allowNestingInOutputLocations[0] = true;
1384        } else if (potentialNestedOutput != null) {
1385            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestOutputInOutput, new String JavaDoc[] {potentialNestedOutput.makeRelative().toString(), outputLocations[0].makeRelative().toString()}));
1386        }
1387
1388        for (int i = 0 ; i < length; i++) {
1389            IClasspathEntry resolvedEntry = classpath[i];
1390            IPath path = resolvedEntry.getPath();
1391            int index;
1392            switch(resolvedEntry.getEntryKind()){
1393
1394                case IClasspathEntry.CPE_SOURCE :
1395                    hasSource = true;
1396                    if ((index = Util.indexOfMatchingPath(path, outputLocations, outputCount)) != -1){
1397                        allowNestingInOutputLocations[index] = true;
1398                    }
1399                    break;
1400
1401                case IClasspathEntry.CPE_LIBRARY:
1402                    hasLibFolder |= !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(path.lastSegment());
1403                    if ((index = Util.indexOfMatchingPath(path, outputLocations, outputCount)) != -1){
1404                        allowNestingInOutputLocations[index] = true;
1405                    }
1406                    break;
1407            }
1408        }
1409        if (!hasSource && !hasLibFolder) { // if no source and no lib folder, then allowed
1410
for (int i = 0; i < outputCount; i++) allowNestingInOutputLocations[i] = true;
1411        }
1412
1413        HashSet JavaDoc pathes = new HashSet JavaDoc(length);
1414
1415        // check all entries
1416
for (int i = 0 ; i < length; i++) {
1417            IClasspathEntry entry = classpath[i];
1418            if (entry == null) continue;
1419            IPath entryPath = entry.getPath();
1420            int kind = entry.getEntryKind();
1421
1422            // Build some common strings for status message
1423
boolean isProjectRelative = projectName.equals(entryPath.segment(0));
1424            String JavaDoc entryPathMsg = isProjectRelative ? entryPath.removeFirstSegments(1).toString() : entryPath.makeRelative().toString();
1425
1426            // complain if duplicate path
1427
if (!pathes.add(entryPath)){
1428                return new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, Messages.bind(Messages.classpath_duplicateEntryPath, new String JavaDoc[] {entryPathMsg, projectName}));
1429            }
1430            // no further check if entry coincidates with project or output location
1431
if (entryPath.equals(projectPath)){
1432                // complain if self-referring project entry
1433
if (kind == IClasspathEntry.CPE_PROJECT){
1434                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, Messages.bind(Messages.classpath_cannotReferToItself, entryPath.makeRelative().toString()));
1435                }
1436                // tolerate nesting output in src if SRC==prj
1437
continue;
1438            }
1439
1440            // allow nesting source entries in each other as long as the outer entry excludes the inner one
1441
if (kind == IClasspathEntry.CPE_SOURCE
1442                    || (kind == IClasspathEntry.CPE_LIBRARY && !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(entryPath.lastSegment()))){
1443                for (int j = 0; j < classpath.length; j++){
1444                    IClasspathEntry otherEntry = classpath[j];
1445                    if (otherEntry == null) continue;
1446                    int otherKind = otherEntry.getEntryKind();
1447                    IPath otherPath = otherEntry.getPath();
1448                    if (entry != otherEntry
1449                        && (otherKind == IClasspathEntry.CPE_SOURCE
1450                                || (otherKind == IClasspathEntry.CPE_LIBRARY
1451                                        && !org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(otherPath.lastSegment())))){
1452                        char[][] inclusionPatterns, exclusionPatterns;
1453                        if (otherPath.isPrefixOf(entryPath)
1454                                && !otherPath.equals(entryPath)
1455                                && !Util.isExcluded(entryPath.append("*"), inclusionPatterns = ((ClasspathEntry)otherEntry).fullInclusionPatternChars(), exclusionPatterns = ((ClasspathEntry)otherEntry).fullExclusionPatternChars(), false)) { //$NON-NLS-1$
1456
String JavaDoc exclusionPattern = entryPath.removeFirstSegments(otherPath.segmentCount()).segment(0);
1457                            if (Util.isExcluded(entryPath, inclusionPatterns, exclusionPatterns, false)) {
1458                                return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_mustEndWithSlash, new String JavaDoc[] {exclusionPattern, entryPath.makeRelative().toString()}));
1459                            } else {
1460                                if (otherKind == IClasspathEntry.CPE_SOURCE) {
1461                                    exclusionPattern += '/';
1462                                    if (!disableExclusionPatterns) {
1463                                        return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestEntryInEntry, new String JavaDoc[] {entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString(), exclusionPattern}));
1464                                    } else {
1465                                        return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestEntryInEntryNoExclusion, new String JavaDoc[] {entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString(), exclusionPattern}));
1466                                    }
1467                                } else {
1468                                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestEntryInLibrary, new String JavaDoc[] {entryPath.makeRelative().toString(), otherEntry.getPath().makeRelative().toString()}));
1469                                }
1470                            }
1471                        }
1472                    }
1473                }
1474            }
1475
1476            // prevent nesting output location inside entry unless enclosing is a source entry which explicitly exclude the output location
1477
char[][] inclusionPatterns = ((ClasspathEntry)entry).fullInclusionPatternChars();
1478            char[][] exclusionPatterns = ((ClasspathEntry)entry).fullExclusionPatternChars();
1479            for (int j = 0; j < outputCount; j++){
1480                IPath currentOutput = outputLocations[j];
1481                if (entryPath.equals(currentOutput)) continue;
1482                if (entryPath.isPrefixOf(currentOutput)) {
1483                    if (kind != IClasspathEntry.CPE_SOURCE || !Util.isExcluded(currentOutput, inclusionPatterns, exclusionPatterns, true)) {
1484                        return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestOutputInEntry, new String JavaDoc[] {currentOutput.makeRelative().toString(), entryPath.makeRelative().toString()}));
1485                    }
1486                }
1487            }
1488
1489            // prevent nesting entry inside output location - when distinct from project or a source folder
1490
for (int j = 0; j < outputCount; j++){
1491                if (allowNestingInOutputLocations[j]) continue;
1492                IPath currentOutput = outputLocations[j];
1493                if (currentOutput.isPrefixOf(entryPath)) {
1494                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotNestEntryInOutput, new String JavaDoc[] {entryPath.makeRelative().toString(), currentOutput.makeRelative().toString()}));
1495                }
1496            }
1497        }
1498        // ensure that no specific output is coincidating with another source folder (only allowed if matching current source folder)
1499
// 36465 - for 2.0 backward compatibility, only check specific output locations (the default can still coincidate)
1500
// perform one separate iteration so as to not take precedence over previously checked scenarii (in particular should
1501
// diagnose nesting source folder issue before this one, for example, [src]"Project/", [src]"Project/source/" and output="Project/" should
1502
// first complain about missing exclusion pattern
1503
for (int i = 0 ; i < length; i++) {
1504            IClasspathEntry entry = classpath[i];
1505            if (entry == null) continue;
1506            IPath entryPath = entry.getPath();
1507            int kind = entry.getEntryKind();
1508
1509            // Build some common strings for status message
1510
boolean isProjectRelative = projectName.equals(entryPath.segment(0));
1511            String JavaDoc entryPathMsg = isProjectRelative ? entryPath.removeFirstSegments(1).toString() : entryPath.makeRelative().toString();
1512
1513            if (kind == IClasspathEntry.CPE_SOURCE) {
1514                IPath output = entry.getOutputLocation();
1515                if (output == null) continue; // 36465 - for 2.0 backward compatibility, only check specific output locations (the default can still coincidate)
1516
// if (output == null) output = projectOutputLocation; // if no specific output, still need to check using default output (this line would check default output)
1517
for (int j = 0; j < length; j++) {
1518                    IClasspathEntry otherEntry = classpath[j];
1519                    if (otherEntry == entry) continue;
1520
1521                    // Build some common strings for status message
1522
boolean opStartsWithProject = projectName.equals(otherEntry.getPath().segment(0));
1523                    String JavaDoc otherPathMsg = opStartsWithProject ? otherEntry.getPath().removeFirstSegments(1).toString() : otherEntry.getPath().makeRelative().toString();
1524
1525                    switch (otherEntry.getEntryKind()) {
1526                        case IClasspathEntry.CPE_SOURCE :
1527                            if (otherEntry.getPath().equals(output)) {
1528                                return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotUseDistinctSourceFolderAsOutput, new String JavaDoc[] {entryPathMsg, otherPathMsg, projectName}));
1529                            }
1530                            break;
1531                        case IClasspathEntry.CPE_LIBRARY :
1532                            if (otherEntry.getPath().equals(output)) {
1533                                return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_cannotUseLibraryAsOutput, new String JavaDoc[] {entryPathMsg, otherPathMsg, projectName}));
1534                            }
1535                    }
1536                }
1537            }
1538        }
1539        return JavaModelStatus.VERIFIED_OK;
1540    }
1541
1542    /**
1543     * Returns a Java model status describing the problem related to this classpath entry if any,
1544     * a status object with code <code>IStatus.OK</code> if the entry is fine (that is, if the
1545     * given classpath entry denotes a valid element to be referenced onto a classpath).
1546     *
1547     * @param project the given java project
1548     * @param entry the given classpath entry
1549     * @param checkSourceAttachment a flag to determine if source attachement should be checked
1550     * @param recurseInContainers flag indicating whether validation should be applied to container entries recursively
1551     * @return a java model status describing the problem related to this classpath entry if any, a status object with code <code>IStatus.OK</code> if the entry is fine
1552     */

1553    public static IJavaModelStatus validateClasspathEntry(IJavaProject project, IClasspathEntry entry, boolean checkSourceAttachment, boolean recurseInContainers){
1554
1555        IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
1556        IPath path = entry.getPath();
1557
1558        // Build some common strings for status message
1559
String JavaDoc projectName = project.getElementName();
1560        boolean pathStartsWithProject = projectName.equals(path.segment(0));
1561        String JavaDoc entryPathMsg = pathStartsWithProject ? path.removeFirstSegments(1).makeRelative().toString() : path.toString();
1562
1563        switch(entry.getEntryKind()){
1564
1565            // container entry check
1566
case IClasspathEntry.CPE_CONTAINER :
1567                if (path.segmentCount() >= 1){
1568                    try {
1569                        IClasspathContainer container = JavaModelManager.getJavaModelManager().getClasspathContainer(path, project);
1570                        // container retrieval is performing validation check on container entry kinds.
1571
if (container == null){
1572                            return new JavaModelStatus(IJavaModelStatusConstants.CP_CONTAINER_PATH_UNBOUND, project, path);
1573                        } else if (container == JavaModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) {
1574                            // Validate extra attributes
1575
IClasspathAttribute[] extraAttributes = entry.getExtraAttributes();
1576                            if (extraAttributes != null) {
1577                                int length = extraAttributes.length;
1578                                HashSet JavaDoc set = new HashSet JavaDoc(length);
1579                                for (int i=0; i<length; i++) {
1580                                    String JavaDoc attName = extraAttributes[i].getName();
1581                                    if (!set.add(attName)) {
1582                                        return new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, Messages.bind(Messages.classpath_duplicateEntryExtraAttribute, new String JavaDoc[] {attName, entryPathMsg, projectName}));
1583                                    }
1584                                }
1585                            }
1586                            // don't create a marker if initialization is in progress (case of cp initialization batching)
1587
return JavaModelStatus.VERIFIED_OK;
1588                        }
1589                        IClasspathEntry[] containerEntries = container.getClasspathEntries();
1590                        if (containerEntries != null){
1591                            for (int i = 0, length = containerEntries.length; i < length; i++){
1592                                IClasspathEntry containerEntry = containerEntries[i];
1593                                int kind = containerEntry == null ? 0 : containerEntry.getEntryKind();
1594                                if (containerEntry == null
1595                                    || kind == IClasspathEntry.CPE_SOURCE
1596                                    || kind == IClasspathEntry.CPE_VARIABLE
1597                                    || kind == IClasspathEntry.CPE_CONTAINER){
1598                                        String JavaDoc description = container.getDescription();
1599                                        if (description == null) description = path.makeRelative().toString();
1600                                        return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CP_CONTAINER_ENTRY, project, path);
1601                                }
1602                                if (recurseInContainers) {
1603                                    IJavaModelStatus containerEntryStatus = validateClasspathEntry(project, containerEntry, checkSourceAttachment, recurseInContainers);
1604                                    if (!containerEntryStatus.isOK()){
1605                                        return containerEntryStatus;
1606                                    }
1607                                }
1608                            }
1609                        }
1610                    } catch(JavaModelException e){
1611                        return new JavaModelStatus(e);
1612                    }
1613                } else {
1614                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalContainerPath, new String JavaDoc[] {entryPathMsg, projectName}));
1615                }
1616                break;
1617
1618            // variable entry check
1619
case IClasspathEntry.CPE_VARIABLE :
1620                if (path.segmentCount() >= 1){
1621                    try {
1622                        entry = JavaCore.getResolvedClasspathEntry(entry);
1623                    } catch (AssertionFailedException e) {
1624                        // Catch the assertion failure and throw java model exception instead
1625
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=55992
1626
return new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, e.getMessage());
1627                    }
1628                    if (entry == null){
1629                        return new JavaModelStatus(IJavaModelStatusConstants.CP_VARIABLE_PATH_UNBOUND, project, path);
1630                    }
1631
1632                    // get validation status
1633
IJavaModelStatus status = validateClasspathEntry(project, entry, checkSourceAttachment, recurseInContainers);
1634                    if (!status.isOK()) return status;
1635
1636                    // return deprecation status if any
1637
String JavaDoc variableName = path.segment(0);
1638                    String JavaDoc deprecatedMessage = JavaCore.getClasspathVariableDeprecationMessage(variableName);
1639                    if (deprecatedMessage != null) {
1640                        return new JavaModelStatus(IStatus.WARNING, IJavaModelStatusConstants.DEPRECATED_VARIABLE, project, path, deprecatedMessage);
1641                    }
1642                    return status;
1643                } else {
1644                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalVariablePath, new String JavaDoc[] {entryPathMsg, projectName}));
1645                }
1646
1647            // library entry check
1648
case IClasspathEntry.CPE_LIBRARY :
1649                if (path.isAbsolute() && !path.isEmpty()) {
1650                    IPath sourceAttachment = entry.getSourceAttachmentPath();
1651                    Object JavaDoc target = JavaModel.getTarget(workspaceRoot, path, true);
1652                    if (target != null && !JavaCore.IGNORE.equals(project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true))) {
1653                        long projectTargetJDK = CompilerOptions.versionToJdkLevel(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
1654                        long libraryJDK = Util.getJdkLevel(target);
1655                        if (libraryJDK != 0 && libraryJDK > projectTargetJDK) {
1656                            return new JavaModelStatus(IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL, project, path, CompilerOptions.versionFromJdkLevel(libraryJDK));
1657                        }
1658                    }
1659                    if (target instanceof IResource){
1660                        IResource resolvedResource = (IResource) target;
1661                        switch(resolvedResource.getType()){
1662                            case IResource.FILE :
1663                                if (org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(resolvedResource.getName())) {
1664                                    if (checkSourceAttachment
1665                                        && sourceAttachment != null
1666                                        && !sourceAttachment.isEmpty()
1667                                        && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
1668                                        return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String JavaDoc [] {sourceAttachment.toString(), path.toString(), projectName}));
1669                                    }
1670                                } else {
1671                                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalLibraryArchive, new String JavaDoc[] {entryPathMsg, projectName}));
1672                                }
1673                                break;
1674                            case IResource.FOLDER : // internal binary folder
1675
if (checkSourceAttachment
1676                                    && sourceAttachment != null
1677                                    && !sourceAttachment.isEmpty()
1678                                    && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
1679                                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String JavaDoc [] {sourceAttachment.toString(), path.toString(), projectName}));
1680                                }
1681                        }
1682                    } else if (target instanceof File JavaDoc){
1683                        File JavaDoc file = JavaModel.getFile(target);
1684                        if (file == null) {
1685                            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalExternalFolder, new String JavaDoc[] {path.toOSString(), projectName}));
1686                        } else if (!org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(file.getName())) {
1687                            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalLibraryArchive, (new String JavaDoc[] {path.toOSString(), projectName})));
1688                        } else if (checkSourceAttachment
1689                                && sourceAttachment != null
1690                                && !sourceAttachment.isEmpty()
1691                                && JavaModel.getTarget(workspaceRoot, sourceAttachment, true) == null){
1692                                return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceAttachment, new String JavaDoc [] {sourceAttachment.toString(), path.toOSString(), projectName}));
1693                        }
1694                    } else {
1695                        boolean isExternal = path.getDevice() != null || !workspaceRoot.getProject(path.segment(0)).exists();
1696                        if (isExternal) {
1697                            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundLibrary, new String JavaDoc[] {path.toOSString(), projectName}));
1698                        } else {
1699                            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundLibrary, new String JavaDoc[] {entryPathMsg, projectName}));
1700                        }
1701                    }
1702                } else {
1703                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalLibraryPath, new String JavaDoc[] {entryPathMsg, projectName}));
1704                }
1705                break;
1706
1707            // project entry check
1708
case IClasspathEntry.CPE_PROJECT :
1709                if (path.isAbsolute() && path.segmentCount() == 1) {
1710                    IProject prereqProjectRsc = workspaceRoot.getProject(path.segment(0));
1711                    IJavaProject prereqProject = JavaCore.create(prereqProjectRsc);
1712                    try {
1713                        if (!prereqProjectRsc.exists() || !prereqProjectRsc.hasNature(JavaCore.NATURE_ID)){
1714                            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundProject, new String JavaDoc[] {path.segment(0), projectName}));
1715                        }
1716                        if (!prereqProjectRsc.isOpen()){
1717                            return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_closedProject, new String JavaDoc[] {path.segment(0)}));
1718                        }
1719                        if (!JavaCore.IGNORE.equals(project.getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true))) {
1720                            long projectTargetJDK = CompilerOptions.versionToJdkLevel(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
1721                            long prereqProjectTargetJDK = CompilerOptions.versionToJdkLevel(prereqProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true));
1722                            if (prereqProjectTargetJDK > projectTargetJDK) {
1723                                return new JavaModelStatus(IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL, project, path, CompilerOptions.versionFromJdkLevel(prereqProjectTargetJDK));
1724                            }
1725                        }
1726                    } catch (CoreException e){
1727                        return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundProject, new String JavaDoc[] {path.segment(0), projectName}));
1728                    }
1729                } else {
1730                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalProjectPath, new String JavaDoc[] {path.toString(), projectName}));
1731                }
1732                break;
1733
1734            // project source folder
1735
case IClasspathEntry.CPE_SOURCE :
1736                if (((entry.getInclusionPatterns() != null && entry.getInclusionPatterns().length > 0)
1737                            || (entry.getExclusionPatterns() != null && entry.getExclusionPatterns().length > 0))
1738                        && JavaCore.DISABLED.equals(project.getOption(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, true))) {
1739                    return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_EXCLUSION_PATTERNS, project, path);
1740                }
1741                if (entry.getOutputLocation() != null && JavaCore.DISABLED.equals(project.getOption(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, true))) {
1742                    return new JavaModelStatus(IJavaModelStatusConstants.DISABLED_CP_MULTIPLE_OUTPUT_LOCATIONS, project, path);
1743                }
1744                if (path.isAbsolute() && !path.isEmpty()) {
1745                    IPath projectPath= project.getProject().getFullPath();
1746                    if (!projectPath.isPrefixOf(path) || JavaModel.getTarget(workspaceRoot, path, true) == null){
1747                        return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_unboundSourceFolder, new String JavaDoc[] {entryPathMsg, projectName}));
1748                    }
1749                } else {
1750                    return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CLASSPATH, Messages.bind(Messages.classpath_illegalSourceFolderPath, new String JavaDoc[] {entryPathMsg, projectName}));
1751                }
1752                break;
1753        }
1754
1755        // Validate extra attributes
1756
IClasspathAttribute[] extraAttributes = entry.getExtraAttributes();
1757        if (extraAttributes != null) {
1758            int length = extraAttributes.length;
1759            HashSet JavaDoc set = new HashSet JavaDoc(length);
1760            for (int i=0; i<length; i++) {
1761                String JavaDoc attName = extraAttributes[i].getName();
1762                if (!set.add(attName)) {
1763                    return new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, Messages.bind(Messages.classpath_duplicateEntryExtraAttribute, new String JavaDoc[] {attName, entryPathMsg, projectName}));
1764                }
1765            }
1766        }
1767
1768        return JavaModelStatus.VERIFIED_OK;
1769    }
1770}
1771
Popular Tags