Index: apichanges.xml =================================================================== RCS file: /cvs/ant/project/apichanges.xml,v retrieving revision 1.1 diff -u -r1.1 apichanges.xml --- apichanges.xml 14 Oct 2004 17:26:47 -0000 1.1 +++ apichanges.xml 9 Dec 2004 13:42:22 -0000 @@ -76,7 +76,21 @@ - + + + Make ReferencesHelper.createForeignFileReference produce relative links based on properties + + + + + + ReferenceHelper should reuse external project folders as base + directories for references it creates where appropriate. The + new API allows to add/remove these base folders. + + + + Index: manifest.mf =================================================================== RCS file: /cvs/ant/project/manifest.mf,v retrieving revision 1.5 diff -u -r1.5 manifest.mf --- manifest.mf 9 Nov 2004 21:01:15 -0000 1.5 +++ manifest.mf 9 Dec 2004 13:42:22 -0000 @@ -1,5 +1,5 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.project.ant/0 -OpenIDE-Module-Specification-Version: 1.2 +OpenIDE-Module-Specification-Version: 1.3 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/project/ant/Bundle.properties Index: src/org/netbeans/spi/project/support/ant/ReferenceHelper.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java,v retrieving revision 1.20 diff -u -r1.20 ReferenceHelper.java --- src/org/netbeans/spi/project/support/ant/ReferenceHelper.java 26 Oct 2004 18:44:30 -0000 1.20 +++ src/org/netbeans/spi/project/support/ant/ReferenceHelper.java 9 Dec 2004 13:42:22 -0000 @@ -19,9 +19,11 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.netbeans.api.project.Project; @@ -84,6 +86,10 @@ */ static final String REFS_NS = "http://www.netbeans.org/ns/ant-project-references/1"; // NOI18N + /** Set of property names which values can be used as additional base + * directories. */ + private Set/**/ extraBaseDirectories = new HashSet(); + private final AntProjectHelper h; final PropertyEvaluator eval; private final AuxiliaryConfiguration aux; @@ -218,9 +224,14 @@ assert forProjPath != null : "These dirs are not really collocated: " + myProjDir + " & " + forProjDir; propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH; } else { - // Use an absolute path. - forProjPath = forProjDir.getAbsolutePath(); - propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH; + forProjPath = relativizeFileToExtraBaseFolders(forProjDir); + if (forProjPath != null) { + propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH; + } else { + // Use an absolute path. + forProjPath = forProjDir.getAbsolutePath(); + propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH; + } } EditableProperties props = h.getProperties(propertiesFile); String forProjPathProp = "project." + forProjName; // NOI18N @@ -643,8 +654,13 @@ path = PropertyUtils.relativizeFile(myProjDir, file); assert path != null : "expected relative path from " + myProjDir + " to " + file; } else { - propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH; - path = file.getAbsolutePath(); + path = relativizeFileToExtraBaseFolders(file); + if (path != null) { + propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH; + } else { + propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH; + path = file.getAbsolutePath(); + } } EditableProperties props = h.getProperties(propertiesFile); String fileID = file.getName(); @@ -669,7 +685,105 @@ } }); } + + /** + * Test whether file does not lie under an extra base folder and if it does + * then return string in form of "${extra.base}/remaining/path"; or null. + */ + private String relativizeFileToExtraBaseFolders(File f) { + File base = FileUtil.toFile(h.getProjectDirectory()); + String fileToRelativize = f.getAbsolutePath(); + synchronized (extraBaseDirectories) { + Iterator it = extraBaseDirectories.iterator(); + while (it.hasNext()) { + String prop = (String)it.next(); + String path = eval.getProperty(prop); + File extraBase = PropertyUtils.resolveFile(base, path); + path = extraBase.getAbsolutePath(); + if (!path.endsWith(File.separator)) { + path += File.separator; + } + if (fileToRelativize.startsWith(path)) { + return "${"+prop+"}/"+fileToRelativize.substring(path.length()).replace('\\', '/'); // NOI18N + } + } + } + return null; + } + /** + * Add extra folder which can be used as base directory (in addition to + * project base folder) for creating references. Duplicate property names + * are not allowed. Any newly created reference to a file lying under an + * extra base directory will be based on that property and will be stored in + * shared project proeprties. + * @param propertyName property name which value is path to folder which + * can be used as alternative project's base directory + * @since 1.3 + */ + public void addExtraBaseDirectory(String propertyName) { + assert propertyName != null && eval.getProperty(propertyName) != null; + synchronized (extraBaseDirectories) { + if (!extraBaseDirectories.add(propertyName)) { + throw new IllegalArgumentException("Already extra base directory property: "+propertyName); // NOI18N + } + } + } + + /** + * Remove extra base directory. The base directory property had to be added + * by {@link #addExtraBaseDirectory} method call. At the time when this + * method is called the property must still exist and must be valid. This + * method will replace all references of the extra base directory property + * with its current value and if needed it may move such a property from + * shared project properties into the private properties. + * @param propertyName property name which was added by + * {@link #addExtraBaseDirectory} method. + * @since 1.3 + */ + public void removeExtraBaseDirectory(String propertyName) { + synchronized (extraBaseDirectories) { + if (!extraBaseDirectories.remove(propertyName)) { + throw new IllegalArgumentException("Non-existing extra base directory property: "+propertyName); // NOI18N + } + // substitute all references of removed extra base folder property with its value + String tag = "${"+propertyName+"}"; // NOI18N + // was extra base property defined in shared file or not: + boolean shared = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH).containsKey(propertyName); + String value = eval.getProperty(propertyName); + EditableProperties propProj = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); + EditableProperties propPriv = h.getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH); + boolean modifiedProj = false; + boolean modifiedPriv = false; + Iterator it = propProj.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry)it.next(); + String val = (String)entry.getValue(); + int index; + if ((index = val.indexOf(tag)) != -1) { + val = val.substring(0, index) +value + val.substring(index+tag.length()); + if (shared) { + // substitute extra base folder property with its value + entry.setValue(val); + modifiedProj = true; + } else { + // move property to private properties file + it.remove(); + propPriv.put(entry.getKey(), val); + modifiedPriv = true; + modifiedProj = true; + } + } + } + if (modifiedProj) { + h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, propProj); + } + if (modifiedPriv) { + h.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH, propPriv); + } + } + } + /** * Find reference ID (e.g. something you can then pass to RawReference * as foreignProjectName) for the given property base name, prefix and path. Index: test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java =================================================================== RCS file: /cvs/ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java,v retrieving revision 1.13 diff -u -r1.13 ReferenceHelperTest.java --- test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java 26 Aug 2004 21:16:02 -0000 1.13 +++ test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java 9 Dec 2004 13:42:22 -0000 @@ -702,4 +702,45 @@ assertNull("wrong target name, will not be found", art); } + public void testAddRemoveExtraBaseDirectory() throws Exception { + // test foreign file reference for non-collocated jar under extra base folder + FileObject nonCollocatedLib = scratch.getFileObject("separate").createFolder("jars").createData("mylib.jar"); + File f = FileUtil.toFile(nonCollocatedLib); + EditableProperties props = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); + props.setProperty("externalSourceRoot", "../separate"); + h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props); + r.addExtraBaseDirectory("externalSourceRoot"); + String ref = r.createForeignFileReference(f, "jar"); + assertEquals("foreign file reference created", "${file.reference.mylib.jar}", ref); + String refval = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH).getProperty(ref.substring(2, ref.length()-1)); + assertEquals("reference is using extra base folder", "${externalSourceRoot}/jars/mylib.jar", refval); + assertEquals("reference is correctly evaluated", f, h.resolveFile(h.getStandardPropertyEvaluator().evaluate(refval))); + // test removal of extra base folder + r.removeExtraBaseDirectory("externalSourceRoot"); + refval = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH).getProperty(ref.substring(2, ref.length()-1)); + assertEquals("reference does not contain extra base folder", "../separate/jars/mylib.jar", refval); + assertEquals("reference is correctly evaluated", f, h.resolveFile(refval)); + + // the same test as above but with extra base folder defined in PRIVATE props + nonCollocatedLib = scratch.createFolder("separate2").createFolder("jars").createData("mylib2.jar"); + f = FileUtil.toFile(nonCollocatedLib); + props = h.getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH); + String absolutePath = FileUtil.toFile(scratch.getFileObject("separate2")).getAbsolutePath(); + props.setProperty("externalSourceRootAbsolute", absolutePath); + h.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH, props); + r.addExtraBaseDirectory("externalSourceRootAbsolute"); + ref = r.createForeignFileReference(f, "jar"); + assertEquals("foreign file reference created", "${file.reference.mylib2.jar}", ref); + refval = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH).getProperty(ref.substring(2, ref.length()-1)); + assertEquals("reference is using extra base folder", "${externalSourceRootAbsolute}/jars/mylib2.jar", refval); + assertEquals("reference is correctly evaluated", f, h.resolveFile(h.getStandardPropertyEvaluator().evaluate(refval))); + r.removeExtraBaseDirectory("externalSourceRootAbsolute"); + refval = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH).getProperty(ref.substring(2, ref.length()-1)); + assertNull("reference was removed from PROJECT_PROPERTIES_PATH", refval); + refval = h.getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH).getProperty(ref.substring(2, ref.length()-1)); + assertNotNull("reference was moved to PRIVATE_PROPERTIES_PATH", refval); + assertEquals("reference does not contain extra base folder", absolutePath+"/jars/mylib2.jar", refval); + assertEquals("reference is correctly evaluated", f, h.resolveFile(h.getStandardPropertyEvaluator().evaluate(refval))); + } + }