001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.api.project.ant;
043:
044: import java.io.File;
045: import java.net.MalformedURLException;
046: import java.net.URI;
047: import java.net.URL;
048: import java.util.ArrayList;
049: import java.util.Collections;
050: import java.util.HashSet;
051: import java.util.List;
052: import java.util.Properties;
053: import java.util.Set;
054: import java.util.logging.Logger;
055: import org.netbeans.api.project.FileOwnerQuery;
056: import org.netbeans.api.project.Project;
057: import org.openide.ErrorManager;
058: import org.openide.filesystems.FileObject;
059: import org.openide.filesystems.FileUtil;
060: import org.openide.filesystems.URLMapper;
061:
062: // XXX may also need displayName field (any default? or only in SimpleAntArtifact?)
063:
064: /**
065: * Represents one artifact of an Ant build.
066: * For example, if a build script is known to generate a JAR of a certain name
067: * as a result of running a certain target, this object will name that JAR
068: * and point to the script and target responsible for creating it. You can use
069: * this information to add an <samp><ant></samp> task to another project
070: * which will generate that JAR as a dependency before using it.
071: * @see org.netbeans.spi.project.support.ant.SimpleAntArtifact
072: * @author Jesse Glick
073: */
074: public abstract class AntArtifact {
075:
076: private final Properties PROPS = new Properties();
077:
078: /**
079: * Empty constructor for use from subclasses.
080: */
081: protected AntArtifact() {
082: try {
083: if (getClass().getMethod("getArtifactLocation")
084: .getDeclaringClass() == AntArtifact.class
085: && // NOI18N
086: getClass().getMethod("getArtifactLocations")
087: .getDeclaringClass() == AntArtifact.class) { // NOI18N
088: // #72308: at least one must be overridden
089: throw new IllegalStateException(getClass().getName()
090: + ".getArtifactLocations() must be overridden"); // NOI18N
091: }
092: } catch (NoSuchMethodException x) {
093: throw new AssertionError(x);
094: }
095: }
096:
097: /**
098: * Get the type of the build artifact.
099: * This can refer to both the physical content type or format;
100: * and to the intended category of usage.
101: * Typically a given client (e.g. superproject) will be interested
102: * in only a certain artifact type for a certain purpose, e.g.
103: * inclusion in a Java classpath.
104: * <p>
105: * Particular type identifiers should be agreed upon between
106: * providers and clients.
107: * For example, <a href="@JAVA/PROJECT@/org/netbeans/api/java/project/JavaProjectConstants.html#ARTIFACT_TYPE_JAR"><code>JavaProjectConstants.ARTIFACT_TYPE_JAR</code></a>
108: * is defined for JAR outputs.
109: * Others may be defined as needed; for example, tag library JARs,
110: * WARs, EJB JARs, deployment descriptor fragments, etc.
111: * XXX format - NMTOKEN maybe
112: * @return the type (format or usage) of the build artifact
113: */
114: public abstract String getType();
115:
116: /**
117: * Get a location for the Ant script that is able to produce this artifact.
118: * The name <samp>build.xml</samp> is conventional.
119: * @return the location of an Ant project file (might not currently exist)
120: */
121: public abstract File getScriptLocation();
122:
123: /**
124: * Get the name of the Ant target that is able to produce this artifact.
125: * E.g. <samp>jar</samp> would be conventional for JAR artifacts.
126: * @return an Ant target name
127: */
128: public abstract String getTargetName();
129:
130: /**
131: * Get the name of an Ant target that will delete this artifact.
132: * Typically this should be <samp>clean</samp>.
133: * The target may delete other build products as well.
134: * @return an Ant target name
135: */
136: public abstract String getCleanTargetName();
137:
138: /**
139: * Get the location of the build artifact relative to the Ant script.
140: * See {@link #getArtifactLocations}.
141: * @return a URI to the build artifact, resolved relative to {@link #getScriptLocation};
142: * may be either relative, or an absolute <code>file</code>-protocol URI
143: * @deprecated use {@link #getArtifactLocations} instead
144: */
145: @Deprecated
146: public URI getArtifactLocation() {
147: return getArtifactLocations()[0];
148: }
149:
150: private static final Set<String> warnedClasses = Collections
151: .synchronizedSet(new HashSet<String>());
152:
153: /**
154: * Get the locations of the build artifacts relative to the Ant script.
155: * For example, <samp>dist/mylib.jar</samp>. The method is not defined
156: * as abstract only for backward compatibility reasons. <strong>It must be
157: * overridden.</strong> The order is important and should stay the same
158: * unless the artifact was changed.
159: * @return an array of URIs to the build artifacts, resolved relative to {@link #getScriptLocation};
160: * may be either relative, or an absolute <code>file</code>-protocol URI
161: * @since 1.5
162: */
163: public URI[] getArtifactLocations() {
164: String name = getClass().getName();
165: if (warnedClasses.add(name)) {
166: Logger
167: .getLogger(AntArtifact.class.getName())
168: .warning(
169: name
170: + ".getArtifactLocations() must be overridden");
171: }
172: return new URI[] { getArtifactLocation() };
173: }
174:
175: /**
176: * Returns identifier of the AntArtifact which must be <strong>unique within
177: * one project</strong>. By default it is target name which produces the
178: * artifact, but if your target produces more that one artifact then
179: * you must override this method and uniquely identify each artifact.
180: */
181: public String getID() {
182: return getTargetName();
183: }
184:
185: /**
186: * Convenience method to find the actual artifact, if it currently exists.
187: * See {@link #getArtifactFiles}.
188: * @return the artifact file on disk, or null if it could not be found
189: * @deprecated use {@link #getArtifactFiles} instead
190: */
191: @Deprecated
192: public final FileObject getArtifactFile() {
193: FileObject fos[] = getArtifactFiles();
194: if (fos.length > 0) {
195: return fos[0];
196: } else {
197: return null;
198: }
199: }
200:
201: private FileObject getArtifactFile(URI artifactLocation) {
202: assert !artifactLocation.isAbsolute()
203: || (!artifactLocation.isOpaque() && "file"
204: .equals(artifactLocation.getScheme())) // NOI18N
205: : artifactLocation;
206: URL artifact;
207: try {
208: // XXX this should probably use something in PropertyUtils?
209: artifact = getScriptLocation().toURI().resolve(
210: artifactLocation).normalize().toURL();
211: } catch (MalformedURLException e) {
212: ErrorManager.getDefault().notify(
213: ErrorManager.INFORMATIONAL, e);
214: return null;
215: }
216: FileObject fo = URLMapper.findFileObject(artifact);
217: if (fo != null) {
218: assert FileUtil.toFile(fo) != null : fo;
219: return fo;
220: } else {
221: return null;
222: }
223: }
224:
225: /**
226: * Convenience method to find the actual artifacts, if they currently exist.
227: * Uses {@link #getScriptFile} or {@link #getScriptLocation} and resolves {@link #getArtifactLocations} from it.
228: * Note that a project which has been cleaned more recently than it has been built
229: * will generally not have the build artifacts on disk and so this call may easily
230: * return empty array. If you do not rely on the actual presence of the file but just need to
231: * refer to it abstractly, use {@link #getArtifactLocations} instead.
232: * @return the artifact files which exist on disk, or empty array if none could be found
233: * @since 1.5
234: */
235: public final FileObject[] getArtifactFiles() {
236: List<FileObject> l = new ArrayList<FileObject>();
237: for (URI artifactLocation : getArtifactLocations()) {
238: FileObject fo = getArtifactFile(artifactLocation);
239: if (fo != null) {
240: l.add(fo);
241: }
242: }
243: return l.toArray(new FileObject[l.size()]);
244: }
245:
246: /**
247: * Convenience method to find the actual script file, if it currently exists.
248: * Uses {@link #getScriptLocation}.
249: * The script must exist on disk (Ant cannot run scripts from NetBeans
250: * filesystems unless they are represented on disk).
251: * @return the Ant build script file, or null if it could not be found
252: */
253: public final FileObject getScriptFile() {
254: FileObject fo = FileUtil.toFileObject(getScriptLocation());
255: assert fo == null || FileUtil.toFile(fo) != null : fo;
256: return fo;
257: }
258:
259: /**
260: * Find the project associated with this script, if any.
261: * The default implementation uses {@link #getScriptLocation} and {@link FileOwnerQuery},
262: * but subclasses may override that to return something else.
263: * @return the associated project, or null if there is none or it could not be located
264: */
265: public Project getProject() {
266: return FileOwnerQuery.getOwner(getScriptLocation().toURI());
267: }
268:
269: /**
270: * Optional properties which are used for Ant target execution. Only
271: * properties necessary for customization of Ant target execution should
272: * be used. These properties are stored in project.xml of project using
273: * this artifact so care should be taken in defining what properties
274: * are used, e.g. never use absolute path like values
275: * @since 1.5
276: */
277: public Properties getProperties() {
278: return PROPS;
279: }
280:
281: }
|