/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.netbeans.modules.ruby.api.project.rake;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.openide.ErrorManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
// XXX may also need displayName field (any default? or only in SimpleRakeArtifact?)
/**
* Represents one artifact of an Ant build.
* For example, if a build script is known to generate a JAR of a certain name
* as a result of running a certain target, this object will name that JAR
* and point to the script and target responsible for creating it. You can use
* this information to add an <samp><ant></samp> task to another project
* which will generate that JAR as a dependency before using it.
* @see org.netbeans.modules.ruby.spi.project.support.rake.SimpleRakeArtifact
* @author Jesse Glick
*/
public abstract class RakeArtifact {
private final Properties PROPS = new Properties();
/**
* Empty constructor for use from subclasses.
*/
protected RakeArtifact() {}
/**
* Get the type of the build artifact.
* This can refer to both the physical content type or format;
* and to the intended category of usage.
* Typically a given client (e.g. superproject) will be interested
* in only a certain artifact type for a certain purpose, e.g.
* inclusion in a Java classpath.
* <p>
* Particular type identifiers should be agreed upon between
* providers and clients.
* For example, <a href="@org-netbeans-modules-java-project@/org/netbeans/modules/gsfpath/api/project/JavaProjectConstants.html#ARTIFACT_TYPE_JAR"><code>JavaProjectConstants.ARTIFACT_TYPE_JAR</code></a>
* is defined for JAR outputs.
* Others may be defined as needed; for example, tag library JARs,
* WARs, EJB JARs, deployment descriptor fragments, etc.
* XXX format - NMTOKEN maybe
* @return the type (format or usage) of the build artifact
*/
public abstract String getType();
/**
* Get a location for the Ant script that is able to produce this artifact.
* The name <samp>build.xml</samp> is conventional.
* @return the location of an Ant project file (might not currently exist)
*/
public abstract File getScriptLocation();
/**
* Get the name of the Ant target that is able to produce this artifact.
* E.g. <samp>jar</samp> would be conventional for JAR artifacts.
* @return an Ant target name
*/
public abstract String getTargetName();
/**
* Get the name of an Ant target that will delete this artifact.
* Typically this should be <samp>clean</samp>.
* The target may delete other build products as well.
* @return an Ant target name
*/
public abstract String getCleanTargetName();
/**
* Get the location of the build artifact relative to the Ant script.
* See {@link #getArtifactLocations}.
* @return a URI to the build artifact, resolved relative to {@link #getScriptLocation};
* may be either relative, or an absolute <code>file</code>-protocol URI
* @deprecated use {@link #getArtifactLocations} instead
*/
@Deprecated
public URI getArtifactLocation() {
return getArtifactLocations()[0];
}
private static final Set<String> warnedClasses = Collections.synchronizedSet(new HashSet<String>());
/**
* Get the locations of the build artifacts relative to the Ant script.
* For example, <samp>dist/mylib.jar</samp>. The method is not defined
* as abstract only for backward compatibility reasons. <strong>It must be
* overridden.</strong> The order is important and should stay the same
* unless the artifact was changed.
* @return an array of URIs to the build artifacts, resolved relative to {@link #getScriptLocation};
* may be either relative, or an absolute <code>file</code>-protocol URI
* @since 1.5
*/
public URI[] getArtifactLocations() {
String name = getClass().getName();
if (warnedClasses.add(name)) {
ErrorManager.getDefault().log(ErrorManager.WARNING, "Warning: " + name + ".getArtifactLocations() must be overridden");
}
return new URI[]{getArtifactLocation()};
}
/**
* Returns identifier of the RakeArtifact which must be <strong>unique within
* one project</strong>. By default it is target name which produces the
* artifact, but if your target produces more that one artifact then
* you must override this method and uniquely identify each artifact.
*/
public String getID() {
return getTargetName();
}
/**
* Convenience method to find the actual artifact, if it currently exists.
* See {@link #getArtifactFiles}.
* @return the artifact file on disk, or null if it could not be found
* @deprecated use {@link #getArtifactFiles} instead
*/
@Deprecated
public final FileObject getArtifactFile() {
FileObject fos[] = getArtifactFiles();
if (fos.length > 0) {
return fos[0];
} else {
return null;
}
}
private FileObject getArtifactFile(URI artifactLocation) {
assert !artifactLocation.isAbsolute() ||
(!artifactLocation.isOpaque() && "file".equals(artifactLocation.getScheme())) // NOI18N
: artifactLocation;
URL artifact;
try {
// XXX this should probably use something in PropertyUtils?
artifact = getScriptLocation().toURI().resolve(artifactLocation).normalize().toURL();
} catch (MalformedURLException e) {
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
return null;
}
FileObject fo = URLMapper.findFileObject(artifact);
if (fo != null) {
assert FileUtil.toFile(fo) != null : fo;
return fo;
} else {
return null;
}
}
/**
* Convenience method to find the actual artifacts, if they currently exist.
* Uses {@link #getScriptFile} or {@link #getScriptLocation} and resolves {@link #getArtifactLocations} from it.
* Note that a project which has been cleaned more recently than it has been built
* will generally not have the build artifacts on disk and so this call may easily
* return empty array. If you do not rely on the actual presence of the file but just need to
* refer to it abstractly, use {@link #getArtifactLocations} instead.
* @return the artifact files which exist on disk, or empty array if none could be found
* @since 1.5
*/
public final FileObject[] getArtifactFiles() {
URI artifactLocations[] = getArtifactLocations();
List<FileObject> l = new ArrayList<FileObject>();
for (int i=0; i<artifactLocations.length; i++) {
FileObject fo = getArtifactFile(artifactLocations[i]);
if (fo != null) {
l.add(fo);
}
}
return l.toArray(new FileObject[l.size()]);
}
/**
* Convenience method to find the actual script file, if it currently exists.
* Uses {@link #getScriptLocation}.
* The script must exist on disk (Ant cannot run scripts from NetBeans
* filesystems unless they are represented on disk).
* @return the Ant build script file, or null if it could not be found
*/
public final FileObject getScriptFile() {
FileObject fo = FileUtil.toFileObject(getScriptLocation());
assert fo == null || FileUtil.toFile(fo) != null : fo;
return fo;
}
/**
* Find the project associated with this script, if any.
* The default implementation uses {@link #getScriptLocation} and {@link FileOwnerQuery},
* but subclasses may override that to return something else.
* @return the associated project, or null if there is none or it could not be located
*/
public Project getProject() {
return FileOwnerQuery.getOwner(getScriptLocation().toURI());
}
/**
* Optional properties which are used for Ant target execution. Only
* properties necessary for customization of Ant target execution should
* be used. These properties are stored in project.xml of project using
* this artifact so care should be taken in defining what properties
* are used, e.g. never use absolute path like values
* @since 1.5
*/
public Properties getProperties() {
return PROPS;
}
}