Length.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.tools.ant.taskdefs;
import java.io.File;
import java.io.OutputStream;
import java.io.PrintStream;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.condition.Condition;
import org.apache.tools.ant.types.Comparison;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.types.resources.Resources;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.PropertyOutputStream;
/**
* Gets lengths: of files/resources, byte size; of strings, length (optionally trimmed).
* The task is overloaded in this way for semantic reasons, much like Available.
* @since Ant 1.6.3
*/
public class Length extends Task implements Condition {
private static final String ALL = "all";
private static final String EACH = "each";
private static final String STRING = "string";
private static final String LENGTH_REQUIRED
= "Use of the Length condition requires that the length attribute be set.";
private String property;
private String string;
private Boolean trim;
private String mode = ALL;
private Comparison when = Comparison.EQUAL;
private Long length;
private Resources resources;
/**
* The property in which the length will be stored.
* @param property the <code>String</code> property key.
*/
public synchronized void setProperty(String property) {
this.property = property;
}
/**
* Set the single resource for this task.
* @param resource the Resource whose length to retrieve.
*/
public synchronized void setResource(Resource resource) {
add(resource);
}
/**
* Set the single file for this task.
* @param file the <code>File</code> whose length to retrieve.
*/
public synchronized void setFile(File file) {
add(new FileResource(file));
}
/**
* Add a FileSet.
* @param fs the <code>FileSet</code> to add.
*/
public synchronized void add(FileSet fs) {
add((ResourceCollection) fs);
}
/**
* Add a ResourceCollection.
* @param c the <code>ResourceCollection</code> to add.
* @since Ant 1.7
*/
public synchronized void add(ResourceCollection c) {
if (c == null) {
return;
}
resources = (resources == null) ? new Resources() : resources;
resources.add(c);
}
/**
* Set the target count number for use as a Condition.
* @param ell the long length to compare with.
*/
public synchronized void setLength(long ell) {
length = Long.valueOf(ell);
}
/**
* Set the comparison for use as a Condition.
* @param w EnumeratedAttribute When.
* @see org.apache.tools.ant.types.Comparison
*/
public synchronized void setWhen(When w) {
setWhen((Comparison) w);
}
/**
* Set the comparison for use as a Condition.
* @param c Comparison.
* @see org.apache.tools.ant.types.Comparison
* @since Ant 1.7
*/
public synchronized void setWhen(Comparison c) {
when = c;
}
/**
* Set the execution mode for working with files.
* @param m the <code>FileMode</code> to use.
*/
public synchronized void setMode(FileMode m) {
this.mode = m.getValue();
}
/**
* Set the string whose length to get.
* @param string <code>String</code>.
*/
public synchronized void setString(String string) {
this.string = string;
this.mode = STRING;
}
/**
* Set whether to trim in string mode. Default false.
* @param trim <code>boolean</code>.
*/
public synchronized void setTrim(boolean trim) {
this.trim = Boolean.valueOf(trim);
}
/**
* Learn whether strings will be trimmed. Default false.
* @return boolean trim setting.
*/
public boolean getTrim() {
return Boolean.TRUE.equals(trim);
}
/**
* Execute the length task.
*/
@Override
public void execute() {
validate();
OutputStream out =
property == null ? new LogOutputStream(this, Project.MSG_INFO)
: new PropertyOutputStream(getProject(), property);
PrintStream ps = new PrintStream(out);
switch (mode) {
case STRING:
ps.print(getLength(string, getTrim()));
ps.close();
break;
case EACH:
handleResources(new EachHandler(ps));
break;
case ALL:
handleResources(new AllHandler(ps));
break;
}
}
/**
* Fulfill the condition contract.
* @return true if the condition is true.
* @throws BuildException if an error occurs.
*/
@Override
public boolean eval() {
validate();
if (length == null) {
throw new BuildException(LENGTH_REQUIRED);
}
Long ell;
if (STRING.equals(mode)) {
ell = Long.valueOf(getLength(string, getTrim()));
} else {
AccumHandler h = new AccumHandler();
handleResources(h);
ell = Long.valueOf(h.getAccum());
}
return when.evaluate(ell.compareTo(length));
}
private void validate() {
if (string != null) {
if (resources != null) {
throw new BuildException(
"the string length function is incompatible with the file/resource length function");
}
if (!(STRING.equals(mode))) {
throw new BuildException(
"the mode attribute is for use with the file/resource length function");
}
} else if (resources != null) {
if (!(EACH.equals(mode) || ALL.equals(mode))) {
throw new BuildException(
"invalid mode setting for file/resource length function: \""
+ mode + "\"");
}
if (trim != null) {
throw new BuildException(
"the trim attribute is for use with the string length function only");
}
} else {
throw new BuildException(
"you must set either the string attribute or specify one or more files using the file attribute or nested resource collections");
}
}
private void handleResources(Handler h) {
for (Resource r : resources) {
if (!r.isExists()) {
log(r + " does not exist", Project.MSG_WARN);
}
if (r.isDirectory()) {
log(r + " is a directory; length may not be meaningful", Project.MSG_WARN);
}
h.handle(r);
}
h.complete();
}
private static long getLength(String s, boolean t) {
return (t ? s.trim() : s).length();
}
/** EnumeratedAttribute operation mode */
public static class FileMode extends EnumeratedAttribute {
static final String[] MODES = new String[] {EACH, ALL}; //NOSONAR
/**
* Return the possible values for FileMode.
* @return <code>String[]</code>.
*/
@Override
public String[] getValues() {
return MODES;
}
}
/**
* EnumeratedAttribute for the when attribute.
*/
public static class When extends Comparison {
//extend Comparison; retain for BC only
}
private abstract class Handler {
private PrintStream ps;
Handler(PrintStream ps) {
this.ps = ps;
}
protected PrintStream getPs() {
return ps;
}
protected abstract void handle(Resource r);
void complete() {
FileUtils.close(ps);
}
}
private class EachHandler extends Handler {
EachHandler(PrintStream ps) {
super(ps);
}
@Override
protected void handle(Resource r) {
getPs().print(r.toString());
getPs().print(" : ");
//when writing to the log, we'll see what's happening:
long size = r.getSize();
if (size == Resource.UNKNOWN_SIZE) {
getPs().println("unknown");
} else {
getPs().println(size);
}
}
}
private class AccumHandler extends Handler {
private long accum = 0L;
AccumHandler() {
super(null);
}
protected AccumHandler(PrintStream ps) {
super(ps);
}
protected long getAccum() {
return accum;
}
@Override
protected synchronized void handle(Resource r) {
long size = r.getSize();
if (size == Resource.UNKNOWN_SIZE) {
log("Size unknown for " + r.toString(), Project.MSG_WARN);
} else {
accum += size;
}
}
}
private class AllHandler extends AccumHandler {
AllHandler(PrintStream ps) {
super(ps);
}
@Override
void complete() {
getPs().print(getAccum());
super.complete();
}
}
}