|
@@ -0,0 +1,204 @@ |
|
|
|
|
|
/* |
|
|
|
|
|
* 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.IOException; |
|
|
|
|
|
import java.io.RandomAccessFile; |
|
|
|
|
|
import java.util.Iterator; |
|
|
|
|
|
|
|
|
|
|
|
import org.apache.tools.ant.BuildException; |
|
|
|
|
|
import org.apache.tools.ant.Project; |
|
|
|
|
|
import org.apache.tools.ant.Task; |
|
|
|
|
|
import org.apache.tools.ant.types.EnumeratedAttribute; |
|
|
|
|
|
import org.apache.tools.ant.types.Path; |
|
|
|
|
|
import org.apache.tools.ant.types.ResourceCollection; |
|
|
|
|
|
import org.apache.tools.ant.types.resources.FileResource; |
|
|
|
|
|
import org.apache.tools.ant.util.FileUtils; |
|
|
|
|
|
import org.apache.tools.ant.util.StringUtils; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Set the length of one or more files, as the intermittently available |
|
|
|
|
|
* <code>truncate</code> Unix utility/function. |
|
|
|
|
|
* @since Ant 1.7.1 |
|
|
|
|
|
*/ |
|
|
|
|
|
public class Truncate extends Task { |
|
|
|
|
|
|
|
|
|
|
|
private static final Long ZERO = new Long(0L); |
|
|
|
|
|
|
|
|
|
|
|
private static final String NO_CHILD = "No files specified."; |
|
|
|
|
|
|
|
|
|
|
|
private static final String INVALID_LENGTH = "Cannot truncate to length "; |
|
|
|
|
|
|
|
|
|
|
|
private static final String READ_WRITE = "rw"; |
|
|
|
|
|
|
|
|
|
|
|
private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); |
|
|
|
|
|
|
|
|
|
|
|
private static final byte[] FILL_BUFFER = new byte[1024]; |
|
|
|
|
|
|
|
|
|
|
|
private Path path; |
|
|
|
|
|
private boolean create = true; |
|
|
|
|
|
private boolean mkdirs = false; |
|
|
|
|
|
|
|
|
|
|
|
private Long length; |
|
|
|
|
|
private Long adjust; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Set a single target File. |
|
|
|
|
|
* @param f the single File |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setFile(File f) { |
|
|
|
|
|
add(new FileResource(f)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Add a nested (filesystem-only) ResourceCollection. |
|
|
|
|
|
* @param rc the ResourceCollection to add. |
|
|
|
|
|
*/ |
|
|
|
|
|
public void add(ResourceCollection rc) { |
|
|
|
|
|
getPath().add(rc); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Set the amount by which files' lengths should be adjusted. |
|
|
|
|
|
* It is permissible to append K / M / G / T / P. |
|
|
|
|
|
* @param adjust (positive or negative) adjustment amount. |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setAdjust(Long adjust) { |
|
|
|
|
|
this.adjust = adjust; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Set the length to which files should be set. |
|
|
|
|
|
* It is permissible to append K / M / G / T / P. |
|
|
|
|
|
* @param adjust (positive) adjustment amount. |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setLength(Long length) { |
|
|
|
|
|
this.length = length; |
|
|
|
|
|
if (length != null && length.longValue() < 0) { |
|
|
|
|
|
throw new BuildException(INVALID_LENGTH + length); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Set whether to create nonexistent files. |
|
|
|
|
|
* @param create boolean, default <code>true</code>. |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setCreate(boolean create) { |
|
|
|
|
|
this.create = create; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Set whether, when creating nonexistent files, nonexistent directories |
|
|
|
|
|
* should also be created. |
|
|
|
|
|
* @param mkdirs boolean, default <code>false</code>. |
|
|
|
|
|
*/ |
|
|
|
|
|
public void setMkdirs(boolean mkdirs) { |
|
|
|
|
|
this.mkdirs = mkdirs; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** {@inheritDoc}. */ |
|
|
|
|
|
public void execute() { |
|
|
|
|
|
if (length != null && adjust != null) { |
|
|
|
|
|
throw new BuildException( |
|
|
|
|
|
"length and adjust are mutually exclusive options"); |
|
|
|
|
|
} |
|
|
|
|
|
if (length == null && adjust == null) { |
|
|
|
|
|
length = ZERO; |
|
|
|
|
|
} |
|
|
|
|
|
if (path == null) { |
|
|
|
|
|
throw new BuildException(NO_CHILD); |
|
|
|
|
|
} |
|
|
|
|
|
for (Iterator it = path.iterator(); it.hasNext();) { |
|
|
|
|
|
File f = ((FileResource) it.next()).getFile(); |
|
|
|
|
|
if (shouldProcess(f)) { |
|
|
|
|
|
process(f); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean shouldProcess(File f) { |
|
|
|
|
|
if (f.isFile()) { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
if (!create) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
Exception exception = null; |
|
|
|
|
|
try { |
|
|
|
|
|
if (FILE_UTILS.createNewFile(f, mkdirs)) { |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
|
exception = e; |
|
|
|
|
|
} |
|
|
|
|
|
String msg = "Unable to create " + f; |
|
|
|
|
|
if (exception == null) { |
|
|
|
|
|
log(msg, Project.MSG_WARN); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
throw new BuildException(msg, exception); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void process(File f) { |
|
|
|
|
|
long len = f.length(); |
|
|
|
|
|
long newLength = length == null |
|
|
|
|
|
? len + adjust.longValue() : length.longValue(); |
|
|
|
|
|
|
|
|
|
|
|
if (len == newLength) { |
|
|
|
|
|
//nothing to do! |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
RandomAccessFile raf = null; |
|
|
|
|
|
try { |
|
|
|
|
|
raf = new RandomAccessFile(f, READ_WRITE); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
throw new BuildException("Could not open " + f + " for writing", e); |
|
|
|
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
if (newLength > len) { |
|
|
|
|
|
long pos = len; |
|
|
|
|
|
raf.seek(pos); |
|
|
|
|
|
while (pos < newLength) { |
|
|
|
|
|
long writeCount = Math.min(FILL_BUFFER.length, |
|
|
|
|
|
newLength - pos); |
|
|
|
|
|
raf.write(FILL_BUFFER, 0, (int) writeCount); |
|
|
|
|
|
pos += writeCount; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
raf.setLength(newLength); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
|
throw new BuildException("Exception working with " + raf, e); |
|
|
|
|
|
} finally { |
|
|
|
|
|
try { |
|
|
|
|
|
raf.close(); |
|
|
|
|
|
} catch (IOException e) { |
|
|
|
|
|
log("Caught " + e + " closing " + raf, Project.MSG_WARN); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private synchronized Path getPath() { |
|
|
|
|
|
if (path == null) { |
|
|
|
|
|
path = new Path(getProject()); |
|
|
|
|
|
} |
|
|
|
|
|
return path; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |