0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.ruby.spi.project.support.rake;
0043:
0044: import java.io.File;
0045: import java.io.IOException;
0046: import java.net.URI;
0047: import java.net.URISyntaxException;
0048: import java.util.ArrayList;
0049: import java.util.Arrays;
0050: import java.util.Collection;
0051: import java.util.Collections;
0052: import java.util.HashMap;
0053: import java.util.HashSet;
0054: import java.util.Iterator;
0055: import java.util.List;
0056: import java.util.Map;
0057: import java.util.Properties;
0058: import java.util.Set;
0059: import java.util.TreeSet;
0060: import java.util.regex.Matcher;
0061: import java.util.regex.Pattern;
0062: import org.netbeans.api.project.Project;
0063: import org.netbeans.api.project.ProjectManager;
0064: import org.netbeans.api.project.ProjectUtils;
0065: import org.netbeans.modules.ruby.api.project.rake.RakeArtifact;
0066: import org.netbeans.modules.ruby.api.project.rake.RakeArtifactQuery;
0067: import org.netbeans.api.queries.CollocationQuery;
0068: import org.netbeans.modules.ruby.modules.project.rake.RakeBasedProjectFactorySingleton;
0069: import org.netbeans.modules.ruby.modules.project.rake.Util;
0070: import org.netbeans.spi.project.AuxiliaryConfiguration;
0071: import org.netbeans.spi.project.SubprojectProvider;
0072: import org.openide.ErrorManager;
0073: import org.openide.filesystems.FileObject;
0074: import org.openide.filesystems.FileUtil;
0075: import org.openide.util.Mutex;
0076: import org.openide.util.NbCollections;
0077: import org.openide.xml.XMLUtil;
0078: import org.w3c.dom.Document;
0079: import org.w3c.dom.Element;
0080: import org.w3c.dom.NodeList;
0081:
0082: // XXX need a method to update non-key data in references e.g. during projectOpened()
0083:
0084: /**
0085: * Helps manage inter-project references.
0086: * Normally you would create an instance of this object and keep it in your
0087: * project object in order to support {@link SubprojectProvider} and various
0088: * operations that change settings which might refer to build artifacts from
0089: * other projects: e.g. when changing the classpath for a Java-based project
0090: * you would want to use this helper to scan potential classpath entries for
0091: * JARs coming from other projects that you would like to be able to build
0092: * as dependencies before your project is built.
0093: * <p>
0094: * You probably only need the higher-level methods such as {@link #addReference}
0095: * and {@link #removeReference(String,String)}; the lower-level methods such as {@link #addRawReference}
0096: * are provided for completeness, but typical client code should not need them.
0097: * <p>
0098: * Only deals with references needed to support build artifacts coming from
0099: * foreign projects. If for some reason you wish to store other kinds of
0100: * references to foreign projects, you do not need this class; just store
0101: * them however you wish, and be sure to create an appropriate {@link SubprojectProvider}.
0102: * <p>
0103: * Modification methods (add, remove) mark the project as modified but do not save it.
0104: * @author Jesse Glick
0105: */
0106: public final class ReferenceHelper {
0107:
0108: /**
0109: * XML element name used to store references in <code>project.xml</code>.
0110: */
0111: static final String REFS_NAME = "references"; // NOI18N
0112:
0113: /**
0114: * XML element name used to store one reference in <code>project.xml</code>.
0115: */
0116: static final String REF_NAME = "reference"; // NOI18N
0117:
0118: /**
0119: * XML namespace used to store references in <code>project.xml</code>.
0120: */
0121: static final String REFS_NS = "http://www.netbeans.org/ns/rake-project-references/1"; // NOI18N
0122:
0123: /**
0124: * Newer version of {@link #REFS_NS} supporting Properties and with changed semantics of <script>.
0125: */
0126: static final String REFS_NS2 = "http://www.netbeans.org/ns/rake-project-references/2"; // NOI18N
0127:
0128: /** Set of property names which values can be used as additional base
0129: * directories. */
0130: private Set<String> extraBaseDirectories = new HashSet<String>();
0131:
0132: private final RakeProjectHelper h;
0133: final PropertyEvaluator eval;
0134: private final AuxiliaryConfiguration aux;
0135:
0136: /**
0137: * Create a new reference helper.
0138: * It needs an {@link RakeProjectHelper} object in order to update references
0139: * in <code>project.xml</code>,
0140: * as well as set project or private properties referring to the locations
0141: * of foreign projects on disk.
0142: * <p>
0143: * The property evaluator may be used in {@link #getForeignFileReferenceAsArtifact},
0144: * {@link ReferenceHelper.RawReference#toRakeArtifact}, or
0145: * {@link #createSubprojectProvider}. Typically this would
0146: * be {@link RakeProjectHelper#getStandardPropertyEvaluator}. You can substitute
0147: * a custom evaluator but be warned that this helper class assumes that
0148: * {@link RakeProjectHelper#PROJECT_PROPERTIES_PATH} and {@link RakeProjectHelper#PRIVATE_PROPERTIES_PATH}
0149: * have their customary meanings; specifically that they are both used when evaluating
0150: * properties (such as the location of a foreign project) and that private properties
0151: * can override public properties.
0152: * @param helper an Ant project helper object representing this project's configuration
0153: * @param aux an auxiliary configuration provider needed to store references
0154: * @param eval a property evaluator
0155: */
0156: public ReferenceHelper(RakeProjectHelper helper,
0157: AuxiliaryConfiguration aux, PropertyEvaluator eval) {
0158: h = helper;
0159: this .aux = aux;
0160: this .eval = eval;
0161: }
0162:
0163: /**
0164: * Load <references> from project.xml.
0165: * @return can return null if there are no references stored yet
0166: */
0167: private Element loadReferences() {
0168: assert ProjectManager.mutex().isReadAccess()
0169: || ProjectManager.mutex().isWriteAccess();
0170: Element references = aux.getConfigurationFragment(REFS_NAME,
0171: REFS_NS2, true);
0172: if (references == null) {
0173: references = aux.getConfigurationFragment(REFS_NAME,
0174: REFS_NS, true);
0175: }
0176: return references;
0177: }
0178:
0179: /**
0180: * Store <references> to project.xml (i.e. to memory and mark project modified).
0181: */
0182: private void storeReferences(Element references) {
0183: assert ProjectManager.mutex().isWriteAccess();
0184: assert references != null
0185: && references.getLocalName().equals(REFS_NAME)
0186: && (REFS_NS.equals(references.getNamespaceURI()) || REFS_NS2
0187: .equals(references.getNamespaceURI()));
0188: aux.putConfigurationFragment(references, true);
0189: }
0190:
0191: private void removeOldReferences() {
0192: assert ProjectManager.mutex().isWriteAccess();
0193: aux.removeConfigurationFragment(REFS_NAME, REFS_NS, true);
0194: }
0195:
0196: /**
0197: * Add a reference to an artifact coming from a foreign project.
0198: * <p>
0199: * For more info see {@link #addReference(RakeArtifact, URI)}.
0200: * @param artifact the artifact to add
0201: * @return true if a reference or some property was actually added or modified,
0202: * false if everything already existed and was not modified
0203: * @throws IllegalArgumentException if the artifact is not associated with a project
0204: * @deprecated to add reference use {@link #addReference(RakeArtifact, URI)};
0205: * to check whether reference exist or not use {@link #isReferenced(RakeArtifact, URI)}.
0206: * This method creates reference for the first artifact location only.
0207: */
0208: @Deprecated
0209: public boolean addReference(final RakeArtifact artifact)
0210: throws IllegalArgumentException {
0211: Object ret[] = addReference0(artifact, artifact
0212: .getArtifactLocations()[0]);
0213: return ((Boolean) ret[0]).booleanValue();
0214: }
0215:
0216: // @return array of two elements: [Boolean - any modification, String - reference]
0217: private Object[] addReference0(final RakeArtifact artifact,
0218: final URI location) throws IllegalArgumentException {
0219: return ProjectManager.mutex().writeAccess(
0220: new Mutex.Action<Object[]>() {
0221: public Object[] run() {
0222: int index = findLocationIndex(artifact,
0223: location);
0224: Project forProj = artifact.getProject();
0225: if (forProj == null) {
0226: throw new IllegalArgumentException(
0227: "No project associated with "
0228: + artifact); // NOI18N
0229: }
0230: // Set up the raw reference.
0231: File forProjDir = FileUtil.toFile(forProj
0232: .getProjectDirectory());
0233: assert forProjDir != null : forProj
0234: .getProjectDirectory();
0235: String projName = getUsableReferenceID(ProjectUtils
0236: .getInformation(forProj).getName());
0237: String forProjName = findReferenceID(projName,
0238: "project.", forProjDir
0239: .getAbsolutePath());
0240: if (forProjName == null) {
0241: forProjName = generateUniqueID(projName,
0242: "project.", forProjDir
0243: .getAbsolutePath());
0244: }
0245: RawReference ref;
0246: File scriptFile = artifact.getScriptLocation();
0247: if (canUseVersion10(artifact, forProjDir)) {
0248: String rel = PropertyUtils.relativizeFile(
0249: forProjDir, scriptFile);
0250: URI scriptLocation;
0251: try {
0252: scriptLocation = new URI(null, null,
0253: rel, null);
0254: } catch (URISyntaxException ex) {
0255: scriptLocation = forProjDir.toURI()
0256: .relativize(scriptFile.toURI());
0257: }
0258: ref = new RawReference(forProjName,
0259: artifact.getType(), scriptLocation,
0260: artifact.getTargetName(), artifact
0261: .getCleanTargetName(),
0262: artifact.getID());
0263: } else {
0264: String scriptLocation;
0265: if (scriptFile.getAbsolutePath()
0266: .startsWith(
0267: forProjDir
0268: .getAbsolutePath())) {
0269: String rel = PropertyUtils
0270: .relativizeFile(forProjDir,
0271: scriptFile);
0272: assert rel != null : "Relativization must succeed for files: "
0273: + forProjDir + " " + scriptFile;
0274: scriptLocation = "${project."
0275: + forProjName + "}/" + rel;
0276: } else {
0277: scriptLocation = "build.script.reference."
0278: + forProjName;
0279: setPathProperty(forProjDir, scriptFile,
0280: scriptLocation);
0281: scriptLocation = "${" + scriptLocation
0282: + "}";
0283: }
0284: ref = new RawReference(forProjName,
0285: artifact.getType(), scriptLocation,
0286: artifact.getTargetName(), artifact
0287: .getCleanTargetName(),
0288: artifact.getID(), artifact
0289: .getProperties());
0290: }
0291: boolean success = addRawReference0(ref);
0292: // Set up ${project.whatever}.
0293: FileObject myProjDirFO = RakeBasedProjectFactorySingleton
0294: .getProjectFor(h).getProjectDirectory();
0295: File myProjDir = FileUtil.toFile(myProjDirFO);
0296: if (setPathProperty(myProjDir, forProjDir,
0297: "project." + forProjName)) {
0298: success = true;
0299: }
0300: // Set up ${reference.whatever.whatever}.
0301: String propertiesFile;
0302: String forProjPathProp = "project."
0303: + forProjName; // NOI18N
0304: URI artFile = location;
0305: String refPath;
0306: if (artFile.isAbsolute()) {
0307: refPath = new File(artFile)
0308: .getAbsolutePath();
0309: propertiesFile = RakeProjectHelper.PRIVATE_PROPERTIES_PATH;
0310: } else {
0311: refPath = "${" + forProjPathProp + "}/"
0312: + artFile.getPath(); // NOI18N
0313: propertiesFile = RakeProjectHelper.PROJECT_PROPERTIES_PATH;
0314: }
0315: EditableProperties props = h
0316: .getProperties(propertiesFile);
0317: String refPathProp = "reference."
0318: + forProjName
0319: + '.'
0320: + getUsableReferenceID(artifact.getID()); // NOI18N
0321: if (index > 0) {
0322: refPathProp += "." + index;
0323: }
0324: if (!refPath.equals(props
0325: .getProperty(refPathProp))) {
0326: props.put(refPathProp, refPath);
0327: h.putProperties(propertiesFile, props);
0328: success = true;
0329: }
0330: return new Object[] { success,
0331: "${" + refPathProp + "}" }; // NOI18N
0332: }
0333: });
0334: }
0335:
0336: private int findLocationIndex(final RakeArtifact artifact,
0337: final URI location) throws IllegalArgumentException {
0338: if (location == null) {
0339: throw new IllegalArgumentException(
0340: "location cannot be null");
0341: }
0342: URI uris[] = artifact.getArtifactLocations();
0343: for (int i = 0; i < uris.length; i++) {
0344: if (uris[i].equals(location)) {
0345: return i;
0346: }
0347: }
0348: throw new IllegalArgumentException("location (" + location
0349: + ") must be in RakeArtifact's locations (" + artifact
0350: + ")");
0351: }
0352:
0353: /**
0354: * Test whether the artifact can be stored as /1 artifact or not.
0355: */
0356: private static boolean canUseVersion10(RakeArtifact aa,
0357: File projectDirectory) {
0358: // is there multiple outputs?
0359: if (aa.getArtifactLocations().length > 1) {
0360: return false;
0361: }
0362: // has some properties?
0363: if (aa.getProperties().keySet().size() > 0) {
0364: return false;
0365: }
0366: // does Ant script lies under project directory?
0367: if (!aa.getScriptLocation().getAbsolutePath().startsWith(
0368: projectDirectory.getAbsolutePath())) {
0369: return false;
0370: }
0371: return true;
0372: }
0373:
0374: /**
0375: * Helper method which checks collocation status of two files and based on
0376: * that it will in private or project properties file set up property with
0377: * the given name and with absolute or relative path value.
0378: * @return was there any change or not
0379: */
0380: private boolean setPathProperty(File base, File path,
0381: String propertyName) {
0382: String[] values;
0383: String[] propertiesFiles;
0384:
0385: String relativePath = relativizeFileToExtraBaseFolders(path);
0386: // try relativize against external base dirs
0387: if (relativePath != null) {
0388: propertiesFiles = new String[] { RakeProjectHelper.PROJECT_PROPERTIES_PATH };
0389: values = new String[] { relativePath };
0390: } else if (CollocationQuery.areCollocated(base, path)) {
0391: // Fine, using a relative path to subproject.
0392: relativePath = PropertyUtils.relativizeFile(base, path);
0393: assert relativePath != null : "These dirs are not really collocated: "
0394: + base + " & " + path;
0395: values = new String[] { relativePath,
0396: path.getAbsolutePath() };
0397: propertiesFiles = new String[] {
0398: RakeProjectHelper.PROJECT_PROPERTIES_PATH,
0399: RakeProjectHelper.PRIVATE_PROPERTIES_PATH, };
0400: } else {
0401: // use an absolute path.
0402: // mkleint: when the AlwaysRelativeCollocationQueryImplementation gets removed
0403: // this code gets called more frequently
0404: // to get the previous behaviour replace CollocationQuery.areCollocated(base, path)
0405: // with PropertyUtils.relativizeFile(base, path) != null
0406: propertiesFiles = new String[] { RakeProjectHelper.PRIVATE_PROPERTIES_PATH };
0407: values = new String[] { path.getAbsolutePath() };
0408: }
0409:
0410: boolean metadataChanged = false;
0411: for (int i = 0; i < propertiesFiles.length; i++) {
0412: EditableProperties props = h
0413: .getProperties(propertiesFiles[i]);
0414: if (!values[i].equals(props.getProperty(propertyName))) {
0415: props.put(propertyName, values[i]);
0416: h.putProperties(propertiesFiles[i], props);
0417: metadataChanged = true;
0418: }
0419: }
0420:
0421: if (propertiesFiles.length == 1) {
0422: // check presence of this property in opposite property file and
0423: // remove it if necessary
0424: String propertiesFile = (propertiesFiles[0] == RakeProjectHelper.PROJECT_PROPERTIES_PATH ? RakeProjectHelper.PRIVATE_PROPERTIES_PATH
0425: : RakeProjectHelper.PROJECT_PROPERTIES_PATH);
0426: EditableProperties props = h.getProperties(propertiesFile);
0427: if (props.remove(propertyName) != null) {
0428: h.putProperties(propertiesFile, props);
0429: }
0430: }
0431: return metadataChanged;
0432: }
0433:
0434: /**
0435: * Add a reference to an artifact's location coming from a foreign project.
0436: * <p>
0437: * Records the name of the foreign project.
0438: * Normally the foreign project name is that project's code name,
0439: * but it may be uniquified if that name is already taken to refer
0440: * to a different project with the same code name.
0441: * <p>
0442: * Adds a project property if necessary to refer to its location of the foreign
0443: * project - a shared property if the foreign project
0444: * is {@link CollocationQuery collocated} with this one, else a private property.
0445: * This property is named <samp>project.<i>foreignProjectName</i></samp>.
0446: * Example: <samp>project.mylib=../mylib</samp>
0447: * <p>
0448: * Adds a project property to refer to the artifact's location.
0449: * This property is named <samp>reference.<i>foreignProjectName</i>.<i>targetName</i></samp>
0450: * and will use <samp>${project.<i>foreignProjectName</i>}</samp> and be a shared
0451: * property - unless the artifact location is an absolute URI, in which case the property
0452: * will also be private.
0453: * Example: <samp>reference.mylib.jar=${project.mylib}/dist/mylib.jar</samp>
0454: * <p>
0455: * Also records the artifact type, (relative) script path, and build and
0456: * clean target names.
0457: * <p>
0458: * If the reference already exists (keyed by foreign project object
0459: * and target name), nothing is done, unless some other field (script location,
0460: * clean target name, or artifact type) needed to be updated, in which case
0461: * the new information replaces the old. Similarly, the artifact location
0462: * property is updated if necessary.
0463: * <p>
0464: * Acquires write access.
0465: * @param artifact the artifact to add
0466: * @param location the artifact's location to create reference to
0467: * @return name of reference which was created or already existed
0468: * @throws IllegalArgumentException if the artifact is not associated with a project
0469: * or if the location is not artifact's location
0470: * @since 1.5
0471: */
0472: public String addReference(final RakeArtifact artifact, URI location)
0473: throws IllegalArgumentException {
0474: Object ret[] = addReference0(artifact, location);
0475: return (String) ret[1];
0476: }
0477:
0478: /**
0479: * Tests whether reference for artifact's location was already created by
0480: * {@link #addReference(RakeArtifact, URI)} for this project or not. This
0481: * method returns false also in case when reference exist but needs to be
0482: * updated.
0483: * <p>
0484: * Acquires read access.
0485: * @param artifact the artifact to add
0486: * @param location the artifact's location to create reference to
0487: * @return true if already referenced
0488: * @throws IllegalArgumentException if the artifact is not associated with a project
0489: * or if the location is not artifact's location
0490: * @since 1.5
0491: */
0492: public boolean isReferenced(final RakeArtifact artifact,
0493: final URI location) throws IllegalArgumentException {
0494: return ProjectManager.mutex().readAccess(
0495: new Mutex.Action<Boolean>() {
0496: public Boolean run() {
0497: int index = findLocationIndex(artifact,
0498: location);
0499: Project forProj = artifact.getProject();
0500: if (forProj == null) {
0501: throw new IllegalArgumentException(
0502: "No project associated with "
0503: + artifact); // NOI18N
0504: }
0505: File forProjDir = FileUtil.toFile(forProj
0506: .getProjectDirectory());
0507: assert forProjDir != null : forProj
0508: .getProjectDirectory();
0509: String projName = getUsableReferenceID(ProjectUtils
0510: .getInformation(forProj).getName());
0511: String forProjName = findReferenceID(projName,
0512: "project.", forProjDir
0513: .getAbsolutePath());
0514: if (forProjName == null) {
0515: return false;
0516: }
0517: RawReference ref = getRawReference(forProjName,
0518: getUsableReferenceID(artifact.getID()));
0519: if (ref == null) {
0520: return false;
0521: }
0522: File script = h.resolveFile(eval.evaluate(ref
0523: .getScriptLocationValue()));
0524: if (!artifact.getType().equals(
0525: ref.getArtifactType())
0526: || !artifact.getID()
0527: .equals(ref.getID())
0528: || !artifact.getScriptLocation()
0529: .equals(script)
0530: || !artifact.getProperties().equals(
0531: ref.getProperties())
0532: || !artifact.getTargetName().equals(
0533: ref.getTargetName())
0534: || !artifact
0535: .getCleanTargetName()
0536: .equals(
0537: ref
0538: .getCleanTargetName())) {
0539: return false;
0540: }
0541:
0542: String reference = "reference."
0543: + forProjName
0544: + '.'
0545: + getUsableReferenceID(artifact.getID()); // NOI18N
0546: if (index > 0) {
0547: reference += "." + index;
0548: }
0549: return eval.getProperty(reference) != null;
0550: }
0551: });
0552: }
0553:
0554: /**
0555: * Add a raw reference to a foreign project artifact.
0556: * Does not check if such a project already exists; does not create a project
0557: * property to refer to it; does not do any backreference usage notifications.
0558: * <p>
0559: * If the reference already exists (keyed by foreign project name and target name),
0560: * nothing is done, unless some other field (script location, clean target name,
0561: * or artifact type) needed to be updated, in which case the new information
0562: * replaces the old.
0563: * <p>
0564: * Note that since {@link RawReference} is just a descriptor, it is not guaranteed
0565: * that after adding one {@link #getRawReferences} or {@link #getRawReference}
0566: * would return the identical object.
0567: * <p>
0568: * Acquires write access.
0569: * @param ref a raw reference descriptor
0570: * @return true if a reference was actually added or modified,
0571: * false if it already existed and was not modified
0572: */
0573: public boolean addRawReference(final RawReference ref) {
0574: return ProjectManager.mutex().writeAccess(
0575: new Mutex.Action<Boolean>() {
0576: public Boolean run() {
0577: try {
0578: return addRawReference0(ref);
0579: } catch (IllegalArgumentException e) {
0580: ErrorManager.getDefault().notify(
0581: ErrorManager.INFORMATIONAL, e);
0582: return false;
0583: }
0584: }
0585: });
0586: }
0587:
0588: private boolean addRawReference0(final RawReference ref)
0589: throws IllegalArgumentException {
0590: Element references = loadReferences();
0591: if (references == null) {
0592: references = XMLUtil.createDocument("ignore", null, null,
0593: null).createElementNS(ref.getNS(), REFS_NAME); // NOI18N
0594: }
0595: boolean modified = false;
0596: if (references.getNamespaceURI().equals(REFS_NS)
0597: && ref.getNS().equals(REFS_NS2)) {
0598: // upgrade all references to version /2 here:
0599: references = upgradeTo20(references);
0600: removeOldReferences();
0601: modified = true;
0602: }
0603: modified = updateRawReferenceElement(ref, references);
0604: if (modified) {
0605: storeReferences(references);
0606: }
0607: return modified;
0608: }
0609:
0610: private Element upgradeTo20(Element references) {
0611: Element references20 = XMLUtil.createDocument("ignore", null,
0612: null, null).createElementNS(REFS_NS2, REFS_NAME); // NOI18N
0613: RawReference rr[] = getRawReferences(references);
0614: for (int i = 0; i < rr.length; i++) {
0615: rr[i].upgrade();
0616: updateRawReferenceElement(rr[i], references20);
0617: }
0618: return references20;
0619: }
0620:
0621: private static boolean updateRawReferenceElement(RawReference ref,
0622: Element references) throws IllegalArgumentException {
0623: // Linear search; always keeping references sorted first by foreign project
0624: // name, then by target name.
0625: Element nextRefEl = null;
0626: Iterator<Element> it = Util.findSubElements(references)
0627: .iterator();
0628: while (it.hasNext()) {
0629: Element testRefEl = it.next();
0630: RawReference testRef = RawReference.create(testRefEl);
0631: if (testRef.getForeignProjectName().compareTo(
0632: ref.getForeignProjectName()) > 0) {
0633: // gone too far, go back
0634: nextRefEl = testRefEl;
0635: break;
0636: }
0637: if (testRef.getForeignProjectName().equals(
0638: ref.getForeignProjectName())) {
0639: if (testRef.getID().compareTo(ref.getID()) > 0) {
0640: // again, gone too far, go back
0641: nextRefEl = testRefEl;
0642: break;
0643: }
0644: if (testRef.getID().equals(ref.getID())) {
0645: // Key match, check if it needs to be updated.
0646: if (testRef.getArtifactType().equals(
0647: ref.getArtifactType())
0648: && testRef.getScriptLocationValue().equals(
0649: ref.getScriptLocationValue())
0650: && testRef.getProperties().equals(
0651: ref.getProperties())
0652: && testRef.getTargetName().equals(
0653: ref.getTargetName())
0654: && testRef.getCleanTargetName().equals(
0655: ref.getCleanTargetName())) {
0656: // Match on other fields. Return without changing anything.
0657: return false;
0658: }
0659: // Something needs updating.
0660: // Delete the old ref and set nextRef to the next item in line.
0661: references.removeChild(testRefEl);
0662: if (it.hasNext()) {
0663: nextRefEl = it.next();
0664: } else {
0665: nextRefEl = null;
0666: }
0667: break;
0668: }
0669: }
0670: }
0671: // Need to insert a new record before nextRef.
0672: Element newRefEl = ref.toXml(references.getNamespaceURI(),
0673: references.getOwnerDocument());
0674: // Note: OK if nextRefEl == null, that means insert as last child.
0675: references.insertBefore(newRefEl, nextRefEl);
0676: return true;
0677: }
0678:
0679: /**
0680: * Remove a reference to an artifact coming from a foreign project.
0681: * <p>
0682: * The property giving the location of the artifact is removed if it existed.
0683: * <p>
0684: * If this was the last reference to the foreign project, its location
0685: * property is removed as well.
0686: * <p>
0687: * If the reference does not exist, nothing is done.
0688: * <p>
0689: * Acquires write access.
0690: * @param foreignProjectName the local name of the foreign project
0691: * (usually its code name)
0692: * @param id the ID of the build artifact (usually build target name)
0693: * @return true if a reference or some property was actually removed,
0694: * false if the reference was not there and no property was removed
0695: * @deprecated use {@link #destroyReference} instead; was unused anyway
0696: */
0697: @Deprecated
0698: public boolean removeReference(final String foreignProjectName,
0699: final String id) {
0700: return removeReference(foreignProjectName, id, false, null);
0701: }
0702:
0703: /**
0704: * Checks whether this is last reference and therefore the artifact can
0705: * be removed from project.xml or not
0706: */
0707: private boolean isLastReference(String ref) {
0708: Object ret[] = findArtifactAndLocation(ref);
0709: if (ret[0] == null || ret[1] == null) {
0710: return true;
0711: }
0712: RakeArtifact aa = (RakeArtifact) ret[0];
0713: URI uri = (URI) ret[1];
0714: URI uris[] = aa.getArtifactLocations();
0715: boolean lastReference = true;
0716: // are there any other referenced jars or not:
0717: for (int i = 0; i < uris.length; i++) {
0718: if (uris[i].equals(uri)) {
0719: continue;
0720: }
0721: if (isReferenced(aa, uris[i])) {
0722: lastReference = false;
0723: break;
0724: }
0725: }
0726: return lastReference;
0727: }
0728:
0729: private boolean removeReference(final String foreignProjectName,
0730: final String id, final boolean escaped,
0731: final String reference) {
0732: return ProjectManager.mutex().writeAccess(
0733: new Mutex.Action<Boolean>() {
0734: public Boolean run() {
0735: boolean success = false;
0736: try {
0737: if (isLastReference("${" + reference + "}")) {
0738: success = removeRawReference0(
0739: foreignProjectName, id, escaped);
0740: }
0741: } catch (IllegalArgumentException e) {
0742: ErrorManager.getDefault().notify(
0743: ErrorManager.INFORMATIONAL, e);
0744: return false;
0745: }
0746: // Note: try to delete obsoleted properties from both project.properties
0747: // and private.properties, just in case.
0748: String[] PROPS_PATHS = {
0749: RakeProjectHelper.PROJECT_PROPERTIES_PATH,
0750: RakeProjectHelper.PRIVATE_PROPERTIES_PATH, };
0751: // if raw reference was removed then try to clean also project reference property:
0752: if (success) {
0753: // Check whether there are any other references using foreignProjectName.
0754: // If not, we can delete ${project.foreignProjectName}.
0755: RawReference[] refs = new RawReference[0];
0756: Element references = loadReferences();
0757: if (references != null) {
0758: refs = getRawReferences(references);
0759: }
0760: boolean deleteProjProp = true;
0761: for (int i = 0; i < refs.length; i++) {
0762: if (refs[i].getForeignProjectName()
0763: .equals(foreignProjectName)) {
0764: deleteProjProp = false;
0765: break;
0766: }
0767: }
0768: if (deleteProjProp) {
0769: String projProp = "project."
0770: + foreignProjectName; // NOI18N
0771: for (int i = 0; i < PROPS_PATHS.length; i++) {
0772: EditableProperties props = h
0773: .getProperties(PROPS_PATHS[i]);
0774: if (props.containsKey(projProp)) {
0775: props.remove(projProp);
0776: h.putProperties(PROPS_PATHS[i],
0777: props);
0778: success = true;
0779: }
0780: }
0781: }
0782: }
0783:
0784: String refProp = reference;
0785: if (refProp == null) {
0786: refProp = "reference." + foreignProjectName
0787: + '.' + getUsableReferenceID(id); // NOI18N
0788: }
0789: // remove also build script property if exist any:
0790: String buildScriptProperty = "build.script.reference."
0791: + foreignProjectName;
0792: for (String path : PROPS_PATHS) {
0793: EditableProperties props = h
0794: .getProperties(path);
0795: if (props.containsKey(refProp)) {
0796: props.remove(refProp);
0797: h.putProperties(path, props);
0798: success = true;
0799: }
0800: if (props.containsKey(buildScriptProperty)) {
0801: props.remove(buildScriptProperty);
0802: h.putProperties(path, props);
0803: success = true;
0804: }
0805: }
0806: return success;
0807: }
0808: });
0809: }
0810:
0811: /**
0812: * Remove reference to a file.
0813: * <p>
0814: * If the reference does not exist, nothing is done.
0815: * <p>
0816: * Acquires write access.
0817: * @param fileReference file reference as created by
0818: * {@link #createForeignFileReference(File, String)}
0819: * @return true if the reference was actually removed; otherwise false
0820: * @deprecated use {@link #destroyReference} instead; was unused anyway
0821: */
0822: @Deprecated
0823: public boolean removeReference(final String fileReference) {
0824: return removeFileReference(fileReference);
0825: }
0826:
0827: private boolean removeFileReference(final String fileReference) {
0828: return ProjectManager.mutex().writeAccess(
0829: new Mutex.Action<Boolean>() {
0830: public Boolean run() {
0831: boolean success = false;
0832: // Note: try to delete obsoleted properties from both project.properties
0833: // and private.properties, just in case.
0834: String[] PROPS_PATHS = {
0835: RakeProjectHelper.PROJECT_PROPERTIES_PATH,
0836: RakeProjectHelper.PRIVATE_PROPERTIES_PATH, };
0837: String refProp = fileReference;
0838: if (refProp.startsWith("${")
0839: && refProp.endsWith("}")) {
0840: refProp = refProp.substring(2, refProp
0841: .length() - 1);
0842: }
0843: for (String path : PROPS_PATHS) {
0844: EditableProperties props = h
0845: .getProperties(path);
0846: if (props.containsKey(refProp)) {
0847: props.remove(refProp);
0848: h.putProperties(path, props);
0849: success = true;
0850: }
0851: }
0852: return success;
0853: }
0854: });
0855: }
0856:
0857: /**
0858: * Remove a raw reference to an artifact coming from a foreign project.
0859: * Does not attempt to manipulate backreferences in the foreign project
0860: * nor project properties.
0861: * <p>
0862: * If the reference does not exist, nothing is done.
0863: * <p>
0864: * Acquires write access.
0865: * @param foreignProjectName the local name of the foreign project
0866: * (usually its code name)
0867: * @param id the ID of the build artifact (usually build target name)
0868: * @return true if a reference was actually removed, false if it was not there
0869: */
0870: public boolean removeRawReference(final String foreignProjectName,
0871: final String id) {
0872: return ProjectManager.mutex().writeAccess(
0873: new Mutex.Action<Boolean>() {
0874: public Boolean run() {
0875: try {
0876: return removeRawReference0(
0877: foreignProjectName, id, false);
0878: } catch (IllegalArgumentException e) {
0879: ErrorManager.getDefault().notify(
0880: ErrorManager.INFORMATIONAL, e);
0881: return false;
0882: }
0883: }
0884: });
0885: }
0886:
0887: private boolean removeRawReference0(
0888: final String foreignProjectName, final String id,
0889: boolean escaped) throws IllegalArgumentException {
0890: Element references = loadReferences();
0891: if (references == null) {
0892: return false;
0893: }
0894: boolean success = removeRawReferenceElement(foreignProjectName,
0895: id, references, escaped);
0896: if (success) {
0897: storeReferences(references);
0898: }
0899: return success;
0900: }
0901:
0902: private static boolean removeRawReferenceElement(
0903: String foreignProjectName, String id, Element references,
0904: boolean escaped) throws IllegalArgumentException {
0905: // As with addRawReference, do a linear search through.
0906: for (Element testRefEl : Util.findSubElements(references)) {
0907: RawReference testRef = RawReference.create(testRefEl);
0908: String refID = testRef.getID();
0909: String refName = testRef.getForeignProjectName();
0910: if (escaped) {
0911: refID = getUsableReferenceID(testRef.getID());
0912: refName = getUsableReferenceID(testRef
0913: .getForeignProjectName());
0914: }
0915: if (refName.compareTo(foreignProjectName) > 0) {
0916: // searched past it
0917: return false;
0918: }
0919: if (refName.equals(foreignProjectName)) {
0920: if (refID.compareTo(id) > 0) {
0921: // again, searched past it
0922: return false;
0923: }
0924: if (refID.equals(id)) {
0925: // Key match, remove it.
0926: references.removeChild(testRefEl);
0927: return true;
0928: }
0929: }
0930: }
0931: // Searched through to the end and did not find it.
0932: return false;
0933: }
0934:
0935: /**
0936: * Get a list of raw references from this project to others.
0937: * If necessary, you may use {@link RawReference#toRakeArtifact} to get
0938: * live information from each reference, such as its associated project.
0939: * <p>
0940: * Acquires read access.
0941: * @return a (possibly empty) list of raw references from this project
0942: */
0943: public RawReference[] getRawReferences() {
0944: return ProjectManager.mutex().readAccess(
0945: new Mutex.Action<RawReference[]>() {
0946: public RawReference[] run() {
0947: Element references = loadReferences();
0948: if (references != null) {
0949: try {
0950: return getRawReferences(references);
0951: } catch (IllegalArgumentException e) {
0952: ErrorManager.getDefault().notify(
0953: ErrorManager.INFORMATIONAL, e);
0954: }
0955: }
0956: return new RawReference[0];
0957: }
0958: });
0959: }
0960:
0961: private static RawReference[] getRawReferences(Element references)
0962: throws IllegalArgumentException {
0963: List<Element> subEls = Util.findSubElements(references);
0964: List<RawReference> refs = new ArrayList<RawReference>(subEls
0965: .size());
0966: for (Element subEl : subEls) {
0967: refs.add(RawReference.create(subEl));
0968: }
0969: return refs.toArray(new RawReference[refs.size()]);
0970: }
0971:
0972: /**
0973: * Get a particular raw reference from this project to another.
0974: * If necessary, you may use {@link RawReference#toRakeArtifact} to get
0975: * live information from each reference, such as its associated project.
0976: * <p>
0977: * Acquires read access.
0978: * @param foreignProjectName the local name of the foreign project
0979: * (usually its code name)
0980: * @param id the ID of the build artifact (usually the build target name)
0981: * @return the specified raw reference from this project,
0982: * or null if none such could be found
0983: */
0984: public RawReference getRawReference(
0985: final String foreignProjectName, final String id) {
0986: return getRawReference(foreignProjectName, id, false);
0987: }
0988:
0989: // not private only to allow unit testing
0990: RawReference getRawReference(final String foreignProjectName,
0991: final String id, final boolean escaped) {
0992: return ProjectManager.mutex().readAccess(
0993: new Mutex.Action<RawReference>() {
0994: public RawReference run() {
0995: Element references = loadReferences();
0996: if (references != null) {
0997: try {
0998: return getRawReference(
0999: foreignProjectName, id,
1000: references, escaped);
1001: } catch (IllegalArgumentException e) {
1002: ErrorManager.getDefault().notify(
1003: ErrorManager.INFORMATIONAL, e);
1004: }
1005: }
1006: return null;
1007: }
1008: });
1009: }
1010:
1011: private static RawReference getRawReference(
1012: String foreignProjectName, String id, Element references,
1013: boolean escaped) throws IllegalArgumentException {
1014: for (Element subEl : Util.findSubElements(references)) {
1015: RawReference ref = RawReference.create(subEl);
1016: String refID = ref.getID();
1017: String refName = ref.getForeignProjectName();
1018: if (escaped) {
1019: refID = getUsableReferenceID(ref.getID());
1020: refName = getUsableReferenceID(ref
1021: .getForeignProjectName());
1022: }
1023: if (refName.equals(foreignProjectName) && refID.equals(id)) {
1024: return ref;
1025: }
1026: }
1027: return null;
1028: }
1029:
1030: /**
1031: * Create an Ant-interpretable string referring to a file on disk.
1032: * If the file refers to a known Ant artifact according to
1033: * {@link RakeArtifactQuery#findArtifactFromFile}, of the expected type
1034: * and associated with a particular project,
1035: * the behavior is identical to {@link #createForeignFileReference(RakeArtifact)}.
1036: * Otherwise, a reference for the file is created. The file path will
1037: * be relative in case {@link CollocationQuery#areCollocated} says that
1038: * the file is collocated with this project's main directory, else it
1039: * will be an absolute path.
1040: * <p>
1041: * Acquires write access.
1042: * @param file a file to refer to (need not currently exist)
1043: * @param expectedArtifactType the required {@link RakeArtifact#getType}
1044: * @return a string which can refer to that file somehow
1045: */
1046: public String createForeignFileReference(final File file,
1047: final String expectedArtifactType) {
1048: if (!file.equals(FileUtil.normalizeFile(file))) {
1049: throw new IllegalArgumentException(
1050: "Parameter file was not "
1051: + // NOI18N
1052: "normalized. Was " + file + " instead of "
1053: + FileUtil.normalizeFile(file)); // NOI18N
1054: }
1055: return ProjectManager.mutex().writeAccess(
1056: new Mutex.Action<String>() {
1057: public String run() {
1058: RakeArtifact art = RakeArtifactQuery
1059: .findArtifactFromFile(file);
1060: if (art != null
1061: && art.getType().equals(
1062: expectedArtifactType)
1063: && art.getProject() != null) {
1064: try {
1065: return createForeignFileReference(art);
1066: } catch (IllegalArgumentException iae) {
1067: throw new AssertionError(iae);
1068: }
1069: } else {
1070: String propertiesFile;
1071: String path;
1072: File myProjDir = FileUtil
1073: .toFile(RakeBasedProjectFactorySingleton
1074: .getProjectFor(h)
1075: .getProjectDirectory());
1076: String fileID = file.getName();
1077: // if the file is folder then add to ID string also parent folder name,
1078: // i.e. if external source folder name is "src" the ID will
1079: // be a bit more selfdescribing, e.g. project-src in case
1080: // of ID for ant/project/src directory.
1081: if (file.isDirectory()
1082: && file.getParentFile() != null) {
1083: fileID = file.getParentFile().getName()
1084: + "-" + file.getName();
1085: }
1086: fileID = PropertyUtils
1087: .getUsablePropertyName(fileID);
1088: String prop = findReferenceID(fileID,
1089: "file.reference.", file
1090: .getAbsolutePath()); // NOI18N
1091: if (prop == null) {
1092: prop = generateUniqueID(fileID,
1093: "file.reference.", file
1094: .getAbsolutePath()); // NOI18N
1095: }
1096: setPathProperty(myProjDir, file,
1097: "file.reference." + prop);
1098: return "${file.reference." + prop + '}'; // NOI18N
1099: }
1100: }
1101: });
1102: }
1103:
1104: /**
1105: * Test whether file does not lie under an extra base folder and if it does
1106: * then return string in form of "${extra.base}/remaining/path"; or null.
1107: */
1108: private String relativizeFileToExtraBaseFolders(File f) {
1109: File base = FileUtil.toFile(h.getProjectDirectory());
1110: String fileToRelativize = f.getAbsolutePath();
1111: for (String prop : extraBaseDirectories) {
1112: String path = eval.getProperty(prop);
1113: File extraBase = PropertyUtils.resolveFile(base, path);
1114: path = extraBase.getAbsolutePath();
1115: if (!path.endsWith(File.separator)) {
1116: path += File.separator;
1117: }
1118: if (fileToRelativize.startsWith(path)) {
1119: return "${"
1120: + prop
1121: + "}/"
1122: + fileToRelativize.substring(path.length())
1123: .replace('\\', '/'); // NOI18N
1124: }
1125: }
1126: return null;
1127: }
1128:
1129: /**
1130: * Add extra folder which can be used as base directory (in addition to
1131: * project base folder) for creating references. Duplicate property names
1132: * are not allowed. Any newly created reference to a file lying under an
1133: * extra base directory will be based on that property and will be stored in
1134: * shared project properties.
1135: * <p>Acquires write access.
1136: * @param propertyName property name which value is path to folder which
1137: * can be used as alternative project's base directory; cannot be null;
1138: * property must exist
1139: * @throws IllegalArgumentException if propertyName is null or such a
1140: * property does not exist
1141: * @since 1.4
1142: */
1143: public void addExtraBaseDirectory(final String propertyName) {
1144: if (propertyName == null
1145: || eval.getProperty(propertyName) == null) {
1146: throw new IllegalArgumentException(
1147: "propertyName is null or such a property does not exist: "
1148: + propertyName); // NOI18N
1149: }
1150: ProjectManager.mutex().writeAccess(new Runnable() {
1151: public void run() {
1152: if (!extraBaseDirectories.add(propertyName)) {
1153: throw new IllegalArgumentException(
1154: "Already extra base directory property: "
1155: + propertyName); // NOI18N
1156: }
1157: }
1158: });
1159: }
1160:
1161: /**
1162: * Remove extra base directory. The base directory property had to be added
1163: * by {@link #addExtraBaseDirectory} method call. At the time when this
1164: * method is called the property must still exist and must be valid. This
1165: * method will replace all references of the extra base directory property
1166: * with its current value and if needed it may move such a property from
1167: * shared project properties into the private properties.
1168: * <p>Acquires write access.
1169: * @param propertyName property name which was added by
1170: * {@link #addExtraBaseDirectory} method.
1171: * @throws IllegalArgumentException if given property is not extra base
1172: * directory
1173: * @since 1.4
1174: */
1175: public void removeExtraBaseDirectory(final String propertyName) {
1176: ProjectManager.mutex().writeAccess(new Runnable() {
1177: public void run() {
1178: if (!extraBaseDirectories.remove(propertyName)) {
1179: throw new IllegalArgumentException(
1180: "Non-existing extra base directory property: "
1181: + propertyName); // NOI18N
1182: }
1183: // substitute all references of removed extra base folder property with its value
1184: String tag = "${" + propertyName + "}"; // NOI18N
1185: // was extra base property defined in shared file or not:
1186: boolean shared = h.getProperties(
1187: RakeProjectHelper.PROJECT_PROPERTIES_PATH)
1188: .containsKey(propertyName);
1189: String value = eval.getProperty(propertyName);
1190: EditableProperties propProj = h
1191: .getProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH);
1192: EditableProperties propPriv = h
1193: .getProperties(RakeProjectHelper.PRIVATE_PROPERTIES_PATH);
1194: boolean modifiedProj = false;
1195: boolean modifiedPriv = false;
1196: Iterator<Map.Entry<String, String>> it = propProj
1197: .entrySet().iterator();
1198: while (it.hasNext()) {
1199: Map.Entry<String, String> entry = it.next();
1200: String val = entry.getValue();
1201: int index;
1202: if ((index = val.indexOf(tag)) != -1) {
1203: val = val.substring(0, index) + value
1204: + val.substring(index + tag.length());
1205: if (shared) {
1206: // substitute extra base folder property with its value
1207: entry.setValue(val);
1208: } else {
1209: // move property to private properties file
1210: it.remove();
1211: propPriv.put(entry.getKey(), val);
1212: modifiedPriv = true;
1213: }
1214: modifiedProj = true;
1215: }
1216: }
1217: if (modifiedProj) {
1218: h.putProperties(
1219: RakeProjectHelper.PROJECT_PROPERTIES_PATH,
1220: propProj);
1221: }
1222: if (modifiedPriv) {
1223: h.putProperties(
1224: RakeProjectHelper.PRIVATE_PROPERTIES_PATH,
1225: propPriv);
1226: }
1227: }
1228: });
1229: }
1230:
1231: /**
1232: * Find reference ID (e.g. something you can then pass to RawReference
1233: * as foreignProjectName) for the given property base name, prefix and path.
1234: * @param property project name or jar filename
1235: * @param prefix prefix used for reference, i.e. "project." for project
1236: * reference or "file.reference." for file reference
1237: * @param path absolute filename the reference points to
1238: * @return found reference ID or null
1239: */
1240: private String findReferenceID(String property, String prefix,
1241: String path) {
1242: Map<String, String> m = h.getStandardPropertyEvaluator()
1243: .getProperties();
1244: for (Map.Entry<String, String> e : m.entrySet()) {
1245: String key = e.getKey();
1246: if (key.startsWith(prefix + property)) {
1247: String v = h.resolvePath(e.getValue());
1248: if (path.equals(v)) {
1249: return key.substring(prefix.length());
1250: }
1251: }
1252: }
1253: return null;
1254: }
1255:
1256: /**
1257: * Generate unique reference ID for the given property base name, prefix
1258: * and path. See also {@link #findReferenceID(String, String, String)}.
1259: * @param property project name or jar filename
1260: * @param prefix prefix used for reference, i.e. "project." for project
1261: * reference or "file.reference." for file reference
1262: * @param path absolute filename the reference points to
1263: * @return generated unique reference ID
1264: */
1265: private String generateUniqueID(String property, String prefix,
1266: String value) {
1267: PropertyEvaluator pev = h.getStandardPropertyEvaluator();
1268: if (pev.getProperty(prefix + property) == null) {
1269: return property;
1270: }
1271: int i = 1;
1272: while (pev.getProperty(prefix + property + "-" + i) != null) {
1273: i++;
1274: }
1275: return property + "-" + i;
1276: }
1277:
1278: /**
1279: * Create an Ant-interpretable string referring to a known build artifact file.
1280: * Simply calls {@link #addReference} and returns an Ant string which will
1281: * refer to that artifact correctly.
1282: * <p>
1283: * Acquires write access.
1284: * @param artifact a known build artifact to refer to
1285: * @return a string which can refer to that artifact file somehow
1286: * @throws IllegalArgumentException if the artifact is not associated with a project
1287: * @deprecated use {@link #addReference(RakeArtifact, URI)} instead
1288: */
1289: @Deprecated
1290: public String createForeignFileReference(RakeArtifact artifact)
1291: throws IllegalArgumentException {
1292: Object ret[] = addReference0(artifact, artifact
1293: .getArtifactLocations()[0]);
1294: return (String) ret[1];
1295: }
1296:
1297: /**
1298: * Project reference ID cannot contain dot character.
1299: * File reference can.
1300: */
1301: private static String getUsableReferenceID(String ID) {
1302: return PropertyUtils.getUsablePropertyName(ID)
1303: .replace('.', '_');
1304: }
1305:
1306: private static final Pattern FOREIGN_FILE_REFERENCE = Pattern
1307: .compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\.([\\d&&[^.${}]]+)\\}"); // NOI18N
1308: private static final Pattern FOREIGN_FILE_REFERENCE_OLD = Pattern
1309: .compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\}"); // NOI18N
1310: private static final Pattern FOREIGN_PLAIN_FILE_REFERENCE = Pattern
1311: .compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N
1312:
1313: /**
1314: * Try to find an <code>RakeArtifact</code> object corresponding to a given
1315: * foreign file reference.
1316: * If the supplied string is not a recognized reference to a build
1317: * artifact, returns null.
1318: * <p>Acquires read access.
1319: * @param reference a reference string as present in an Ant property
1320: * @return a corresponding Ant artifact object if there is one, else null
1321: * @deprecated use {@link #findArtifactAndLocation} instead
1322: */
1323: @Deprecated
1324: public RakeArtifact getForeignFileReferenceAsArtifact(
1325: final String reference) {
1326: Object ret[] = findArtifactAndLocation(reference);
1327: return (RakeArtifact) ret[0];
1328: }
1329:
1330: /**
1331: * Try to find an <code>RakeArtifact</code> object and location corresponding
1332: * to a given reference. If the supplied string is not a recognized
1333: * reference to a build artifact, returns null.
1334: * <p>
1335: * Acquires read access.
1336: * @param reference a reference string as present in an Ant property and as
1337: * created by {@link #addReference(RakeArtifact, URI)}
1338: * @return always returns array of two items. The items may be null. First
1339: * one is instance of RakeArtifact and second is instance of URI and is
1340: * RakeArtifact's location
1341: * @since 1.5
1342: */
1343: public Object[] findArtifactAndLocation(final String reference) {
1344: return ProjectManager.mutex().readAccess(
1345: new Mutex.Action<Object[]>() {
1346: public Object[] run() {
1347: RakeArtifact aa = null;
1348: Matcher m = FOREIGN_FILE_REFERENCE
1349: .matcher(reference);
1350: boolean matches = m.matches();
1351: int index = 0;
1352: if (!matches) {
1353: m = FOREIGN_FILE_REFERENCE_OLD
1354: .matcher(reference);
1355: matches = m.matches();
1356: } else {
1357: try {
1358: index = Integer.parseInt(m.group(3));
1359: } catch (NumberFormatException ex) {
1360: ErrorManager
1361: .getDefault()
1362: .log(
1363: ErrorManager.INFORMATIONAL,
1364: "Could not parse reference ("
1365: + reference
1366: + ") for the jar index. "
1367: + // NOI18N
1368: "Expected number: "
1369: + m.group(3)); // NOI18N
1370: matches = false;
1371: }
1372: }
1373: if (matches) {
1374: RawReference ref = getRawReference(m
1375: .group(1), m.group(2), true);
1376: if (ref != null) {
1377: aa = ref
1378: .toRakeArtifact(ReferenceHelper.this );
1379: }
1380: }
1381: if (aa == null) {
1382: return new Object[] { null, null };
1383: }
1384: if (index >= aa.getArtifactLocations().length) {
1385: // #55413: we no longer have that many items...treat it as dead.
1386: return new Object[] { null, null };
1387: }
1388: URI uri = aa.getArtifactLocations()[index];
1389: return new Object[] { aa, uri };
1390: }
1391: });
1392: }
1393:
1394: /**
1395: * Remove a reference to a foreign file from the project.
1396: * See {@link #destroyReference} for more information.
1397: * @param reference an Ant-interpretable foreign file reference as created e.g.
1398: * by {@link #createForeignFileReference(File,String)} or
1399: * by {@link #createForeignFileReference(RakeArtifact)}
1400: * @deprecated use {@link #destroyReference} instead which does exactly
1401: * the same but has more appropriate name
1402: */
1403: @Deprecated
1404: public void destroyForeignFileReference(String reference) {
1405: destroyReference(reference);
1406: }
1407:
1408: /**
1409: * Remove a reference to a foreign file from the project.
1410: * If the passed string consists of an Ant property reference corresponding to
1411: * a known inter-project reference created by
1412: * {@link #addReference(RakeArtifact, URI)} or file reference created by
1413: * {@link #createForeignFileReference(File, String)}, that reference is removed.
1414: * Since this would break any other identical foreign
1415: * file references present in the project, you should first confirm that this
1416: * reference was the last one of its kind (by string match).
1417: * <p>
1418: * If the passed string is anything else (i.e. a plain file path, relative or
1419: * absolute), nothing is done.
1420: * <p>
1421: * Acquires write access.
1422: * @param reference an Ant-interpretable foreign file reference as created e.g.
1423: * by {@link #createForeignFileReference(File,String)} or
1424: * by {@link #createForeignFileReference(RakeArtifact)}
1425: * @return true if reference was really destroyed or not
1426: * @since 1.5
1427: */
1428: public boolean destroyReference(String reference) {
1429: Matcher m = FOREIGN_FILE_REFERENCE.matcher(reference);
1430: boolean matches = m.matches();
1431: if (!matches) {
1432: m = FOREIGN_FILE_REFERENCE_OLD.matcher(reference);
1433: matches = m.matches();
1434: }
1435: if (matches) {
1436: String forProjName = m.group(1);
1437: String id = m.group(2);
1438: return removeReference(forProjName, id, true, reference
1439: .substring(2, reference.length() - 1));
1440: }
1441: m = FOREIGN_PLAIN_FILE_REFERENCE.matcher(reference);
1442: if (m.matches()) {
1443: return removeFileReference(reference);
1444: }
1445: return false;
1446: }
1447:
1448: /**
1449: * Create an object permitting this project to represent subprojects.
1450: * Would be placed into the project's lookup.
1451: * @return a subproject provider object suitable for the project lookup
1452: * @see Project#getLookup
1453: */
1454: public SubprojectProvider createSubprojectProvider() {
1455: return new SubprojectProviderImpl(this );
1456: }
1457:
1458: /**
1459: * Access from SubprojectProviderImpl.
1460: */
1461: RakeProjectHelper getRakeProjectHelper() {
1462: return h;
1463: }
1464:
1465: /**Tries to fix references after copy/rename/move operation on the project.
1466: * Handles relative/absolute paths.
1467: *
1468: * @param originalPath the project folder of the original project
1469: * @see org.netbeans.spi.project.CopyOperationImplementation
1470: * @see org.netbeans.spi.project.MoveOperationImplementation
1471: * @since 1.9
1472: */
1473: public void fixReferences(File originalPath) {
1474: String[] prefixesToFix = new String[] { "file.reference.",
1475: "project." };
1476: EditableProperties pub = h
1477: .getProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH);
1478: EditableProperties priv = h
1479: .getProperties(RakeProjectHelper.PRIVATE_PROPERTIES_PATH);
1480:
1481: File projectDir = FileUtil.toFile(h.getProjectDirectory());
1482:
1483: List<String> pubRemove = new ArrayList<String>();
1484: List<String> privRemove = new ArrayList<String>();
1485: Map<String, String> pubAdd = new HashMap<String, String>();
1486: Map<String, String> privAdd = new HashMap<String, String>();
1487:
1488: for (Map.Entry<String, String> e : pub.entrySet()) {
1489: String key = e.getKey();
1490: boolean cont = false;
1491:
1492: for (String prefix : prefixesToFix) {
1493: if (key.startsWith(prefix)) {
1494: cont = true;
1495: break;
1496: }
1497: }
1498: if (!cont)
1499: continue;
1500:
1501: String value = e.getValue();
1502:
1503: File absolutePath = FileUtil.normalizeFile(PropertyUtils
1504: .resolveFile(originalPath, value));
1505:
1506: //TODO: extra base dir relativization:
1507: if (!CollocationQuery.areCollocated(absolutePath,
1508: projectDir)) {
1509: pubRemove.add(key);
1510: privAdd.put(key, absolutePath.getAbsolutePath());
1511: }
1512: }
1513:
1514: for (Map.Entry<String, String> e : pub.entrySet()) {
1515: String key = e.getKey();
1516: boolean cont = false;
1517:
1518: for (String prefix : prefixesToFix) {
1519: if (key.startsWith(prefix)) {
1520: cont = true;
1521: break;
1522: }
1523: }
1524: if (!cont)
1525: continue;
1526:
1527: String value = e.getValue();
1528:
1529: File absolutePath = FileUtil.normalizeFile(PropertyUtils
1530: .resolveFile(originalPath, value));
1531:
1532: if (absolutePath.getAbsolutePath().startsWith(
1533: originalPath.getAbsolutePath())) {
1534: //#65141: in private.properties, a full path into originalPath may be given, fix:
1535: String relative = PropertyUtils.relativizeFile(
1536: originalPath, absolutePath);
1537:
1538: absolutePath = new File(projectDir, relative);
1539:
1540: privRemove.add(key);
1541: privAdd.put(key, absolutePath.getAbsolutePath());
1542: }
1543:
1544: //TODO: extra base dir relativization:
1545: if (CollocationQuery
1546: .areCollocated(absolutePath, projectDir)) {
1547: pubAdd.put(key, PropertyUtils.relativizeFile(
1548: projectDir, absolutePath));
1549: }
1550: }
1551:
1552: for (String s : pubRemove) {
1553: pub.remove(s);
1554: }
1555:
1556: for (String s : privRemove) {
1557: priv.remove(s);
1558: }
1559:
1560: pub.putAll(pubAdd);
1561: priv.putAll(privAdd);
1562:
1563: h.putProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH, pub);
1564: h
1565: .putProperties(
1566: RakeProjectHelper.PRIVATE_PROPERTIES_PATH, priv);
1567: }
1568:
1569: /**
1570: * A raw reference descriptor representing a link to a foreign project
1571: * and some build artifact used from it.
1572: * This class corresponds directly to what it stored in <code>project.xml</code>
1573: * to refer to a target in a foreign project.
1574: * See {@link RakeArtifact} for the precise meaning of several of the fields in this class.
1575: */
1576: public static final class RawReference {
1577:
1578: private final String foreignProjectName;
1579: private final String artifactType;
1580: private URI scriptLocation;
1581: // introduced in /2 version
1582: private String newScriptLocation;
1583: private final String targetName;
1584: private final String cleanTargetName;
1585: private final String artifactID;
1586: private final Properties props;
1587:
1588: /**
1589: * Create a raw reference descriptor.
1590: * As this is basically just a struct, does no real work.
1591: * @param foreignProjectName the name of the foreign project (usually its code name)
1592: * @param artifactType the {@link RakeArtifact#getType type} of the build artifact
1593: * @param scriptLocation the relative URI to the build script from the project directory
1594: * @param targetName the Ant target name
1595: * @param cleanTargetName the Ant clean target name
1596: * @param artifactID the {@link RakeArtifact#getID ID} of the build artifact
1597: * @throws IllegalArgumentException if the script location is given an absolute URI
1598: */
1599: public RawReference(String foreignProjectName,
1600: String artifactType, URI scriptLocation,
1601: String targetName, String cleanTargetName,
1602: String artifactID) throws IllegalArgumentException {
1603: this (foreignProjectName, artifactType, scriptLocation,
1604: null, targetName, cleanTargetName, artifactID,
1605: new Properties());
1606: }
1607:
1608: /**
1609: * Create a raw reference descriptor.
1610: * As this is basically just a struct, does no real work.
1611: * @param foreignProjectName the name of the foreign project (usually its code name)
1612: * @param artifactType the {@link RakeArtifact#getType type} of the build artifact
1613: * @param newScriptLocation absolute path to the build script; can contain Ant-like properties
1614: * @param targetName the Ant target name
1615: * @param cleanTargetName the Ant clean target name
1616: * @param artifactID the {@link RakeArtifact#getID ID} of the build artifact
1617: * @param props optional properties to be used for target execution; never null
1618: * @throws IllegalArgumentException if the script location is given an absolute URI
1619: * @since 1.5
1620: */
1621: public RawReference(String foreignProjectName,
1622: String artifactType, String newScriptLocation,
1623: String targetName, String cleanTargetName,
1624: String artifactID, Properties props)
1625: throws IllegalArgumentException {
1626: this (foreignProjectName, artifactType, null,
1627: newScriptLocation, targetName, cleanTargetName,
1628: artifactID, props);
1629: }
1630:
1631: private RawReference(String foreignProjectName,
1632: String artifactType, URI scriptLocation,
1633: String newScriptLocation, String targetName,
1634: String cleanTargetName, String artifactID,
1635: Properties props) throws IllegalArgumentException {
1636: this .foreignProjectName = foreignProjectName;
1637: this .artifactType = artifactType;
1638: if (scriptLocation != null && scriptLocation.isAbsolute()) {
1639: throw new IllegalArgumentException(
1640: "Cannot use an absolute URI " + scriptLocation
1641: + " for script location"); // NOI18N
1642: }
1643: this .scriptLocation = scriptLocation;
1644: this .newScriptLocation = newScriptLocation;
1645: this .targetName = targetName;
1646: this .cleanTargetName = cleanTargetName;
1647: this .artifactID = artifactID;
1648: this .props = props;
1649: }
1650:
1651: private static final List/*<String>*/SUB_ELEMENT_NAMES = Arrays
1652: .asList(new String[] { "foreign-project", // NOI18N
1653: "artifact-type", // NOI18N
1654: "script", // NOI18N
1655: "target", // NOI18N
1656: "clean-target", // NOI18N
1657: "id", // NOI18N
1658: });
1659:
1660: /**
1661: * Create a RawReference by parsing an XML <reference> fragment.
1662: * @throws IllegalArgumentException if anything is missing or duplicated or malformed etc.
1663: */
1664: static RawReference create(Element xml)
1665: throws IllegalArgumentException {
1666: if (REFS_NS.equals(xml.getNamespaceURI())) {
1667: return create1(xml);
1668: } else {
1669: return create2(xml);
1670: }
1671: }
1672:
1673: private static RawReference create1(Element xml)
1674: throws IllegalArgumentException {
1675: if (!REF_NAME.equals(xml.getLocalName())
1676: || !REFS_NS.equals(xml.getNamespaceURI())) {
1677: throw new IllegalArgumentException("bad element name: "
1678: + xml); // NOI18N
1679: }
1680: NodeList nl = xml.getElementsByTagNameNS("*", "*"); // NOI18N
1681: if (nl.getLength() != 6) {
1682: throw new IllegalArgumentException(
1683: "missing or extra data: " + xml); // NOI18N
1684: }
1685: String[] values = new String[nl.getLength()];
1686: for (int i = 0; i < nl.getLength(); i++) {
1687: Element el = (Element) nl.item(i);
1688: if (!REFS_NS.equals(el.getNamespaceURI())) {
1689: throw new IllegalArgumentException(
1690: "bad subelement ns: " + el); // NOI18N
1691: }
1692: String elName = el.getLocalName();
1693: int idx = SUB_ELEMENT_NAMES.indexOf(elName);
1694: if (idx == -1) {
1695: throw new IllegalArgumentException(
1696: "bad subelement name: " + elName); // NOI18N
1697: }
1698: String val = Util.findText(el);
1699: if (val == null) {
1700: throw new IllegalArgumentException(
1701: "empty subelement: " + el); // NOI18N
1702: }
1703: if (values[idx] != null) {
1704: throw new IllegalArgumentException("duplicate "
1705: + elName + ": " + values[idx] + " and "
1706: + val); // NOI18N
1707: }
1708: values[idx] = val;
1709: }
1710: assert !Arrays.asList(values).contains(null);
1711: URI scriptLocation = URI.create(values[2]); // throws IllegalArgumentException
1712: return new RawReference(values[0], values[1],
1713: scriptLocation, values[3], values[4], values[5]);
1714: }
1715:
1716: private static RawReference create2(Element xml)
1717: throws IllegalArgumentException {
1718: if (!REF_NAME.equals(xml.getLocalName())
1719: || !REFS_NS2.equals(xml.getNamespaceURI())) {
1720: throw new IllegalArgumentException("bad element name: "
1721: + xml); // NOI18N
1722: }
1723: List nl = Util.findSubElements(xml);
1724: if (nl.size() < 6) {
1725: throw new IllegalArgumentException(
1726: "missing or extra data: " + xml); // NOI18N
1727: }
1728: String[] values = new String[6];
1729: for (int i = 0; i < 6; i++) {
1730: Element el = (Element) nl.get(i);
1731: if (!REFS_NS2.equals(el.getNamespaceURI())) {
1732: throw new IllegalArgumentException(
1733: "bad subelement ns: " + el); // NOI18N
1734: }
1735: String elName = el.getLocalName();
1736: int idx = SUB_ELEMENT_NAMES.indexOf(elName);
1737: if (idx == -1) {
1738: throw new IllegalArgumentException(
1739: "bad subelement name: " + elName); // NOI18N
1740: }
1741: String val = Util.findText(el);
1742: if (val == null) {
1743: throw new IllegalArgumentException(
1744: "empty subelement: " + el); // NOI18N
1745: }
1746: if (values[idx] != null) {
1747: throw new IllegalArgumentException("duplicate "
1748: + elName + ": " + values[idx] + " and "
1749: + val); // NOI18N
1750: }
1751: values[idx] = val;
1752: }
1753: Properties props = new Properties();
1754: if (nl.size() == 7) {
1755: Element el = (Element) nl.get(6);
1756: if (!REFS_NS2.equals(el.getNamespaceURI())) {
1757: throw new IllegalArgumentException(
1758: "bad subelement ns: " + el); // NOI18N
1759: }
1760: if (!"properties".equals(el.getLocalName())) { // NOI18N
1761: throw new IllegalArgumentException(
1762: "bad subelement. expected 'properties': "
1763: + el); // NOI18N
1764: }
1765: for (Element el2 : Util.findSubElements(el)) {
1766: String key = el2.getAttribute("name");
1767: String value = Util.findText(el2);
1768: // #53553: NPE
1769: if (value == null) {
1770: value = ""; // NOI18N
1771: }
1772: props.setProperty(key, value);
1773: }
1774: }
1775: assert !Arrays.asList(values).contains(null);
1776: return new RawReference(values[0], values[1], values[2],
1777: values[3], values[4], values[5], props);
1778: }
1779:
1780: /**
1781: * Write a RawReference as an XML <reference> fragment.
1782: */
1783: Element toXml(String namespace, Document ownerDocument) {
1784: Element el = ownerDocument.createElementNS(namespace,
1785: REF_NAME);
1786: String[] values = {
1787: foreignProjectName,
1788: artifactType,
1789: newScriptLocation != null ? newScriptLocation
1790: : scriptLocation.toString(), targetName,
1791: cleanTargetName, artifactID, };
1792: for (int i = 0; i < 6; i++) {
1793: Element subel = ownerDocument.createElementNS(
1794: namespace, (String) SUB_ELEMENT_NAMES.get(i));
1795: subel.appendChild(ownerDocument
1796: .createTextNode(values[i]));
1797: el.appendChild(subel);
1798: }
1799: if (props.keySet().size() > 0) {
1800: assert namespace.equals(REFS_NS2) : "can happen only in /2"; // NOI18N
1801: Element propEls = ownerDocument.createElementNS(
1802: namespace, "properties"); // NOI18N
1803: el.appendChild(propEls);
1804: for (String key : new TreeSet<String>(NbCollections
1805: .checkedSetByFilter(props.keySet(),
1806: String.class, true))) {
1807: Element propEl = ownerDocument.createElementNS(
1808: namespace, "property"); // NOI18N
1809: propEl.appendChild(ownerDocument
1810: .createTextNode(props.getProperty(key)));
1811: propEl.setAttribute("name", key); // NOI18N
1812: propEls.appendChild(propEl);
1813: }
1814: }
1815: return el;
1816: }
1817:
1818: private String getNS() {
1819: if (newScriptLocation != null) {
1820: return REFS_NS2;
1821: } else {
1822: return REFS_NS;
1823: }
1824: }
1825:
1826: /**
1827: * Get the name of the foreign project as referred to from this project.
1828: * Usually this will be the code name of the foreign project, but it may
1829: * instead be a uniquified name.
1830: * The name can be used in project properties and the build script to refer
1831: * to the foreign project from among subprojects.
1832: * @return the foreign project name
1833: */
1834: public String getForeignProjectName() {
1835: return foreignProjectName;
1836: }
1837:
1838: /**
1839: * Get the type of the foreign project's build artifact.
1840: * For example, <a href="@JAVA/PROJECT@/org/netbeans/modules/gsfpath/api/project/JavaProjectConstants.html#ARTIFACT_TYPE_JAR"><code>JavaProjectConstants.ARTIFACT_TYPE_JAR</code></a>.
1841: * @return the artifact type
1842: */
1843: public String getArtifactType() {
1844: return artifactType;
1845: }
1846:
1847: /**
1848: * Get the location of the foreign project's build script relative to the
1849: * project directory.
1850: * This is the script which would be called to build the desired artifact.
1851: * @return the script location
1852: * @deprecated use {@link #getScriptLocationValue} instead; may return null now
1853: */
1854: @Deprecated
1855: public URI getScriptLocation() {
1856: return scriptLocation;
1857: }
1858:
1859: /**
1860: * Get absolute path location of the foreign project's build script.
1861: * This is the script which would be called to build the desired artifact.
1862: * @return absolute path possibly containing Ant properties
1863: */
1864: public String getScriptLocationValue() {
1865: if (newScriptLocation != null) {
1866: return newScriptLocation;
1867: } else {
1868: return "${project." + foreignProjectName + "}/"
1869: + scriptLocation.toString();
1870: }
1871: }
1872:
1873: /**
1874: * Get the Ant target name to build the artifact.
1875: * @return the target name
1876: */
1877: public String getTargetName() {
1878: return targetName;
1879: }
1880:
1881: /**
1882: * Get the Ant target name to clean the artifact.
1883: * @return the clean target name
1884: */
1885: public String getCleanTargetName() {
1886: return cleanTargetName;
1887: }
1888:
1889: /**
1890: * Get the ID of the foreign project's build artifact.
1891: * See also {@link RakeArtifact#getID}.
1892: * @return the artifact identifier
1893: */
1894: public String getID() {
1895: return artifactID;
1896: }
1897:
1898: public Properties getProperties() {
1899: return props;
1900: }
1901:
1902: /**
1903: * Attempt to convert this reference to a live artifact object.
1904: * This involves finding the referenced foreign project on disk
1905: * (among standard project and private properties) and asking it
1906: * for the artifact named by the given target.
1907: * Given that object, you can find important further information
1908: * such as the location of the actual artifact on disk.
1909: * <p>
1910: * Note that non-key attributes of the returned artifact (i.e.
1911: * type, script location, and clean target name) might not match
1912: * those in this raw reference.
1913: * <p>
1914: * Acquires read access.
1915: * @param helper an associated reference helper used to resolve the foreign
1916: * project location
1917: * @return the actual Ant artifact object, or null if it could not be located
1918: */
1919: public RakeArtifact toRakeArtifact(final ReferenceHelper helper) {
1920: return ProjectManager.mutex().readAccess(
1921: new Mutex.Action<RakeArtifact>() {
1922: public RakeArtifact run() {
1923: RakeProjectHelper h = helper.h;
1924: String path = helper.eval
1925: .getProperty("project."
1926: + foreignProjectName); // NOI18N
1927: if (path == null) {
1928: // Undefined foreign project.
1929: return null;
1930: }
1931: FileObject foreignProjectDir = h
1932: .resolveFileObject(path);
1933: if (foreignProjectDir == null) {
1934: // Nonexistent foreign project dir.
1935: return null;
1936: }
1937: Project p;
1938: try {
1939: p = ProjectManager.getDefault()
1940: .findProject(foreignProjectDir);
1941: } catch (IOException e) {
1942: // Could not load it.
1943: ErrorManager.getDefault().notify(
1944: ErrorManager.INFORMATIONAL, e);
1945: return null;
1946: }
1947: if (p == null) {
1948: // Was not a project dir.
1949: return null;
1950: }
1951: return RakeArtifactQuery.findArtifactByID(
1952: p, artifactID);
1953: }
1954: });
1955: }
1956:
1957: private void upgrade() {
1958: assert newScriptLocation == null && scriptLocation != null : "was already upgraded "
1959: + this ;
1960: newScriptLocation = "${project." + foreignProjectName
1961: + "}/" + scriptLocation.toString(); // NOI18N
1962: scriptLocation = null;
1963: }
1964:
1965: public String toString() {
1966: return "ReferenceHelper.RawReference<" + foreignProjectName
1967: + "," + artifactType + "," + newScriptLocation != null ? newScriptLocation
1968: : scriptLocation + "," + targetName + ","
1969: + cleanTargetName + "," + artifactID + ">"; // NOI18N
1970: }
1971:
1972: }
1973:
1974: }
|