KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > spi > project > support > ant > ReferenceHelper


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.spi.project.support.ant;
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.net.URI JavaDoc;
25 import java.net.URISyntaxException JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Arrays JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Properties JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.TreeSet JavaDoc;
36 import java.util.regex.Matcher JavaDoc;
37 import java.util.regex.Pattern JavaDoc;
38 import org.netbeans.api.project.Project;
39 import org.netbeans.api.project.ProjectManager;
40 import org.netbeans.api.project.ProjectUtils;
41 import org.netbeans.api.project.ant.AntArtifact;
42 import org.netbeans.api.project.ant.AntArtifactQuery;
43 import org.netbeans.api.queries.CollocationQuery;
44 import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton;
45 import org.netbeans.modules.project.ant.Util;
46 import org.netbeans.spi.project.AuxiliaryConfiguration;
47 import org.netbeans.spi.project.SubprojectProvider;
48 import org.openide.ErrorManager;
49 import org.openide.filesystems.FileObject;
50 import org.openide.filesystems.FileUtil;
51 import org.openide.util.Mutex;
52 import org.openide.util.NbCollections;
53 import org.openide.xml.XMLUtil;
54 import org.w3c.dom.Document JavaDoc;
55 import org.w3c.dom.Element JavaDoc;
56 import org.w3c.dom.NodeList JavaDoc;
57
58 // XXX need a method to update non-key data in references e.g. during projectOpened()
59

60 /**
61  * Helps manage inter-project references.
62  * Normally you would create an instance of this object and keep it in your
63  * project object in order to support {@link SubprojectProvider} and various
64  * operations that change settings which might refer to build artifacts from
65  * other projects: e.g. when changing the classpath for a Java-based project
66  * you would want to use this helper to scan potential classpath entries for
67  * JARs coming from other projects that you would like to be able to build
68  * as dependencies before your project is built.
69  * <p>
70  * You probably only need the higher-level methods such as {@link #addReference}
71  * and {@link #removeReference(String,String)}; the lower-level methods such as {@link #addRawReference}
72  * are provided for completeness, but typical client code should not need them.
73  * <p>
74  * Only deals with references needed to support build artifacts coming from
75  * foreign projects. If for some reason you wish to store other kinds of
76  * references to foreign projects, you do not need this class; just store
77  * them however you wish, and be sure to create an appropriate {@link SubprojectProvider}.
78  * <p>
79  * Modification methods (add, remove) mark the project as modified but do not save it.
80  * @author Jesse Glick
81  */

82 public final class ReferenceHelper {
83     
84     /**
85      * XML element name used to store references in <code>project.xml</code>.
86      */

87     static final String JavaDoc REFS_NAME = "references"; // NOI18N
88

89     /**
90      * XML element name used to store one reference in <code>project.xml</code>.
91      */

92     static final String JavaDoc REF_NAME = "reference"; // NOI18N
93

94     /**
95      * XML namespace used to store references in <code>project.xml</code>.
96      */

97     static final String JavaDoc REFS_NS = "http://www.netbeans.org/ns/ant-project-references/1"; // NOI18N
98

99     /**
100      * Newer version of {@link #REFS_NS} supporting Properties and with changed semantics of <script>.
101      */

102     static final String JavaDoc REFS_NS2 = "http://www.netbeans.org/ns/ant-project-references/2"; // NOI18N
103

104     /** Set of property names which values can be used as additional base
105      * directories. */

106     private Set JavaDoc<String JavaDoc> extraBaseDirectories = new HashSet JavaDoc<String JavaDoc>();
107     
108     private final AntProjectHelper h;
109     final PropertyEvaluator eval;
110     private final AuxiliaryConfiguration aux;
111
112     /**
113      * Create a new reference helper.
114      * It needs an {@link AntProjectHelper} object in order to update references
115      * in <code>project.xml</code>,
116      * as well as set project or private properties referring to the locations
117      * of foreign projects on disk.
118      * <p>
119      * The property evaluator may be used in {@link #getForeignFileReferenceAsArtifact},
120      * {@link ReferenceHelper.RawReference#toAntArtifact}, or
121      * {@link #createSubprojectProvider}. Typically this would
122      * be {@link AntProjectHelper#getStandardPropertyEvaluator}. You can substitute
123      * a custom evaluator but be warned that this helper class assumes that
124      * {@link AntProjectHelper#PROJECT_PROPERTIES_PATH} and {@link AntProjectHelper#PRIVATE_PROPERTIES_PATH}
125      * have their customary meanings; specifically that they are both used when evaluating
126      * properties (such as the location of a foreign project) and that private properties
127      * can override public properties.
128      * @param helper an Ant project helper object representing this project's configuration
129      * @param aux an auxiliary configuration provider needed to store references
130      * @param eval a property evaluator
131      */

132     public ReferenceHelper(AntProjectHelper helper, AuxiliaryConfiguration aux, PropertyEvaluator eval) {
133         h = helper;
134         this.aux = aux;
135         this.eval = eval;
136     }
137
138     /**
139      * Load <references> from project.xml.
140      * @return can return null if there are no references stored yet
141      */

142     private Element JavaDoc loadReferences() {
143         assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess();
144         Element JavaDoc references = aux.getConfigurationFragment(REFS_NAME, REFS_NS2, true);
145         if (references == null) {
146             references = aux.getConfigurationFragment(REFS_NAME, REFS_NS, true);
147         }
148         return references;
149     }
150
151     /**
152      * Store <references> to project.xml (i.e. to memory and mark project modified).
153      */

154     private void storeReferences(Element JavaDoc references) {
155         assert ProjectManager.mutex().isWriteAccess();
156         assert references != null && references.getLocalName().equals(REFS_NAME) &&
157             (REFS_NS.equals(references.getNamespaceURI()) || REFS_NS2.equals(references.getNamespaceURI()));
158         aux.putConfigurationFragment(references, true);
159     }
160     
161     private void removeOldReferences() {
162         assert ProjectManager.mutex().isWriteAccess();
163         aux.removeConfigurationFragment(REFS_NAME, REFS_NS, true);
164     }
165     
166     /**
167      * Add a reference to an artifact coming from a foreign project.
168      * <p>
169      * For more info see {@link #addReference(AntArtifact, URI)}.
170      * @param artifact the artifact to add
171      * @return true if a reference or some property was actually added or modified,
172      * false if everything already existed and was not modified
173      * @throws IllegalArgumentException if the artifact is not associated with a project
174      * @deprecated to add reference use {@link #addReference(AntArtifact, URI)};
175      * to check whether reference exist or not use {@link #isReferenced(AntArtifact, URI)}.
176      * This method creates reference for the first artifact location only.
177      */

178     @Deprecated JavaDoc
179     public boolean addReference(final AntArtifact artifact) throws IllegalArgumentException JavaDoc {
180         Object JavaDoc ret[] = addReference0(artifact, artifact.getArtifactLocations()[0]);
181         return ((Boolean JavaDoc)ret[0]).booleanValue();
182     }
183
184     // @return array of two elements: [Boolean - any modification, String - reference]
185
private Object JavaDoc[] addReference0(final AntArtifact artifact, final URI JavaDoc location) throws IllegalArgumentException JavaDoc {
186         return ProjectManager.mutex().writeAccess(new Mutex.Action<Object JavaDoc[]>() {
187             public Object JavaDoc[] run() {
188                 int index = findLocationIndex(artifact, location);
189                 Project forProj = artifact.getProject();
190                 if (forProj == null) {
191                     throw new IllegalArgumentException JavaDoc("No project associated with " + artifact); // NOI18N
192
}
193                 // Set up the raw reference.
194
File JavaDoc forProjDir = FileUtil.toFile(forProj.getProjectDirectory());
195                 assert forProjDir != null : forProj.getProjectDirectory();
196                 String JavaDoc projName = getUsableReferenceID(ProjectUtils.getInformation(forProj).getName());
197                 String JavaDoc forProjName = findReferenceID(projName, "project.", forProjDir.getAbsolutePath());
198                 if (forProjName == null) {
199                     forProjName = generateUniqueID(projName, "project.", forProjDir.getAbsolutePath());
200                 }
201                 RawReference ref;
202                 File JavaDoc scriptFile = artifact.getScriptLocation();
203                 if (canUseVersion10(artifact, forProjDir)) {
204                     String JavaDoc rel = PropertyUtils.relativizeFile(forProjDir, scriptFile);
205                     URI JavaDoc scriptLocation;
206                     try {
207                         scriptLocation = new URI JavaDoc(null, null, rel, null);
208                     } catch (URISyntaxException JavaDoc ex) {
209                         scriptLocation = forProjDir.toURI().relativize(scriptFile.toURI());
210                     }
211                     ref = new RawReference(forProjName, artifact.getType(), scriptLocation, artifact.getTargetName(), artifact.getCleanTargetName(), artifact.getID());
212                 } else {
213                     String JavaDoc scriptLocation;
214                     if (scriptFile.getAbsolutePath().startsWith(forProjDir.getAbsolutePath())) {
215                         String JavaDoc rel = PropertyUtils.relativizeFile(forProjDir, scriptFile);
216                         assert rel != null : "Relativization must succeed for files: "+forProjDir+ " "+scriptFile;
217                         scriptLocation = "${project."+forProjName+"}/"+rel;
218                     } else {
219                         scriptLocation = "build.script.reference." + forProjName;
220                         setPathProperty(forProjDir, scriptFile, scriptLocation);
221                         scriptLocation = "${"+scriptLocation+"}";
222                     }
223                     ref = new RawReference(forProjName, artifact.getType(), scriptLocation,
224                         artifact.getTargetName(), artifact.getCleanTargetName(),
225                         artifact.getID(), artifact.getProperties());
226                 }
227                 boolean success = addRawReference0(ref);
228                 // Set up ${project.whatever}.
229
FileObject myProjDirFO = AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory();
230                 File JavaDoc myProjDir = FileUtil.toFile(myProjDirFO);
231                 if (setPathProperty(myProjDir, forProjDir, "project." + forProjName)) {
232                     success = true;
233                 }
234                 // Set up ${reference.whatever.whatever}.
235
String JavaDoc propertiesFile;
236                 String JavaDoc forProjPathProp = "project." + forProjName; // NOI18N
237
URI JavaDoc artFile = location;
238                 String JavaDoc refPath;
239                 if (artFile.isAbsolute()) {
240                     refPath = new File JavaDoc(artFile).getAbsolutePath();
241                     propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH;
242                 } else {
243                     refPath = "${" + forProjPathProp + "}/" + artFile.getPath(); // NOI18N
244
propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH;
245                 }
246                 EditableProperties props = h.getProperties(propertiesFile);
247                 String JavaDoc refPathProp = "reference." + forProjName + '.' + getUsableReferenceID(artifact.getID()); // NOI18N
248
if (index > 0) {
249                     refPathProp += "."+index;
250                 }
251                 if (!refPath.equals(props.getProperty(refPathProp))) {
252                     props.put(refPathProp, refPath);
253                     h.putProperties(propertiesFile, props);
254                     success = true;
255                 }
256                 return new Object JavaDoc[] {success, "${" + refPathProp + "}"}; // NOI18N
257
}
258         });
259     }
260     
261     private int findLocationIndex(final AntArtifact artifact, final URI JavaDoc location) throws IllegalArgumentException JavaDoc {
262         if (location == null) {
263             throw new IllegalArgumentException JavaDoc("location cannot be null");
264         }
265         URI JavaDoc uris[] = artifact.getArtifactLocations();
266         for (int i=0; i<uris.length; i++) {
267             if (uris[i].equals(location)) {
268                 return i;
269             }
270         }
271         throw new IllegalArgumentException JavaDoc("location ("+location+") must be in AntArtifact's locations ("+artifact+")");
272     }
273
274     /**
275      * Test whether the artifact can be stored as /1 artifact or not.
276      */

277     private static boolean canUseVersion10(AntArtifact aa, File JavaDoc projectDirectory) {
278         // is there multiple outputs?
279
if (aa.getArtifactLocations().length > 1) {
280             return false;
281         }
282         // has some properties?
283
if (aa.getProperties().keySet().size() > 0) {
284             return false;
285         }
286         // does Ant script lies under project directory?
287
if (!aa.getScriptLocation().getAbsolutePath().startsWith(projectDirectory.getAbsolutePath())) {
288             return false;
289         }
290         return true;
291     }
292
293     /**
294      * Helper method which checks collocation status of two files and based on
295      * that it will in private or project properties file set up property with
296      * the given name and with absolute or relative path value.
297      * @return was there any change or not
298      */

299     private boolean setPathProperty(File JavaDoc base, File JavaDoc path, String JavaDoc propertyName) {
300         String JavaDoc[] values;
301         String JavaDoc[] propertiesFiles;
302         
303         String JavaDoc relativePath = relativizeFileToExtraBaseFolders(path);
304         // try relativize against external base dirs
305
if (relativePath != null) {
306             propertiesFiles = new String JavaDoc[] {
307                 AntProjectHelper.PROJECT_PROPERTIES_PATH
308             };
309             values = new String JavaDoc[] {
310                 relativePath
311             };
312         }
313         else if (CollocationQuery.areCollocated(base, path)) {
314             // Fine, using a relative path to subproject.
315
relativePath = PropertyUtils.relativizeFile(base, path);
316             assert relativePath != null : "These dirs are not really collocated: " + base + " & " + path;
317             values = new String JavaDoc[] {
318                 relativePath,
319                 path.getAbsolutePath()
320             };
321             propertiesFiles = new String JavaDoc[] {
322                 AntProjectHelper.PROJECT_PROPERTIES_PATH,
323                 AntProjectHelper.PRIVATE_PROPERTIES_PATH,
324             };
325         } else {
326             // use an absolute path.
327
propertiesFiles = new String JavaDoc[] {
328                 AntProjectHelper.PRIVATE_PROPERTIES_PATH
329             };
330             values = new String JavaDoc[] {
331                 path.getAbsolutePath()
332             };
333         }
334         
335         boolean metadataChanged = false;
336         for (int i=0; i<propertiesFiles.length; i++) {
337             EditableProperties props = h.getProperties(propertiesFiles[i]);
338             if (!values[i].equals(props.getProperty(propertyName))) {
339                 props.put(propertyName, values[i]);
340                 h.putProperties(propertiesFiles[i], props);
341                 metadataChanged = true;
342             }
343         }
344         
345         if (propertiesFiles.length == 1) {
346             // check presence of this property in opposite property file and
347
// remove it if necessary
348
String JavaDoc propertiesFile = (propertiesFiles[0] == AntProjectHelper.PROJECT_PROPERTIES_PATH ?
349                 AntProjectHelper.PRIVATE_PROPERTIES_PATH : AntProjectHelper.PROJECT_PROPERTIES_PATH);
350             EditableProperties props = h.getProperties(propertiesFile);
351             if (props.remove(propertyName) != null) {
352                 h.putProperties(propertiesFile, props);
353             }
354         }
355         return metadataChanged;
356     }
357     
358     /**
359      * Add a reference to an artifact's location coming from a foreign project.
360      * <p>
361      * Records the name of the foreign project.
362      * Normally the foreign project name is that project's code name,
363      * but it may be uniquified if that name is already taken to refer
364      * to a different project with the same code name.
365      * <p>
366      * Adds a project property if necessary to refer to its location of the foreign
367      * project - a shared property if the foreign project
368      * is {@link CollocationQuery collocated} with this one, else a private property.
369      * This property is named <samp>project.<i>foreignProjectName</i></samp>.
370      * Example: <samp>project.mylib=../mylib</samp>
371      * <p>
372      * Adds a project property to refer to the artifact's location.
373      * This property is named <samp>reference.<i>foreignProjectName</i>.<i>targetName</i></samp>
374      * and will use <samp>${project.<i>foreignProjectName</i>}</samp> and be a shared
375      * property - unless the artifact location is an absolute URI, in which case the property
376      * will also be private.
377      * Example: <samp>reference.mylib.jar=${project.mylib}/dist/mylib.jar</samp>
378      * <p>
379      * Also records the artifact type, (relative) script path, and build and
380      * clean target names.
381      * <p>
382      * If the reference already exists (keyed by foreign project object
383      * and target name), nothing is done, unless some other field (script location,
384      * clean target name, or artifact type) needed to be updated, in which case
385      * the new information replaces the old. Similarly, the artifact location
386      * property is updated if necessary.
387      * <p>
388      * Acquires write access.
389      * @param artifact the artifact to add
390      * @param location the artifact's location to create reference to
391      * @return name of reference which was created or already existed
392      * @throws IllegalArgumentException if the artifact is not associated with a project
393      * or if the location is not artifact's location
394      * @since 1.5
395      */

396     public String JavaDoc addReference(final AntArtifact artifact, URI JavaDoc location) throws IllegalArgumentException JavaDoc {
397         Object JavaDoc ret[] = addReference0(artifact, location);
398         return (String JavaDoc)ret[1];
399     }
400     
401     /**
402      * Tests whether reference for artifact's location was already created by
403      * {@link #addReference(AntArtifact, URI)} for this project or not. This
404      * method returns false also in case when reference exist but needs to be
405      * updated.
406      * <p>
407      * Acquires read access.
408      * @param artifact the artifact to add
409      * @param location the artifact's location to create reference to
410      * @return true if already referenced
411      * @throws IllegalArgumentException if the artifact is not associated with a project
412      * or if the location is not artifact's location
413      * @since 1.5
414      */

415     public boolean isReferenced(final AntArtifact artifact, final URI JavaDoc location) throws IllegalArgumentException JavaDoc {
416         return ProjectManager.mutex().readAccess(new Mutex.Action<Boolean JavaDoc>() {
417             public Boolean JavaDoc run() {
418                 int index = findLocationIndex(artifact, location);
419                 Project forProj = artifact.getProject();
420                 if (forProj == null) {
421                     throw new IllegalArgumentException JavaDoc("No project associated with " + artifact); // NOI18N
422
}
423                 File JavaDoc forProjDir = FileUtil.toFile(forProj.getProjectDirectory());
424                 assert forProjDir != null : forProj.getProjectDirectory();
425                 String JavaDoc projName = getUsableReferenceID(ProjectUtils.getInformation(forProj).getName());
426                 String JavaDoc forProjName = findReferenceID(projName, "project.", forProjDir.getAbsolutePath());
427                 if (forProjName == null) {
428                     return false;
429                 }
430                 RawReference ref = getRawReference(forProjName, getUsableReferenceID(artifact.getID()));
431                 if (ref == null) {
432                     return false;
433                 }
434                 File JavaDoc script = h.resolveFile(eval.evaluate(ref.getScriptLocationValue()));
435                 if (!artifact.getType().equals(ref.getArtifactType()) ||
436                         !artifact.getID().equals(ref.getID()) ||
437                         !artifact.getScriptLocation().equals(script) ||
438                         !artifact.getProperties().equals(ref.getProperties()) ||
439                         !artifact.getTargetName().equals(ref.getTargetName()) ||
440                         !artifact.getCleanTargetName().equals(ref.getCleanTargetName())) {
441                     return false;
442                 }
443                 
444                 String JavaDoc reference = "reference." + forProjName + '.' + getUsableReferenceID(artifact.getID()); // NOI18N
445
if (index > 0) {
446                     reference += "."+index;
447                 }
448                 return eval.getProperty(reference) != null;
449             }
450         });
451     }
452     
453     /**
454      * Add a raw reference to a foreign project artifact.
455      * Does not check if such a project already exists; does not create a project
456      * property to refer to it; does not do any backreference usage notifications.
457      * <p>
458      * If the reference already exists (keyed by foreign project name and target name),
459      * nothing is done, unless some other field (script location, clean target name,
460      * or artifact type) needed to be updated, in which case the new information
461      * replaces the old.
462      * <p>
463      * Note that since {@link RawReference} is just a descriptor, it is not guaranteed
464      * that after adding one {@link #getRawReferences} or {@link #getRawReference}
465      * would return the identical object.
466      * <p>
467      * Acquires write access.
468      * @param ref a raw reference descriptor
469      * @return true if a reference was actually added or modified,
470      * false if it already existed and was not modified
471      */

472     public boolean addRawReference(final RawReference ref) {
473         return ProjectManager.mutex().writeAccess(new Mutex.Action<Boolean JavaDoc>() {
474             public Boolean JavaDoc run() {
475                 try {
476                     return addRawReference0(ref);
477                 } catch (IllegalArgumentException JavaDoc e) {
478                     ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
479                     return false;
480                 }
481             }
482         });
483     }
484     
485     private boolean addRawReference0(final RawReference ref) throws IllegalArgumentException JavaDoc {
486         Element JavaDoc references = loadReferences();
487         if (references == null) {
488             references = XMLUtil.createDocument("ignore", null, null, null).createElementNS(ref.getNS(), REFS_NAME); // NOI18N
489
}
490         boolean modified = false;
491         if (references.getNamespaceURI().equals(REFS_NS) && ref.getNS().equals(REFS_NS2)) {
492             // upgrade all references to version /2 here:
493
references = upgradeTo20(references);
494             removeOldReferences();
495             modified = true;
496         } else if (references.getNamespaceURI().equals(REFS_NS2) && ref.getNS().equals(REFS_NS)) { // #91760
497
ref.upgrade();
498         }
499         modified |= updateRawReferenceElement(ref, references);
500         if (modified) {
501             storeReferences(references);
502         }
503         return modified;
504     }
505     
506     private Element JavaDoc upgradeTo20(Element JavaDoc references) {
507         Element JavaDoc references20 = XMLUtil.createDocument("ignore", null, null, null).createElementNS(REFS_NS2, REFS_NAME); // NOI18N
508
RawReference rr[] = getRawReferences(references);
509         for (int i=0; i<rr.length; i++) {
510             rr[i].upgrade();
511             updateRawReferenceElement(rr[i], references20);
512         }
513         return references20;
514     }
515     
516     private static boolean updateRawReferenceElement(RawReference ref, Element JavaDoc references) throws IllegalArgumentException JavaDoc {
517         // Linear search; always keeping references sorted first by foreign project
518
// name, then by target name.
519
Element JavaDoc nextRefEl = null;
520         Iterator JavaDoc<Element JavaDoc> it = Util.findSubElements(references).iterator();
521         while (it.hasNext()) {
522             Element JavaDoc testRefEl = it.next();
523             RawReference testRef = RawReference.create(testRefEl);
524             if (testRef.getForeignProjectName().compareTo(ref.getForeignProjectName()) > 0) {
525                 // gone too far, go back
526
nextRefEl = testRefEl;
527                 break;
528             }
529             if (testRef.getForeignProjectName().equals(ref.getForeignProjectName())) {
530                 if (testRef.getID().compareTo(ref.getID()) > 0) {
531                     // again, gone too far, go back
532
nextRefEl = testRefEl;
533                     break;
534                 }
535                 if (testRef.getID().equals(ref.getID())) {
536                     // Key match, check if it needs to be updated.
537
if (testRef.getArtifactType().equals(ref.getArtifactType()) &&
538                             testRef.getScriptLocationValue().equals(ref.getScriptLocationValue()) &&
539                             testRef.getProperties().equals(ref.getProperties()) &&
540                             testRef.getTargetName().equals(ref.getTargetName()) &&
541                             testRef.getCleanTargetName().equals(ref.getCleanTargetName())) {
542                         // Match on other fields. Return without changing anything.
543
return false;
544                     }
545                     // Something needs updating.
546
// Delete the old ref and set nextRef to the next item in line.
547
references.removeChild(testRefEl);
548                     if (it.hasNext()) {
549                         nextRefEl = it.next();
550                     } else {
551                         nextRefEl = null;
552                     }
553                     break;
554                 }
555             }
556         }
557         // Need to insert a new record before nextRef.
558
Element JavaDoc newRefEl = ref.toXml(references.getNamespaceURI(), references.getOwnerDocument());
559         // Note: OK if nextRefEl == null, that means insert as last child.
560
references.insertBefore(newRefEl, nextRefEl);
561         return true;
562     }
563     
564     /**
565      * Remove a reference to an artifact coming from a foreign project.
566      * <p>
567      * The property giving the location of the artifact is removed if it existed.
568      * <p>
569      * If this was the last reference to the foreign project, its location
570      * property is removed as well.
571      * <p>
572      * If the reference does not exist, nothing is done.
573      * <p>
574      * Acquires write access.
575      * @param foreignProjectName the local name of the foreign project
576      * (usually its code name)
577      * @param id the ID of the build artifact (usually build target name)
578      * @return true if a reference or some property was actually removed,
579      * false if the reference was not there and no property was removed
580      * @deprecated use {@link #destroyReference} instead; was unused anyway
581      */

582     @Deprecated JavaDoc
583     public boolean removeReference(final String JavaDoc foreignProjectName, final String JavaDoc id) {
584         return removeReference(foreignProjectName, id, false, null);
585     }
586     
587     /**
588      * Checks whether this is last reference and therefore the artifact can
589      * be removed from project.xml or not
590      */

591     private boolean isLastReference(String JavaDoc ref) {
592        Object JavaDoc ret[] = findArtifactAndLocation(ref);
593        if (ret[0] == null || ret[1] == null) {
594            return true;
595        }
596        AntArtifact aa = (AntArtifact)ret[0];
597        URI JavaDoc uri = (URI JavaDoc)ret[1];
598        URI JavaDoc uris[] = aa.getArtifactLocations();
599        boolean lastReference = true;
600        // are there any other referenced jars or not:
601
for (int i=0; i<uris.length; i++) {
602            if (uris[i].equals(uri)) {
603                continue;
604            }
605            if (isReferenced(aa, uris[i])) {
606                lastReference = false;
607                break;
608            }
609        }
610        return lastReference;
611     }
612     
613     private boolean removeReference(final String JavaDoc foreignProjectName, final String JavaDoc id, final boolean escaped, final String JavaDoc reference) {
614         return ProjectManager.mutex().writeAccess(new Mutex.Action<Boolean JavaDoc>() {
615             public Boolean JavaDoc run() {
616                 boolean success = false;
617                 try {
618                     if (isLastReference("${"+reference+"}")) {
619                         success = removeRawReference0(foreignProjectName, id, escaped);
620                     }
621                 } catch (IllegalArgumentException JavaDoc e) {
622                     ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
623                     return false;
624                 }
625                 // Note: try to delete obsoleted properties from both project.properties
626
// and private.properties, just in case.
627
String JavaDoc[] PROPS_PATHS = {
628                     AntProjectHelper.PROJECT_PROPERTIES_PATH,
629                     AntProjectHelper.PRIVATE_PROPERTIES_PATH,
630                 };
631                 // if raw reference was removed then try to clean also project reference property:
632
if (success) {
633                     // Check whether there are any other references using foreignProjectName.
634
// If not, we can delete ${project.foreignProjectName}.
635
RawReference[] refs = new RawReference[0];
636                     Element JavaDoc references = loadReferences();
637                     if (references != null) {
638                         refs = getRawReferences(references);
639                     }
640                     boolean deleteProjProp = true;
641                     for (int i = 0; i < refs.length; i++) {
642                         if (refs[i].getForeignProjectName().equals(foreignProjectName)) {
643                             deleteProjProp = false;
644                             break;
645                         }
646                     }
647                     if (deleteProjProp) {
648                         String JavaDoc projProp = "project." + foreignProjectName; // NOI18N
649
for (int i = 0; i < PROPS_PATHS.length; i++) {
650                             EditableProperties props = h.getProperties(PROPS_PATHS[i]);
651                             if (props.containsKey(projProp)) {
652                                 props.remove(projProp);
653                                 h.putProperties(PROPS_PATHS[i], props);
654                                 success = true;
655                             }
656                         }
657                     }
658                 }
659                 
660                 String JavaDoc refProp = reference;
661                 if (refProp == null) {
662                     refProp = "reference." + foreignProjectName + '.' + getUsableReferenceID(id); // NOI18N
663
}
664                 // remove also build script property if exist any:
665
String JavaDoc buildScriptProperty = "build.script.reference." + foreignProjectName;
666                 for (String JavaDoc path : PROPS_PATHS) {
667                     EditableProperties props = h.getProperties(path);
668                     if (props.containsKey(refProp)) {
669                         props.remove(refProp);
670                         h.putProperties(path, props);
671                         success = true;
672                     }
673                     if (props.containsKey(buildScriptProperty)) {
674                         props.remove(buildScriptProperty);
675                         h.putProperties(path, props);
676                         success = true;
677                     }
678                 }
679                 return success;
680             }
681         });
682     }
683     
684     /**
685      * Remove reference to a file.
686      * <p>
687      * If the reference does not exist, nothing is done.
688      * <p>
689      * Acquires write access.
690      * @param fileReference file reference as created by
691      * {@link #createForeignFileReference(File, String)}
692      * @return true if the reference was actually removed; otherwise false
693      * @deprecated use {@link #destroyReference} instead; was unused anyway
694      */

695     @Deprecated JavaDoc
696     public boolean removeReference(final String JavaDoc fileReference) {
697         return removeFileReference(fileReference);
698     }
699     
700     private boolean removeFileReference(final String JavaDoc fileReference) {
701         return ProjectManager.mutex().writeAccess(new Mutex.Action<Boolean JavaDoc>() {
702             public Boolean JavaDoc run() {
703                 boolean success = false;
704                 // Note: try to delete obsoleted properties from both project.properties
705
// and private.properties, just in case.
706
String JavaDoc[] PROPS_PATHS = {
707                     AntProjectHelper.PROJECT_PROPERTIES_PATH,
708                     AntProjectHelper.PRIVATE_PROPERTIES_PATH,
709                 };
710                 String JavaDoc refProp = fileReference;
711                 if (refProp.startsWith("${") && refProp.endsWith("}")) {
712                     refProp = refProp.substring(2, refProp.length()-1);
713                 }
714                 for (String JavaDoc path : PROPS_PATHS) {
715                     EditableProperties props = h.getProperties(path);
716                     if (props.containsKey(refProp)) {
717                         props.remove(refProp);
718                         h.putProperties(path, props);
719                         success = true;
720                     }
721                 }
722                 return success;
723             }
724         });
725     }
726     
727     /**
728      * Remove a raw reference to an artifact coming from a foreign project.
729      * Does not attempt to manipulate backreferences in the foreign project
730      * nor project properties.
731      * <p>
732      * If the reference does not exist, nothing is done.
733      * <p>
734      * Acquires write access.
735      * @param foreignProjectName the local name of the foreign project
736      * (usually its code name)
737      * @param id the ID of the build artifact (usually build target name)
738      * @return true if a reference was actually removed, false if it was not there
739      */

740     public boolean removeRawReference(final String JavaDoc foreignProjectName, final String JavaDoc id) {
741         return ProjectManager.mutex().writeAccess(new Mutex.Action<Boolean JavaDoc>() {
742             public Boolean JavaDoc run() {
743                 try {
744                     return removeRawReference0(foreignProjectName, id, false);
745                 } catch (IllegalArgumentException JavaDoc e) {
746                     ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
747                     return false;
748                 }
749             }
750         });
751     }
752     
753     private boolean removeRawReference0(final String JavaDoc foreignProjectName, final String JavaDoc id, boolean escaped) throws IllegalArgumentException JavaDoc {
754         Element JavaDoc references = loadReferences();
755         if (references == null) {
756             return false;
757         }
758         boolean success = removeRawReferenceElement(foreignProjectName, id, references, escaped);
759         if (success) {
760             storeReferences(references);
761         }
762         return success;
763     }
764     
765     private static boolean removeRawReferenceElement(String JavaDoc foreignProjectName, String JavaDoc id, Element JavaDoc references, boolean escaped) throws IllegalArgumentException JavaDoc {
766         // As with addRawReference, do a linear search through.
767
for (Element JavaDoc testRefEl : Util.findSubElements(references)) {
768             RawReference testRef = RawReference.create(testRefEl);
769             String JavaDoc refID = testRef.getID();
770             String JavaDoc refName = testRef.getForeignProjectName();
771             if (escaped) {
772                 refID = getUsableReferenceID(testRef.getID());
773                 refName = getUsableReferenceID(testRef.getForeignProjectName());
774             }
775             if (refName.compareTo(foreignProjectName) > 0) {
776                 // searched past it
777
return false;
778             }
779             if (refName.equals(foreignProjectName)) {
780                 if (refID.compareTo(id) > 0) {
781                     // again, searched past it
782
return false;
783                 }
784                 if (refID.equals(id)) {
785                     // Key match, remove it.
786
references.removeChild(testRefEl);
787                     return true;
788                 }
789             }
790         }
791         // Searched through to the end and did not find it.
792
return false;
793     }
794
795     /**
796      * Get a list of raw references from this project to others.
797      * If necessary, you may use {@link RawReference#toAntArtifact} to get
798      * live information from each reference, such as its associated project.
799      * <p>
800      * Acquires read access.
801      * @return a (possibly empty) list of raw references from this project
802      */

803     public RawReference[] getRawReferences() {
804         return ProjectManager.mutex().readAccess(new Mutex.Action<RawReference[]>() {
805             public RawReference[] run() {
806                 Element JavaDoc references = loadReferences();
807                 if (references != null) {
808                     try {
809                         return getRawReferences(references);
810                     } catch (IllegalArgumentException JavaDoc e) {
811                         ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
812                     }
813                 }
814                 return new RawReference[0];
815             }
816         });
817     }
818     
819     private static RawReference[] getRawReferences(Element JavaDoc references) throws IllegalArgumentException JavaDoc {
820         List JavaDoc<Element JavaDoc> subEls = Util.findSubElements(references);
821         List JavaDoc<RawReference> refs = new ArrayList JavaDoc<RawReference>(subEls.size());
822         for (Element JavaDoc subEl : subEls) {
823             refs.add(RawReference.create(subEl));
824         }
825         return refs.toArray(new RawReference[refs.size()]);
826     }
827     
828     /**
829      * Get a particular raw reference from this project to another.
830      * If necessary, you may use {@link RawReference#toAntArtifact} to get
831      * live information from each reference, such as its associated project.
832      * <p>
833      * Acquires read access.
834      * @param foreignProjectName the local name of the foreign project
835      * (usually its code name)
836      * @param id the ID of the build artifact (usually the build target name)
837      * @return the specified raw reference from this project,
838      * or null if none such could be found
839      */

840     public RawReference getRawReference(final String JavaDoc foreignProjectName, final String JavaDoc id) {
841         return getRawReference(foreignProjectName, id, false);
842     }
843     
844     // not private only to allow unit testing
845
RawReference getRawReference(final String JavaDoc foreignProjectName, final String JavaDoc id, final boolean escaped) {
846         return ProjectManager.mutex().readAccess(new Mutex.Action<RawReference>() {
847             public RawReference run() {
848                 Element JavaDoc references = loadReferences();
849                 if (references != null) {
850                     try {
851                         return getRawReference(foreignProjectName, id, references, escaped);
852                     } catch (IllegalArgumentException JavaDoc e) {
853                         ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
854                     }
855                 }
856                 return null;
857             }
858         });
859     }
860     
861     private static RawReference getRawReference(String JavaDoc foreignProjectName, String JavaDoc id, Element JavaDoc references, boolean escaped) throws IllegalArgumentException JavaDoc {
862         for (Element JavaDoc subEl : Util.findSubElements(references)) {
863             RawReference ref = RawReference.create(subEl);
864             String JavaDoc refID = ref.getID();
865             String JavaDoc refName = ref.getForeignProjectName();
866             if (escaped) {
867                 refID = getUsableReferenceID(ref.getID());
868                 refName = getUsableReferenceID(ref.getForeignProjectName());
869             }
870             if (refName.equals(foreignProjectName) && refID.equals(id)) {
871                 return ref;
872             }
873         }
874         return null;
875     }
876     
877     /**
878      * Create an Ant-interpretable string referring to a file on disk.
879      * If the file refers to a known Ant artifact according to
880      * {@link AntArtifactQuery#findArtifactFromFile}, of the expected type
881      * and associated with a particular project,
882      * the behavior is identical to {@link #createForeignFileReference(AntArtifact)}.
883      * Otherwise, a reference for the file is created. The file path will
884      * be relative in case {@link CollocationQuery#areCollocated} says that
885      * the file is collocated with this project's main directory, else it
886      * will be an absolute path.
887      * <p>
888      * Acquires write access.
889      * @param file a file to refer to (need not currently exist)
890      * @param expectedArtifactType the required {@link AntArtifact#getType}
891      * @return a string which can refer to that file somehow
892      */

893     public String JavaDoc createForeignFileReference(final File JavaDoc file, final String JavaDoc expectedArtifactType) {
894         if (!file.equals(FileUtil.normalizeFile(file))) {
895             throw new IllegalArgumentException JavaDoc("Parameter file was not "+ // NOI18N
896
"normalized. Was "+file+" instead of "+FileUtil.normalizeFile(file)); // NOI18N
897
}
898         return ProjectManager.mutex().writeAccess(new Mutex.Action<String JavaDoc>() {
899             public String JavaDoc run() {
900                 AntArtifact art = AntArtifactQuery.findArtifactFromFile(file);
901                 if (art != null && art.getType().equals(expectedArtifactType) && art.getProject() != null) {
902                     try {
903                         return createForeignFileReference(art);
904                     } catch (IllegalArgumentException JavaDoc iae) {
905                         throw new AssertionError JavaDoc(iae);
906                     }
907                 } else {
908                     String JavaDoc propertiesFile;
909                     String JavaDoc path;
910                     File JavaDoc myProjDir = FileUtil.toFile(AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory());
911                     String JavaDoc fileID = file.getName();
912                     // if the file is folder then add to ID string also parent folder name,
913
// i.e. if external source folder name is "src" the ID will
914
// be a bit more selfdescribing, e.g. project-src in case
915
// of ID for ant/project/src directory.
916
if (file.isDirectory() && file.getParentFile() != null) {
917                         fileID = file.getParentFile().getName()+"-"+file.getName();
918                     }
919                     fileID = PropertyUtils.getUsablePropertyName(fileID);
920                     String JavaDoc prop = findReferenceID(fileID, "file.reference.", file.getAbsolutePath()); // NOI18N
921
if (prop == null) {
922                         prop = generateUniqueID(fileID, "file.reference.", file.getAbsolutePath()); // NOI18N
923
}
924                     setPathProperty(myProjDir, file, "file.reference." + prop);
925                     return "${file.reference." + prop + '}'; // NOI18N
926
}
927             }
928         });
929     }
930     
931     /**
932      * Test whether file does not lie under an extra base folder and if it does
933      * then return string in form of "${extra.base}/remaining/path"; or null.
934      */

935     private String JavaDoc relativizeFileToExtraBaseFolders(File JavaDoc f) {
936         File JavaDoc base = FileUtil.toFile(h.getProjectDirectory());
937         String JavaDoc fileToRelativize = f.getAbsolutePath();
938         for (String JavaDoc prop : extraBaseDirectories) {
939             String JavaDoc path = eval.getProperty(prop);
940             File JavaDoc extraBase = PropertyUtils.resolveFile(base, path);
941             path = extraBase.getAbsolutePath();
942             if (!path.endsWith(File.separator)) {
943                 path += File.separator;
944             }
945             if (fileToRelativize.startsWith(path)) {
946                 return "${"+prop+"}/"+fileToRelativize.substring(path.length()).replace('\\', '/'); // NOI18N
947
}
948         }
949         return null;
950     }
951
952     /**
953      * Add extra folder which can be used as base directory (in addition to
954      * project base folder) for creating references. Duplicate property names
955      * are not allowed. Any newly created reference to a file lying under an
956      * extra base directory will be based on that property and will be stored in
957      * shared project properties.
958      * <p>Acquires write access.
959      * @param propertyName property name which value is path to folder which
960      * can be used as alternative project's base directory; cannot be null;
961      * property must exist
962      * @throws IllegalArgumentException if propertyName is null or such a
963      * property does not exist
964      * @since 1.4
965      */

966     public void addExtraBaseDirectory(final String JavaDoc propertyName) {
967         if (propertyName == null || eval.getProperty(propertyName) == null) {
968             throw new IllegalArgumentException JavaDoc("propertyName is null or such a property does not exist: "+propertyName); // NOI18N
969
}
970         ProjectManager.mutex().writeAccess(new Runnable JavaDoc() {
971                 public void run() {
972                     if (!extraBaseDirectories.add(propertyName)) {
973                         throw new IllegalArgumentException JavaDoc("Already extra base directory property: "+propertyName); // NOI18N
974
}
975                 }
976             });
977     }
978     
979     /**
980      * Remove extra base directory. The base directory property had to be added
981      * by {@link #addExtraBaseDirectory} method call. At the time when this
982      * method is called the property must still exist and must be valid. This
983      * method will replace all references of the extra base directory property
984      * with its current value and if needed it may move such a property from
985      * shared project properties into the private properties.
986      * <p>Acquires write access.
987      * @param propertyName property name which was added by
988      * {@link #addExtraBaseDirectory} method.
989      * @throws IllegalArgumentException if given property is not extra base
990      * directory
991      * @since 1.4
992      */

993     public void removeExtraBaseDirectory(final String JavaDoc propertyName) {
994         ProjectManager.mutex().writeAccess(new Runnable JavaDoc() {
995                 public void run() {
996                     if (!extraBaseDirectories.remove(propertyName)) {
997                         throw new IllegalArgumentException JavaDoc("Non-existing extra base directory property: "+propertyName); // NOI18N
998
}
999                     // substitute all references of removed extra base folder property with its value
1000
String JavaDoc tag = "${"+propertyName+"}"; // NOI18N
1001
// was extra base property defined in shared file or not:
1002
boolean shared = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH).containsKey(propertyName);
1003                    String JavaDoc value = eval.getProperty(propertyName);
1004                    EditableProperties propProj = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
1005                    EditableProperties propPriv = h.getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
1006                    boolean modifiedProj = false;
1007                    boolean modifiedPriv = false;
1008                    Iterator JavaDoc<Map.Entry JavaDoc<String JavaDoc,String JavaDoc>> it = propProj.entrySet().iterator();
1009                    while (it.hasNext()) {
1010                        Map.Entry JavaDoc<String JavaDoc,String JavaDoc> entry = it.next();
1011                        String JavaDoc val = entry.getValue();
1012                        int index;
1013                        if ((index = val.indexOf(tag)) != -1) {
1014                            val = val.substring(0, index) +value + val.substring(index+tag.length());
1015                            if (shared) {
1016                                // substitute extra base folder property with its value
1017
entry.setValue(val);
1018                            } else {
1019                                // move property to private properties file
1020
it.remove();
1021                                propPriv.put(entry.getKey(), val);
1022                                modifiedPriv = true;
1023                            }
1024                            modifiedProj = true;
1025                        }
1026                    }
1027                    if (modifiedProj) {
1028                        h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, propProj);
1029                    }
1030                    if (modifiedPriv) {
1031                        h.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH, propPriv);
1032                    }
1033                }
1034            });
1035    }
1036    
1037    /**
1038     * Find reference ID (e.g. something you can then pass to RawReference
1039     * as foreignProjectName) for the given property base name, prefix and path.
1040     * @param property project name or jar filename
1041     * @param prefix prefix used for reference, i.e. "project." for project
1042     * reference or "file.reference." for file reference
1043     * @param path absolute filename the reference points to
1044     * @return found reference ID or null
1045     */

1046    private String JavaDoc findReferenceID(String JavaDoc property, String JavaDoc prefix, String JavaDoc path) {
1047        Map JavaDoc<String JavaDoc,String JavaDoc> m = h.getStandardPropertyEvaluator().getProperties();
1048        for (Map.Entry JavaDoc<String JavaDoc,String JavaDoc> e : m.entrySet()) {
1049            String JavaDoc key = e.getKey();
1050            if (key.startsWith(prefix+property)) {
1051                String JavaDoc v = h.resolvePath(e.getValue());
1052                if (path.equals(v)) {
1053                    return key.substring(prefix.length());
1054                }
1055            }
1056        }
1057        return null;
1058    }
1059    
1060    /**
1061     * Generate unique reference ID for the given property base name, prefix
1062     * and path. See also {@link #findReferenceID(String, String, String)}.
1063     * @param property project name or jar filename
1064     * @param prefix prefix used for reference, i.e. "project." for project
1065     * reference or "file.reference." for file reference
1066     * @param path absolute filename the reference points to
1067     * @return generated unique reference ID
1068     */

1069    private String JavaDoc generateUniqueID(String JavaDoc property, String JavaDoc prefix, String JavaDoc value) {
1070        PropertyEvaluator pev = h.getStandardPropertyEvaluator();
1071        if (pev.getProperty(prefix+property) == null) {
1072            return property;
1073        }
1074        int i = 1;
1075        while (pev.getProperty(prefix+property+"-"+i) != null) {
1076            i++;
1077        }
1078        return property+"-"+i;
1079    }
1080    
1081    /**
1082     * Create an Ant-interpretable string referring to a known build artifact file.
1083     * Simply calls {@link #addReference} and returns an Ant string which will
1084     * refer to that artifact correctly.
1085     * <p>
1086     * Acquires write access.
1087     * @param artifact a known build artifact to refer to
1088     * @return a string which can refer to that artifact file somehow
1089     * @throws IllegalArgumentException if the artifact is not associated with a project
1090     * @deprecated use {@link #addReference(AntArtifact, URI)} instead
1091     */

1092    @Deprecated JavaDoc
1093    public String JavaDoc createForeignFileReference(AntArtifact artifact) throws IllegalArgumentException JavaDoc {
1094        Object JavaDoc ret[] = addReference0(artifact, artifact.getArtifactLocations()[0]);
1095        return (String JavaDoc)ret[1];
1096    }
1097
1098    /**
1099     * Project reference ID cannot contain dot character.
1100     * File reference can.
1101     */

1102    private static String JavaDoc getUsableReferenceID(String JavaDoc ID) {
1103        return PropertyUtils.getUsablePropertyName(ID).replace('.', '_');
1104    }
1105    
1106    
1107    private static final Pattern JavaDoc FOREIGN_FILE_REFERENCE = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\.([\\d&&[^.${}]]+)\\}"); // NOI18N
1108
private static final Pattern JavaDoc FOREIGN_FILE_REFERENCE_OLD = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\}"); // NOI18N
1109
private static final Pattern JavaDoc FOREIGN_PLAIN_FILE_REFERENCE = Pattern.compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N
1110

1111    /**
1112     * Try to find an <code>AntArtifact</code> object corresponding to a given
1113     * foreign file reference.
1114     * If the supplied string is not a recognized reference to a build
1115     * artifact, returns null.
1116     * <p>Acquires read access.
1117     * @param reference a reference string as present in an Ant property
1118     * @return a corresponding Ant artifact object if there is one, else null
1119     * @deprecated use {@link #findArtifactAndLocation} instead
1120     */

1121    @Deprecated JavaDoc
1122    public AntArtifact getForeignFileReferenceAsArtifact(final String JavaDoc reference) {
1123        Object JavaDoc ret[] = findArtifactAndLocation(reference);
1124        return (AntArtifact)ret[0];
1125    }
1126    
1127    /**
1128     * Try to find an <code>AntArtifact</code> object and location corresponding
1129     * to a given reference. If the supplied string is not a recognized
1130     * reference to a build artifact, returns an array of nulls.
1131     * <p>
1132     * Acquires read access.
1133     * @param reference a reference string as present in an Ant property and as
1134     * created by {@link #addReference(AntArtifact, URI)}
1135     * @return always returns array of two items. The items may be both null. First
1136     * one is instance of AntArtifact and second is instance of URI and is
1137     * AntArtifact's location
1138     * @since 1.5
1139     */

1140    public Object JavaDoc[] findArtifactAndLocation(final String JavaDoc reference) {
1141        return ProjectManager.mutex().readAccess(new Mutex.Action<Object JavaDoc[]>() {
1142            public Object JavaDoc[] run() {
1143                AntArtifact aa = null;
1144                Matcher JavaDoc m = FOREIGN_FILE_REFERENCE.matcher(reference);
1145                boolean matches = m.matches();
1146                int index = 0;
1147                if (!matches) {
1148                    m = FOREIGN_FILE_REFERENCE_OLD.matcher(reference);
1149                    matches = m.matches();
1150                } else {
1151                    try {
1152                        index = Integer.parseInt(m.group(3));
1153                    } catch (NumberFormatException JavaDoc ex) {
1154                        ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL,
1155                            "Could not parse reference ("+reference+") for the jar index. " + // NOI18N
1156
"Expected number: "+m.group(3)); // NOI18N
1157
matches = false;
1158                    }
1159                }
1160                if (matches) {
1161                    RawReference ref = getRawReference(m.group(1), m.group(2), true);
1162                    if (ref != null) {
1163                        aa = ref.toAntArtifact(ReferenceHelper.this);
1164                    }
1165                }
1166                if (aa == null) {
1167                    return new Object JavaDoc[] {null, null};
1168                }
1169                if (index >= aa.getArtifactLocations().length) {
1170                    // #55413: we no longer have that many items...treat it as dead.
1171
return new Object JavaDoc[] {null, null};
1172                }
1173                URI JavaDoc uri = aa.getArtifactLocations()[index];
1174                return new Object JavaDoc[] {aa, uri};
1175            }
1176        });
1177    }
1178    
1179    /**
1180     * Remove a reference to a foreign file from the project.
1181     * See {@link #destroyReference} for more information.
1182     * @param reference an Ant-interpretable foreign file reference as created e.g.
1183     * by {@link #createForeignFileReference(File,String)} or
1184     * by {@link #createForeignFileReference(AntArtifact)}
1185     * @deprecated use {@link #destroyReference} instead which does exactly
1186     * the same but has more appropriate name
1187     */

1188    @Deprecated JavaDoc
1189    public void destroyForeignFileReference(String JavaDoc reference) {
1190        destroyReference(reference);
1191    }
1192    
1193    /**
1194     * Remove a reference to a foreign file from the project.
1195     * If the passed string consists of an Ant property reference corresponding to
1196     * a known inter-project reference created by
1197     * {@link #addReference(AntArtifact, URI)} or file reference created by
1198     * {@link #createForeignFileReference(File, String)}, that reference is removed.
1199     * Since this would break any other identical foreign
1200     * file references present in the project, you should first confirm that this
1201     * reference was the last one of its kind (by string match).
1202     * <p>
1203     * If the passed string is anything else (i.e. a plain file path, relative or
1204     * absolute), nothing is done.
1205     * <p>
1206     * Acquires write access.
1207     * @param reference an Ant-interpretable foreign file reference as created e.g.
1208     * by {@link #createForeignFileReference(File,String)} or
1209     * by {@link #createForeignFileReference(AntArtifact)}
1210     * @return true if reference was really destroyed or not
1211     * @since 1.5
1212     */

1213    public boolean destroyReference(String JavaDoc reference) {
1214        Matcher JavaDoc m = FOREIGN_FILE_REFERENCE.matcher(reference);
1215        boolean matches = m.matches();
1216        if (!matches) {
1217            m = FOREIGN_FILE_REFERENCE_OLD.matcher(reference);
1218            matches = m.matches();
1219        }
1220        if (matches) {
1221            String JavaDoc forProjName = m.group(1);
1222            String JavaDoc id = m.group(2);
1223            return removeReference(forProjName, id, true, reference.substring(2, reference.length()-1));
1224        }
1225        m = FOREIGN_PLAIN_FILE_REFERENCE.matcher(reference);
1226        if (m.matches()) {
1227            return removeFileReference(reference);
1228        }
1229        return false;
1230    }
1231    
1232    /**
1233     * Create an object permitting this project to represent subprojects.
1234     * Would be placed into the project's lookup.
1235     * @return a subproject provider object suitable for the project lookup
1236     * @see Project#getLookup
1237     */

1238    public SubprojectProvider createSubprojectProvider() {
1239        return new SubprojectProviderImpl(this);
1240    }
1241    
1242    /**
1243     * Access from SubprojectProviderImpl.
1244     */

1245    AntProjectHelper getAntProjectHelper() {
1246        return h;
1247    }
1248    
1249    /**Tries to fix references after copy/rename/move operation on the project.
1250     * Handles relative/absolute paths.
1251     *
1252     * @param originalPath the project folder of the original project
1253     * @see org.netbeans.spi.project.CopyOperationImplementation
1254     * @see org.netbeans.spi.project.MoveOperationImplementation
1255     * @since 1.9
1256     */

1257    public void fixReferences(File JavaDoc originalPath) {
1258        String JavaDoc[] prefixesToFix = new String JavaDoc[] {"file.reference.", "project."};
1259        EditableProperties pub = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
1260        EditableProperties priv = h.getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
1261        
1262        File JavaDoc projectDir = FileUtil.toFile(h.getProjectDirectory());
1263        
1264        List JavaDoc<String JavaDoc> pubRemove = new ArrayList JavaDoc<String JavaDoc>();
1265        List JavaDoc<String JavaDoc> privRemove = new ArrayList JavaDoc<String JavaDoc>();
1266        Map JavaDoc<String JavaDoc,String JavaDoc> pubAdd = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
1267        Map JavaDoc<String JavaDoc,String JavaDoc> privAdd = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
1268        
1269        for (Map.Entry JavaDoc<String JavaDoc,String JavaDoc> e : pub.entrySet()) {
1270            String JavaDoc key = e.getKey();
1271            boolean cont = false;
1272            
1273            for (String JavaDoc prefix : prefixesToFix) {
1274                if (key.startsWith(prefix)) {
1275                    cont = true;
1276                    break;
1277                }
1278            }
1279            if (!cont)
1280                continue;
1281            
1282            String JavaDoc value = e.getValue();
1283            
1284            File JavaDoc absolutePath = FileUtil.normalizeFile(PropertyUtils.resolveFile(originalPath, value));
1285            
1286            //TODO: extra base dir relativization:
1287
if (!CollocationQuery.areCollocated(absolutePath, projectDir)) {
1288                pubRemove.add(key);
1289                privAdd.put(key, absolutePath.getAbsolutePath());
1290            }
1291        }
1292        
1293        for (Map.Entry JavaDoc<String JavaDoc,String JavaDoc> e : pub.entrySet()) {
1294            String JavaDoc key = e.getKey();
1295            boolean cont = false;
1296            
1297            for (String JavaDoc prefix : prefixesToFix) {
1298                if (key.startsWith(prefix)) {
1299                    cont = true;
1300                    break;
1301                }
1302            }
1303            if (!cont)
1304                continue;
1305            
1306            String JavaDoc value = e.getValue();
1307            
1308            File JavaDoc absolutePath = FileUtil.normalizeFile(PropertyUtils.resolveFile(originalPath, value));
1309            
1310        if (absolutePath.getAbsolutePath().startsWith(originalPath.getAbsolutePath())) {
1311        //#65141: in private.properties, a full path into originalPath may be given, fix:
1312
String JavaDoc relative = PropertyUtils.relativizeFile(originalPath, absolutePath);
1313        
1314        absolutePath = new File JavaDoc(projectDir, relative);
1315        
1316        privRemove.add(key);
1317        privAdd.put(key, absolutePath.getAbsolutePath());
1318        }
1319        
1320            //TODO: extra base dir relativization:
1321
if (CollocationQuery.areCollocated(absolutePath, projectDir)) {
1322                pubAdd.put(key, PropertyUtils.relativizeFile(projectDir, absolutePath));
1323            }
1324        }
1325        
1326        for (String JavaDoc s : pubRemove) {
1327            pub.remove(s);
1328        }
1329        
1330        for (String JavaDoc s : privRemove) {
1331            priv.remove(s);
1332        }
1333        
1334        pub.putAll(pubAdd);
1335        priv.putAll(privAdd);
1336        
1337        h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, pub);
1338        h.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH, priv);
1339    }
1340    
1341    /**
1342     * A raw reference descriptor representing a link to a foreign project
1343     * and some build artifact used from it.
1344     * This class corresponds directly to what it stored in <code>project.xml</code>
1345     * to refer to a target in a foreign project.
1346     * See {@link AntArtifact} for the precise meaning of several of the fields in this class.
1347     */

1348    public static final class RawReference {
1349        
1350        private final String JavaDoc foreignProjectName;
1351        private final String JavaDoc artifactType;
1352        private URI JavaDoc scriptLocation;
1353        // introduced in /2 version
1354
private String JavaDoc newScriptLocation;
1355        private final String JavaDoc targetName;
1356        private final String JavaDoc cleanTargetName;
1357        private final String JavaDoc artifactID;
1358        private final Properties JavaDoc props;
1359        
1360        /**
1361         * Create a raw reference descriptor.
1362         * As this is basically just a struct, does no real work.
1363         * @param foreignProjectName the name of the foreign project (usually its code name)
1364         * @param artifactType the {@link AntArtifact#getType type} of the build artifact
1365         * @param scriptLocation the relative URI to the build script from the project directory
1366         * @param targetName the Ant target name
1367         * @param cleanTargetName the Ant clean target name
1368         * @param artifactID the {@link AntArtifact#getID ID} of the build artifact
1369         * @throws IllegalArgumentException if the script location is given an absolute URI
1370         */

1371        public RawReference(String JavaDoc foreignProjectName, String JavaDoc artifactType, URI JavaDoc scriptLocation, String JavaDoc targetName, String JavaDoc cleanTargetName, String JavaDoc artifactID) throws IllegalArgumentException JavaDoc {
1372           this(foreignProjectName, artifactType, scriptLocation, null, targetName, cleanTargetName, artifactID, new Properties JavaDoc());
1373        }
1374        
1375        /**
1376         * Create a raw reference descriptor.
1377         * As this is basically just a struct, does no real work.
1378         * @param foreignProjectName the name of the foreign project (usually its code name)
1379         * @param artifactType the {@link AntArtifact#getType type} of the build artifact
1380         * @param newScriptLocation absolute path to the build script; can contain Ant-like properties
1381         * @param targetName the Ant target name
1382         * @param cleanTargetName the Ant clean target name
1383         * @param artifactID the {@link AntArtifact#getID ID} of the build artifact
1384         * @param props optional properties to be used for target execution; never null
1385         * @throws IllegalArgumentException if the script location is given an absolute URI
1386         * @since 1.5
1387         */

1388        public RawReference(String JavaDoc foreignProjectName, String JavaDoc artifactType, String JavaDoc newScriptLocation, String JavaDoc targetName, String JavaDoc cleanTargetName, String JavaDoc artifactID, Properties JavaDoc props) throws IllegalArgumentException JavaDoc {
1389           this(foreignProjectName, artifactType, null, newScriptLocation, targetName, cleanTargetName, artifactID, props);
1390        }
1391        
1392        private RawReference(String JavaDoc foreignProjectName, String JavaDoc artifactType, URI JavaDoc scriptLocation, String JavaDoc newScriptLocation, String JavaDoc targetName, String JavaDoc cleanTargetName, String JavaDoc artifactID, Properties JavaDoc props) throws IllegalArgumentException JavaDoc {
1393            this.foreignProjectName = foreignProjectName;
1394            this.artifactType = artifactType;
1395            if (scriptLocation != null && scriptLocation.isAbsolute()) {
1396                throw new IllegalArgumentException JavaDoc("Cannot use an absolute URI " + scriptLocation + " for script location"); // NOI18N
1397
}
1398            this.scriptLocation = scriptLocation;
1399            this.newScriptLocation = newScriptLocation;
1400            this.targetName = targetName;
1401            this.cleanTargetName = cleanTargetName;
1402            this.artifactID = artifactID;
1403            this.props = props;
1404        }
1405        
1406        private static final List JavaDoc/*<String>*/ SUB_ELEMENT_NAMES = Arrays.asList(new String JavaDoc[] {
1407            "foreign-project", // NOI18N
1408
"artifact-type", // NOI18N
1409
"script", // NOI18N
1410
"target", // NOI18N
1411
"clean-target", // NOI18N
1412
"id", // NOI18N
1413
});
1414        
1415        /**
1416         * Create a RawReference by parsing an XML &lt;reference&gt; fragment.
1417         * @throws IllegalArgumentException if anything is missing or duplicated or malformed etc.
1418         */

1419        static RawReference create(Element JavaDoc xml) throws IllegalArgumentException JavaDoc {
1420            if (REFS_NS.equals(xml.getNamespaceURI())) {
1421                return create1(xml);
1422            } else {
1423                return create2(xml);
1424            }
1425        }
1426        
1427        private static RawReference create1(Element JavaDoc xml) throws IllegalArgumentException JavaDoc {
1428            if (!REF_NAME.equals(xml.getLocalName()) || !REFS_NS.equals(xml.getNamespaceURI())) {
1429                throw new IllegalArgumentException JavaDoc("bad element name: " + xml); // NOI18N
1430
}
1431            NodeList JavaDoc nl = xml.getElementsByTagNameNS("*", "*"); // NOI18N
1432
if (nl.getLength() != 6) {
1433                throw new IllegalArgumentException JavaDoc("missing or extra data: " + xml); // NOI18N
1434
}
1435            String JavaDoc[] values = new String JavaDoc[nl.getLength()];
1436            for (int i = 0; i < nl.getLength(); i++) {
1437                Element JavaDoc el = (Element JavaDoc)nl.item(i);
1438                if (!REFS_NS.equals(el.getNamespaceURI())) {
1439                    throw new IllegalArgumentException JavaDoc("bad subelement ns: " + el); // NOI18N
1440
}
1441                String JavaDoc elName = el.getLocalName();
1442                int idx = SUB_ELEMENT_NAMES.indexOf(elName);
1443                if (idx == -1) {
1444                    throw new IllegalArgumentException JavaDoc("bad subelement name: " + elName); // NOI18N
1445
}
1446                String JavaDoc val = Util.findText(el);
1447                if (val == null) {
1448                    throw new IllegalArgumentException JavaDoc("empty subelement: " + el); // NOI18N
1449
}
1450                if (values[idx] != null) {
1451                    throw new IllegalArgumentException JavaDoc("duplicate " + elName + ": " + values[idx] + " and " + val); // NOI18N
1452
}
1453                values[idx] = val;
1454            }
1455            assert !Arrays.asList(values).contains(null);
1456            URI JavaDoc scriptLocation = URI.create(values[2]); // throws IllegalArgumentException
1457
return new RawReference(values[0], values[1], scriptLocation, values[3], values[4], values[5]);
1458        }
1459        
1460        private static RawReference create2(Element JavaDoc xml) throws IllegalArgumentException JavaDoc {
1461            if (!REF_NAME.equals(xml.getLocalName()) || !REFS_NS2.equals(xml.getNamespaceURI())) {
1462                throw new IllegalArgumentException JavaDoc("bad element name: " + xml); // NOI18N
1463
}
1464            List JavaDoc nl = Util.findSubElements(xml);
1465            if (nl.size() < 6) {
1466                throw new IllegalArgumentException JavaDoc("missing or extra data: " + xml); // NOI18N
1467
}
1468            String JavaDoc[] values = new String JavaDoc[6];
1469            for (int i = 0; i < 6; i++) {
1470                Element JavaDoc el = (Element JavaDoc)nl.get(i);
1471                if (!REFS_NS2.equals(el.getNamespaceURI())) {
1472                    throw new IllegalArgumentException JavaDoc("bad subelement ns: " + el); // NOI18N
1473
}
1474                String JavaDoc elName = el.getLocalName();
1475                int idx = SUB_ELEMENT_NAMES.indexOf(elName);
1476                if (idx == -1) {
1477                    throw new IllegalArgumentException JavaDoc("bad subelement name: " + elName); // NOI18N
1478
}
1479                String JavaDoc val = Util.findText(el);
1480                if (val == null) {
1481                    throw new IllegalArgumentException JavaDoc("empty subelement: " + el); // NOI18N
1482
}
1483                if (values[idx] != null) {
1484                    throw new IllegalArgumentException JavaDoc("duplicate " + elName + ": " + values[idx] + " and " + val); // NOI18N
1485
}
1486                values[idx] = val;
1487            }
1488            Properties JavaDoc props = new Properties JavaDoc();
1489            if (nl.size() == 7) {
1490                Element JavaDoc el = (Element JavaDoc)nl.get(6);
1491                if (!REFS_NS2.equals(el.getNamespaceURI())) {
1492                    throw new IllegalArgumentException JavaDoc("bad subelement ns: " + el); // NOI18N
1493
}
1494                if (!"properties".equals(el.getLocalName())) { // NOI18N
1495
throw new IllegalArgumentException JavaDoc("bad subelement. expected 'properties': " + el); // NOI18N
1496
}
1497                for (Element JavaDoc el2 : Util.findSubElements(el)) {
1498                    String JavaDoc key = el2.getAttribute("name");
1499                    String JavaDoc value = Util.findText(el2);
1500                    // #53553: NPE
1501
if (value == null) {
1502                        value = ""; // NOI18N
1503
}
1504                    props.setProperty(key, value);
1505                }
1506            }
1507            assert !Arrays.asList(values).contains(null);
1508            return new RawReference(values[0], values[1], values[2], values[3], values[4], values[5], props);
1509        }
1510        
1511        /**
1512         * Write a RawReference as an XML &lt;reference&gt; fragment.
1513         */

1514        Element JavaDoc toXml(String JavaDoc namespace, Document JavaDoc ownerDocument) {
1515            Element JavaDoc el = ownerDocument.createElementNS(namespace, REF_NAME);
1516            String JavaDoc[] values = {
1517                foreignProjectName,
1518                artifactType,
1519                newScriptLocation != null ? newScriptLocation : scriptLocation.toString(),
1520                targetName,
1521                cleanTargetName,
1522                artifactID,
1523            };
1524            for (int i = 0; i < 6; i++) {
1525                Element JavaDoc subel = ownerDocument.createElementNS(namespace, (String JavaDoc)SUB_ELEMENT_NAMES.get(i));
1526                subel.appendChild(ownerDocument.createTextNode(values[i]));
1527                el.appendChild(subel);
1528            }
1529            if (props.keySet().size() > 0) {
1530                assert namespace.equals(REFS_NS2) : "can happen only in /2"; // NOI18N
1531
Element JavaDoc propEls = ownerDocument.createElementNS(namespace, "properties"); // NOI18N
1532
el.appendChild(propEls);
1533                for (String JavaDoc key : new TreeSet JavaDoc<String JavaDoc>(NbCollections.checkedSetByFilter(props.keySet(), String JavaDoc.class, true))) {
1534                    Element JavaDoc propEl = ownerDocument.createElementNS(namespace, "property"); // NOI18N
1535
propEl.appendChild(ownerDocument.createTextNode(props.getProperty(key)));
1536                    propEl.setAttribute("name", key); // NOI18N
1537
propEls.appendChild(propEl);
1538                }
1539            }
1540            return el;
1541        }
1542        
1543        private String JavaDoc getNS() {
1544            if (newScriptLocation != null) {
1545                return REFS_NS2;
1546            } else {
1547                return REFS_NS;
1548            }
1549        }
1550        
1551        /**
1552         * Get the name of the foreign project as referred to from this project.
1553         * Usually this will be the code name of the foreign project, but it may
1554         * instead be a uniquified name.
1555         * The name can be used in project properties and the build script to refer
1556         * to the foreign project from among subprojects.
1557         * @return the foreign project name
1558         */

1559        public String JavaDoc getForeignProjectName() {
1560            return foreignProjectName;
1561        }
1562        
1563        /**
1564         * Get the type of the foreign project's build artifact.
1565         * For example, <a HREF="@JAVA/PROJECT@/org/netbeans/api/java/project/JavaProjectConstants.html#ARTIFACT_TYPE_JAR"><code>JavaProjectConstants.ARTIFACT_TYPE_JAR</code></a>.
1566         * @return the artifact type
1567         */

1568        public String JavaDoc getArtifactType() {
1569            return artifactType;
1570        }
1571        
1572        /**
1573         * Get the location of the foreign project's build script relative to the
1574         * project directory.
1575         * This is the script which would be called to build the desired artifact.
1576         * @return the script location
1577         * @deprecated use {@link #getScriptLocationValue} instead; may return null now
1578         */

1579        @Deprecated JavaDoc
1580        public URI JavaDoc getScriptLocation() {
1581            return scriptLocation;
1582        }
1583        
1584        /**
1585         * Get absolute path location of the foreign project's build script.
1586         * This is the script which would be called to build the desired artifact.
1587         * @return absolute path possibly containing Ant properties
1588         */

1589        public String JavaDoc getScriptLocationValue() {
1590            if (newScriptLocation != null) {
1591                return newScriptLocation;
1592            } else {
1593                return "${project."+foreignProjectName+"}/"+scriptLocation.toString();
1594            }
1595        }
1596        
1597        /**
1598         * Get the Ant target name to build the artifact.
1599         * @return the target name
1600         */

1601        public String JavaDoc getTargetName() {
1602            return targetName;
1603        }
1604        
1605        /**
1606         * Get the Ant target name to clean the artifact.
1607         * @return the clean target name
1608         */

1609        public String JavaDoc getCleanTargetName() {
1610            return cleanTargetName;
1611        }
1612        
1613        /**
1614         * Get the ID of the foreign project's build artifact.
1615         * See also {@link AntArtifact#getID}.
1616         * @return the artifact identifier
1617         */

1618        public String JavaDoc getID() {
1619            return artifactID;
1620        }
1621        
1622        public Properties JavaDoc getProperties() {
1623            return props;
1624        }
1625        
1626        /**
1627         * Attempt to convert this reference to a live artifact object.
1628         * This involves finding the referenced foreign project on disk
1629         * (among standard project and private properties) and asking it
1630         * for the artifact named by the given target.
1631         * Given that object, you can find important further information
1632         * such as the location of the actual artifact on disk.
1633         * <p>
1634         * Note that non-key attributes of the returned artifact (i.e.
1635         * type, script location, and clean target name) might not match
1636         * those in this raw reference.
1637         * <p>
1638         * Acquires read access.
1639         * @param helper an associated reference helper used to resolve the foreign
1640         * project location
1641         * @return the actual Ant artifact object, or null if it could not be located
1642         */

1643        public AntArtifact toAntArtifact(final ReferenceHelper helper) {
1644            return ProjectManager.mutex().readAccess(new Mutex.Action<AntArtifact>() {
1645                public AntArtifact run() {
1646                    AntProjectHelper h = helper.h;
1647                    String JavaDoc path = helper.eval.getProperty("project." + foreignProjectName); // NOI18N
1648
if (path == null) {
1649                        // Undefined foreign project.
1650
return null;
1651                    }
1652                    FileObject foreignProjectDir = h.resolveFileObject(path);
1653                    if (foreignProjectDir == null) {
1654                        // Nonexistent foreign project dir.
1655
return null;
1656                    }
1657                    Project p;
1658                    try {
1659                        p = ProjectManager.getDefault().findProject(foreignProjectDir);
1660                    } catch (IOException JavaDoc e) {
1661                        // Could not load it.
1662
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
1663                        return null;
1664                    }
1665                    if (p == null) {
1666                        // Was not a project dir.
1667
return null;
1668                    }
1669                    return AntArtifactQuery.findArtifactByID(p, artifactID);
1670                }
1671            });
1672        }
1673        
1674        private void upgrade() {
1675            assert newScriptLocation == null && scriptLocation != null : "was already upgraded "+this;
1676            newScriptLocation = "${project."+foreignProjectName+"}/" +scriptLocation.toString(); // NOI18N
1677
scriptLocation = null;
1678        }
1679        
1680        public String JavaDoc toString() {
1681            return "ReferenceHelper.RawReference<" + foreignProjectName + "," +
1682                artifactType + "," + newScriptLocation != null ? newScriptLocation : scriptLocation +
1683                "," + targetName + "," + cleanTargetName + "," + artifactID + ">"; // NOI18N
1684
}
1685        
1686    }
1687    
1688}
1689
Popular Tags