The same example rewritten as CoreSelector with setting the all values
(same as defaults are) would be
If you want to provide your own interface implementation you can do
that via the *classname attributes. If the classes are not on Ant's core
classpath, you will have to provide the path via nested <classpath>
element, so that the selector can find the classes.
A useful scenario for this selector is inside a build environment
for homepage generation (e.g. with
Apache Forrest).
files are uploaded to the server. The
ModifiedSelector saves therefore much upload time.
This selector uses reflection for setting the values of its three interfaces
(using org.apache.tools.ant.IntrospectionHelper) therefore no special
'configuration interfaces' has to be implemented by new caches, algorithms or
comparators. All present setXX methods can be used. E.g. the DigestAlgorithm
can use a specified provider for computing its value. For selecting this
there is a setProvider(String providername) method. So you can use
a nested <param name="algorithm.provider" value="MyProvider"/>.
| Method from org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector Detail: |
public void addClasspath(Path path) {
if (classpath != null) {
throw new BuildException("< classpath > can be set only once.");
}
classpath = path;
}
|
public void addParam(Parameter parameter) {
configParameter.add(parameter);
}
Support for nested <param> tags. |
public void addParam(String key,
Object value) {
Parameter par = new Parameter();
par.setName(key);
par.setValue(String.valueOf(value));
configParameter.add(par);
}
Support for nested <param> tags. |
public void buildFinished(BuildEvent event) {
if (getDelayUpdate()) {
saveCache();
}
}
Signals that the last target has finished. |
public void buildStarted(BuildEvent event) {
// no-op
}
Signals that a build has started. |
public void configure() {
//
// ----- The "Singleton" -----
//
if (isConfigured) {
return;
}
isConfigured = true;
//
// ----- Set default values -----
//
Project p = getProject();
String filename = "cache.properties";
File cachefile = null;
if (p != null) {
// normal use inside Ant
cachefile = new File(p.getBaseDir(), filename);
// set self as a BuildListener to delay cachefile saves
getProject().addBuildListener(this);
} else {
// no reference to project - e.g. during normal JUnit tests
cachefile = new File(filename);
setDelayUpdate(false);
}
Cache defaultCache = new PropertiesfileCache(cachefile);
Algorithm defaultAlgorithm = new DigestAlgorithm();
Comparator defaultComparator = new EqualComparator();
update = true;
selectDirectories = true;
//
// ----- Set the main attributes, pattern '*' -----
//
for (Iterator itConfig = configParameter.iterator(); itConfig.hasNext();) {
Parameter par = (Parameter) itConfig.next();
if (par.getName().indexOf(".") > 0) {
// this is a *.* parameter for later use
specialParameter.add(par);
} else {
useParameter(par);
}
}
configParameter = new Vector();
// specify the algorithm classname
if (algoName != null) {
// use Algorithm defined via name
if ("hashvalue".equals(algoName.getValue())) {
algorithm = new HashvalueAlgorithm();
} else if ("digest".equals(algoName.getValue())) {
algorithm = new DigestAlgorithm();
} else if ("checksum".equals(algoName.getValue())) {
algorithm = new ChecksumAlgorithm();
}
} else {
if (algorithmClass != null) {
// use Algorithm specified by classname
algorithm = (Algorithm) loadClass(
algorithmClass,
"is not an Algorithm.",
Algorithm.class);
} else {
// nothing specified - use default
algorithm = defaultAlgorithm;
}
}
// specify the cache classname
if (cacheName != null) {
// use Cache defined via name
if ("propertyfile".equals(cacheName.getValue())) {
cache = new PropertiesfileCache();
}
} else {
if (cacheClass != null) {
// use Cache specified by classname
cache = (Cache) loadClass(cacheClass, "is not a Cache.", Cache.class);
} else {
// nothing specified - use default
cache = defaultCache;
}
}
// specify the comparator classname
if (compName != null) {
// use Algorithm defined via name
if ("equal".equals(compName.getValue())) {
comparator = new EqualComparator();
} else if ("rule".equals(compName.getValue())) {
// TODO there is a problem with the constructor for the RBC.
// you have to provide the rules in the constructors - no setters
// available.
throw new BuildException("RuleBasedCollator not yet supported.");
// Have to think about lazy initialization here... JHM
// comparator = new java.text.RuleBasedCollator();
}
} else {
if (comparatorClass != null) {
// use Algorithm specified by classname
comparator = (Comparator) loadClass(
comparatorClass,
"is not a Comparator.",
Comparator.class);
} else {
// nothing specified - use default
comparator = defaultComparator;
}
}
//
// ----- Set the special attributes, pattern '*.*' -----
//
for (Iterator itSpecial = specialParameter.iterator(); itSpecial.hasNext();) {
Parameter par = (Parameter) itSpecial.next();
useParameter(par);
}
specialParameter = new Vector();
}
Configures this Selector.
Does this work only once per Selector object.
Because some problems while configuring from Selector
the configuration is done in the following order:
- collect the configuration data
- wait for the first isSelected() call
- set the default values
- set values for name pattern '*': update, cache, algorithm, comparator
- set values for name pattern '*.*: cache.cachefile, ...
This configuration algorithm is needed because you don't know
the order of arriving config-data. E.g. if you first set the
cache.cachefilename and after that the cache itself,
the default value for cachefilename is used, because setting the
cache implies creating a new Cache instance - with its defaults. |
public Algorithm getAlgorithm() {
return algorithm;
}
Get the algorithm type to use. |
public Cache getCache() {
return cache;
}
Get the cache type to use. |
public ClassLoader getClassLoader() {
if (myClassLoader == null) {
myClassLoader = (classpath == null)
// the usual classloader
? getClass().getClassLoader()
// additional use the provided classpath
: getProject().createClassLoader(classpath);
}
return myClassLoader;
}
Returns and initializes the classloader for this class. |
public Comparator getComparator() {
return comparator;
}
Get the comparator type to use. |
public boolean getDelayUpdate() {
return delayUpdate;
}
Getter for the delay update |
public int getModified() {
return modified;
}
Getter for the modified count |
public boolean isSelected(Resource resource) {
if (resource.isFilesystemOnly()) {
// We have a 'resourced' file, so reconvert it and use
// the 'old' implementation.
FileResource fileResource = (FileResource) resource;
File file = fileResource.getFile();
String filename = fileResource.getName();
File basedir = fileResource.getBaseDir();
return isSelected(basedir, filename, file);
} else {
try {
// How to handle non-file-Resources? I copy temporarily the
// resource to a file and use the file-implementation.
FileUtils fu = FileUtils.getFileUtils();
File tmpFile = fu.createTempFile("modified-", ".tmp", null, true, true);
Resource tmpResource = new FileResource(tmpFile);
ResourceUtils.copyResource(resource, tmpResource);
boolean isSelected = isSelected(tmpFile.getParentFile(),
tmpFile.getName(),
resource.toLongString());
return isSelected;
} catch (UnsupportedOperationException uoe) {
log("The resource '"
+ resource.getName()
+ "' does not provide an InputStream, so it is not checked. "
+ "Akkording to 'selres' attribute value it is "
+ ((selectResourcesWithoutInputStream) ? "" : " not")
+ "selected.", Project.MSG_INFO);
return selectResourcesWithoutInputStream;
} catch (Exception e) {
throw new BuildException(e);
}
}
}
Implementation of ResourceSelector.isSelected(). |
public boolean isSelected(File basedir,
String filename,
File file) {
return isSelected(basedir, filename, file.getAbsolutePath());
}
Implementation of BaseExtendSelector.isSelected(). |
protected Object loadClass(String classname,
String msg,
Class type) {
try {
// load the specified class
ClassLoader cl = getClassLoader();
Class clazz = null;
if (cl != null) {
clazz = cl.loadClass(classname);
} else {
clazz = Class.forName(classname);
}
Object rv = clazz.newInstance();
if (!type.isInstance(rv)) {
throw new BuildException("Specified class (" + classname + ") " + msg);
}
return rv;
} catch (ClassNotFoundException e) {
throw new BuildException("Specified class (" + classname + ") not found.");
} catch (Exception e) {
throw new BuildException(e);
}
}
Loads the specified class and initializes an object of that class.
Throws a BuildException using the given message if an error occurs during
loading/instantiation or if the object is not from the given type. |
public void messageLogged(BuildEvent event) {
// no-op
}
Signals a message logging event. |
protected void saveCache() {
if (getModified() > 0) {
cache.save();
setModified(0);
}
}
|
public void setAlgorithm(ModifiedSelector.AlgorithmName name) {
algoName = name;
}
Set the algorithm type to use. |
public void setAlgorithmClass(String classname) {
algorithmClass = classname;
}
Setter for algorithmClass. |
public void setCache(ModifiedSelector.CacheName name) {
cacheName = name;
}
Set the cache type to use. |
public void setCacheClass(String classname) {
cacheClass = classname;
}
|
public void setClassLoader(ClassLoader loader) {
myClassLoader = loader;
}
Set the used ClassLoader.
If you invoke this selector by API (e.g. inside some testcases) the selector
will use a different classloader for loading the interface implementations than
the caller. Therefore you will get a ClassCastException if you get the
implementations from the selector and cast them. |
public void setComparator(ModifiedSelector.ComparatorName name) {
compName = name;
}
Set the comparator type to use. |
public void setComparatorClass(String classname) {
comparatorClass = classname;
}
Setter for comparatorClass. |
public void setDelayUpdate(boolean delayUpdate) {
this.delayUpdate = delayUpdate;
}
Setter for the delay update |
public void setModified(int modified) {
this.modified = modified;
}
Setter for the modified count |
public void setParameters(Parameter[] parameters) {
if (parameters != null) {
for (int i = 0; i < parameters.length; i++) {
configParameter.add(parameters[i]);
}
}
}
Defined in org.apache.tools.ant.types.Parameterizable.
Overwrite implementation in superclass because only special
parameters are valid. |
public void setSeldirs(boolean seldirs) {
selectDirectories = seldirs;
}
Support for seldirs attribute. |
public void setSelres(boolean newValue) {
this.selectResourcesWithoutInputStream = newValue;
}
Support for selres attribute. |
public void setUpdate(boolean update) {
this.update = update;
}
Support for update attribute. |
public void targetFinished(BuildEvent event) {
if (getDelayUpdate()) {
saveCache();
}
}
Signals that a target has finished. |
public void targetStarted(BuildEvent event) {
// no-op
}
Signals that a target is starting. |
public void taskFinished(BuildEvent event) {
if (getDelayUpdate()) {
saveCache();
}
}
Signals that a task has finished. |
public void taskStarted(BuildEvent event) {
// no-op
}
Signals that a task is starting. |
public String toString() {
StringBuffer buf = new StringBuffer("{modifiedselector");
buf.append(" update=").append(update);
buf.append(" seldirs=").append(selectDirectories);
buf.append(" cache=").append(cache);
buf.append(" algorithm=").append(algorithm);
buf.append(" comparator=").append(comparator);
buf.append("}");
return buf.toString();
}
Override Object.toString(). |
protected void tryToSetAParameter(Object obj,
String name,
String value) {
Project prj = (getProject() != null) ? getProject() : new Project();
IntrospectionHelper iHelper
= IntrospectionHelper.getHelper(prj, obj.getClass());
try {
iHelper.setAttribute(prj, obj, name, value);
} catch (org.apache.tools.ant.BuildException e) {
// no-op
}
}
Try to set a value on an object using reflection.
Helper method for easier access to IntrospectionHelper.setAttribute(). |
public void useParameter(Parameter parameter) {
String key = parameter.getName();
String value = parameter.getValue();
if ("cache".equals(key)) {
CacheName cn = new CacheName();
cn.setValue(value);
setCache(cn);
} else if ("algorithm".equals(key)) {
AlgorithmName an = new AlgorithmName();
an.setValue(value);
setAlgorithm(an);
} else if ("comparator".equals(key)) {
ComparatorName cn = new ComparatorName();
cn.setValue(value);
setComparator(cn);
} else if ("update".equals(key)) {
boolean updateValue =
("true".equalsIgnoreCase(value))
? true
: false;
setUpdate(updateValue);
} else if ("delayupdate".equals(key)) {
boolean updateValue =
("true".equalsIgnoreCase(value))
? true
: false;
setDelayUpdate(updateValue);
} else if ("seldirs".equals(key)) {
boolean sdValue =
("true".equalsIgnoreCase(value))
? true
: false;
setSeldirs(sdValue);
} else if (key.startsWith(CACHE_START)) {
String name = key.substring(CACHE_START.length());
tryToSetAParameter(cache, name, value);
} else if (key.startsWith(ALGORITHM_START)) {
String name = key.substring(ALGORITHM_START.length());
tryToSetAParameter(algorithm, name, value);
} else if (key.startsWith(COMPARATOR_START)) {
String name = key.substring(COMPARATOR_START.length());
tryToSetAParameter(comparator, name, value);
} else {
setError("Invalid parameter " + key);
}
}
Support for nested tags.
Parameter named cache, algorithm,
comparator or update are mapped to
the respective set-Method.
Parameter which names starts with cache. or
algorithm. or comparator. are tried
to set on the appropriate object via its set-methods.
Other parameters are invalid and an BuildException will
be thrown. |
public void verifySettings() {
configure();
if (cache == null) {
setError("Cache must be set.");
} else if (algorithm == null) {
setError("Algorithm must be set.");
} else if (!cache.isValid()) {
setError("Cache must be proper configured.");
} else if (!algorithm.isValid()) {
setError("Algorithm must be proper configured.");
}
}
Overrides BaseSelector.verifySettings(). |