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