Index: ant/project/apichanges.xml
===================================================================
RCS file: /cvs/ant/project/apichanges.xml,v
retrieving revision 1.15
diff -u -r1.15 apichanges.xml
--- ant/project/apichanges.xml 2 Mar 2007 03:34:19 -0000 1.15
+++ ant/project/apichanges.xml 6 Apr 2007 13:50:17 -0000
@@ -81,6 +81,26 @@
+
+
+
+ Support for externally extending the project's build script
+
+
+
+
+
+
+ Add framework for extending the project's build script with 3rd party snippets,
+ allowing automated extensions to the build process.
+
+
+
+
+
+
+
+
Index: ant/project/manifest.mf
===================================================================
RCS file: /cvs/ant/project/manifest.mf,v
retrieving revision 1.19
diff -u -r1.19 manifest.mf
--- ant/project/manifest.mf 2 Mar 2007 03:34:19 -0000 1.19
+++ ant/project/manifest.mf 6 Apr 2007 13:50:17 -0000
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.modules.project.ant/1
-OpenIDE-Module-Specification-Version: 1.15
+OpenIDE-Module-Specification-Version: 1.16
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/project/ant/Bundle.properties
OpenIDE-Module-Install: org/netbeans/modules/project/ant/AntProjectModule.class
Index: ant/project/nbproject/project.xml
===================================================================
RCS file: /cvs/ant/project/nbproject/project.xml,v
retrieving revision 1.21
diff -u -r1.21 project.xml
--- ant/project/nbproject/project.xml 27 Mar 2007 00:01:23 -0000 1.21
+++ ant/project/nbproject/project.xml 6 Apr 2007 13:50:17 -0000
@@ -165,5 +165,6 @@
build/tasks.jar
+
Index: ant/project/src/org/netbeans/api/project/ant/AntBuildExtender.java
===================================================================
RCS file: ant/project/src/org/netbeans/api/project/ant/AntBuildExtender.java
diff -N ant/project/src/org/netbeans/api/project/ant/AntBuildExtender.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ant/project/src/org/netbeans/api/project/ant/AntBuildExtender.java 6 Apr 2007 13:50:18 -0000
@@ -0,0 +1,304 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License (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.html
+ * or http://www.netbeans.org/cddl.txt.
+ *
+ * When distributing Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://www.netbeans.org/cddl.txt.
+ * If applicable, add the following below the CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * 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.
+ */
+
+package org.netbeans.api.project.ant;
+
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
+import org.netbeans.spi.project.support.ant.AntProjectHelper;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * Allows extending the project's build script with 3rd party additions.
+ * Check the Project's lookup to see if the feature is supported by a given Ant project type.
+ * Typical usage:
+ *
+ * - Lookup the instance of AntBuildExtender in the project at hand
+ * - Create the external build script file with your targets and configuration
+ * - Use the AntBuildExtender to wire your script and targets into the main build lifecycle
+ * - Call {@link org.netbeans.api.project.ProjectManager#saveProject} to persist the changes and
+ * regenerate the main build script
+ *
+ *
+ * Please note that it's easy to break the build script functionality and any script extensions
+ * shall be done with care. A few rules to follow:
+ *
+ * - Pick a reasonably unique extension id
+ * - Prefix target names and properties you define in your extension with the extension id to prevent clashes.
+ *
+ * @author mkleint
+ * @since org.netbeans.modules.project.ant 1.16
+ */
+public final class AntBuildExtender {
+ private HashMap extensions;
+ private AntBuildExtenderImplementation implementation;
+
+ static {
+ AntBuildExtenderAccessorImpl.createAccesor();
+ }
+
+ AntBuildExtender(AntBuildExtenderImplementation implementation) {
+ this.implementation = implementation;
+ }
+
+ /**
+ * Get a list of target names in the main build script that are allowed to be
+ * extended by adding the "depends" attribute definition to them.
+ * @return list of target names
+ */
+ public List getExtendableTargets() {
+ List targets = new ArrayList();
+ targets.addAll(implementation.getExtendableTargets());
+ targets = Collections.unmodifiableList(targets);
+ return targets;
+ }
+
+ /**
+ * Adds a new build script extension.
+ * @param id identification of the extension
+ * @param extensionXml fileobject referencing the build script for the extension,
+ * needs to be located in nbproject directory or below.
+ * @return the newly created extension.
+ */
+ public synchronized Extension addExtension(String id, FileObject extensionXml) {
+ assert extensionXml != null;
+ assert extensionXml.isValid() && extensionXml.isData();
+ //TODO assert the owner is the same as the owner of this instance of entender.
+ assert FileOwnerQuery.getOwner(extensionXml) == implementation.getOwningProject();
+ FileObject nbproj = implementation.getOwningProject().getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_XML_PATH).getParent();
+ assert FileUtil.isParentOf(nbproj, extensionXml);
+ if (extensions == null) {
+ readProjectMetadata();
+ }
+ if (extensions.get(id) != null) {
+ throw new IllegalStateException("Extension with id '" + id + "' already exists.");
+ }
+ Extension ex = new Extension(id, extensionXml, FileUtil.getRelativePath(nbproj, extensionXml));
+ extensions.put(id, ex);
+ updateProjectMetadata();
+ return ex;
+ }
+ /**
+ * Remove an existing build script extension. Make sure to remove the extension's script file
+ * before/after removing the extension.
+ * @param id identification of the extension
+ */
+ public synchronized void removeExtension(String id) {
+ if (extensions == null) {
+ readProjectMetadata();
+ }
+ if (extensions.get(id) == null) {
+ // oh well, just ignore.
+ return;
+ }
+ extensions.remove(id);
+ updateProjectMetadata();
+ }
+
+ /**
+ * Get an extension by the id.
+ * @param id identification token
+ * @return Extention with the given id or null if not found.
+ */
+ public synchronized Extension getExtension(String id) {
+ if (extensions == null) {
+ readProjectMetadata();
+ }
+ return extensions.get(id);
+ }
+
+
+ synchronized Set getExtensions() {
+ Set ext = new HashSet();
+ if (extensions == null) {
+ readProjectMetadata();
+ }
+ ext.addAll(extensions.values());
+ return ext;
+ }
+
+ private static final DocumentBuilder db;
+ static {
+ try {
+ db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new AssertionError(e);
+ }
+ }
+ private static Document createNewDocument() {
+ // #50198: for thread safety, use a separate document.
+ // Using XMLUtil.createDocument is much too slow.
+ synchronized (db) {
+ return db.newDocument();
+ }
+ }
+
+
+ private void updateProjectMetadata() {
+ Document doc = createNewDocument();
+ Element root = doc.createElement(AntBuildExtenderImplementation.ELEMENT_ROOT);
+ if (extensions != null) {
+ FileObject nbproj = implementation.getOwningProject().getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_XML_PATH).getParent();
+ for (Extension ext : extensions.values()) {
+ Element child = doc.createElement(AntBuildExtenderImplementation.ELEMENT_EXTENSION);
+ child.setAttribute(AntBuildExtenderImplementation.ATTR_ID, ext.id);
+ String relPath = FileUtil.getRelativePath(nbproj, ext.file);
+ assert relPath != null;
+ child.setAttribute(AntBuildExtenderImplementation.ATTR_FILE, relPath);
+ root.appendChild(child);
+ for (String target : ext.dependencies.keySet()) {
+ for (String depTarget : ext.dependencies.get(target)) {
+ Element dep = doc.createElement(AntBuildExtenderImplementation.ELEMENT_DEPENDENCY);
+ dep.setAttribute(AntBuildExtenderImplementation.ATTR_TARGET, target);
+ dep.setAttribute(AntBuildExtenderImplementation.ATTR_DEPENDSON, depTarget);
+ child.appendChild(dep);
+ }
+ }
+ }
+ }
+ implementation.updateBuildExtensionMetadata(root);
+ }
+
+ private void readProjectMetadata() {
+ Element cfgEl = implementation.getBuildExtensionMetadata();
+ List rootIds = new ArrayList();
+ FileObject nbproj = implementation.getOwningProject().getProjectDirectory().getFileObject(AntProjectHelper.PROJECT_XML_PATH).getParent();
+ extensions = new HashMap();
+ if (cfgEl != null) {
+ String namespace = cfgEl.getNamespaceURI();
+ NodeList roots = cfgEl.getElementsByTagNameNS(namespace, AntBuildExtenderImplementation.ELEMENT_EXTENSION);
+ for (int i=0; i 0 : "Illegal project.xml";
+ String value = root.getAttribute(AntBuildExtenderImplementation.ATTR_FILE);
+ FileObject script = nbproj.getFileObject(value);
+ assert script != null : "Missing file " + script;
+ Extension ext = new Extension(id, script, value);
+ extensions.put(id, ext);
+ NodeList deps = root.getElementsByTagNameNS(namespace, AntBuildExtenderImplementation.ELEMENT_DEPENDENCY);
+ for (int j = 0; j < deps.getLength(); j++) {
+ Element dep = (Element)deps.item(j);
+ String target = dep.getAttribute(AntBuildExtenderImplementation.ATTR_TARGET);
+ String dependsOn = dep.getAttribute(AntBuildExtenderImplementation.ATTR_DEPENDSON);
+ assert target != null;
+ assert dependsOn != null;
+ ext.loadDependency(target, dependsOn);
+ }
+ }
+ }
+ }
+
+ /**
+ * Describes and allows to manipulate the build script extension and it's links to the main build script
+ * of the project.
+ */
+ public final class Extension {
+ String id;
+ FileObject file;
+ String path;
+ TreeMap> dependencies;
+
+ Extension(String id, FileObject script, String relPath) {
+ this.id = id;
+ file = script;
+ path = relPath;
+ dependencies = new TreeMap>();
+ }
+
+ String getPath() {
+ return path;
+ }
+
+ /**
+ * Add a dependency of a main build script target on the target in the extension's script.
+ * @param mainBuildTarget name of target in the main build script (see {@link org.netbeans.api.project.ant.AntBuildExtender#getExtendableTargets})
+ * @param extensionTarget name of target in the extention script
+ */
+ public void addDependency(String mainBuildTarget, String extensionTarget) {
+ assert implementation.getExtendableTargets().contains(mainBuildTarget) :
+ "The target '" + mainBuildTarget + "' is not designated by the project type as extensible.";
+ synchronized (AntBuildExtender.class) {
+ loadDependency(mainBuildTarget, extensionTarget);
+ updateProjectMetadata();
+ }
+ }
+
+ private void loadDependency(String mainBuildTarget, String extensionTarget) {
+ synchronized (AntBuildExtender.class) {
+ Collection tars = dependencies.get(mainBuildTarget);
+ if (tars == null) {
+ tars = new ArrayList();
+ dependencies.put(mainBuildTarget, tars);
+ }
+ if (!tars.contains(extensionTarget)) {
+ tars.add(extensionTarget);
+ } else {
+ //log?
+ }
+ }
+ }
+
+
+ /**
+ * Remove a dependency of a main build script target on the target in the extension's script.
+ *
+ * @param mainBuildTarget name of target in the main build script (see {@link org.netbeans.api.project.ant.AntBuildExtender#getExtendableTargets})
+ * @param extensionTarget name of target in the extention script
+ */
+ public void removeDependency(String mainBuildTarget, String extensionTarget) {
+ Collection str = dependencies.get(mainBuildTarget);
+ if (str != null) {
+ str.remove(extensionTarget);
+ updateProjectMetadata();
+ } else {
+ //oh well, just ignore, nothing to update anyway..
+ }
+ }
+
+ Map> getDependencies() {
+ TreeMap> toRet = new TreeMap>();
+ synchronized (AntBuildExtender.class) {
+ for (String str : dependencies.keySet()) {
+ ArrayList col = new ArrayList();
+ col.addAll(dependencies.get(str));
+ toRet.put(str, col);
+ }
+ }
+ return toRet;
+ }
+ }
+}
Index: ant/project/src/org/netbeans/api/project/ant/AntBuildExtenderAccessorImpl.java
===================================================================
RCS file: ant/project/src/org/netbeans/api/project/ant/AntBuildExtenderAccessorImpl.java
diff -N ant/project/src/org/netbeans/api/project/ant/AntBuildExtenderAccessorImpl.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ant/project/src/org/netbeans/api/project/ant/AntBuildExtenderAccessorImpl.java 6 Apr 2007 13:50:18 -0000
@@ -0,0 +1,60 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License (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.html
+ * or http://www.netbeans.org/cddl.txt.
+ *
+ * When distributing Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://www.netbeans.org/cddl.txt.
+ * If applicable, add the following below the CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * To change this template, choose Tools | Template Manager
+ * and open the template in the editor.
+ */
+package org.netbeans.api.project.ant;
+
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import org.netbeans.api.project.ant.AntBuildExtender.Extension;
+import org.netbeans.modules.project.ant.AntBuildExtenderAccessor;
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
+
+/**
+ *
+ * @author mkleint
+ */
+class AntBuildExtenderAccessorImpl extends AntBuildExtenderAccessor {
+
+ static void createAccesor() {
+ if (DEFAULT == null) {
+ DEFAULT= new AntBuildExtenderAccessorImpl();
+ }
+ }
+
+ private AntBuildExtenderAccessorImpl() {
+ }
+
+
+ public AntBuildExtender createExtender(AntBuildExtenderImplementation impl) {
+ return new AntBuildExtender(impl);
+ }
+
+ public Set getExtensions(AntBuildExtender ext) {
+ return ext.getExtensions();
+ }
+
+ public String getPath(Extension extension) {
+ return extension.getPath();
+ }
+
+ public Map> getDependencies(Extension extension) {
+ return extension.getDependencies();
+ }
+
+}
Index: ant/project/src/org/netbeans/modules/project/ant/AntBuildExtenderAccessor.java
===================================================================
RCS file: ant/project/src/org/netbeans/modules/project/ant/AntBuildExtenderAccessor.java
diff -N ant/project/src/org/netbeans/modules/project/ant/AntBuildExtenderAccessor.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ant/project/src/org/netbeans/modules/project/ant/AntBuildExtenderAccessor.java 6 Apr 2007 13:50:18 -0000
@@ -0,0 +1,57 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License (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.html
+ * or http://www.netbeans.org/cddl.txt.
+ *
+ * When distributing Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://www.netbeans.org/cddl.txt.
+ * If applicable, add the following below the CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * 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.
+ */
+
+package org.netbeans.modules.project.ant;
+
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import org.netbeans.api.project.ant.AntBuildExtender;
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
+
+
+/**
+ * @author mkleint
+ */
+public abstract class AntBuildExtenderAccessor {
+
+ public static AntBuildExtenderAccessor DEFAULT = null;
+
+ static {
+ // invokes static initializer of Item.class
+ // that will assign value to the DEFAULT field above
+ Class c = AntBuildExtender.class;
+ try {
+ Class.forName(c.getName(), true, c.getClassLoader());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ public abstract AntBuildExtender createExtender(AntBuildExtenderImplementation impl);
+
+ public abstract Set getExtensions(AntBuildExtender ext);
+
+ public abstract String getPath(AntBuildExtender.Extension extension);
+
+ public abstract Map> getDependencies(AntBuildExtender.Extension extension);
+
+
+}
Index: ant/project/src/org/netbeans/spi/project/ant/AntBuildExtenderImplementation.java
===================================================================
RCS file: ant/project/src/org/netbeans/spi/project/ant/AntBuildExtenderImplementation.java
diff -N ant/project/src/org/netbeans/spi/project/ant/AntBuildExtenderImplementation.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ant/project/src/org/netbeans/spi/project/ant/AntBuildExtenderImplementation.java 6 Apr 2007 13:50:18 -0000
@@ -0,0 +1,104 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License (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.html
+ * or http://www.netbeans.org/cddl.txt.
+ *
+ * When distributing Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://www.netbeans.org/cddl.txt.
+ * If applicable, add the following below the CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * 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.
+ */
+
+package org.netbeans.spi.project.ant;
+
+import java.util.List;
+import org.netbeans.api.project.Project;
+import org.w3c.dom.Element;
+
+/**
+ * A project type's spi for {@link org.netbeans.api.project.ant.AntBuildExtender}'s wiring.
+ * A typical setup in the project type includes:
+ *
+ * - Enhance the Project's metadata schema with the build extensibility content.
-
+ *
- Provide lookup and storage of the metadata content by implementing this interface
-
+ *
- Provide an instance of {@link org.netbeans.api.project.ant.AntBuildExtender} in project's lookup for use by 3rd
+ * party modules.
-
+ *
- Use the new {@link org.netbeans.spi.project.support.ant.GeneratedFilesHelper#GeneratedFilesHelper(AntProjectHelper,AntBuildExtender)} constructor to
+ * create the helper for generating build related files.
-
+ *
+ * @author mkleint
+ * @since org.netbeans.modules.project.ant 1.16
+ */
+public interface AntBuildExtenderImplementation {
+
+ /**
+ *
+ */
+ public static final String ELEMENT_ROOT = "buildExtensions"; //NOI18N
+
+ /**
+ *
+ */
+ public static final String ELEMENT_EXTENSION = "extension"; //NOI18N
+
+ /**
+ *
+ */
+ public static final String ELEMENT_DEPENDENCY = "dependency"; //NOI18N
+
+ /**
+ *
+ */
+ public static final String ATTR_TARGET = "target";
+ /**
+ *
+ */
+ public static final String ATTR_DEPENDSON = "dependsOn";
+ /**
+ *
+ */
+ public static final String ATTR_ID = "id";
+ /**
+ *
+ */
+ public static final String ATTR_FILE = "file";
+
+
+ /**
+ * A declarative list of targets that are intended by the project type to be used
+ * for extensions to plug into.
+ * @return list of target names
+ */
+ List getExtendableTargets();
+
+ /**
+ * Requests an update of build extensibility metadata by the implementation.
+ * A usual implementation takes the content of root
parameter and
+ * copies it into the correct place in the primary project configuration space in project.xml
+ *
+ * @param root the root element for extensibility metadata (See {@link org.netbeans.spi.project.ant.AntBuildExtenderImplementation#ELEMENT_ROOT}) */
+ void updateBuildExtensionMetadata(Element root);
+
+ /**
+ * Returns the project build script extensibility configuration from a known location in project.xml.
+ * (As defined by the project type's schema). Usual implementation will use the primary project configuration
+ * space in project.xml.
+ * @return project metadata element, the root for project extensions (See {@link org.netbeans.spi.project.ant.AntBuildExtenderImplementation#ELEMENT_ROOT}), or null if not
+ * configured.
+ */
+ Element getBuildExtensionMetadata();
+
+ /**
+ * Returns Ant Project instance.
+ * @return The project that this instance of AntBuildExtenderImplementation describes
+ */
+ Project getOwningProject();
+}
Index: ant/project/src/org/netbeans/spi/project/support/ant/AntBuildExtenderSupport.java
===================================================================
RCS file: ant/project/src/org/netbeans/spi/project/support/ant/AntBuildExtenderSupport.java
diff -N ant/project/src/org/netbeans/spi/project/support/ant/AntBuildExtenderSupport.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ant/project/src/org/netbeans/spi/project/support/ant/AntBuildExtenderSupport.java 6 Apr 2007 13:50:18 -0000
@@ -0,0 +1,46 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License (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.html
+ * or http://www.netbeans.org/cddl.txt.
+ *
+ * When distributing Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://www.netbeans.org/cddl.txt.
+ * If applicable, add the following below the CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * 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.
+ */
+
+package org.netbeans.spi.project.support.ant;
+
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
+import org.netbeans.api.project.ant.AntBuildExtender;
+import org.netbeans.modules.project.ant.AntBuildExtenderAccessor;
+
+/**
+ * Factory class for creation of AntBuildExtender instances
+ * @author mkleint
+ * @since org.netbeans.modules.project.ant 1.16
+ */
+public final class AntBuildExtenderSupport {
+
+ /** Creates a new instance of AntBuildExtenderSupport */
+ private AntBuildExtenderSupport() {
+ }
+
+ /**
+ *
+ * @param implementation
+ * @return
+ */
+ public static AntBuildExtender createAntExtender(AntBuildExtenderImplementation implementation) {
+ return AntBuildExtenderAccessor.DEFAULT.createExtender(implementation);
+ }
+
+}
Index: ant/project/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java
===================================================================
RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java,v
retrieving revision 1.17
diff -u -r1.17 GeneratedFilesHelper.java
--- ant/project/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java 3 Aug 2006 19:21:06 -0000 1.17
+++ ant/project/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelper.java 6 Apr 2007 13:50:20 -0000
@@ -29,6 +29,7 @@
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -40,17 +41,30 @@
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.ant.AntBuildExtender;
+import org.netbeans.api.project.ant.AntBuildExtender.Extension;
+import org.netbeans.modules.project.ant.AntBuildExtenderAccessor;
import org.netbeans.modules.project.ant.UserQuestionHandler;
import org.openide.ErrorManager;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
import org.openide.util.Mutex;
import org.openide.util.MutexException;
import org.openide.util.NbBundle;
import org.openide.util.UserQuestionException;
import org.openide.util.Utilities;
+import org.openide.xml.XMLUtil;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
/**
* Helps a project type (re-)generate, and manage the state and versioning of,
@@ -160,6 +174,8 @@
/** Project directory. */
private final FileObject dir;
+ private AntBuildExtender extender;
+
/**
* Create a helper based on the supplied project helper handle.
* @param h an Ant-based project helper supplied to the project type provider
@@ -168,6 +184,19 @@
this.h = h;
dir = h.getProjectDirectory();
}
+
+ /**
+ * Create a helper based on the supplied project helper handle. The created
+ * instance is capable of extending the generated files with 3rd party content.
+ * @param h an Ant-based project helper supplied to the project type provider
+ * @param ex build extensibility support
+ * @since org.netbeans.modules.project.ant 1.16
+ *
+ */
+ public GeneratedFilesHelper(AntProjectHelper h, AntBuildExtender ex) {
+ this(h);
+ extender = ex;
+ }
/**
* Create a helper based only on a project directory.
@@ -255,7 +284,11 @@
new ByteArrayInputStream(projectXmlData), projectXmlF.toURI().toString());
ByteArrayOutputStream result = new ByteArrayOutputStream();
t.transform(projectXmlSource, new StreamResult(result));
- resultData = result.toByteArray();
+ if (BUILD_IMPL_XML_PATH.equals(path)) {
+ resultData = applyBuildExtensions(result.toByteArray(), extender);
+ } else {
+ resultData = result.toByteArray();
+ }
} catch (TransformerException e) {
throw (IOException)new IOException(e.toString()).initCause(e);
}
@@ -369,6 +402,83 @@
}
}
+ private byte[] applyBuildExtensions(byte[] resultData, AntBuildExtender ext) {
+ if (ext == null) {
+ return resultData;
+ }
+ try {
+ ByteArrayInputStream in2 = new ByteArrayInputStream(resultData);
+ InputSource is = new InputSource(in2);
+
+ Document doc = XMLUtil.parse(is, false, true, null, null);
+ Element el = doc.getDocumentElement();
+ Node firstSubnode = el.getFirstChild();
+ //TODO check if first one is text and use it as indentation..
+ for (Extension extension : AntBuildExtenderAccessor.DEFAULT.getExtensions(ext)) {
+ Text after = doc.createTextNode("\n");
+ el.insertBefore(after, firstSubnode);
+ Element imp = createImportElement(AntBuildExtenderAccessor.DEFAULT.getPath(extension), doc);
+ el.insertBefore(imp, after);
+ Text before = doc.createTextNode(" ");
+ el.insertBefore(before, imp);
+ firstSubnode = before;
+ NodeList nl = el.getElementsByTagName("target");
+ Map> deps = AntBuildExtenderAccessor.DEFAULT.getDependencies(extension);
+ for (String targetName : deps.keySet()) {
+ Element targetEl = null;
+ for (int i = 0; i < nl.getLength(); i++) {
+ Element elem = (Element)nl.item(i);
+ String at = elem.getAttribute("name");
+ if (at != null && at.equals(targetName)) {
+ targetEl = elem;
+ break;
+ }
+ }
+// System.out.println("target name=" + targetName);
+// System.out.println("target elem=" + targetEl);
+ if (targetEl != null) {
+ Attr depend = targetEl.getAttributeNode("depends");
+ if (depend == null) {
+ depend = doc.createAttribute("depends");
+ depend.setValue("");
+ targetEl.setAttributeNode(depend);
+ }
+ String oldVal = depend.getValue();
+ for (String targ : deps.get(targetName)) {
+ oldVal = oldVal + "," + targ;
+ }
+ if (oldVal.startsWith(",")) {
+ oldVal = oldVal.substring(1);
+ }
+ depend.setValue(oldVal);
+ } else {
+ //TODO log??
+ }
+ }
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ XMLUtil.write(doc, out, "UTF-8");
+ return out.toByteArray();
+ }
+ catch (IOException ex) {
+ ex.printStackTrace();
+ Exceptions.printStackTrace(ex);
+ return resultData;
+ }
+ catch (SAXException ex) {
+ ex.printStackTrace();
+ Exceptions.printStackTrace(ex);
+ return resultData;
+ }
+ }
+
+ private Element createImportElement(String path, Document doc) {
+ Element el = doc.createElement("import");
+ el.setAttribute("file", path);
+ return el;
+ }
+
/**
* Load data from a stream into a buffer.
*/
Index: ant/project/src/org/netbeans/spi/project/support/ant/package.html
===================================================================
RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/package.html,v
retrieving revision 1.3
diff -u -r1.3 package.html
--- ant/project/src/org/netbeans/spi/project/support/ant/package.html 30 Jun 2006 18:05:51 -0000 1.3
+++ ant/project/src/org/netbeans/spi/project/support/ant/package.html 6 Apr 2007 13:50:20 -0000
@@ -130,6 +130,10 @@
{@link org.netbeans.spi.project.support.ant.ProjectXmlSavedHook} to be told when
to recreate them.
+To allow 3rd party extensions to build scripts, use {@link org.netbeans.spi.project.ant.AntBuildExtenderImplementation} and
+{@link org.netbeans.spi.project.support.ant.AntBuildExtenderSupport}.
+
+
{@link org.netbeans.spi.project.support.ant.EditableProperties} is a
VCS-friendly alternative to {@link java.util.Properties}.
{@link org.netbeans.spi.project.support.ant.PropertyUtils} also provides various
Index: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java
===================================================================
RCS file: /cvs/ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java,v
retrieving revision 1.21
diff -u -r1.21 AntBasedTestUtil.java
--- ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java 27 Mar 2007 00:01:31 -0000 1.21
+++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java 6 Apr 2007 13:50:21 -0000
@@ -19,6 +19,7 @@
package org.netbeans.spi.project.support.ant;
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
@@ -56,6 +57,7 @@
import org.netbeans.spi.project.AuxiliaryConfiguration;
import org.netbeans.api.project.ProjectInformation;
import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.ant.AntBuildExtender;
import org.netbeans.spi.project.ant.AntArtifactProvider;
import org.netbeans.spi.queries.CollocationQueryImplementation;
import org.openide.filesystems.FileObject;
@@ -100,6 +102,9 @@
return new TestAntBasedProjectType();
}
+ public static AntBasedProjectType testAntBasedProjectType(AntBuildExtenderImplementation extender) {
+ return new TestAntBasedProjectType(extender);
+ }
/**
* You can adjust which artifacts are supplied.
*/
@@ -108,15 +113,20 @@
}
private static final class TestAntBasedProjectType implements AntBasedProjectType {
+ private AntBuildExtenderImplementation ext;
TestAntBasedProjectType() {}
+ TestAntBasedProjectType(AntBuildExtenderImplementation ext) {
+ this.ext = ext;
+ }
+
public String getType() {
return "test";
}
public Project createProject(AntProjectHelper helper) throws IOException {
- return new TestAntBasedProject(helper);
+ return new TestAntBasedProject(helper, ext);
}
public String getPrimaryConfigurationDataElementName(boolean shared) {
@@ -136,14 +146,22 @@
private final GeneratedFilesHelper genFilesHelper;
private final Lookup l;
- TestAntBasedProject(AntProjectHelper helper) throws IOException {
+ TestAntBasedProject(AntProjectHelper helper, AntBuildExtenderImplementation ext) throws IOException {
if (helper.getProjectDirectory().getFileObject("nbproject/broken") != null) {
throw new IOException("broken");
}
this.helper = helper;
AuxiliaryConfiguration aux = helper.createAuxiliaryConfiguration();
refHelper = new ReferenceHelper(helper, aux, helper.getStandardPropertyEvaluator());
- genFilesHelper = new GeneratedFilesHelper(helper);
+ Object extContent;
+ if (ext !=null) {
+ AntBuildExtender e = AntBuildExtenderSupport.createAntExtender(ext);
+ genFilesHelper = new GeneratedFilesHelper(helper, e);
+ extContent = e;
+ } else {
+ genFilesHelper = new GeneratedFilesHelper(helper);
+ extContent = new Object();
+ }
l = Lookups.fixed(new Object[] {
new TestInfo(),
helper,
@@ -166,6 +184,7 @@
}
},
"hello",
+ extContent
});
}
@@ -218,6 +237,7 @@
public void removePropertyChangeListener(PropertyChangeListener listener) {}
}
+
private final class TestAntArtifactProvider implements AntArtifactProviderMutable {
Index: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBuildExtenderTest.java
===================================================================
RCS file: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBuildExtenderTest.java
diff -N ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBuildExtenderTest.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBuildExtenderTest.java 6 Apr 2007 13:50:21 -0000
@@ -0,0 +1,135 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development
+ * and Distribution License (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.html
+ * or http://www.netbeans.org/cddl.txt.
+ *
+ * When distributing Covered Code, include this CDDL Header Notice in each file
+ * and include the License file at http://www.netbeans.org/cddl.txt.
+ * If applicable, add the following below the CDDL Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * 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.
+ */
+
+package org.netbeans.spi.project.support.ant;
+
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.TestUtil;
+import org.netbeans.api.project.ant.AntBuildExtender;
+import org.netbeans.junit.NbTestCase;
+import org.openide.filesystems.FileObject;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * Test functionality of AntBuildExtender.
+ * @author mkleint
+ */
+public class AntBuildExtenderTest extends NbTestCase {
+
+ public AntBuildExtenderTest(String name) {
+ super(name);
+ }
+
+ private FileObject scratch;
+ private FileObject projdir;
+ private FileObject extension1;
+ private ProjectManager pm;
+ private Project p;
+ private AntProjectHelper h;
+ private GeneratedFilesHelper gfh;
+ private ExtImpl extenderImpl;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ scratch = TestUtil.makeScratchDir(this);
+ projdir = scratch.createFolder("proj");
+ TestUtil.createFileFromContent(GeneratedFilesHelperTest.class.getResource("data/project.xml"), projdir, "nbproject/project.xml");
+ extension1 = TestUtil.createFileFromContent(GeneratedFilesHelperTest.class.getResource("data/extension1.xml"), projdir, "nbproject/extension1.xml");
+ extenderImpl = new ExtImpl();
+ TestUtil.setLookup(new Object[] {
+ AntBasedTestUtil.testAntBasedProjectType(extenderImpl),
+ });
+ pm = ProjectManager.getDefault();
+ p = pm.findProject(projdir);
+ extenderImpl.project = p;
+ h = p.getLookup().lookup(AntProjectHelper.class);
+ gfh = p.getLookup().lookup(GeneratedFilesHelper.class);
+ assertNotNull(gfh);
+ }
+
+ public void testGetExtendableTargets() {
+ AntBuildExtender instance = p.getLookup().lookup(AntBuildExtender.class);
+
+ List result = instance.getExtendableTargets();
+
+ assertEquals(1, result.size());
+ assertEquals("all", result.get(0));
+ }
+
+ public void testAddExtension() {
+ AntBuildExtender instance = p.getLookup().lookup(AntBuildExtender.class);
+ instance.addExtension("milos", extension1);
+ Element el = extenderImpl.newElement;
+ assertNotNull(el);
+ NodeList nl = el.getElementsByTagName(AntBuildExtenderImplementation.ELEMENT_EXTENSION);
+ assertEquals(1, nl.getLength());
+ Element extens = (Element) nl.item(0);
+ assertEquals("milos", extens.getAttribute(AntBuildExtenderImplementation.ATTR_ID));
+ assertEquals("extension1.xml", extens.getAttribute(AntBuildExtenderImplementation.ATTR_FILE));
+ }
+
+ public void testRemoveExtension() {
+ AntBuildExtender instance = p.getLookup().lookup(AntBuildExtender.class);
+ testAddExtension();
+ Element el = extenderImpl.newElement;
+ extenderImpl.oldElement = el;
+ instance.removeExtension("milos");
+ el = extenderImpl.newElement;
+ assertNotNull(el);
+ NodeList nl = el.getElementsByTagName(AntBuildExtenderImplementation.ELEMENT_EXTENSION);
+ assertEquals(0, nl.getLength());
+ }
+
+ public void testGetExtension() {
+ AntBuildExtender instance = p.getLookup().lookup(AntBuildExtender.class);
+ testAddExtension();
+ AntBuildExtender.Extension ext = instance.getExtension("milos");
+ assertNotNull(ext);
+ }
+
+ private class ExtImpl implements AntBuildExtenderImplementation {
+ Project project;
+ Element newElement;
+ Element oldElement;
+ List targets = Collections.singletonList("all");
+
+ public List getExtendableTargets() {
+ return targets;
+ }
+
+ public void updateBuildExtensionMetadata(Element element) {
+ newElement = element;
+ }
+
+ public Element getBuildExtensionMetadata() {
+ return oldElement;
+ }
+
+ public Project getOwningProject() {
+ return project;
+ }
+
+ }
+
+}
Index: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelperTest.java
===================================================================
RCS file: /cvs/ant/project/test/unit/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelperTest.java,v
retrieving revision 1.9
diff -u -r1.9 GeneratedFilesHelperTest.java
--- ant/project/test/unit/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelperTest.java 3 Aug 2006 19:21:08 -0000 1.9
+++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/GeneratedFilesHelperTest.java 6 Apr 2007 13:50:21 -0000
@@ -19,14 +19,18 @@
package org.netbeans.spi.project.support.ant;
+import org.netbeans.spi.project.ant.AntBuildExtenderImplementation;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.net.URL;
+import java.util.Collections;
+import java.util.List;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.TestUtil;
import org.netbeans.junit.NbTestCase;
import org.netbeans.modules.project.ant.Util;
+import org.netbeans.spi.project.AuxiliaryConfiguration;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Utilities;
@@ -46,21 +50,26 @@
private FileObject scratch;
private FileObject projdir;
+ private FileObject extension1;
private ProjectManager pm;
private Project p;
private AntProjectHelper h;
private GeneratedFilesHelper gfh;
+ private ExtImpl extenderImpl;
protected void setUp() throws Exception {
super.setUp();
scratch = TestUtil.makeScratchDir(this);
projdir = scratch.createFolder("proj");
TestUtil.createFileFromContent(GeneratedFilesHelperTest.class.getResource("data/project.xml"), projdir, "nbproject/project.xml");
+ extension1 = TestUtil.createFileFromContent(GeneratedFilesHelperTest.class.getResource("data/extension1.xml"), projdir, "nbproject/extension1.xml");
+ extenderImpl = new ExtImpl();
TestUtil.setLookup(new Object[] {
- AntBasedTestUtil.testAntBasedProjectType(),
+ AntBasedTestUtil.testAntBasedProjectType(extenderImpl),
});
pm = ProjectManager.getDefault();
p = pm.findProject(projdir);
+ extenderImpl.project = p;
h = p.getLookup().lookup(AntProjectHelper.class);
gfh = p.getLookup().lookup(GeneratedFilesHelper.class);
assertNotNull(gfh);
@@ -198,6 +207,36 @@
}
assertTrue("generated file has platform line endings", ok);
}
+ }
+
+ private class ExtImpl implements AntBuildExtenderImplementation {
+ Project project;
+ Element newElement;
+ Element oldElement;
+
+ public List getExtendableTargets() {
+ return Collections.singletonList("all");
+ }
+
+ public void updateBuildExtensionMetadata(Element element) {
+ newElement = element;
+ }
+
+ public Element getBuildExtensionMetadata() {
+ Element el = project.getLookup().lookup(AuxiliaryConfiguration.class).getConfigurationFragment(ELEMENT_ROOT, "urn:test:extension", true);
+ if (el != null) {
+ NodeList nl = el.getElementsByTagName(AntBuildExtenderImplementation.ELEMENT_ROOT);
+ if (nl.getLength() == 1) {
+ return (Element) nl.item(0);
+ }
+ }
+ return null;
+ }
+
+ public Project getOwningProject() {
+ return project;
+ }
+
}
}
Index: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/data/extension1.xml
===================================================================
RCS file: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/data/extension1.xml
diff -N ant/project/test/unit/src/org/netbeans/spi/project/support/ant/data/extension1.xml
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/data/extension1.xml 6 Apr 2007 13:50:21 -0000
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+