Index: ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java,v retrieving revision 1.6 diff -u -r1.6 AntProjectHelper.java --- ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java 15 Apr 2004 17:01:20 -0000 1.6 +++ ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java 21 Apr 2004 22:49:22 -0000 @@ -140,20 +140,17 @@ private final Set/**/ modifiedMetadataPaths = new HashSet(); /** - * Properties loaded from metadata files on disk. - * Keys are project-relative paths such as {@link #PROJECT_PROPERTIES_PATH}. - * Values are loaded properties objects, or null if the file did not exist. - * If the key does not exist then it needs to be checked on disk. - */ - private final Map/**/ properties = new HashMap(); - - /** * Registered listeners. * Access must be directly synchronized. */ private final List/**/ listeners = new ArrayList(); - // XXX lock any loaded files while the project is modified, to prevent manual editing, + /** + * List of loaded properties. + */ + private final ProjectProperties properties; + + // XXX lock any loaded XML files while the project is modified, to prevent manual editing, // and reload any modified files if the project is unmodified private AntProjectHelper(FileObject dir, Document projectXml, ProjectState state, AntBasedProjectType type) { @@ -165,6 +162,7 @@ assert type != null; this.projectXml = projectXml; assert projectXml != null; + properties = new ProjectProperties(dir); } /** @@ -463,31 +461,7 @@ saveXml(privateXml, path); } else { // All else is assumed to be a properties file. - FileObject f = dir.getFileObject(path); - assert properties.containsKey(path); - EditableProperties p = (EditableProperties)properties.get(path); - if (p != null) { - // Supposed to create/modify the file. - if (f == null) { - f = FileUtil.createData(dir, path); - } - FileLock lock = f.lock(); - try { - OutputStream os = f.getOutputStream(lock); - try { - p.store(os); - } finally { - os.close(); - } - } finally { - lock.releaseLock(); - } - } else { - // We are supposed to remove any existing file. - if (f != null) { - f.delete(); - } - } + properties.write(path); } // As metadata files are saved, take them off the modified list. it.remove(); @@ -508,51 +482,10 @@ * @return a set of properties */ public EditableProperties getProperties(String path) { - EditableProperties p = getPropertiesOrNull(path); - if (p != null) { - return p; - } else { - return new EditableProperties(true); + if (path.equals(AntProjectHelper.PROJECT_XML_PATH) || path.equals(AntProjectHelper.PRIVATE_XML_PATH)) { + throw new IllegalArgumentException("Attempt to load properties from a project XML file"); // NOI18N } - } - - /** - * Like {@link #getProperties} but a missing or broken file results - * in a null return rather than an empty properties object. - */ - private EditableProperties getPropertiesOrNull(String path) { - if (path.equals(PROJECT_XML_PATH) || path.equals(PRIVATE_XML_PATH)) { - throw new IllegalArgumentException("Attempt to load/store properties from a project XML file"); // NOI18N - } - if (properties.containsKey(path)) { - // In cache. - EditableProperties p = (EditableProperties)properties.get(path); - if (p == null) { - return null; - } else { - return p.cloneProperties(); - } - } - // Not in cache. - FileObject fo = dir.getFileObject(path); - if (fo != null) { - try { - InputStream is = fo.getInputStream(); - try { - EditableProperties p = new EditableProperties(true); - p.load(is); - properties.put(path, p); - return p.cloneProperties(); - } finally { - is.close(); - } - } catch (IOException e) { - ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); - } - } - // Broken or missing. - properties.put(path, null); - return null; + return properties.getProperties(path); } /** @@ -569,12 +502,27 @@ * @param props a set of properties to store, or null to delete any existing properties file there */ public void putProperties(String path, EditableProperties props) { - if (Utilities.compareObjects(props, getPropertiesOrNull(path))) { - // No change. - return; + if (path.equals(AntProjectHelper.PROJECT_XML_PATH) || path.equals(AntProjectHelper.PRIVATE_XML_PATH)) { + throw new IllegalArgumentException("Attempt to store properties from a project XML file"); // NOI18N + } + if (properties.putProperties(path, props)) { + modifying(path); } - properties.put(path, props.cloneProperties()); - modifying(path); + } + + /** + * Get a property provider that works with loadable project properties. + * Its current values should match {@link #getProperties}, and calls to + * {@link #putProperties} should cause it to fire changes. + * @param path a relative URI in the project directory, e.g. + * {@link #PROJECT_PROPERTIES_PATH} or {@link #PRIVATE_PROPERTIES_PATH} + * @return a property provider implementation + */ + public PropertyProvider getPropertyProvider(String path) { + if (path.equals(AntProjectHelper.PROJECT_XML_PATH) || path.equals(AntProjectHelper.PRIVATE_XML_PATH)) { + throw new IllegalArgumentException("Attempt to store properties from a project XML file"); // NOI18N + } + return properties.getPropertyProvider(path); } /** @@ -717,6 +665,11 @@ return new ExtensibleMetadataProviderImpl(this); } + /** @deprecated Use the variant that accepts a property evaluator instead. */ + public FileBuiltQueryImplementation createGlobFileBuiltQuery(String[] from, String[] to) throws IllegalArgumentException { + return createGlobFileBuiltQuery(getStandardPropertyEvaluator(), from, to); + } + /** * Create an implementation of {@link FileBuiltQuery} that works with files * within the project based on simple glob pattern mappings. @@ -728,7 +681,7 @@ * glob pattern - this must include exactly one asterisk (*) * representing a variable portion of a source file path (always slash-separated * and relative to the project directory) and may include some Ant property - * references which will be resolved as per {@link AntProjectHelper#evaluateString}. + * references which will be resolved as per the property evaluator. * A file is considered out of date if there is no file represented by the * matching target pattern (which has the same format), or the target file is older * than the source file, or the source file is modified as per @@ -753,6 +706,7 @@ *
  • ${test.build.classes.dir}/*.class * * + * @param a property evaluator to interpret the patterns with * @param from a list of glob patterns for source files * @param to a matching list of glob patterns for built files * @return a query implementation @@ -760,8 +714,13 @@ * have zero or multiple asterisks, * or the arrays are not of equal lengths */ - public FileBuiltQueryImplementation createGlobFileBuiltQuery(String[] from, String[] to) throws IllegalArgumentException { - return new GlobFileBuiltQuery(this, from, to); + public FileBuiltQueryImplementation createGlobFileBuiltQuery(PropertyEvaluator eval, String[] from, String[] to) throws IllegalArgumentException { + return new GlobFileBuiltQuery(this, eval, from, to); + } + + /** @deprecated Use the variant that accepts a PropertyEvaluator instead. */ + public AntArtifact createSimpleAntArtifact(String type, String locationProperty, String targetName, String cleanTargetName) { + return createSimpleAntArtifact(type, locationProperty, getStandardPropertyEvaluator(), targetName, cleanTargetName); } /** @@ -770,14 +729,20 @@ * @param type the type of artifact, e.g. {@link AntArtifact#TYPE_JAR} * @param locationProperty an Ant property name giving the project-relative * location of the artifact, e.g. dist.jar + * @param eval a way to evaluate the location property (e.g. {@link #getStandardPropertyEvaluator}) * @param targetName the name of an Ant target which will build the artifact, * e.g. jar * @param cleanTargetName the name of an Ant target which will delete the artifact * (and maybe other build products), e.g. clean * @return an artifact */ - public AntArtifact createSimpleAntArtifact(String type, String locationProperty, String targetName, String cleanTargetName) { - return new SimpleAntArtifact(this, type, locationProperty, targetName, cleanTargetName); + public AntArtifact createSimpleAntArtifact(String type, String locationProperty, PropertyEvaluator eval, String targetName, String cleanTargetName) { + return new SimpleAntArtifact(this, type, locationProperty, eval, targetName, cleanTargetName); + } + + /** @deprecated Use the variant that takes a property evaluator instead. */ + public SharabilityQueryImplementation createSharabilityQuery(String[] sourceRoots, String[] buildDirectories) { + return createSharabilityQuery(getStandardPropertyEvaluator(), sourceRoots, buildDirectories); } /** @@ -816,7 +781,8 @@ * Typical usage would be: *

    *
    -     * helper.createSharabilityQuery(new String[] {"${src.dir}", "${test.src.dir}"},
    +     * helper.createSharabilityQuery(helper.getStandardPropertyEvaluator(),
    +     *                               new String[] {"${src.dir}", "${test.src.dir}"},
          *                               new String[] {"${build.dir}", "${dist.dir}"})
          * 
    *

    @@ -837,19 +803,20 @@ * this implementation in your project lookup and may return UNKNOWN. *

    * + * @param eval a property evaluator to interpret paths with * @param sourceRoots a list of additional paths to treat as sharable * @param buildDirectories a list of paths to treat as not sharable * @return a sharability query implementation suitable for the project lookup * @see Project#getLookup */ - public SharabilityQueryImplementation createSharabilityQuery(String[] sourceRoots, String[] buildDirectories) { + public SharabilityQueryImplementation createSharabilityQuery(PropertyEvaluator eval, String[] sourceRoots, String[] buildDirectories) { String[] includes = new String[sourceRoots.length + 1]; System.arraycopy(sourceRoots, 0, includes, 0, sourceRoots.length); includes[sourceRoots.length] = ""; // NOI18N String[] excludes = new String[buildDirectories.length + 1]; System.arraycopy(buildDirectories, 0, excludes, 0, buildDirectories.length); excludes[buildDirectories.length] = "nbproject/private"; // NOI18N - return new SharabilityQueryImpl(this, includes, excludes); + return new SharabilityQueryImpl(this, eval, includes, excludes); } /** @@ -870,10 +837,10 @@ * @see #PRIVATE_PROPERTIES_PATH * @see PropertyUtils#getGlobalProperties * @see #PROJECT_PROPERTIES_PATH + * @deprecated Please use {@link #getStandardPropertyEvaluator} instead. */ public String evaluate(String prop) { - // XXX could be cached - return PropertyUtils.evaluate(prop, makeEvalPredefs(), makeEvalDefs()); + return getStandardPropertyEvaluator().getProperty(prop); } /** @@ -885,10 +852,10 @@ * @see #PRIVATE_PROPERTIES_PATH * @see PropertyUtils#getGlobalProperties * @see #PROJECT_PROPERTIES_PATH + * @deprecated Please use {@link #getStandardPropertyEvaluator} instead. */ public Map/**/ evaluateAll() { - // XXX could be cached - return PropertyUtils.evaluateAll(makeEvalPredefs(), makeEvalDefs()); + return getStandardPropertyEvaluator().getProperties(); } /** @@ -901,55 +868,86 @@ * @see #PRIVATE_PROPERTIES_PATH * @see PropertyUtils#getGlobalProperties * @see #PROJECT_PROPERTIES_PATH + * @deprecated Please use {@link #getStandardPropertyEvaluator} instead. */ public String evaluateString(String text) { - return PropertyUtils.evaluateString(text, makeEvalPredefs(), makeEvalDefs()); + return getStandardPropertyEvaluator().evaluate(text); } + private PropertyProvider stockPropertyPreprovider = null; + /** - * Create stock predefs: ${basedir} and system properties. + * Get a property provider which defines basedir according to + * the project directory and also copies all system properties in the current VM. + * @return a stock property provider for initial Ant-related definitions + * @see PropertyUtils#sequentialPropertyEvaluator */ - private Map/**/ makeEvalPredefs() { - Map/**/ m = new HashMap(); - Properties p = System.getProperties(); - synchronized (p) { - m.putAll(p); + public PropertyProvider getStockPropertyPreprovider() { + if (stockPropertyPreprovider == null) { + Map/**/ m = new HashMap(); + Properties p = System.getProperties(); + synchronized (p) { + m.putAll(p); + } + m.put("basedir", FileUtil.toFile(dir).getAbsolutePath()); // NOI18N + stockPropertyPreprovider = PropertyUtils.fixedPropertyProvider(m); } - m.put("basedir", FileUtil.toFile(dir).getAbsolutePath()); // NOI18N - return m; + return stockPropertyPreprovider; } + private PropertyEvaluator standardPropertyEvaluator = null; + /** - * Create stock defs: private project props, global user build props, shared project props. + * Get a property evaluator that can evaluate properties according to the default + * file layout for Ant-based projects. + * First, {@link #getStockPropertyPreprovider stock properties} are predefined. + * Then {@link #PRIVATE_PROPERTIES_PATH} is loaded via {@link #getPropertyProvider}, + * then global definitions from {@link PropertyUtils#globalPropertyProvider} + * (though these may be overridden using the property user.properties.file + * in private.properties), then {@link #PROJECT_PROPERTIES_PATH}. + * @return a standard property evaluator */ - private List/*>*/ makeEvalDefs() { - Map/**/ privprops = getProperties(PRIVATE_PROPERTIES_PATH); - // XXX not very efficient... - String userPropsFile = PropertyUtils.evaluate("user.properties.file", makeEvalPredefs(), Collections.singletonList(privprops)); - Map/**/ globprops; - if (userPropsFile != null) { - globprops = new Properties(); - File f = resolveFile(userPropsFile); - if (f.isFile() && f.canRead()) { - try { - InputStream is = new FileInputStream(f); + public PropertyEvaluator getStandardPropertyEvaluator() { + if (standardPropertyEvaluator == null) { + PropertyEvaluator findUserPropertiesFile = PropertyUtils.sequentialPropertyEvaluator( + getStockPropertyPreprovider(), + new PropertyProvider[] { + getPropertyProvider(PRIVATE_PROPERTIES_PATH), + } + ); + String userPropertiesFile = findUserPropertiesFile.getProperty("user.properties.file"); // NOI18N + PropertyProvider globalProperties; + if (userPropertiesFile != null) { + // XXX listen to changes in this, maybe... + // (both in findUserPropertiesFile and in this file on disk) + Properties globprops = new Properties(); + File f = resolveFile(userPropertiesFile); + if (f.isFile() && f.canRead()) { try { - ((Properties)globprops).load(is); - } finally { - is.close(); + InputStream is = new FileInputStream(f); + try { + globprops.load(is); + } finally { + is.close(); + } + } catch (IOException e) { + ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); } - } catch (IOException e) { - ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); } + globalProperties = PropertyUtils.fixedPropertyProvider(globprops); + } else { + globalProperties = PropertyUtils.globalPropertyProvider(); } - } else { - globprops = PropertyUtils.getGlobalProperties(); + standardPropertyEvaluator = PropertyUtils.sequentialPropertyEvaluator( + getStockPropertyPreprovider(), + new PropertyProvider[] { + getPropertyProvider(PRIVATE_PROPERTIES_PATH), + globalProperties, + getPropertyProvider(PROJECT_PROPERTIES_PATH), + } + ); } - return Arrays.asList(new Map/**/[] { - privprops, - globprops, - getProperties(PROJECT_PROPERTIES_PATH), - }); + return standardPropertyEvaluator; } /** Index: ant/project/src/org/netbeans/spi/project/support/ant/GlobFileBuiltQuery.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/GlobFileBuiltQuery.java,v retrieving revision 1.4 diff -u -r1.4 GlobFileBuiltQuery.java --- ant/project/src/org/netbeans/spi/project/support/ant/GlobFileBuiltQuery.java 3 Apr 2004 03:16:59 -0000 1.4 +++ ant/project/src/org/netbeans/spi/project/support/ant/GlobFileBuiltQuery.java 21 Apr 2004 22:49:22 -0000 @@ -48,6 +48,7 @@ final class GlobFileBuiltQuery implements FileBuiltQueryImplementation { private final AntProjectHelper helper; + private final PropertyEvaluator eval; private final FileObject projectDir; private final File projectDirF; private final String[] fromPrefixes; @@ -63,8 +64,9 @@ * Create a new query implementation based on an Ant-based project. * @see AntProjectHelper#createGlobFileBuiltQuery */ - public GlobFileBuiltQuery(AntProjectHelper helper, String[] from, String[] to) throws IllegalArgumentException { + public GlobFileBuiltQuery(AntProjectHelper helper, PropertyEvaluator eval, String[] from, String[] to) throws IllegalArgumentException { this.helper = helper; + this.eval = eval; projectDir = helper.getProjectDirectory(); projectDirF = FileUtil.toFile(projectDir); assert projectDirF != null; @@ -97,7 +99,7 @@ projectDir.addFileChangeListener(FileUtil.weakFileChangeListener(fileL, projectDir)); */ weakFileL = FileUtil.weakFileChangeListener(fileL, null); - // XXX add properties listener to helper... if anything changes, refresh all + // XXX add properties listener to evaluator... if anything changes, refresh all // status objects and clear the stati cache; can then also keep a cache of // evaluated path prefixes & suffixes } @@ -154,7 +156,7 @@ throw new IllegalArgumentException("Cannot check for status on file " + file + " outside of " + projectDir); // NOI18N } for (int i = 0; i < fromPrefixes.length; i++) { - String prefixEval = helper.evaluateString(fromPrefixes[i]); + String prefixEval = eval.evaluate(fromPrefixes[i]); if (prefixEval == null) { return null; } @@ -162,7 +164,7 @@ continue; } String remainder = path.substring(prefixEval.length()); - String suffixEval = helper.evaluateString(fromSuffixes[i]); + String suffixEval = eval.evaluate(fromSuffixes[i]); if (suffixEval == null) { continue; } @@ -170,11 +172,11 @@ continue; } String particular = remainder.substring(0, remainder.length() - suffixEval.length()); - String toPrefixEval = helper.evaluateString(toPrefixes[i]); + String toPrefixEval = eval.evaluate(toPrefixes[i]); if (toPrefixEval == null) { continue; } - String toSuffixEval = helper.evaluateString(toSuffixes[i]); + String toSuffixEval = eval.evaluate(toSuffixes[i]); if (toSuffixEval == null) { continue; } Index: ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java =================================================================== RCS file: ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java diff -N ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java 21 Apr 2004 22:49:22 -0000 @@ -0,0 +1,212 @@ +/* + * 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-2004 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.project.support.ant; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.openide.ErrorManager; +import org.openide.filesystems.FileLock; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.Utilities; + +/** + * Manages the loaded property files for {@link AntProjectHelper}. + * @author Jesse Glick + */ +final class ProjectProperties { + + private final FileObject dir; + + /** + * Properties loaded from metadata files on disk. + * Keys are project-relative paths such as {@link #PROJECT_PROPERTIES_PATH}. + * Values are loaded property providers. + */ + private final Map/**/ properties = new HashMap(); + + /** + * Create a project properties helper object. + * @param dir the project directory + */ + public ProjectProperties(FileObject dir) { + this.dir = dir; + } + + /** + * Get properties from a given path. + * @param path the project-relative path + * @return the applicable properties (created if empty; never null) + */ + public EditableProperties getProperties(String path) { + return getPP(path).getEditableProperties(); + } + + /** + * Store properties in memory. + * @param path the project-relative path + * @param props the new properties, or null to remove the properties file + * @return true if an actual change was made + */ + public boolean putProperties(String path, EditableProperties props) { + return getPP(path).put(props); + } + + /** + * Write cached properties to disk. + * @param the project-relative path + * @throws IOException if the file could not be written + */ + public void write(String path) throws IOException { + assert properties.containsKey(path); + getPP(path).write(); + } + + /** + * Make a property provider that loads from this file + * and fires changes when it is written to (even in memory). + */ + public PropertyProvider getPropertyProvider(String path) { + return getPP(path); + } + + private PP getPP(String path) { + PP pp = (PP)properties.get(path); + if (pp == null) { + pp = new PP(path, dir); + properties.put(path, pp); + } + return pp; + } + + private static final class PP implements PropertyProvider { + + // XXX lock any loaded property files while the project is modified, to prevent manual editing, + // and reload any modified files if the project is unmodified + + private final String path; + private final FileObject dir; + private EditableProperties properties = null; + private boolean loaded = false; + private final List/**/ listeners = new ArrayList(); + + public PP(String path, FileObject dir) { + this.path = path; + this.dir = dir; + } + + public EditableProperties getEditableProperties() { + if (!loaded) { + FileObject fo = dir.getFileObject(path); + if (fo != null) { + try { + EditableProperties p; + InputStream is = fo.getInputStream(); + try { + p = new EditableProperties(true); + p.load(is); + } finally { + is.close(); + } + properties = p; + } catch (IOException e) { + ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); + } + } + loaded = true; + } + if (properties != null) { + return properties.cloneProperties(); + } else { + return new EditableProperties(true); + } + } + + public boolean put(EditableProperties nue) { + loaded = true; + boolean modifying = !Utilities.compareObjects(nue, properties); + if (modifying) { + if (nue != null) { + properties = nue.cloneProperties(); + } else { + properties = null; + } + fireChange(); + } + return modifying; + } + + public void write() throws IOException { + assert loaded; + FileObject f = dir.getFileObject(path); + if (properties != null) { + // Supposed to create/modify the file. + if (f == null) { + f = FileUtil.createData(dir, path); + } + FileLock lock = f.lock(); + try { + OutputStream os = f.getOutputStream(lock); + try { + properties.store(os); + } finally { + os.close(); + } + } finally { + lock.releaseLock(); + } + } else { + // We are supposed to remove any existing file. + if (f != null) { + f.delete(); + } + } + } + + public Map getProperties() { + return getEditableProperties(); + } + + public synchronized void addChangeListener(ChangeListener l) { + listeners.add(l); + } + + public synchronized void removeChangeListener(ChangeListener l) { + listeners.remove(l); + } + + private void fireChange() { + ChangeListener[] ls; + synchronized (this) { + if (listeners.isEmpty()) { + return; + } + ls = (ChangeListener[])listeners.toArray(new ChangeListener[listeners.size()]); + } + ChangeEvent ev = new ChangeEvent(this); + for (int i = 0; i < ls.length; i++) { + ls[i].stateChanged(ev); + } + } + + } + +} Index: ant/project/src/org/netbeans/spi/project/support/ant/PropertyEvaluator.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/PropertyEvaluator.java,v retrieving revision 1.1 diff -u -r1.1 PropertyEvaluator.java --- ant/project/src/org/netbeans/spi/project/support/ant/PropertyEvaluator.java 16 Apr 2004 21:21:04 -0000 1.1 +++ ant/project/src/org/netbeans/spi/project/support/ant/PropertyEvaluator.java 21 Apr 2004 22:49:22 -0000 @@ -23,8 +23,10 @@ * it will be used with the project manager mutex. Changes should be fired * synchronously. * @author Jesse Glick + * @see PropertyUtils#sequentialPropertyEvaluator + * @see AntProjectHelper#getStandardPropertyEvaluator */ -/*XXX: public*/ interface PropertyEvaluator { +public interface PropertyEvaluator { /** * Evaluate a single property. Index: ant/project/src/org/netbeans/spi/project/support/ant/PropertyProvider.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/PropertyProvider.java,v retrieving revision 1.1 diff -u -r1.1 PropertyProvider.java --- ant/project/src/org/netbeans/spi/project/support/ant/PropertyProvider.java 16 Apr 2004 21:21:04 -0000 1.1 +++ ant/project/src/org/netbeans/spi/project/support/ant/PropertyProvider.java 21 Apr 2004 22:49:22 -0000 @@ -25,7 +25,7 @@ * synchronously. * @author Jesse Glick */ -/*XXX public*/ interface PropertyProvider { +public interface PropertyProvider { /** * Get all defined properties. Index: ant/project/src/org/netbeans/spi/project/support/ant/PropertyUtils.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/PropertyUtils.java,v retrieving revision 1.5 diff -u -r1.5 PropertyUtils.java --- ant/project/src/org/netbeans/spi/project/support/ant/PropertyUtils.java 21 Apr 2004 15:48:57 -0000 1.5 +++ ant/project/src/org/netbeans/spi/project/support/ant/PropertyUtils.java 21 Apr 2004 22:49:22 -0000 @@ -151,7 +151,7 @@ * is changed. * @return a property producer */ - /*XXX public*/ static synchronized PropertyProvider globalPropertyProvider() { + public static synchronized PropertyProvider globalPropertyProvider() { if (globalPropertyProvider != null) { PropertyProvider pp = (PropertyProvider)globalPropertyProvider.get(); if (pp != null) { @@ -234,6 +234,7 @@ * @param predefs an unevaluated set of initial definitions * @param defs an ordered list of property mappings, e.g. {@link EditableProperties} instances * @return a value for the property, or null if it was not defined or a circularity error was detected + * @deprecated Please use {@link #fixedPropertyProvider} and {@link #sequentialPropertyEvaluator} instead. */ public static String evaluate(String prop, Map/**/ predefs, List/*>*/ defs) { // XXX could be faster for large mappings by using reverse analysis @@ -256,6 +257,7 @@ * @param defs an ordered list of property mappings, e.g. {@link EditableProperties} instances * @param predefs an unevaluated set of initial definitions * @return values for all defined properties, or null if a circularity error was detected + * @deprecated Please use {@link #fixedPropertyProvider} and {@link #sequentialPropertyEvaluator} instead. */ public static Map/**/ evaluateAll(Map/**/ predefs, List/*>*/ defs) { Map/**/ m = new HashMap(predefs); @@ -311,6 +313,7 @@ * @param predefs an unevaluated set of initial definitions * @param defs an ordered list of property mappings, e.g. {@link EditableProperties} instances * @return a value for the text, or null if a circularity error was detected + * @deprecated Please use {@link #fixedPropertyProvider} and {@link #sequentialPropertyEvaluator} instead. */ public static String evaluateString(String text, Map/**/ predefs, List/*>*/ defs) { // XXX could be faster for large mappings; see evaluate(...) @@ -541,7 +544,7 @@ * after passing it to this method) * @return a matching property producer */ - /*XXX public?*/ static PropertyProvider fixedPropertyProvider(Map/**/ defs) { + public static PropertyProvider fixedPropertyProvider(Map/**/ defs) { return new FixedPropertyProvider(defs); } @@ -572,7 +575,7 @@ * @param providers a sequential list of property groups * @return an evaluator */ - /*XXX public*/ static PropertyEvaluator sequentialPropertyEvaluator(PropertyProvider preprovider, PropertyProvider[] providers) { + public static PropertyEvaluator sequentialPropertyEvaluator(PropertyProvider preprovider, PropertyProvider[] providers) { return new SequentialPropertyEvaluator(preprovider, providers); } Index: ant/project/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.5 diff -u -r1.5 ReferenceHelper.java --- ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java 3 Apr 2004 07:00:36 -0000 1.5 +++ ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java 21 Apr 2004 22:49:23 -0000 @@ -83,20 +83,38 @@ static final String REFS_NS = "http://www.netbeans.org/ns/ant-project-references/1"; // NOI18N private final AntProjectHelper h; + final PropertyEvaluator eval; private final AuxiliaryConfiguration aux; + /** @deprecated Use the constructor that takes a property evaluator instead. */ + public ReferenceHelper(AntProjectHelper helper, AuxiliaryConfiguration aux) { + this(helper, aux, helper.getStandardPropertyEvaluator()); + } + /** * Create a new reference helper. * It needs an {@link AntProjectHelper} object in order to update references * in project.xml, * as well as set project or private properties referring to the locations * of foreign projects on disk. + *

    + * The property evaluator may be used in {@link #getForeignFileReferenceAsArtifact}, + * {@link ReferenceHelper.RawReference#toAntArtifact}, or + * {@link #createSubprojectProvider}. Typically this would + * be {@link AntProjectHelper#getStandardPropertyEvaluator}. You can substitute + * a custom evaluator but be warned that this helper class assumes that + * {@link AntProjectHelper#PROJECT_PROPERTIES_PATH} and {@link AntProjectHelper#PRIVATE_PROPERTIES_PATH} + * have their customary meanings; specifically that they are both used when evaluating + * properties (such as the location of a foreign project) and that private properties + * can override public properties. * @param helper an Ant project helper object representing this project's configuration * @param aux an auxiliary configuration provider needed to store references + * @param eval a property evaluator */ - public ReferenceHelper(AntProjectHelper helper, AuxiliaryConfiguration aux) { + public ReferenceHelper(AntProjectHelper helper, AuxiliaryConfiguration aux, PropertyEvaluator eval) { h = helper; this.aux = aux; + this.eval = eval; } /** @@ -827,7 +845,7 @@ return (AntArtifact)ProjectManager.mutex().readAccess(new Mutex.Action() { public Object run() { AntProjectHelper h = helper.h; - String path = h.evaluate("project." + foreignProjectName); // NOI18N + String path = helper.eval.getProperty("project." + foreignProjectName); // NOI18N if (path == null) { // Undefined foreign project. return null; Index: ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java,v retrieving revision 1.2 diff -u -r1.2 SharabilityQueryImpl.java --- ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java 14 Apr 2004 09:17:47 -0000 1.2 +++ ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java 21 Apr 2004 22:49:23 -0000 @@ -13,6 +13,8 @@ package org.netbeans.spi.project.support.ant; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.File; import java.util.ArrayList; import java.util.List; @@ -24,9 +26,10 @@ * Standard impl of {@link SharabilityQueryImplementation}. * @author Jesse Glick */ -final class SharabilityQueryImpl implements SharabilityQueryImplementation, AntProjectListener { +final class SharabilityQueryImpl implements SharabilityQueryImplementation, PropertyChangeListener { private final AntProjectHelper h; + private final PropertyEvaluator eval; private final String[] includes; private final String[] excludes; /** Absolute paths of directories or files to treat as sharable (except for the excludes). */ @@ -34,12 +37,13 @@ /** Absolute paths of directories or files to treat as not sharable. */ private String[] excludePaths; - SharabilityQueryImpl(AntProjectHelper h, String[] includes, String[] excludes) { + SharabilityQueryImpl(AntProjectHelper h, PropertyEvaluator eval, String[] includes, String[] excludes) { this.h = h; + this.eval = eval; this.includes = includes; this.excludes = excludes; computeFiles(); - h.addAntProjectListener((AntProjectListener)WeakListeners.create(AntProjectListener.class, this, h)); + eval.addPropertyChangeListener(WeakListeners.propertyChange(this, eval)); } /** Compute the absolute paths which are and are not sharable. */ @@ -56,7 +60,7 @@ private String[] computeFrom(String[] list) { List/**/ result = new ArrayList(list.length); for (int i = 0; i < list.length; i++) { - String val = h.evaluateString(list[i]); + String val = eval.evaluate(list[i]); if (val != null) { File f = h.resolveFile(val); result.add(f.getAbsolutePath()); @@ -97,12 +101,8 @@ return false; } - public void propertiesChanged(AntProjectEvent ev) { + public void propertyChange(PropertyChangeEvent evt) { computeFiles(); - } - - public void configurationXmlChanged(AntProjectEvent ev) { - // ignore } } Index: ant/project/src/org/netbeans/spi/project/support/ant/SimpleAntArtifact.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/SimpleAntArtifact.java,v retrieving revision 1.4 diff -u -r1.4 SimpleAntArtifact.java --- ant/project/src/org/netbeans/spi/project/support/ant/SimpleAntArtifact.java 3 Apr 2004 06:47:56 -0000 1.4 +++ ant/project/src/org/netbeans/spi/project/support/ant/SimpleAntArtifact.java 21 Apr 2004 22:49:23 -0000 @@ -32,22 +32,24 @@ private final AntProjectHelper h; private final String type; private final String locationProperty; + private final PropertyEvaluator eval; private final String targetName; private final String cleanTargetName; /** * @see AntProjectHelper#createSimpleAntArtifact */ - public SimpleAntArtifact(AntProjectHelper helper, String type, String locationProperty, String targetName, String cleanTargetName) { + public SimpleAntArtifact(AntProjectHelper helper, String type, String locationProperty, PropertyEvaluator eval, String targetName, String cleanTargetName) { this.h = helper; this.type = type; this.locationProperty = locationProperty; + this.eval = eval; this.targetName = targetName; this.cleanTargetName = cleanTargetName; } public URI getArtifactLocation() { - String locationResolved = h.evaluate(locationProperty); + String locationResolved = eval.getProperty(locationProperty); if (locationResolved == null) { return URI.create("file:/UNDEFINED"); // NOI18N } Index: ant/project/src/org/netbeans/spi/project/support/ant/SubprojectProviderImpl.java =================================================================== RCS file: /cvs/ant/project/src/org/netbeans/spi/project/support/ant/SubprojectProviderImpl.java,v retrieving revision 1.2 diff -u -r1.2 SubprojectProviderImpl.java --- ant/project/src/org/netbeans/spi/project/support/ant/SubprojectProviderImpl.java 16 Mar 2004 15:27:25 -0000 1.2 +++ ant/project/src/org/netbeans/spi/project/support/ant/SubprojectProviderImpl.java 21 Apr 2004 22:49:23 -0000 @@ -52,7 +52,7 @@ String foreignProjectName = (String)it.next(); String prop = "project." + foreignProjectName; // NOI18N AntProjectHelper h = helper.getAntProjectHelper(); - String foreignProjectDirS = h.evaluate(prop); + String foreignProjectDirS = helper.eval.getProperty(prop); if (foreignProjectDirS == null) { // Missing for some reason. Skip it. continue; Index: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntProjectHelperTest.java =================================================================== RCS file: /cvs/ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntProjectHelperTest.java,v retrieving revision 1.4 diff -u -r1.4 AntProjectHelperTest.java --- ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntProjectHelperTest.java 15 Apr 2004 17:01:21 -0000 1.4 +++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntProjectHelperTest.java 21 Apr 2004 22:49:23 -0000 @@ -544,5 +544,61 @@ // or other general fixed metadata // XXX try overwriting data } + + public void testCreatePropertyProvider() throws Exception { + PropertyProvider pp = h.getPropertyProvider(AntProjectHelper.PROJECT_PROPERTIES_PATH); + Map/**/ defs = pp.getProperties(); + assertEquals("correct number of defs", 3, defs.size()); + assertEquals("correct value", "value1", defs.get("shared.prop")); + // Test changes. + PropertyUtilsTest.TestCL l = new PropertyUtilsTest.TestCL(); + pp.addChangeListener(l); + EditableProperties p = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); + p.setProperty("foo", "bar"); + assertFalse("no events from uncommitted changes", l.changed); + h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, p); + assertTrue("got a change from setting a property", l.changed); + l.changed = false; + defs = pp.getProperties(); + assertEquals("correct new size", 4, defs.size()); + assertEquals("correct new value", "bar", defs.get("foo")); + // No-op changes. + p = p.cloneProperties(); + p.setProperty("foo", "bar2"); + p.setProperty("foo", "bar"); + h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, p); + assertFalse("no events from no-op changes", l.changed); + // Deleting a property file. + h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, null); + assertTrue("got a change from removing a property file", l.changed); + l.changed = false; + assertEquals("now have no definitions", Collections.EMPTY_MAP, pp.getProperties()); + // Start off with no file, then create it. + String path = "foo.properties"; + pp = h.getPropertyProvider(path); + pp.addChangeListener(l); + assertEquals("no defs initially", Collections.EMPTY_MAP, pp.getProperties()); + assertNull("no file made yet", h.getProjectDirectory().getFileObject(path)); + p = new EditableProperties(); + p.setProperty("one", "1"); + p.setProperty("two", "2"); + h.putProperties(path, p); + assertTrue("making the file fired a change", l.changed); + l.changed = false; + defs = pp.getProperties(); + assertEquals("two defs", 2, defs.size()); + assertEquals("right value #1", "1", defs.get("one")); + assertEquals("right value #2", "2", defs.get("two")); + assertNull("no file yet saved to disk", h.getProjectDirectory().getFileObject(path)); + p.setProperty("three", "3"); + assertFalse("no events from uncomm. change", l.changed); + h.putProperties(path, p); + assertTrue("now have changed new file", l.changed); + l.changed = false; + defs = pp.getProperties(); + assertEquals("three defs", 3, defs.size()); + // XXX test that saving the project fires no additional changes + // XXX test changes fired if file modified (or created or removed) on disk + } } Index: ant/project/test/unit/src/org/netbeans/spi/project/support/ant/PropertyUtilsTest.java =================================================================== RCS file: /cvs/ant/project/test/unit/src/org/netbeans/spi/project/support/ant/PropertyUtilsTest.java,v retrieving revision 1.4 diff -u -r1.4 PropertyUtilsTest.java --- ant/project/test/unit/src/org/netbeans/spi/project/support/ant/PropertyUtilsTest.java 21 Apr 2004 15:48:57 -0000 1.4 +++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/PropertyUtilsTest.java 21 Apr 2004 22:49:23 -0000 @@ -438,7 +438,7 @@ } - private static final class TestCL implements ChangeListener { + static final class TestCL implements ChangeListener { public boolean changed = false; Index: ant/project/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.3 diff -u -r1.3 ReferenceHelperTest.java --- ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java 3 Apr 2004 07:00:35 -0000 1.3 +++ ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java 21 Apr 2004 22:49:23 -0000 @@ -312,7 +312,7 @@ public void testAddRemoveArtifact() throws Exception { // Add one artifact. Check that the raw reference is there. assertFalse("project not initially modified", pm.isModified(p)); - AntArtifact art = new SimpleAntArtifact(sisterh, "jar", "build.jar", "dojar", "clean"); + AntArtifact art = sisterh.createSimpleAntArtifact("jar", "build.jar", "dojar", "clean"); assertTrue("added a ref to proj2.dojar", r.addReference(art)); assertTrue("project now modified", pm.isModified(p)); ReferenceHelper.RawReference[] refs = r.getRawReferences(); @@ -340,7 +340,7 @@ assertFalse("no-op add", r.addReference(art)); assertFalse("project not modified by no-op add", pm.isModified(p)); // Try another artifact from the same project. - art = new SimpleAntArtifact(sisterh, "javadoc", "build.javadoc", "dojavadoc", "clean"); + art = sisterh.createSimpleAntArtifact("javadoc", "build.javadoc", "dojavadoc", "clean"); assertTrue("added a ref to proj2.dojavadoc", r.addReference(art)); assertTrue("project now modified", pm.isModified(p)); refs = r.getRawReferences(); @@ -370,7 +370,7 @@ assertFalse("project not modified by no-op add", pm.isModified(p)); // Try modifying the second artifact in some way. // Note that only changes in the type, clean target, and artifact path count as modifications. - art = new SimpleAntArtifact(sisterh, "javadoc.html", "build.javadoc", "dojavadoc", "clean"); + art = sisterh.createSimpleAntArtifact("javadoc.html", "build.javadoc", "dojavadoc", "clean"); assertTrue("successful modification of proj2.dojavadoc by type", r.addReference(art)); assertTrue("project modified by ref mod", pm.isModified(p)); refs = r.getRawReferences(); @@ -381,10 +381,10 @@ assertEquals("correct script location", URI.create("build.xml"), ref.getScriptLocation()); assertEquals("correct target name", "dojavadoc", ref.getTargetName()); assertEquals("correct clean target name", "clean", ref.getCleanTargetName()); - art = new SimpleAntArtifact(sisterh, "javadoc.html", "build.javadoc", "dojavadoc", "realclean"); + art = sisterh.createSimpleAntArtifact("javadoc.html", "build.javadoc", "dojavadoc", "realclean"); assertTrue("successful modification of proj2.dojavadoc by clean target", r.addReference(art)); pm.saveProject(p); - art = new SimpleAntArtifact(sisterh, "javadoc.html", "build.javadoc.complete", "dojavadoc", "realclean"); + art = sisterh.createSimpleAntArtifact("javadoc.html", "build.javadoc.complete", "dojavadoc", "realclean"); assertTrue("successful modification of proj2.dojavadoc by artifact location property", r.addReference(art)); assertTrue("project modified by ref mod", pm.isModified(p)); refs = r.getRawReferences(); @@ -467,11 +467,11 @@ * @throws Exception in case of unexpected failures */ public void testSubprojectProviderImpl() throws Exception { - AntArtifact art = new SimpleAntArtifact(sisterh, "jar", "build.jar", "dojar", "clean"); + AntArtifact art = sisterh.createSimpleAntArtifact("jar", "build.jar", "dojar", "clean"); assertTrue("added a ref to proj2.dojar", r.addReference(art)); - art = new SimpleAntArtifact(sisterh, "javadoc", "build.javadoc", "dojavadoc", "clean"); + art = sisterh.createSimpleAntArtifact("javadoc", "build.javadoc", "dojavadoc", "clean"); assertTrue("added a ref to proj2.dojavadoc", r.addReference(art)); - art = new SimpleAntArtifact(seph, "jar", "build.jar", "dojar", "clean"); + art = seph.createSimpleAntArtifact("jar", "build.jar", "dojar", "clean"); assertTrue("added a ref to proj3.dojar", r.addReference(art)); SubprojectProvider sp = r.createSubprojectProvider(); Set/**/ subprojs = sp.getSubProjects();