1 |
|
---|
2 | package ise.antelope.tasks;
|
---|
3 |
|
---|
4 | import java.io.*;
|
---|
5 |
|
---|
6 | import org.apache.tools.ant.*;
|
---|
7 | import org.apache.tools.ant.taskdefs.*;
|
---|
8 |
|
---|
9 | /**
|
---|
10 | * This task is similar to the Unix/Linux split utility, it splits a file into
|
---|
11 | * a number of smaller pieces. It will also split a property value or a string.
|
---|
12 | *
|
---|
13 | * @author Dale Anson
|
---|
14 | * @version $Revision: 1.3 $
|
---|
15 | * @since Ant 1.6
|
---|
16 | */
|
---|
17 | public class SplitTask extends Task {
|
---|
18 |
|
---|
19 | private String prefix = "x";
|
---|
20 | private int bytes = -1;
|
---|
21 | private int lines = 1000;
|
---|
22 | private String value = null;
|
---|
23 | private File file = null;
|
---|
24 | private File outputDir = null;
|
---|
25 | private boolean failOnError = true;
|
---|
26 |
|
---|
27 | /**
|
---|
28 | * Buffer size for read and write operations, default is 1 MB, but this is
|
---|
29 | * a user setting.
|
---|
30 | */
|
---|
31 | public int BUFFER_SIZE = 1024 * 1024;
|
---|
32 |
|
---|
33 |
|
---|
34 | /**
|
---|
35 | * The start of the file names to write. Files are named using this string,
|
---|
36 | * followed by a "." and a number, e.g. x.0, x.1, etc. The Unix/Linux split
|
---|
37 | * uses a letter scheme for the suffix, that is not supported here.
|
---|
38 | *
|
---|
39 | * Should the dot go away? If the user wants a dot, it could be part of this
|
---|
40 | * attribute. Right now, the dot is there, and there is no way for the user
|
---|
41 | * to make it go away.
|
---|
42 | *
|
---|
43 | * @param x The new prefix value
|
---|
44 | */
|
---|
45 | public void setPrefix( String x ) {
|
---|
46 | prefix = x;
|
---|
47 | }
|
---|
48 |
|
---|
49 | /**
|
---|
50 | * Set the number of bytes per part. This is not a required parameter,
|
---|
51 | * the default is to use lines rather than bytes.
|
---|
52 | *
|
---|
53 | * Use bytes or lines, not both. In general, use bytes or size for binary
|
---|
54 | * files, lines for text files.
|
---|
55 | *
|
---|
56 | * @param b number of bytes per part.
|
---|
57 | */
|
---|
58 | public void setBytes( int b ) {
|
---|
59 | bytes = b;
|
---|
60 | lines = -1;
|
---|
61 | }
|
---|
62 |
|
---|
63 | /**
|
---|
64 | * The linux split command allows modifiers: b for 512, k for 1K, m for 1
|
---|
65 | * Meg. Use this method for a similar effect. This is not a required
|
---|
66 | * parameter, the default is to use lines rather than size.
|
---|
67 | *
|
---|
68 | * Use bytes or lines, not both. In general, use bytes or size for binary
|
---|
69 | * files, lines for text files.
|
---|
70 | *
|
---|
71 | * @param b the number of bytes per part, with an optional modifier. If
|
---|
72 | * there is no modifier, treat same as setBytes(int). For example,
|
---|
73 | * setSize("100k") is the same as setBytes(100 * 1024). Note that the
|
---|
74 | * maximum size must be smaller than Integer.MAX_VALUE (2147483647).
|
---|
75 | */
|
---|
76 | public void setSize( String b ) {
|
---|
77 | try {
|
---|
78 | int size = calcSize(b);;
|
---|
79 | setBytes( size );
|
---|
80 | }
|
---|
81 | catch ( NumberFormatException e ) {
|
---|
82 | throw new BuildException( "Invalid size parameter: " + b );
|
---|
83 | }
|
---|
84 | }
|
---|
85 |
|
---|
86 |
|
---|
87 | /**
|
---|
88 | * Set how much memory to use as an internal buffer. The default internal
|
---|
89 | * buffer size is 1 MB.
|
---|
90 | *
|
---|
91 | * @param b the number of bytes per part, with an optional modifier. If
|
---|
92 | * there is no modifier, treat same as setBytes(int). For example,
|
---|
93 | * setSize("100k") is the same as setBytes(100 * 1024). Note that the
|
---|
94 | * maximum size must be smaller than Integer.MAX_VALUE (2147483647).
|
---|
95 | */
|
---|
96 | public void setBuffersize(String b) {
|
---|
97 | try {
|
---|
98 | int size = calcSize(b);;
|
---|
99 | BUFFER_SIZE = size;
|
---|
100 | }
|
---|
101 | catch ( NumberFormatException e ) {
|
---|
102 | throw new BuildException( "Invalid buffer size parameter: " + b );
|
---|
103 | }
|
---|
104 | }
|
---|
105 |
|
---|
106 | /**
|
---|
107 | * Set the number of lines per part, default is 1000. This is not a required
|
---|
108 | * parameter, but is the default setting for splitting.
|
---|
109 | *
|
---|
110 | * Use bytes or lines, not both. In general, use bytes or size for binary
|
---|
111 | * files, lines for text files.
|
---|
112 | *
|
---|
113 | * @param x The number of lines per part.
|
---|
114 | */
|
---|
115 | public void setLines( int x ) {
|
---|
116 | lines = x;
|
---|
117 | bytes = -1;
|
---|
118 | }
|
---|
119 |
|
---|
120 | /**
|
---|
121 | * Split the text value of the given property.
|
---|
122 | *
|
---|
123 | * One of property, value, or file are required.
|
---|
124 | *
|
---|
125 | * @param p the name of the property whose value will be split.
|
---|
126 | */
|
---|
127 | public void setProperty( String p ) {
|
---|
128 | String v = getProject().getProperty( p );
|
---|
129 | if ( v == null || v.equals( "" ) )
|
---|
130 | throw new BuildException( "Property " + p + " has no value." );
|
---|
131 | setValue( v );
|
---|
132 | }
|
---|
133 |
|
---|
134 | /**
|
---|
135 | * Split the given string.
|
---|
136 | *
|
---|
137 | * One of property, value, or file are required.
|
---|
138 | *
|
---|
139 | * @param v a string
|
---|
140 | */
|
---|
141 | public void setValue( String v ) {
|
---|
142 | if ( v == null || v.equals( "" ) )
|
---|
143 | throw new BuildException( "Value is null or empty." );
|
---|
144 | value = v;
|
---|
145 | }
|
---|
146 |
|
---|
147 | /**
|
---|
148 | * Split the contents of the given file.
|
---|
149 | *
|
---|
150 | * One of property, value, or file are required.
|
---|
151 | *
|
---|
152 | * @param f the name of the file
|
---|
153 | */
|
---|
154 | public void setFile( File f ) {
|
---|
155 | file = f;
|
---|
156 | }
|
---|
157 |
|
---|
158 | /**
|
---|
159 | * Where to put the parts. If file has been set and output directory has not
|
---|
160 | * been set, output to directory containing file.
|
---|
161 | *
|
---|
162 | * @param d the output directory
|
---|
163 | */
|
---|
164 | public void setOutputdir( File d ) {
|
---|
165 | outputDir = d;
|
---|
166 | }
|
---|
167 |
|
---|
168 | /**
|
---|
169 | * Determines whether the build should fail if there is an error. Default is
|
---|
170 | * true.
|
---|
171 | *
|
---|
172 | * @param fail true or false
|
---|
173 | */
|
---|
174 | public void setFailonerror( boolean fail ) {
|
---|
175 | failOnError = fail;
|
---|
176 | }
|
---|
177 |
|
---|
178 |
|
---|
179 | /**
|
---|
180 | * Split the given property, value, or file into pieces.
|
---|
181 | *
|
---|
182 | * @exception BuildException only if failOnError is true
|
---|
183 | */
|
---|
184 | public void execute() throws BuildException {
|
---|
185 | // check params --
|
---|
186 | // must have value or file
|
---|
187 | if ( value == null && file == null )
|
---|
188 | throw new BuildException( "Must have property, value, or file." );
|
---|
189 | // if no file, must have outputDir
|
---|
190 | if ( file == null && outputDir == null )
|
---|
191 | throw new BuildException( "Must have output directory." );
|
---|
192 | // must have only one of value or file
|
---|
193 | if ( value != null && file != null )
|
---|
194 | throw new BuildException( "Must not have more than one of property, value, or file." );
|
---|
195 |
|
---|
196 | try {
|
---|
197 | if ( value != null )
|
---|
198 | splitValue();
|
---|
199 | else
|
---|
200 | splitFile();
|
---|
201 | }
|
---|
202 | catch ( Exception e ) {
|
---|
203 | if ( failOnError )
|
---|
204 | throw new BuildException( e.getMessage() );
|
---|
205 | else
|
---|
206 | log( e.getMessage() );
|
---|
207 | }
|
---|
208 | }
|
---|
209 |
|
---|
210 | /**
|
---|
211 | * Split a string value into several files. Since the length of a String
|
---|
212 | * can be no more than Integer.MAX_VALUE, no special handling of the split
|
---|
213 | * sizes is required.
|
---|
214 | *
|
---|
215 | * @exception IOException if there is an i/o problem
|
---|
216 | */
|
---|
217 | private void splitValue() throws Exception {
|
---|
218 | if ( !outputDir.exists() && !outputDir.mkdirs() ) {
|
---|
219 | throw new IOException( "Unable to create output directory." );
|
---|
220 | }
|
---|
221 |
|
---|
222 | StringReader reader = new StringReader( value );
|
---|
223 | int bytes_read = 0;
|
---|
224 | int suffix = 0;
|
---|
225 | if ( bytes > 0 ) {
|
---|
226 | // make files all the same number of bytes
|
---|
227 | char[] buffer = new char[ bytes ];
|
---|
228 | while ( bytes_read > -1 ) {
|
---|
229 | bytes_read = reader.read( buffer, 0, bytes );
|
---|
230 | if ( bytes_read == -1 )
|
---|
231 | break;
|
---|
232 | FileWriter fw = new FileWriter( new File( outputDir, prefix + "." + String.valueOf( suffix ) ) );
|
---|
233 | fw.write( buffer, 0, bytes_read );
|
---|
234 | fw.flush();
|
---|
235 | fw.close();
|
---|
236 | ++suffix;
|
---|
237 | }
|
---|
238 | }
|
---|
239 | else {
|
---|
240 | // make files all the same number of lines
|
---|
241 | splitByLines( reader );
|
---|
242 | }
|
---|
243 | }
|
---|
244 |
|
---|
245 | /**
|
---|
246 | * Split a file into several files. Need some special handling here since
|
---|
247 | * a file could be larger than Integer.MAX_VALUE, in fact, a file can be at
|
---|
248 | * most Long.MAX_VALUE.
|
---|
249 | *
|
---|
250 | * @exception IOException if there is an i/o problem
|
---|
251 | */
|
---|
252 | private void splitFile() throws IOException {
|
---|
253 | if ( !file.exists() )
|
---|
254 | throw new FileNotFoundException( file.toString() );
|
---|
255 | if ( file.length() == 0 )
|
---|
256 | throw new BuildException( "Zero length file." );
|
---|
257 | if ( outputDir == null )
|
---|
258 | outputDir = file.getParentFile();
|
---|
259 | if ( !outputDir.exists() && !outputDir.mkdirs() ) {
|
---|
260 | throw new IOException( "Unable to create output directory." );
|
---|
261 | }
|
---|
262 |
|
---|
263 | if ( bytes > 0 ) {
|
---|
264 | int suffix = 0;
|
---|
265 | int num_parts = ( int ) ( file.length() / ( long ) bytes );
|
---|
266 | int last_part_size = ( int ) ( file.length() % ( long ) bytes );
|
---|
267 | boolean one_more = last_part_size > 0;
|
---|
268 | BufferedInputStream bis = new BufferedInputStream( new FileInputStream( file ) );
|
---|
269 | for ( int i = 0; i < num_parts; i++ ) {
|
---|
270 | // make files all the same number of bytes
|
---|
271 | FileOutputStream fos = new FileOutputStream( new File( outputDir, prefix + "." + String.valueOf( suffix ) ) );
|
---|
272 | copyToStream( bis, fos, bytes );
|
---|
273 | fos.flush();
|
---|
274 | fos.close();
|
---|
275 | ++suffix;
|
---|
276 | }
|
---|
277 | if ( one_more ) {
|
---|
278 | FileOutputStream fos = new FileOutputStream( new File( outputDir, prefix + "." + String.valueOf( suffix ) ) );
|
---|
279 | copyToStream( bis, fos, last_part_size );
|
---|
280 | fos.flush();
|
---|
281 | fos.close();
|
---|
282 | }
|
---|
283 | bis.close();
|
---|
284 | }
|
---|
285 | else {
|
---|
286 | // make files all the same number of lines
|
---|
287 | splitByLines( new FileReader( file ) );
|
---|
288 | }
|
---|
289 | }
|
---|
290 |
|
---|
291 | private void splitByLines( Reader reader ) throws IOException {
|
---|
292 | int suffix = 0;
|
---|
293 | LineNumberReader lnr = new LineNumberReader( reader );
|
---|
294 | String line = lnr.readLine();
|
---|
295 | BufferedWriter writer = new BufferedWriter( new FileWriter( new File( outputDir, prefix + "." + String.valueOf( suffix ) ) ) );
|
---|
296 | while ( line != null ) {
|
---|
297 | writer.write( line );
|
---|
298 | writer.newLine();
|
---|
299 | if ( lnr.getLineNumber() % lines == 0 ) {
|
---|
300 | writer.flush();
|
---|
301 | writer.close();
|
---|
302 | ++suffix;
|
---|
303 | writer = new BufferedWriter( new FileWriter( new File( outputDir, prefix + "." + String.valueOf( suffix ) ) ) );
|
---|
304 | }
|
---|
305 | line = lnr.readLine();
|
---|
306 | }
|
---|
307 | writer.flush();
|
---|
308 | writer.close();
|
---|
309 | }
|
---|
310 |
|
---|
311 | private int calcSize(String b) throws NumberFormatException {
|
---|
312 | if ( b == null || b.length() == 0 )
|
---|
313 | return 0;
|
---|
314 | b = b.toLowerCase();
|
---|
315 | String modifier = b.substring( b.length() - 1 );
|
---|
316 | int multiplier = 1;
|
---|
317 | b = b.substring( 0, b.length() - 1 );
|
---|
318 | if ( modifier.equals( "b" ) ) {
|
---|
319 | multiplier = 512;
|
---|
320 | }
|
---|
321 | else if ( modifier.equals( "k" ) ) {
|
---|
322 | multiplier = 1024;
|
---|
323 | }
|
---|
324 | else if ( modifier.equals( "m" ) ) {
|
---|
325 | multiplier = 1024 * 1024;
|
---|
326 | }
|
---|
327 | else {
|
---|
328 | // modifier is not recognized, so put it back, maybe it's a number
|
---|
329 | b = b + modifier;
|
---|
330 | }
|
---|
331 | int size = Integer.parseInt( b ) * multiplier;
|
---|
332 | if (size <= 0) {
|
---|
333 | throw new NumberFormatException();
|
---|
334 | }
|
---|
335 | return size;
|
---|
336 | }
|
---|
337 |
|
---|
338 |
|
---|
339 | /**
|
---|
340 | * Copies a stream to another stream.
|
---|
341 | *
|
---|
342 | * @param from stream to copy from
|
---|
343 | * @param to file to write
|
---|
344 | * @param size number of bytes to copy from 'from' to 'to'
|
---|
345 | * @return actual number of bytes copied from 'from' to 'to'
|
---|
346 | * @exception IOException on any file error
|
---|
347 | */
|
---|
348 | private int copyToStream( InputStream from, OutputStream to, int size ) throws IOException {
|
---|
349 | int buffer_size = BUFFER_SIZE;
|
---|
350 | if ( size <= BUFFER_SIZE ) {
|
---|
351 | buffer_size = size;
|
---|
352 | }
|
---|
353 | byte[] buffer = new byte[ Math.min( BUFFER_SIZE, size ) ];
|
---|
354 | int bytes_read;
|
---|
355 | int total = 0;
|
---|
356 | int offset = 0;
|
---|
357 | while ( total < size ) {
|
---|
358 | bytes_read = from.read( buffer, 0, Math.min( buffer_size, size - offset ) );
|
---|
359 | if ( bytes_read == -1 )
|
---|
360 | break;
|
---|
361 | to.write( buffer, 0, bytes_read );
|
---|
362 | total += bytes_read;
|
---|
363 | offset += bytes_read;
|
---|
364 | }
|
---|
365 | to.flush();
|
---|
366 | return total;
|
---|
367 | }
|
---|
368 | }
|
---|