? diff Index: j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java =================================================================== RCS file: /cvs/java/j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java,v retrieving revision 1.60 diff -u -r1.60 J2SEProject.java --- j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java 4 Jan 2006 17:48:38 -0000 1.60 +++ j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java 15 May 2006 13:37:25 -0000 @@ -25,9 +25,12 @@ import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectInformation; import org.netbeans.api.project.ProjectManager; +import org.netbeans.api.project.SourceGroup; +import org.netbeans.api.project.Sources; import org.netbeans.api.project.ant.AntArtifact; import org.netbeans.modules.java.j2seproject.classpath.ClassPathProviderImpl; import org.netbeans.modules.java.j2seproject.classpath.J2SEProjectClassPathExtender; +import org.netbeans.modules.java.j2seproject.classpath.J2SEProjectClassPathModifier; import org.netbeans.modules.java.j2seproject.queries.CompiledSourceForBinaryQuery; import org.netbeans.modules.java.j2seproject.queries.JavadocForBinaryQueryImpl; import org.netbeans.modules.java.j2seproject.queries.SourceLevelQueryImpl; @@ -144,6 +147,7 @@ private Lookup createLookup(AuxiliaryConfiguration aux) { SubprojectProvider spp = refHelper.createSubprojectProvider(); + final J2SEProjectClassPathModifier cpMod = new J2SEProjectClassPathModifier(this, this.updateHelper, eval, refHelper); return Lookups.fixed(new Object[] { new Info(), aux, @@ -165,7 +169,8 @@ new J2SESharabilityQuery (this.helper, evaluator(), getSourceRoots(), getTestSourceRoots()), //Does not use APH to get/put properties/cfgdata new J2SEFileBuiltQuery (this.helper, evaluator(),getSourceRoots(),getTestSourceRoots()), //Does not use APH to get/put properties/cfgdata new RecommendedTemplatesImpl (this.updateHelper), - new J2SEProjectClassPathExtender(this, this.updateHelper, eval,refHelper), + new J2SEProjectClassPathExtender(cpMod), + cpMod, this, // never cast an externally obtained Project to J2SEProject - use lookup instead new J2SEProjectOperations(this), new J2SEProjectWebServicesSupportProvider() Index: j2seproject/src/org/netbeans/modules/java/j2seproject/classpath/ClassPathProviderImpl.java =================================================================== RCS file: /cvs/java/j2seproject/src/org/netbeans/modules/java/j2seproject/classpath/ClassPathProviderImpl.java,v retrieving revision 1.17 diff -u -r1.17 ClassPathProviderImpl.java --- j2seproject/src/org/netbeans/modules/java/j2seproject/classpath/ClassPathProviderImpl.java 23 Nov 2005 11:00:04 -0000 1.17 +++ j2seproject/src/org/netbeans/modules/java/j2seproject/classpath/ClassPathProviderImpl.java 15 May 2006 13:37:25 -0000 @@ -19,6 +19,7 @@ import java.util.HashMap; import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.project.SourceGroup; import org.netbeans.spi.java.classpath.ClassPathFactory; import org.netbeans.spi.java.classpath.ClassPathProvider; import org.netbeans.spi.java.project.classpath.support.ProjectClassPathSupport; @@ -38,6 +39,12 @@ private static final String DIST_JAR = "dist.jar"; // NOI18N private static final String BUILD_TEST_CLASSES_DIR = "build.test.classes.dir"; // NOI18N + private static final String JAVAC_CLASSPATH = "javac.classpath"; //NOI18N + private static final String JAVAC_TEST_CLASSPATH = "javac.test.classpath"; //NOI18N + private static final String RUN_CLASSPATH = "run.classpath"; //NOI18N + private static final String RUN_TEST_CLASSPATH = "run.test.classpath"; //NOI18N + + private final AntProjectHelper helper; private final File projectDirectory; private final PropertyEvaluator evaluator; @@ -149,12 +156,12 @@ if (type == 0) { cp = ClassPathFactory.createClassPath( ProjectClassPathSupport.createPropertyBasedClassPathImplementation( - projectDirectory, evaluator, new String[] {"javac.classpath"})); // NOI18N + projectDirectory, evaluator, new String[] {JAVAC_CLASSPATH})); // NOI18N } else { cp = ClassPathFactory.createClassPath( ProjectClassPathSupport.createPropertyBasedClassPathImplementation( - projectDirectory, evaluator, new String[] {"javac.test.classpath"})); // NOI18N + projectDirectory, evaluator, new String[] {JAVAC_TEST_CLASSPATH})); // NOI18N } cache[2+type] = cp; } @@ -177,12 +184,12 @@ if (type == 0) { cp = ClassPathFactory.createClassPath( ProjectClassPathSupport.createPropertyBasedClassPathImplementation( - projectDirectory, evaluator, new String[] {"run.classpath"})); // NOI18N + projectDirectory, evaluator, new String[] {RUN_CLASSPATH})); // NOI18N } else if (type == 1) { cp = ClassPathFactory.createClassPath( ProjectClassPathSupport.createPropertyBasedClassPathImplementation( - projectDirectory, evaluator, new String[] {"run.test.classpath"})); // NOI18N + projectDirectory, evaluator, new String[] {RUN_TEST_CLASSPATH})); // NOI18N } else if (type == 2) { //Only to make the CompiledDataNode hapy @@ -269,6 +276,39 @@ public synchronized void propertyChange(PropertyChangeEvent evt) { dirCache.remove(evt.getPropertyName()); + } + + public String getPropertyName (SourceGroup sg, String classPathId) { + FileObject root = sg.getRootFolder(); + FileObject[] path = getPrimarySrcPath(); + for (int i=0; i*/ changed = new ArrayList/**/ (libraries.length); + for (int i=0; i< libraries.length; i++) { + assert libraries[i] != null; + ClassPathSupport.Item item = ClassPathSupport.Item.create( libraries[i], null ); + if (operation == ADD && !resources.contains(item)) { + resources.add (item); + changed.add(item); + } + else if (operation == REMOVE && resources.contains(item)) { + resources.remove(item); + changed.add(item); + } + } + if (!changed.isEmpty()) { + String itemRefs[] = cs.encodeToStrings( resources.iterator() ); + props = helper.getProperties (AntProjectHelper.PROJECT_PROPERTIES_PATH); //PathParser may change the EditableProperties + props.setProperty(classPathProperty, itemRefs); + if (operation == ADD) { + for (Iterator/**/ it = changed.iterator(); it.hasNext();) { + String prop = cs.getLibraryReference( (ClassPathSupport.Item) it.next() ); + prop = prop.substring(2, prop.length()-1); // XXX make a PropertyUtils method for this! + ClassPathSupport.relativizeLibraryClassPath(props, helper.getAntProjectHelper(), prop); + } + } + helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props); + ProjectManager.getDefault().saveProject(project); + return Boolean.TRUE; + } + return Boolean.FALSE; + } + } + )).booleanValue(); + } catch (MutexException e) { + throw (IOException) e.getException(); + } + } + + private String getClassPathProperty (final SourceGroup sg, final String classPathId) throws UnsupportedOperationException { + assert sg != null : "SourceGroup cannot be null"; //NOI18N + assert classPathId != null : "ClassPathId cannot be null"; //NOI18N + final String classPathProperty = ((ClassPathProviderImpl)project.getLookup().lookup(ClassPathProviderImpl.class)).getPropertyName (sg, classPathId); + if (classPathProperty == null) { + throw new UnsupportedOperationException ("Modification of [" + sg.getRootFolder().getPath() +", " + classPathId + "] is not supported"); //NOI8N + } + return classPathProperty; + } +} Index: j2seproject/test/unit/src/org/netbeans/modules/java/j2seproject/classpath/J2SEProjectClassPathModifierTest.java =================================================================== RCS file: j2seproject/test/unit/src/org/netbeans/modules/java/j2seproject/classpath/J2SEProjectClassPathModifierTest.java diff -N j2seproject/test/unit/src/org/netbeans/modules/java/j2seproject/classpath/J2SEProjectClassPathModifierTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ j2seproject/test/unit/src/org/netbeans/modules/java/j2seproject/classpath/J2SEProjectClassPathModifierTest.java 15 May 2006 13:37:25 -0000 @@ -0,0 +1,324 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ +package org.netbeans.modules.java.j2seproject.classpath; + +import java.beans.Customizer; +import java.beans.PropertyChangeListener; +import java.io.OutputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.Collections; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import junit.framework.*; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.util.List; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.project.JavaProjectConstants; +import org.netbeans.api.java.project.classpath.ProjectClassPathModifier; +import org.netbeans.api.project.FileOwnerQuery; +import org.netbeans.api.project.Project; +import org.netbeans.api.project.ProjectManager; +import org.netbeans.api.project.SourceGroup; +import org.netbeans.api.project.Sources; +import org.netbeans.api.project.TestUtil; +import org.netbeans.api.project.ant.AntArtifact; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.api.project.libraries.LibraryManager; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.java.j2seproject.J2SEProjectGenerator; +import org.netbeans.modules.java.j2seproject.UpdateHelper; +import org.netbeans.modules.java.j2seproject.ui.customizer.J2SEProjectProperties; +import org.netbeans.spi.java.project.classpath.ProjectClassPathExtender; +import org.netbeans.spi.project.ant.AntArtifactProvider; +import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryProvider; +import org.netbeans.spi.project.libraries.LibraryTypeProvider; +import org.netbeans.spi.project.support.ant.AntProjectHelper; +import org.netbeans.spi.project.support.ant.EditableProperties; +import org.netbeans.spi.project.support.ant.PropertyEvaluator; +import org.netbeans.spi.project.support.ant.PropertyUtils; +import org.netbeans.spi.project.support.ant.ReferenceHelper; +import org.openide.ErrorManager; +import org.openide.filesystems.FileLock; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.modules.SpecificationVersion; +import org.openide.util.Lookup; +import org.openide.util.Mutex; +import org.openide.util.MutexException; + +/** + * + * @author tom + */ +public class J2SEProjectClassPathModifierTest extends NbTestCase { + + private FileObject scratch; + private AntProjectHelper helper; + private PropertyEvaluator eval; + private FileObject src; + private FileObject test; + private Project prj; + + public J2SEProjectClassPathModifierTest(String testName) { + super(testName); + } + + protected void setUp() throws Exception { + super.setUp(); + TestUtil.setLookup(new Object[] { + new org.netbeans.modules.java.j2seproject.J2SEProjectType(), + new org.netbeans.modules.projectapi.SimpleFileOwnerQueryImplementation(), + new TestLibraryProvider (), + }); + this.scratch = TestUtil.makeScratchDir(this); + FileObject projdir = scratch.createFolder("proj"); //NOI18N + J2SEProjectGenerator.setDefaultSourceLevel(new SpecificationVersion ("1.4")); //NOI18N + this.helper = J2SEProjectGenerator.createProject(FileUtil.toFile(projdir),"proj",null,null); //NOI18N + this.eval = this.helper.getStandardPropertyEvaluator(); + J2SEProjectGenerator.setDefaultSourceLevel(null); + this.prj = FileOwnerQuery.getOwner(projdir); + assertNotNull (this.prj); + this.src = projdir.getFileObject("src"); + assertNotNull (this.src); + this.test = projdir.getFileObject("test"); + assertNotNull (this.test); + + } + + protected void tearDown() throws Exception { + } + + public void testAddRemoveRoot () throws Exception { + final FileObject rootFolder = this.scratch.createFolder("Root"); + final FileObject jarFile = this.scratch.createData("archive","jar"); + FileLock lck = jarFile.lock(); + try { + ZipOutputStream jf = new ZipOutputStream (jarFile.getOutputStream(lck)); + try { + jf.putNextEntry(new ZipEntry("Test.properties")); + }finally { + jf.close(); + } + } finally { + lck.releaseLock(); + } + final FileObject jarRoot = FileUtil.getArchiveRoot(jarFile); + ProjectClassPathModifier.addRoots (new URL[] {rootFolder.getURL()}, this.src, ClassPath.COMPILE); + String cp = this.eval.getProperty("javac.classpath"); + assertNotNull (cp); + String[] cpRoots = PropertyUtils.tokenizePath (cp); + assertNotNull (cpRoots); + assertEquals(1,cpRoots.length); + assertEquals(rootFolder,this.helper.resolveFileObject(cpRoots[0])); + ProjectClassPathModifier.removeRoots (new URL[] {rootFolder.getURL()},this.src, ClassPath.COMPILE); + cp = this.eval.getProperty("javac.classpath"); + assertNotNull (cp); + cpRoots = PropertyUtils.tokenizePath (cp); + assertNotNull (cpRoots); + assertEquals(0,cpRoots.length); + ProjectClassPathModifier.addRoots(new URL[] {jarRoot.getURL()},this.test,ClassPath.EXECUTE); + cp = this.eval.getProperty("run.test.classpath"); + assertNotNull (cp); + cpRoots = PropertyUtils.tokenizePath (cp); + assertNotNull (cpRoots); + assertEquals(4,cpRoots.length); + assertEquals(this.helper.resolveFileObject(cpRoots[3]),jarFile); + } + + public void testAddRemoveArtifact () throws Exception { + FileObject projdir = scratch.createFolder("libPrj"); //NOI18N + J2SEProjectGenerator.setDefaultSourceLevel(new SpecificationVersion ("1.4")); //NOI18N + AntProjectHelper helper = J2SEProjectGenerator.createProject(FileUtil.toFile(projdir),"libProj",null,null); //NOI18N + J2SEProjectGenerator.setDefaultSourceLevel(null); + Project libPrj = FileOwnerQuery.getOwner(projdir); + assertNotNull (this.prj); + AntArtifactProvider ap = (AntArtifactProvider) libPrj.getLookup().lookup(AntArtifactProvider.class); + AntArtifact[] aas = ap.getBuildArtifacts(); + AntArtifact output = null; + for (int i=0; i + + + New ProjectClassPathModifier API/SPI for modification of project's classpath + + + + + + The new SPI interface ProjectClassPathModifierImplementation was created to allow extension modules to + add or remove classpath elements (archive files, folders, libraries, subprojects) from the + project's classpath. The ProjectClassPathModifierImplementation is provided by the project types in the + project's Lookup. The API client uses the static methods of ProjectClassPathModifier which delegate to + the SPI. + + + + + + + + + + Semantic changes in the BrokenReferencesModel.updateReference behavior Index: project/manifest.mf =================================================================== RCS file: /cvs/java/project/manifest.mf,v retrieving revision 1.18 diff -u -r1.18 manifest.mf --- project/manifest.mf 12 Dec 2005 15:40:01 -0000 1.18 +++ project/manifest.mf 15 May 2006 13:37:26 -0000 @@ -2,6 +2,6 @@ OpenIDE-Module: org.netbeans.modules.java.project/1 OpenIDE-Module-Layer: org/netbeans/modules/java/project/layer.xml OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/project/Bundle.properties -OpenIDE-Module-Specification-Version: 1.9 +OpenIDE-Module-Specification-Version: 1.10 OpenIDE-Module-Requires: org.netbeans.modules.project.uiapi.ActionsFactory Index: project/nbproject/project.properties =================================================================== RCS file: /cvs/java/project/nbproject/project.properties,v retrieving revision 1.20 diff -u -r1.20 project.properties --- project/nbproject/project.properties 17 Apr 2006 15:48:42 -0000 1.20 +++ project/nbproject/project.properties 15 May 2006 13:37:26 -0000 @@ -11,6 +11,7 @@ is.autoload=true +javac.source=1.5 javadoc.title=Java Project API javadoc.overview=${basedir}/overview.html javadoc.arch=${basedir}/arch.xml Index: project/src/org/netbeans/api/java/project/classpath/ProjectClassPathModifier.java =================================================================== RCS file: project/src/org/netbeans/api/java/project/classpath/ProjectClassPathModifier.java diff -N project/src/org/netbeans/api/java/project/classpath/ProjectClassPathModifier.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ project/src/org/netbeans/api/java/project/classpath/ProjectClassPathModifier.java 15 May 2006 13:37:26 -0000 @@ -0,0 +1,312 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.api.java.project.classpath; + +import java.io.Externalizable; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.project.FileOwnerQuery; +import org.netbeans.api.project.Project; +import org.netbeans.api.project.SourceGroup; +import org.netbeans.api.project.ant.AntArtifact; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.modules.java.project.classpath.ProjectClassPathModifierAccessor; +import org.netbeans.spi.java.project.classpath.ProjectClassPathExtender; +import org.netbeans.spi.java.project.classpath.ProjectClassPathModifierImplementation; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.filesystems.URLMapper; + +/** + * An API for project's classpaths modification. + * An client can use this interface to add/remove classpath element (folder, archive, library, subproject) + * to/from the project's classpath. Not all operations on all project's classpath are supported, if the project + * type does not support a modification of a given classpath the UnsupportedOperationException is thrown. + * @since org.netbeans.modules.java.project/1 1.10 + */ +public class ProjectClassPathModifier { + + + + private ProjectClassPathModifier () { + + } + + /** + * Adds libraries into the project's classpath if the + * libraries are not already included. + * @param libraries to be added + * @param projectArtifact a file which classpath should be extended + * @param classPathType the type of classpath to be extended, @see ClassPath + * @return true in case the classpath was changed (at least one library was added to the classpath), + * the value false is returned when all the libraries are already included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * adding of a library to the classpath of the given id. + */ + @SuppressWarnings("deprecation") //NOI18N + public static boolean addLibraries (final Library[] libraries, final FileObject projectArtfact, final String classPathType) throws IOException, UnsupportedOperationException { + final Extensible extensible = findExtensible (projectArtfact, classPathType); + if (extensible != null) { + if (extensible.pcmi != null) { + assert extensible.sg != null; + assert extensible.classPathType != null; + return ProjectClassPathModifierAccessor.INSTANCE.addLibraries (libraries, extensible.pcmi, extensible.sg, extensible.classPathType); + } + else if (extensible.pcpe != null) { + boolean result = false; + for (int i=0; i< libraries.length; i++) { + result |= extensible.pcpe.addLibrary (libraries[i]); + } + return result; + } + } + throw new UnsupportedOperationException (); + } + + + /** + * Removes libraries from the project's classpath if the + * libraries are included on it. + * @param libraries to be removed + * @param projectArtfact a file from whose classpath the libraries should be removed + * @param classPathType the type of classpath, @see ClassPath + * @return true in case the classpath was changed, (at least one library was removed from the classpath), + * the value false is returned when none of the libraries was included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * removing of a library from the classpath of the given id. + */ + public static boolean removeLibraries (final Library[] libraries, final FileObject projectArtfact, final String classPathType) throws IOException, UnsupportedOperationException { + final Extensible extensible = findExtensible (projectArtfact, classPathType); + if (extensible != null && extensible.pcmi != null) { + assert extensible.sg != null; + assert extensible.classPathType != null; + return ProjectClassPathModifierAccessor.INSTANCE.removeLibraries (libraries, extensible.pcmi, extensible.sg, extensible.classPathType); + } + throw new UnsupportedOperationException (); + } + + /** + * Adds archive files or folders into the project's classpath if the + * entries are not already there. + * @param classPathRoots roots to be added, each root has to be either a root of an archive or a folder + * @param projectArtfact a file which classpath should be extended + * @param classPathType the type of classpath to be extended, @see ClassPath + * @return true in case the classpath was changed, (at least one classpath root was added to the classpath), + * the value false is returned when all the classpath roots are already included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * adding of a root to the classpath of the given id. + */ + @SuppressWarnings("deprecation") //NOI18N + public static boolean addRoots (final URL[] classPathRoots, final FileObject projectArtfact, final String classPathType) throws IOException, UnsupportedOperationException { + final Extensible extensible = findExtensible(projectArtfact, classPathType); + if (extensible != null) { + if (extensible.pcmi != null) { + assert extensible.sg != null; + assert extensible.classPathType != null; + return ProjectClassPathModifierAccessor.INSTANCE.addRoots (classPathRoots, extensible.pcmi, extensible.sg, extensible.classPathType); + } + else if (extensible.pcpe != null) { + boolean result = false; + for (int i=0; i< classPathRoots.length; i++) { + URL urlToAdd = classPathRoots[i]; + if ("jar".equals(urlToAdd.getProtocol())) { + urlToAdd = FileUtil.getArchiveFile (urlToAdd); + } + final FileObject fo = URLMapper.findFileObject(urlToAdd); + if (fo == null) { + throw new UnsupportedOperationException ("Adding of a non existent root is not supported by project."); //NOI18N + } + result |= extensible.pcpe.addArchiveFile (fo); + } + return result; + } + } + throw new UnsupportedOperationException (); + } + + /** + * Removes archive files or folders from the project's classpath if the + * entries are included on it. + * @param classPathRoots roots to be removed, each root has to be either a root of an archive or a folder + * @param projectArtfact a file from whose classpath the roots should be removed + * @param classPathType the type of classpath, @see ClassPath + * @return true in case the classpath was changed, (at least one classpath root was removed from the classpath), + * the value false is returned when none of the classpath roots was included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * removing of a root from the classpath of the given id. + */ + public static boolean removeRoots (final URL[] classPathRoots, final FileObject projectArtfact, final String classPathType) throws IOException, UnsupportedOperationException { + final Extensible extensible = findExtensible (projectArtfact, classPathType); + if (extensible != null && extensible.pcmi != null) { + assert extensible.sg != null; + assert extensible.classPathType != null; + return ProjectClassPathModifierAccessor.INSTANCE.removeRoots (classPathRoots, extensible.pcmi, extensible.sg, extensible.classPathType); + } + throw new UnsupportedOperationException (); + } + + /** + * Adds artifacts (e.g. subprojects) into project's classpath if the + * artifacts are not already on it. + * @param artifacts to be added + * @param artifactElements the URIs of the build output, the artifactElements has to have the same length + * as artifacts. + * (must be owned by the artifact and be relative to it) + * @param projectArtfact a file which classpath should be extended + * @param classPathType the type of classpath to be extended, @see ClassPath + * @return true in case the classpath was changed, (at least one artifact was added to the classpath), + * the value false is returned when all the artifacts are already included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * adding of an artifact to the classpath of the given id. + */ + @SuppressWarnings("deprecation") //NOI18N + public static boolean addAntArtifacts (final AntArtifact[] artifacts, final URI[] artifactElements, + final FileObject projectArtfact, final String classPathType) throws IOException, UnsupportedOperationException { + final Extensible extensible = findExtensible (projectArtfact, classPathType); + if (extensible != null) { + assert artifacts.length == artifactElements.length; + if (extensible.pcmi != null) { + assert extensible.sg != null; + assert extensible.classPathType != null; + return ProjectClassPathModifierAccessor.INSTANCE.addAntArtifacts (artifacts, artifactElements, extensible.pcmi, extensible.sg, extensible.classPathType); + } + else if (extensible.pcpe != null) { + boolean result = false; + for (int i=0; i< artifacts.length; i++) { + result |= extensible.pcpe.addAntArtifact (artifacts[i], artifactElements[i]); + } + return result; + } + } + throw new UnsupportedOperationException (); + } + + /** + * Removes artifacts (e.g. subprojects) from project's classpath if the + * artifacts are included on it. + * @param artifacts to be added + * @param artifactElements the URIs of the build output, the artifactElements has to have the same length + * as artifacts. + * (must be owned by the artifact and be relative to it) + * @param projectArtfact a file from whose classpath the dependent projects should be removed + * @param classPathType the type of classpath, @see ClassPath + * @return true in case the classpath was changed, (at least one artifact was removed from the classpath), + * the value false is returned when none of the artifacts was included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * removing of an artifact from the classpath of the given id. + */ + public static boolean removeAntArtifacts (final AntArtifact[] artifacts, final URI[] artifactElements, + final FileObject projectArtifact, final String classPathType) throws IOException, UnsupportedOperationException { + final Extensible extensible = findExtensible (projectArtifact, classPathType); + if (extensible != null && extensible.pcmi != null) { + assert extensible.sg != null; + assert extensible.classPathType != null; + return ProjectClassPathModifierAccessor.INSTANCE.removeAntArtifacts (artifacts, artifactElements, extensible.pcmi, extensible.sg, extensible.classPathType); + } + throw new UnsupportedOperationException (); + } + + + /** + * Returns {@link ProjectClassPathModifier#Extensible} for given project artifact and classpath type. + * An Extensible implies a classpath to be extended. Different project type may provide different types + * of Extensible. + * @param projectArtifact a file owned by SourceGroup which classpath should be changed + * @param classPathType a classpath type, @see ClassPath + * @return an Extensible or null. In case when the project supports the {@link ProjectClassPathModifierImplementation}, + * this interface is used to find an Extensible. If this interface is not provided, but project provides + * the deprecated {@link ProjectClassPathExtender} interface and classpath type is {@link ClassPath@COMPILE} the + * single Extensible, without assigned SourceGroup, is returned. + * In case when neither {@link ProjectClassPathModifierImplementation} nor {@link ProjectClassPathExtender} + * is supported null is returned. + */ + @SuppressWarnings("deprecation") //NOI18N + private static Extensible findExtensible (final FileObject fo, final String classPathType) { + assert fo != null; + assert classPathType != null; + final Project project = FileOwnerQuery.getOwner(fo); + if (project == null) { + return null; + } + final ProjectClassPathModifierImplementation pm = (ProjectClassPathModifierImplementation) project.getLookup().lookup(ProjectClassPathModifierImplementation.class); + if (pm != null) { + final SourceGroup[] sgs = ProjectClassPathModifierAccessor.INSTANCE.getExtensibleSourceGroups(pm); + assert sgs != null : "Class: " + pm.getClass() + " returned null as source groups."; //NOI18N + for (SourceGroup sg : sgs) { + if ((fo == sg.getRootFolder() || FileUtil.isParentOf(sg.getRootFolder(),fo)) && sg.contains(fo)) { + final String[] types = ProjectClassPathModifierAccessor.INSTANCE.getExtensibleClassPathTypes(pm,sg); + assert types != null : "Class: " + pm.getClass() + " returned null as classpath types."; //NOI18N + for (String type : types) { + if (classPathType.equals(type)) { + return new Extensible (pm, sg, type); + } + } + } + } + } + else if (classPathType.equals(ClassPath.COMPILE)) { + final ProjectClassPathExtender pe = (ProjectClassPathExtender) project.getLookup().lookup(ProjectClassPathExtender.class); + if (pe != null) { + return new Extensible (pe); + } + } + return null; + } + + + /** + * Extensible represents a classpath which may be changed by the + * {@link ProjectClassPathModifier}. It encapsulates the compilation + * unit and class path type, @see ClassPath. + */ + private static final class Extensible { + + private final String classPathType; + private final SourceGroup sg; + private final ProjectClassPathModifierImplementation pcmi; + @SuppressWarnings("deprecation") //NOI18N + private final ProjectClassPathExtender pcpe; + + + private Extensible (final ProjectClassPathModifierImplementation pcmi , final SourceGroup sg, final String classPathType) { + assert pcmi != null; + assert sg != null; + assert classPathType != null; + this.pcmi = pcmi; + this.sg = sg; + this.classPathType = classPathType; + this.pcpe = null; + } + + @SuppressWarnings("deprecation") //NOI18N + private Extensible (final ProjectClassPathExtender pcpe) { + assert pcpe != null; + this.pcpe = pcpe; + this.pcmi = null; + this.sg = null; + this.classPathType = ClassPath.COMPILE; + } + } + +} Index: project/src/org/netbeans/modules/java/project/classpath/ProjectClassPathModifierAccessor.java =================================================================== RCS file: project/src/org/netbeans/modules/java/project/classpath/ProjectClassPathModifierAccessor.java diff -N project/src/org/netbeans/modules/java/project/classpath/ProjectClassPathModifierAccessor.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ project/src/org/netbeans/modules/java/project/classpath/ProjectClassPathModifierAccessor.java 15 May 2006 13:37:26 -0000 @@ -0,0 +1,63 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.modules.java.project.classpath; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import org.netbeans.api.java.project.classpath.ProjectClassPathModifier; +import org.netbeans.api.project.SourceGroup; +import org.netbeans.api.project.ant.AntArtifact; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.spi.java.project.classpath.ProjectClassPathModifierImplementation; +import org.openide.ErrorManager; + +/** + * + * @author tom + */ +public abstract class ProjectClassPathModifierAccessor { + + public static ProjectClassPathModifierAccessor INSTANCE; + + static { + Class c = ProjectClassPathModifierImplementation.class; + try { + Class.forName (c.getName(), true, c.getClassLoader()); + } catch (Exception ex) { + ErrorManager.getDefault().notify(ex); + } + } + + /** Creates a new instance of ProjectClassPathModifierAccessor */ + public ProjectClassPathModifierAccessor() { + } + + public abstract SourceGroup[] getExtensibleSourceGroups (ProjectClassPathModifierImplementation m); + + public abstract String[] getExtensibleClassPathTypes (ProjectClassPathModifierImplementation m, SourceGroup sg); + + public abstract boolean addLibraries (Library[] libraries, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + public abstract boolean removeLibraries (Library[] libraries, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + public abstract boolean addRoots (URL[] classPathRoots, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + public abstract boolean removeRoots (URL[] classPathRoots, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + public abstract boolean addAntArtifacts (AntArtifact[] artifacts, URI[] artifactElements, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + public abstract boolean removeAntArtifacts (AntArtifact[] artifacts, URI[] artifactElements, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + +} Index: project/src/org/netbeans/spi/java/project/classpath/ProjectClassPathExtender.java =================================================================== RCS file: /cvs/java/project/src/org/netbeans/spi/java/project/classpath/ProjectClassPathExtender.java,v retrieving revision 1.3 diff -u -r1.3 ProjectClassPathExtender.java --- project/src/org/netbeans/spi/java/project/classpath/ProjectClassPathExtender.java 12 Jul 2005 04:06:10 -0000 1.3 +++ project/src/org/netbeans/spi/java/project/classpath/ProjectClassPathExtender.java 15 May 2006 13:37:26 -0000 @@ -24,9 +24,10 @@ * A project can provide this interface in its {@link org.netbeans.api.project.Project#getLookup lookup} to * allow clients to extend its compilation classpath * by a new classpath element (JAR, folder, dependent project, or library). - * @since org.netbeans.modules.java.project/1 1.3 + * @since org.netbeans.modules.java.project/1 1.3 + * @deprecated Please use the {@link ProjectClassPathModifier} instead. */ -public interface ProjectClassPathExtender { +public @Deprecated interface ProjectClassPathExtender { /** * Adds a library into the project's compile classpath if the @@ -34,6 +35,7 @@ * @param library to be added * @return true in case the classpath was changed * @exception IOException in case the project metadata cannot be changed + * @deprecated Please use {@link ProjectClassPathModifier#addLibrary} instead. */ boolean addLibrary(Library library) throws IOException; @@ -43,6 +45,7 @@ * @param archiveFile ZIP/JAR file to be added * @return true in case the classpath was changed * @exception IOException in case the project metadata cannot be changed + * @deprecated Please use {@link ProjectClassPathModifier#addArchive} instead. */ boolean addArchiveFile(FileObject archiveFile) throws IOException; @@ -54,6 +57,7 @@ * (must be owned by the artifact and be relative to it) * @return true in case the classpath was changed * @exception IOException in case the project metadata cannot be changed + * @deprecated Please use {@link ProjectClassPathModifier#addAntArtifact} instead. */ boolean addAntArtifact(AntArtifact artifact, URI artifactElement) throws IOException; Index: project/src/org/netbeans/spi/java/project/classpath/ProjectClassPathModifierImplementation.java =================================================================== RCS file: project/src/org/netbeans/spi/java/project/classpath/ProjectClassPathModifierImplementation.java diff -N project/src/org/netbeans/spi/java/project/classpath/ProjectClassPathModifierImplementation.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ project/src/org/netbeans/spi/java/project/classpath/ProjectClassPathModifierImplementation.java 15 May 2006 13:37:26 -0000 @@ -0,0 +1,205 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.java.project.classpath; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import org.netbeans.api.project.SourceGroup; +import org.netbeans.api.project.ant.AntArtifact; +import org.netbeans.api.project.libraries.Library; +import org.netbeans.modules.java.project.classpath.ProjectClassPathModifierAccessor; +import org.openide.filesystems.FileObject; + +/** + * An SPI for project's classpaths modification. + * A project can provide subclass of this class in its {@link org.netbeans.api.project.Project#getLookup lookup} to + * allow clients to add or remove new classpath elements (JAR, folder, dependent project, or library) to its + * classpaths. + * @since org.netbeans.modules.java.project/1 1.10 + */ +public abstract class ProjectClassPathModifierImplementation { + + static { + ProjectClassPathModifierAccessor.INSTANCE = new Accessor (); + } + + protected ProjectClassPathModifierImplementation () { + } + + + /** + * Returns the {@link SourceGroup}s providing classpath(s) + * which may be modified. + * @return (possibly empty) array of {@link SourceGroup}, never returns null + */ + protected abstract SourceGroup [] getExtensibleSourceGroups (); + + + /** + * Returns the types of classpaths for given {@link SourceGroup} which may be modified. + * @param sourceGroup for which the classpath types should be returned + * @return (possibly empty) array of classpath types, never returns null + */ + protected abstract String[] getExtensibleClassPathTypes (SourceGroup sourceGroup); + + /** + * Adds libraries into the project's classpath if the + * libraries are not already included. + * @param libraries to be added + * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA} + * identifying the compilation unit to change + * @param type the type of the classpath the library should be added to, + * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE} + * @return true in case the classpath was changed (at least one library was added to the classpath), + * the value false is returned when all the libraries are already included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * adding of a library to the classpath of the given id. + */ + protected abstract boolean addLibraries (Library[] libraries, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + + /** + * Removes libraries from the project's classpath if the + * libraries are included on it. + * @param libraries to be removed + * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA} + * identifying the compilation unit to change + * @param type the type of the classpath the library should be removed from, + * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE} + * @return true in case the classpath was changed, (at least one library was removed from the classpath), + * the value false is returned when none of the libraries was included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * removing of a library from the classpath of the given id. + */ + protected abstract boolean removeLibraries (Library[] libraries, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + /** + * Adds archive files or folders into the project's classpath if the + * entries are not already there. + * @param classPathRoots roots to be added, each root has to be either a root of an archive or a folder + * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA} + * identifying the compilation unit to change + * @param type the type of the classpath the root should be added to, + * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE} + * @return true in case the classpath was changed, (at least one classpath root was added to the classpath), + * the value false is returned when all the classpath roots are already included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * adding of a root to the classpath of the given id. + */ + protected abstract boolean addRoots (URL[] classPathRoots, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + /** + * Removes archive files or folders from the project's classpath if the + * entries are included on it. + * @param classPathRoots roots to be removed, each root has to be either a root of an archive or a folder + * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA} + * identifying the compilation unit to change + * @param type the type of the classpath the root should be removed from, + * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE} + * @return true in case the classpath was changed, (at least one classpath root was removed from the classpath), + * the value false is returned when none of the classpath roots was included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * removing of a root from the classpath of the given id. + */ + protected abstract boolean removeRoots (URL[] classPathRoots, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + /** + * Adds artifacts (e.g. subprojects) into project's classpath if the + * artifacts are not already on it. + * @param artifacts to be added + * @param artifactElements the URIs of the build output, the artifactElements has to have the same length + * as artifacts. + * (must be owned by the artifact and be relative to it) + * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA} + * identifying the compilation unit to change + * @param type the type of the classpath the artifact should be added to, + * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE} + * @return true in case the classpath was changed, (at least one artifact was added to the classpath), + * the value false is returned when all the artifacts are already included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * adding of an artifact to the classpath of the given id. + */ + protected abstract boolean addAntArtifacts (AntArtifact[] artifacts, URI[] artifactElements, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + /** + * Removes artifacts (e.g. subprojects) from project's classpath if the + * artifacts are included on it. + * @param artifacts to be added + * @param artifactElements the URIs of the build output, the artifactElements has to have the same length + * as artifacts. + * (must be owned by the artifact and be relative to it) + * @param sourceGroup of type {@link org.netbeans.api.java.project.JavaProjectConstants#SOURCES_TYPE_JAVA} + * identifying the compilation unit to change + * @param type the type of the classpath the artifact should be removed from, + * eg {@link org.netbeans.api.java.classpath.ClassPath.COMPILE} + * @return true in case the classpath was changed, (at least one artifact was removed from the classpath), + * the value false is returned when none of the artifacts was included on the classpath. + * @exception IOException in case the project metadata cannot be changed + * @exception UnsupportedOperationException is thrown when the project does not support + * removing of an artifact from the classpath of the given id. + */ + protected abstract boolean removeAntArtifacts (AntArtifact[] artifacts, URI[] artifactElements, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException; + + + private static class Accessor extends ProjectClassPathModifierAccessor { + + public SourceGroup[] getExtensibleSourceGroups(final ProjectClassPathModifierImplementation m) { + assert m != null; + return m.getExtensibleSourceGroups(); + } + + public String[] getExtensibleClassPathTypes (final ProjectClassPathModifierImplementation m, SourceGroup sg) { + assert m != null; + assert sg != null; + return m.getExtensibleClassPathTypes(sg); + } + + public boolean removeLibraries(Library[] libraries, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException { + assert m!= null; + return m.removeLibraries(libraries, sourceGroup, type); + } + + public boolean removeAntArtifacts(AntArtifact[] artifacts, URI[] artifactElements, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException { + assert m!= null; + return m.removeAntArtifacts(artifacts, artifactElements, sourceGroup, type); + } + + public boolean addLibraries (Library[] libraries, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException { + assert m!= null; + return m.addLibraries(libraries, sourceGroup, type); + } + + public boolean addAntArtifacts (AntArtifact[] artifacts, URI[] artifactElements, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException { + assert m!= null; + return m.addAntArtifacts (artifacts, artifactElements, sourceGroup, type); + } + + public boolean removeRoots (URL[] classPathRoots, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException { + assert m!= null; + return m.removeRoots(classPathRoots, sourceGroup, type); + } + + public boolean addRoots (URL[] classPathRoots, ProjectClassPathModifierImplementation m, SourceGroup sourceGroup, String type) throws IOException, UnsupportedOperationException { + assert m!= null; + return m.addRoots (classPathRoots, sourceGroup, type); + } + + } +}