001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs;
020:
021: import java.io.File;
022: import java.util.Enumeration;
023: import java.util.Vector;
024: import org.apache.tools.ant.BuildException;
025: import org.apache.tools.ant.DirectoryScanner;
026: import org.apache.tools.ant.Project;
027: import org.apache.tools.ant.Task;
028: import org.apache.tools.ant.taskdefs.condition.Condition;
029: import org.apache.tools.ant.types.Resource;
030: import org.apache.tools.ant.types.FileSet;
031: import org.apache.tools.ant.types.resources.Union;
032: import org.apache.tools.ant.types.Mapper;
033: import org.apache.tools.ant.util.FileNameMapper;
034: import org.apache.tools.ant.util.MergingMapper;
035: import org.apache.tools.ant.util.ResourceUtils;
036: import org.apache.tools.ant.util.SourceFileScanner;
037:
038: /**
039: * Sets the given property if the specified target has a timestamp
040: * greater than all of the source files.
041: *
042: * @since Ant 1.2
043: *
044: * @ant.task category="control"
045: */
046:
047: public class UpToDate extends Task implements Condition {
048:
049: private String property;
050: private String value;
051: private File sourceFile;
052: private File targetFile;
053: private Vector sourceFileSets = new Vector();
054: private Union sourceResources = new Union();
055:
056: // CheckStyle:VisibilityModifier OFF - bc
057: protected Mapper mapperElement = null;
058:
059: // CheckStyle:VisibilityModifier ON
060:
061: /**
062: * The property to set if the target file is more up-to-date than
063: * (each of) the source file(s).
064: *
065: * @param property the name of the property to set if Target is up-to-date.
066: */
067: public void setProperty(final String property) {
068: this .property = property;
069: }
070:
071: /**
072: * The value to set the named property to if the target file is more
073: * up-to-date than (each of) the source file(s). Defaults to 'true'.
074: *
075: * @param value the value to set the property to if Target is up-to-date
076: */
077: public void setValue(final String value) {
078: this .value = value;
079: }
080:
081: /**
082: * Returns the value, or "true" if a specific value wasn't provided.
083: */
084: private String getValue() {
085: return (value != null) ? value : "true";
086: }
087:
088: /**
089: * The file which must be more up-to-date than (each of) the source file(s)
090: * if the property is to be set.
091: *
092: * @param file the file we are checking against.
093: */
094: public void setTargetFile(final File file) {
095: this .targetFile = file;
096: }
097:
098: /**
099: * The file that must be older than the target file
100: * if the property is to be set.
101: *
102: * @param file the file we are checking against the target file.
103: */
104: public void setSrcfile(final File file) {
105: this .sourceFile = file;
106: }
107:
108: /**
109: * Nested <srcfiles> element.
110: * @param fs the source files
111: */
112: public void addSrcfiles(final FileSet fs) {
113: sourceFileSets.addElement(fs);
114: }
115:
116: /**
117: * Nested resource collections as sources.
118: * @return the source resources to configure.
119: * @since Ant 1.7
120: */
121: public Union createSrcResources() {
122: return sourceResources;
123: }
124:
125: /**
126: * Defines the FileNameMapper to use (nested mapper element).
127: * @return a mapper to be configured
128: * @throws BuildException if more than one mapper is defined
129: */
130: public Mapper createMapper() throws BuildException {
131: if (mapperElement != null) {
132: throw new BuildException(
133: "Cannot define more than one mapper", getLocation());
134: }
135: mapperElement = new Mapper(getProject());
136: return mapperElement;
137: }
138:
139: /**
140: * A nested filenamemapper
141: * @param fileNameMapper the mapper to add
142: * @since Ant 1.6.3
143: */
144: public void add(FileNameMapper fileNameMapper) {
145: createMapper().add(fileNameMapper);
146: }
147:
148: /**
149: * Evaluate (all) target and source file(s) to
150: * see if the target(s) is/are up-to-date.
151: * @return true if the target(s) is/are up-to-date
152: */
153: public boolean eval() {
154: if (sourceFileSets.size() == 0 && sourceResources.size() == 0
155: && sourceFile == null) {
156: throw new BuildException(
157: "At least one srcfile or a nested "
158: + "<srcfiles> or <srcresources> element "
159: + "must be set.");
160: }
161:
162: if ((sourceFileSets.size() > 0 || sourceResources.size() > 0)
163: && sourceFile != null) {
164: throw new BuildException("Cannot specify both the srcfile "
165: + "attribute and a nested <srcfiles> "
166: + "or <srcresources> element.");
167: }
168:
169: if (targetFile == null && mapperElement == null) {
170: throw new BuildException(
171: "The targetfile attribute or a nested "
172: + "mapper element must be set.");
173: }
174:
175: // if the target file is not there, then it can't be up-to-date
176: if (targetFile != null && !targetFile.exists()) {
177: log("The targetfile \"" + targetFile.getAbsolutePath()
178: + "\" does not exist.", Project.MSG_VERBOSE);
179: return false;
180: }
181:
182: // if the source file isn't there, throw an exception
183: if (sourceFile != null && !sourceFile.exists()) {
184: throw new BuildException(sourceFile.getAbsolutePath()
185: + " not found.");
186: }
187:
188: boolean upToDate = true;
189: if (sourceFile != null) {
190: if (mapperElement == null) {
191: upToDate = upToDate
192: && (targetFile.lastModified() >= sourceFile
193: .lastModified());
194: } else {
195: SourceFileScanner sfs = new SourceFileScanner(this );
196: upToDate = upToDate
197: && (sfs.restrict(new String[] { sourceFile
198: .getAbsolutePath() }, null, null,
199: mapperElement.getImplementation()).length == 0);
200: }
201: }
202:
203: // filesets are separate from the rest for performance
204: // reasons. If we use the code for union below, we'll always
205: // scan all filesets, even if we know the target is out of
206: // date after the first test.
207: Enumeration e = sourceFileSets.elements();
208: while (upToDate && e.hasMoreElements()) {
209: FileSet fs = (FileSet) e.nextElement();
210: DirectoryScanner ds = fs.getDirectoryScanner(getProject());
211: upToDate = upToDate
212: && scanDir(fs.getDir(getProject()), ds
213: .getIncludedFiles());
214: }
215:
216: if (upToDate) {
217: Resource[] r = sourceResources.listResources();
218: upToDate = upToDate
219: && (ResourceUtils.selectOutOfDateSources(this , r,
220: getMapper(), getProject()).length == 0);
221: }
222:
223: return upToDate;
224: }
225:
226: /**
227: * Sets property to true if target file(s) have a more recent timestamp
228: * than (each of) the corresponding source file(s).
229: * @throws BuildException on error
230: */
231: public void execute() throws BuildException {
232: if (property == null) {
233: throw new BuildException("property attribute is required.",
234: getLocation());
235: }
236: boolean upToDate = eval();
237: if (upToDate) {
238: getProject().setNewProperty(property, getValue());
239: if (mapperElement == null) {
240: log("File \"" + targetFile.getAbsolutePath()
241: + "\" is up-to-date.", Project.MSG_VERBOSE);
242: } else {
243: log("All target files are up-to-date.",
244: Project.MSG_VERBOSE);
245: }
246: }
247: }
248:
249: /**
250: * Scan a directory for files to check for "up to date"ness
251: * @param srcDir the directory
252: * @param files the files to scan for
253: * @return true if the files are up to date
254: */
255: protected boolean scanDir(File srcDir, String[] files) {
256: SourceFileScanner sfs = new SourceFileScanner(this );
257: FileNameMapper mapper = getMapper();
258: File dir = srcDir;
259: if (mapperElement == null) {
260: dir = null;
261: }
262: return sfs.restrict(files, srcDir, dir, mapper).length == 0;
263: }
264:
265: private FileNameMapper getMapper() {
266: FileNameMapper mapper = null;
267: if (mapperElement == null) {
268: MergingMapper mm = new MergingMapper();
269: mm.setTo(targetFile.getAbsolutePath());
270: mapper = mm;
271: } else {
272: mapper = mapperElement.getImplementation();
273: }
274: return mapper;
275: }
276: }
|