KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > Concat


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant.taskdefs;
20
21 import java.io.File JavaDoc;
22 import java.io.Reader JavaDoc;
23 import java.io.Writer JavaDoc;
24 import java.io.FileReader JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.PrintWriter JavaDoc;
28 import java.io.OutputStream JavaDoc;
29 import java.io.StringReader JavaDoc;
30 import java.io.BufferedReader JavaDoc;
31 import java.io.BufferedWriter JavaDoc;
32 import java.io.FileInputStream JavaDoc;
33 import java.io.FileOutputStream JavaDoc;
34 import java.io.InputStreamReader JavaDoc;
35 import java.io.OutputStreamWriter JavaDoc;
36 import java.util.Arrays JavaDoc;
37 import java.util.Vector JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import org.apache.tools.ant.Task;
40 import org.apache.tools.ant.Project;
41 import org.apache.tools.ant.BuildException;
42 import org.apache.tools.ant.ProjectComponent;
43 import org.apache.tools.ant.filters.util.ChainReaderHelper;
44 import org.apache.tools.ant.types.Path;
45 import org.apache.tools.ant.types.FileSet;
46 import org.apache.tools.ant.types.FileList;
47 import org.apache.tools.ant.types.FilterChain;
48 import org.apache.tools.ant.types.Resource;
49 import org.apache.tools.ant.types.ResourceCollection;
50 import org.apache.tools.ant.types.resources.Restrict;
51 import org.apache.tools.ant.types.resources.Resources;
52 import org.apache.tools.ant.types.resources.FileResource;
53 import org.apache.tools.ant.types.resources.StringResource;
54 import org.apache.tools.ant.types.resources.selectors.Not;
55 import org.apache.tools.ant.types.resources.selectors.Exists;
56 import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
57 import org.apache.tools.ant.util.FileUtils;
58 import org.apache.tools.ant.util.ConcatResourceInputStream;
59
60 /**
61  * This class contains the 'concat' task, used to concatenate a series
62  * of files into a single stream. The destination of this stream may
63  * be the system console, or a file. The following is a sample
64  * invocation:
65  *
66  * <pre>
67  * &lt;concat destfile=&quot;${build.dir}/index.xml&quot;
68  * append=&quot;false&quot;&gt;
69  *
70  * &lt;fileset dir=&quot;${xml.root.dir}&quot;
71  * includes=&quot;*.xml&quot; /&gt;
72  *
73  * &lt;/concat&gt;
74  * </pre>
75  *
76  */

77 public class Concat extends Task {
78
79     // The size of buffers to be used
80
private static final int BUFFER_SIZE = 8192;
81
82     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
83
84     private static final ResourceSelector EXISTS = new Exists();
85     private static final ResourceSelector NOT_EXISTS = new Not(EXISTS);
86
87     // Attributes.
88

89     /**
90      * The destination of the stream. If <code>null</code>, the system
91      * console is used.
92      */

93     private File JavaDoc destinationFile;
94
95     /**
96      * Whether or not the stream should be appended if the destination file
97      * exists.
98      * Defaults to <code>false</code>.
99      */

100     private boolean append;
101
102     /**
103      * Stores the input file encoding.
104      */

105     private String JavaDoc encoding;
106
107     /** Stores the output file encoding. */
108     private String JavaDoc outputEncoding;
109
110     /** Stores the binary attribute */
111     private boolean binary;
112
113     // Child elements.
114

115     /**
116      * This buffer stores the text within the 'concat' element.
117      */

118     private StringBuffer JavaDoc textBuffer;
119
120     /**
121      * Stores a collection of file sets and/or file lists, used to
122      * select multiple files for concatenation.
123      */

124     private Resources rc;
125
126     /** for filtering the concatenated */
127     private Vector JavaDoc filterChains;
128     /** ignore dates on input files */
129     private boolean forceOverwrite = true;
130     /** String to place at the start of the concatented stream */
131     private TextElement footer;
132     /** String to place at the end of the concatented stream */
133     private TextElement header;
134     /** add missing line.separator to files **/
135     private boolean fixLastLine = false;
136     /** endofline for fixlast line */
137     private String JavaDoc eolString;
138     /** outputwriter */
139     private Writer JavaDoc outputWriter = null;
140
141     /**
142      * Construct a new Concat task.
143      */

144     public Concat() {
145         reset();
146     }
147
148     /**
149      * Reset state to default.
150      */

151     public void reset() {
152         append = false;
153         forceOverwrite = true;
154         destinationFile = null;
155         encoding = null;
156         outputEncoding = null;
157         fixLastLine = false;
158         filterChains = null;
159         footer = null;
160         header = null;
161         binary = false;
162         outputWriter = null;
163         textBuffer = null;
164         eolString = System.getProperty("line.separator");
165         rc = null;
166     }
167
168     // Attribute setters.
169

170     /**
171      * Sets the destination file, or uses the console if not specified.
172      * @param destinationFile the destination file
173      */

174     public void setDestfile(File JavaDoc destinationFile) {
175         this.destinationFile = destinationFile;
176     }
177
178     /**
179      * Sets the behavior when the destination file exists. If set to
180      * <code>true</code> the stream data will be appended to the
181      * existing file, otherwise the existing file will be
182      * overwritten. Defaults to <code>false</code>.
183      * @param append if true append to the file.
184      */

185     public void setAppend(boolean append) {
186         this.append = append;
187     }
188
189     /**
190      * Sets the character encoding
191      * @param encoding the encoding of the input stream and unless
192      * outputencoding is set, the outputstream.
193      */

194     public void setEncoding(String JavaDoc encoding) {
195         this.encoding = encoding;
196         if (outputEncoding == null) {
197             outputEncoding = encoding;
198         }
199     }
200
201     /**
202      * Sets the character encoding for outputting
203      * @param outputEncoding the encoding for the output file
204      * @since Ant 1.6
205      */

206     public void setOutputEncoding(String JavaDoc outputEncoding) {
207         this.outputEncoding = outputEncoding;
208     }
209
210     /**
211      * Force overwrite existing destination file
212      * @param force if true always overwrite, otherwise only overwrite
213      * if the output file is older any of the input files.
214      * @since Ant 1.6
215      */

216     public void setForce(boolean force) {
217         this.forceOverwrite = force;
218     }
219
220     // Nested element creators.
221

222     /**
223      * Path of files to concatenate.
224      * @return the path used for concatenating
225      * @since Ant 1.6
226      */

227      public Path createPath() {
228         Path path = new Path(getProject());
229         add(path);
230         return path;
231     }
232
233     /**
234      * Set of files to concatenate.
235      * @param set the set of files
236      */

237     public void addFileset(FileSet set) {
238         add(set);
239     }
240
241     /**
242      * List of files to concatenate.
243      * @param list the list of files
244      */

245     public void addFilelist(FileList list) {
246         add(list);
247     }
248
249     /**
250      * Add an arbitrary ResourceCollection.
251      * @param c the ResourceCollection to add.
252      * @since Ant 1.7
253      */

254     public void add(ResourceCollection c) {
255         rc = rc == null ? new Resources() : rc;
256         rc.add(c);
257     }
258
259     /**
260      * Adds a FilterChain.
261      * @param filterChain a filterchain to filter the concatenated input
262      * @since Ant 1.6
263      */

264     public void addFilterChain(FilterChain filterChain) {
265         if (filterChains == null) {
266             filterChains = new Vector JavaDoc();
267         }
268         filterChains.addElement(filterChain);
269     }
270
271     /**
272      * This method adds text which appears in the 'concat' element.
273      * @param text the text to be concated.
274      */

275     public void addText(String JavaDoc text) {
276         if (textBuffer == null) {
277             // Initialize to the size of the first text fragment, with
278
// the hopes that it's the only one.
279
textBuffer = new StringBuffer JavaDoc(text.length());
280         }
281
282         // Append the fragment -- we defer property replacement until
283
// later just in case we get a partial property in a fragment.
284
textBuffer.append(text);
285     }
286
287     /**
288      * Add a header to the concatenated output
289      * @param headerToAdd the header
290      * @since Ant 1.6
291      */

292     public void addHeader(TextElement headerToAdd) {
293         this.header = headerToAdd;
294     }
295
296     /**
297      * Add a footer to the concatenated output
298      * @param footerToAdd the footer
299      * @since Ant 1.6
300      */

301     public void addFooter(TextElement footerToAdd) {
302         this.footer = footerToAdd;
303     }
304
305     /**
306      * Append line.separator to files that do not end
307      * with a line.separator, default false.
308      * @param fixLastLine if true make sure each input file has
309      * new line on the concatenated stream
310      * @since Ant 1.6
311      */

312     public void setFixLastLine(boolean fixLastLine) {
313         this.fixLastLine = fixLastLine;
314     }
315
316     /**
317      * Specify the end of line to find and to add if
318      * not present at end of each input file. This attribute
319      * is used in conjunction with fixlastline.
320      * @param crlf the type of new line to add -
321      * cr, mac, lf, unix, crlf, or dos
322      * @since Ant 1.6
323      */

324     public void setEol(FixCRLF.CrLf crlf) {
325         String JavaDoc s = crlf.getValue();
326         if (s.equals("cr") || s.equals("mac")) {
327             eolString = "\r";
328         } else if (s.equals("lf") || s.equals("unix")) {
329             eolString = "\n";
330         } else if (s.equals("crlf") || s.equals("dos")) {
331             eolString = "\r\n";
332         }
333     }
334
335     /**
336      * Set the output writer. This is to allow
337      * concat to be used as a nested element.
338      * @param outputWriter the output writer.
339      * @since Ant 1.6
340      */

341     public void setWriter(Writer JavaDoc outputWriter) {
342         this.outputWriter = outputWriter;
343     }
344
345     /**
346      * Set the binary attribute. If true, concat will concatenate the files
347      * byte for byte. This mode does not allow any filtering or other
348      * modifications to the input streams. The default value is false.
349      * @since Ant 1.6.2
350      * @param binary if true, enable binary mode.
351      */

352     public void setBinary(boolean binary) {
353         this.binary = binary;
354     }
355
356     /**
357      * Validate configuration options.
358      */

359     private ResourceCollection validate() {
360
361         // treat empty nested text as no text
362
sanitizeText();
363
364         // if binary check if incompatible attributes are used
365
if (binary) {
366             if (destinationFile == null) {
367                 throw new BuildException(
368                     "destfile attribute is required for binary concatenation");
369             }
370             if (textBuffer != null) {
371                 throw new BuildException(
372                     "Nested text is incompatible with binary concatenation");
373             }
374             if (encoding != null || outputEncoding != null) {
375                 throw new BuildException(
376                     "Seting input or output encoding is incompatible with binary"
377                     + " concatenation");
378             }
379             if (filterChains != null) {
380                 throw new BuildException(
381                     "Setting filters is incompatible with binary concatenation");
382             }
383             if (fixLastLine) {
384                 throw new BuildException(
385                     "Setting fixlastline is incompatible with binary concatenation");
386             }
387             if (header != null || footer != null) {
388                 throw new BuildException(
389                     "Nested header or footer is incompatible with binary concatenation");
390             }
391         }
392         if (destinationFile != null && outputWriter != null) {
393             throw new BuildException(
394                 "Cannot specify both a destination file and an output writer");
395         }
396         // Sanity check our inputs.
397
if (rc == null && textBuffer == null) {
398             // Nothing to concatenate!
399
throw new BuildException(
400                 "At least one resource must be provided, or some text.");
401         }
402         if (rc != null) {
403             // If using resources, disallow inline text. This is similar to
404
// using GNU 'cat' with file arguments -- stdin is simply
405
// ignored.
406
if (textBuffer != null) {
407                 throw new BuildException(
408                     "Cannot include inline text when using resources.");
409             }
410             Restrict noexistRc = new Restrict();
411             noexistRc.add(NOT_EXISTS);
412             noexistRc.add(rc);
413             for (Iterator JavaDoc i = noexistRc.iterator(); i.hasNext();) {
414                 log(i.next() + " does not exist.", Project.MSG_ERR);
415             }
416             if (destinationFile != null) {
417                 for (Iterator JavaDoc i = rc.iterator(); i.hasNext();) {
418                     Object JavaDoc o = i.next();
419                     if (o instanceof FileResource) {
420                         File JavaDoc f = ((FileResource) o).getFile();
421                         if (FILE_UTILS.fileNameEquals(f, destinationFile)) {
422                             throw new BuildException("Input file \""
423                                 + f + "\" is the same as the output file.");
424                         }
425                     }
426                 }
427             }
428             Restrict existRc = new Restrict();
429             existRc.add(EXISTS);
430             existRc.add(rc);
431             boolean outofdate = destinationFile == null || forceOverwrite;
432             if (!outofdate) {
433                 for (Iterator JavaDoc i = existRc.iterator(); !outofdate && i.hasNext();) {
434                     Resource r = (Resource) i.next();
435                     outofdate =
436                         (r.getLastModified() == 0L
437                          || r.getLastModified() > destinationFile.lastModified());
438                 }
439             }
440             if (!outofdate) {
441                 log(destinationFile + " is up-to-date.", Project.MSG_VERBOSE);
442                 return null; // no need to do anything
443
}
444             return existRc;
445         } else {
446             StringResource s = new StringResource();
447             s.setProject(getProject());
448             s.setValue(textBuffer.toString());
449             return s;
450         }
451     }
452
453     /**
454      * Execute the concat task.
455      */

456     public void execute() {
457         ResourceCollection c = validate();
458         if (c == null) {
459             return;
460         }
461         // Do nothing if no resources (including nested text)
462
if (c.size() < 1 && header == null && footer == null) {
463             log("No existing resources and no nested text, doing nothing",
464                 Project.MSG_INFO);
465             return;
466         }
467         if (binary) {
468             binaryCat(c);
469         } else {
470             cat(c);
471         }
472     }
473
474     /** perform the binary concatenation */
475     private void binaryCat(ResourceCollection c) {
476         log("Binary concatenation of " + c.size()
477             + " resources to " + destinationFile);
478         FileOutputStream JavaDoc out = null;
479         InputStream JavaDoc in = null;
480         try {
481             try {
482                 out = new FileOutputStream JavaDoc(destinationFile);
483             } catch (Exception JavaDoc t) {
484                 throw new BuildException("Unable to open "
485                     + destinationFile + " for writing", t);
486             }
487             in = new ConcatResourceInputStream(c);
488             ((ConcatResourceInputStream) in).setManagingComponent(this);
489             Thread JavaDoc t = new Thread JavaDoc(new StreamPumper(in, out));
490             t.start();
491             try {
492                 t.join();
493             } catch (InterruptedException JavaDoc e) {
494                 try {
495                     t.join();
496                 } catch (InterruptedException JavaDoc ee) {
497                     // Empty
498
}
499             }
500         } finally {
501             FileUtils.close(in);
502             if (out != null) {
503                 try {
504                     out.close();
505                 } catch (Exception JavaDoc ex) {
506                     throw new BuildException(
507                         "Unable to close " + destinationFile, ex);
508                 }
509             }
510         }
511     }
512
513     /** perform the concatenation */
514     private void cat(ResourceCollection c) {
515         OutputStream JavaDoc os = null;
516         char[] buffer = new char[BUFFER_SIZE];
517
518         try {
519             PrintWriter JavaDoc writer = null;
520
521             if (outputWriter != null) {
522                 writer = new PrintWriter JavaDoc(outputWriter);
523             } else {
524                 if (destinationFile == null) {
525                     // Log using WARN so it displays in 'quiet' mode.
526
os = new LogOutputStream(this, Project.MSG_WARN);
527                 } else {
528                     // ensure that the parent dir of dest file exists
529
File JavaDoc parent = destinationFile.getParentFile();
530                     if (!parent.exists()) {
531                         parent.mkdirs();
532                     }
533                     os = new FileOutputStream JavaDoc(destinationFile.getAbsolutePath(),
534                                               append);
535                 }
536                 if (outputEncoding == null) {
537                     writer = new PrintWriter JavaDoc(
538                         new BufferedWriter JavaDoc(
539                             new OutputStreamWriter JavaDoc(os)));
540                 } else {
541                     writer = new PrintWriter JavaDoc(
542                         new BufferedWriter JavaDoc(
543                             new OutputStreamWriter JavaDoc(os, outputEncoding)));
544                 }
545             }
546             if (header != null) {
547                 if (header.getFiltering()) {
548                     concatenate(
549                         buffer, writer, new StringReader JavaDoc(header.getValue()));
550                 } else {
551                     writer.print(header.getValue());
552                 }
553             }
554             if (c.size() > 0) {
555                 concatenate(buffer, writer, new MultiReader(c));
556             }
557             if (footer != null) {
558                 if (footer.getFiltering()) {
559                     concatenate(
560                         buffer, writer, new StringReader JavaDoc(footer.getValue()));
561                 } else {
562                     writer.print(footer.getValue());
563                 }
564             }
565             writer.flush();
566             if (os != null) {
567                 os.flush();
568             }
569         } catch (IOException JavaDoc ioex) {
570             throw new BuildException("Error while concatenating: "
571                                      + ioex.getMessage(), ioex);
572         } finally {
573             FileUtils.close(os);
574         }
575     }
576
577     /** Concatenate a single reader to the writer using buffer */
578     private void concatenate(char[] buffer, Writer JavaDoc writer, Reader JavaDoc in)
579         throws IOException JavaDoc {
580         if (filterChains != null) {
581             ChainReaderHelper helper = new ChainReaderHelper();
582             helper.setBufferSize(BUFFER_SIZE);
583             helper.setPrimaryReader(in);
584             helper.setFilterChains(filterChains);
585             helper.setProject(getProject());
586             in = new BufferedReader JavaDoc(helper.getAssembledReader());
587         }
588         while (true) {
589             int nRead = in.read(buffer, 0, buffer.length);
590             if (nRead == -1) {
591                 break;
592             }
593             writer.write(buffer, 0, nRead);
594         }
595         writer.flush();
596     }
597
598     /**
599      * Treat empty nested text as no text.
600      *
601      * <p>Depending on the XML parser, addText may have been called
602      * for &quot;ignorable whitespace&quot; as well.</p>
603      */

604     private void sanitizeText() {
605         if (textBuffer != null) {
606             if (textBuffer.substring(0).trim().length() == 0) {
607                 textBuffer = null;
608             }
609         }
610     }
611
612     /**
613      * sub element points to a file or contains text
614      */

615     public static class TextElement extends ProjectComponent {
616         private String JavaDoc value = "";
617         private boolean trimLeading = false;
618         private boolean trim = false;
619         private boolean filtering = true;
620         private String JavaDoc encoding = null;
621
622         /**
623          * whether to filter the text in this element
624          * or not.
625          *
626          * @param filtering true if the text should be filtered.
627          * the default value is true.
628          */

629         public void setFiltering(boolean filtering) {
630             this.filtering = filtering;
631         }
632
633         /** return the filtering attribute */
634         private boolean getFiltering() {
635             return filtering;
636         }
637
638         /**
639          * The encoding of the text element
640          *
641          * @param encoding the name of the charset used to encode
642          */

643         public void setEncoding(String JavaDoc encoding) {
644             this.encoding = encoding;
645         }
646
647         /**
648          * set the text using a file
649          * @param file the file to use
650          * @throws BuildException if the file does not exist, or cannot be
651          * read
652          */

653         public void setFile(File JavaDoc file) throws BuildException {
654             // non-existing files are not allowed
655
if (!file.exists()) {
656                 throw new BuildException("File " + file + " does not exist.");
657             }
658
659             BufferedReader JavaDoc reader = null;
660             try {
661                 if (this.encoding == null) {
662                     reader = new BufferedReader JavaDoc(new FileReader JavaDoc(file));
663                 } else {
664                     reader = new BufferedReader JavaDoc(
665                         new InputStreamReader JavaDoc(new FileInputStream JavaDoc(file),
666                                               this.encoding));
667                 }
668                 value = FileUtils.readFully(reader);
669             } catch (IOException JavaDoc ex) {
670                 throw new BuildException(ex);
671             } finally {
672                 FileUtils.close(reader);
673             }
674         }
675
676         /**
677          * set the text using inline
678          * @param value the text to place inline
679          */

680         public void addText(String JavaDoc value) {
681             this.value += getProject().replaceProperties(value);
682         }
683
684         /**
685          * s:^\s*:: on each line of input
686          * @param strip if true do the trim
687          */

688         public void setTrimLeading(boolean strip) {
689             this.trimLeading = strip;
690         }
691
692         /**
693          * whether to call text.trim()
694          * @param trim if true trim the text
695          */

696         public void setTrim(boolean trim) {
697             this.trim = trim;
698         }
699
700         /**
701          * @return the text, after possible trimming
702          */

703         public String JavaDoc getValue() {
704             if (value == null) {
705                 value = "";
706             }
707             if (value.trim().length() == 0) {
708                 value = "";
709             }
710             if (trimLeading) {
711                 char[] current = value.toCharArray();
712                 StringBuffer JavaDoc b = new StringBuffer JavaDoc(current.length);
713                 boolean startOfLine = true;
714                 int pos = 0;
715                 while (pos < current.length) {
716                     char ch = current[pos++];
717                     if (startOfLine) {
718                         if (ch == ' ' || ch == '\t') {
719                             continue;
720                         }
721                         startOfLine = false;
722                     }
723                     b.append(ch);
724                     if (ch == '\n' || ch == '\r') {
725                         startOfLine = true;
726                     }
727                 }
728                 value = b.toString();
729             }
730             if (trim) {
731                 value = value.trim();
732             }
733             return value;
734         }
735     }
736
737     /**
738      * This class reads from each of the source files in turn.
739      * The concatentated result can then be filtered as
740      * a single stream.
741      */

742     private class MultiReader extends Reader JavaDoc {
743         private Reader JavaDoc reader = null;
744         private int lastPos = 0;
745         private char[] lastChars = new char[eolString.length()];
746         private boolean needAddSeparator = false;
747         private Iterator JavaDoc i;
748
749         private MultiReader(ResourceCollection c) {
750             i = c.iterator();
751         }
752
753         private Reader JavaDoc getReader() throws IOException JavaDoc {
754             if (reader == null && i.hasNext()) {
755                 Resource r = (Resource) i.next();
756                 log("Concating " + r.toLongString(), Project.MSG_VERBOSE);
757                 InputStream JavaDoc is = r.getInputStream();
758                 reader = new BufferedReader JavaDoc(encoding == null
759                     ? new InputStreamReader JavaDoc(is)
760                     : new InputStreamReader JavaDoc(is, encoding));
761                 Arrays.fill(lastChars, (char) 0);
762             }
763             return reader;
764         }
765
766         private void nextReader() throws IOException JavaDoc {
767             close();
768             reader = null;
769         }
770
771         /**
772          * Read a character from the current reader object. Advance
773          * to the next if the reader is finished.
774          * @return the character read, -1 for EOF on the last reader.
775          * @exception IOException - possibly thrown by the read for a reader
776          * object.
777          */

778         public int read() throws IOException JavaDoc {
779             if (needAddSeparator) {
780                 int ret = eolString.charAt(lastPos++);
781                 if (lastPos >= eolString.length()) {
782                     lastPos = 0;
783                     needAddSeparator = false;
784                 }
785                 return ret;
786             }
787             while (getReader() != null) {
788                 int ch = getReader().read();
789                 if (ch == -1) {
790                     nextReader();
791                     if (fixLastLine && isMissingEndOfLine()) {
792                         needAddSeparator = true;
793                         lastPos = 0;
794                     }
795                 } else {
796                     addLastChar((char) ch);
797                     return ch;
798                 }
799             }
800             return -1;
801         }
802
803         /**
804          * Read into the buffer <code>cbuf</code>.
805          * @param cbuf The array to be read into.
806          * @param off The offset.
807          * @param len The length to read.
808          * @exception IOException - possibly thrown by the reads to the
809          * reader objects.
810          */

811         public int read(char[] cbuf, int off, int len)
812             throws IOException JavaDoc {
813
814             int amountRead = 0;
815             while (getReader() != null || needAddSeparator) {
816                 if (needAddSeparator) {
817                     cbuf[off] = eolString.charAt(lastPos++);
818                     if (lastPos >= eolString.length()) {
819                         lastPos = 0;
820                         needAddSeparator = false;
821                     }
822                     len--;
823                     off++;
824                     amountRead++;
825                     if (len == 0) {
826                         return amountRead;
827                     }
828                     continue;
829                 }
830                 int nRead = getReader().read(cbuf, off, len);
831                 if (nRead == -1 || nRead == 0) {
832                     nextReader();
833                     if (fixLastLine && isMissingEndOfLine()) {
834                         needAddSeparator = true;
835                         lastPos = 0;
836                     }
837                 } else {
838                     if (fixLastLine) {
839                         for (int i = nRead;
840                                  i > (nRead - lastChars.length);
841                                  --i) {
842                             if (i <= 0) {
843                                 break;
844                             }
845                             addLastChar(cbuf[off + i - 1]);
846                         }
847                     }
848                     len -= nRead;
849                     off += nRead;
850                     amountRead += nRead;
851                     if (len == 0) {
852                         return amountRead;
853                     }
854                 }
855             }
856             if (amountRead == 0) {
857                 return -1;
858             } else {
859                 return amountRead;
860             }
861         }
862
863         /**
864          * Close the current reader
865          */

866         public void close() throws IOException JavaDoc {
867             if (reader != null) {
868                 reader.close();
869             }
870         }
871
872         /**
873          * if checking for end of line at end of file
874          * add a character to the lastchars buffer
875          */

876         private void addLastChar(char ch) {
877             for (int i = lastChars.length - 2; i >= 0; --i) {
878                 lastChars[i] = lastChars[i + 1];
879             }
880             lastChars[lastChars.length - 1] = ch;
881         }
882
883         /**
884          * return true if the lastchars buffer does
885          * not contain the lineseparator
886          */

887         private boolean isMissingEndOfLine() {
888             for (int i = 0; i < lastChars.length; ++i) {
889                 if (lastChars[i] != eolString.charAt(i)) {
890                     return true;
891                 }
892             }
893             return false;
894         }
895     }
896
897 }
898
899
Popular Tags