source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/Concat.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 31.6 KB
Line 
1/*
2 * Copyright 2002-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.taskdefs;
19
20import java.io.BufferedReader;
21import java.io.BufferedWriter;
22import java.io.File;
23import java.io.FileInputStream;
24import java.io.FileOutputStream;
25import java.io.FileReader;
26import java.io.IOException;
27import java.io.InputStreamReader;
28import java.io.OutputStream;
29import java.io.OutputStreamWriter;
30import java.io.PrintWriter;
31import java.io.Reader;
32import java.io.StringReader;
33import java.io.Writer;
34import java.util.Enumeration;
35import java.util.Iterator;
36import java.util.Vector;
37import org.apache.tools.ant.BuildException;
38import org.apache.tools.ant.DirectoryScanner;
39import org.apache.tools.ant.Project;
40import org.apache.tools.ant.ProjectComponent;
41import org.apache.tools.ant.Task;
42import org.apache.tools.ant.filters.util.ChainReaderHelper;
43import org.apache.tools.ant.types.FileList;
44import org.apache.tools.ant.types.FileSet;
45import org.apache.tools.ant.types.FilterChain;
46import org.apache.tools.ant.types.Path;
47import org.apache.tools.ant.util.FileUtils;
48
49/**
50 * This class contains the 'concat' task, used to concatenate a series
51 * of files into a single stream. The destination of this stream may
52 * be the system console, or a file. The following is a sample
53 * invocation:
54 *
55 * <pre>
56 * &lt;concat destfile=&quot;${build.dir}/index.xml&quot;
57 * append=&quot;false&quot;&gt;
58 *
59 * &lt;fileset dir=&quot;${xml.root.dir}&quot;
60 * includes=&quot;*.xml&quot; /&gt;
61 *
62 * &lt;/concat&gt;
63 * </pre>
64 *
65 */
66public class Concat extends Task {
67
68 // The size of buffers to be used
69 private static final int BUFFER_SIZE = 8192;
70
71 // Attributes.
72
73 /**
74 * The destination of the stream. If <code>null</code>, the system
75 * console is used.
76 */
77 private File destinationFile = null;
78
79 /**
80 * Whether or not the stream should be appended if the destination file
81 * exists.
82 * Defaults to <code>false</code>.
83 */
84 private boolean append = false;
85
86 /**
87 * Stores the input file encoding.
88 */
89 private String encoding = null;
90
91 /** Stores the output file encoding. */
92 private String outputEncoding = null;
93
94 /** Stores the binary attribute */
95 private boolean binary = false;
96
97 // Child elements.
98
99 /**
100 * This buffer stores the text within the 'concat' element.
101 */
102 private StringBuffer textBuffer;
103
104 /**
105 * Stores a collection of file sets and/or file lists, used to
106 * select multiple files for concatenation.
107 */
108 private Vector sources = new Vector();
109
110 /** for filtering the concatenated */
111 private Vector filterChains = null;
112 /** ignore dates on input files */
113 private boolean forceOverwrite = true;
114 /** String to place at the start of the concatented stream */
115 private TextElement footer;
116 /** String to place at the end of the concatented stream */
117 private TextElement header;
118 /** add missing line.separator to files **/
119 private boolean fixLastLine = false;
120 /** endofline for fixlast line */
121 private String eolString = System.getProperty("line.separator");
122 /** outputwriter */
123 private Writer outputWriter = null;
124
125 /** internal variable - used to collect the source files from sources */
126 private Vector sourceFiles = new Vector();
127
128 /** 1.1 utilities and copy utilities */
129 private static FileUtils fileUtils = FileUtils.newFileUtils();
130
131 // Attribute setters.
132
133 /**
134 * Sets the destination file, or uses the console if not specified.
135 * @param destinationFile the destination file
136 */
137 public void setDestfile(File destinationFile) {
138 this.destinationFile = destinationFile;
139 }
140
141 /**
142 * Sets the behavior when the destination file exists. If set to
143 * <code>true</code> the stream data will be appended to the
144 * existing file, otherwise the existing file will be
145 * overwritten. Defaults to <code>false</code>.
146 * @param append if true append to the file.
147 */
148 public void setAppend(boolean append) {
149 this.append = append;
150 }
151
152 /**
153 * Sets the character encoding
154 * @param encoding the encoding of the input stream and unless
155 * outputencoding is set, the outputstream.
156 */
157 public void setEncoding(String encoding) {
158 this.encoding = encoding;
159 if (outputEncoding == null) {
160 outputEncoding = encoding;
161 }
162 }
163
164 /**
165 * Sets the character encoding for outputting
166 * @param outputEncoding the encoding for the output file
167 * @since Ant 1.6
168 */
169 public void setOutputEncoding(String outputEncoding) {
170 this.outputEncoding = outputEncoding;
171 }
172
173 /**
174 * Force overwrite existing destination file
175 * @param force if true always overwrite, otherwise only overwrite
176 * if the output file is older any of the input files.
177 * @since Ant 1.6
178 */
179 public void setForce(boolean force) {
180 this.forceOverwrite = force;
181 }
182
183 // Nested element creators.
184
185 /**
186 * Path of files to concatenate.
187 * @return the path used for concatenating
188 * @since Ant 1.6
189 */
190 public Path createPath() {
191 Path path = new Path(getProject());
192 sources.addElement(path);
193 return path;
194 }
195
196 /**
197 * Set of files to concatenate.
198 * @param set the set of files
199 */
200 public void addFileset(FileSet set) {
201 sources.addElement(set);
202 }
203
204 /**
205 * List of files to concatenate.
206 * @param list the list of files
207 */
208 public void addFilelist(FileList list) {
209 sources.addElement(list);
210 }
211
212 /**
213 * Adds a FilterChain.
214 * @param filterChain a filterchain to filter the concatenated input
215 * @since Ant 1.6
216 */
217 public void addFilterChain(FilterChain filterChain) {
218 if (filterChains == null) {
219 filterChains = new Vector();
220 }
221 filterChains.addElement(filterChain);
222 }
223
224 /**
225 * This method adds text which appears in the 'concat' element.
226 * @param text the text to be concated.
227 */
228 public void addText(String text) {
229 if (textBuffer == null) {
230 // Initialize to the size of the first text fragment, with
231 // the hopes that it's the only one.
232 textBuffer = new StringBuffer(text.length());
233 }
234
235 // Append the fragment -- we defer property replacement until
236 // later just in case we get a partial property in a fragment.
237 textBuffer.append(text);
238 }
239
240 /**
241 * Add a header to the concatenated output
242 * @param header the header
243 * @since Ant 1.6
244 */
245 public void addHeader(TextElement header) {
246 this.header = header;
247 }
248
249 /**
250 * Add a footer to the concatenated output
251 * @param footer the footer
252 * @since Ant 1.6
253 */
254 public void addFooter(TextElement footer) {
255 this.footer = footer;
256 }
257
258 /**
259 * Append line.separator to files that do not end
260 * with a line.separator, default false.
261 * @param fixLastLine if true make sure each input file has
262 * new line on the concatenated stream
263 * @since Ant 1.6
264 */
265 public void setFixLastLine(boolean fixLastLine) {
266 this.fixLastLine = fixLastLine;
267 }
268
269 /**
270 * Specify the end of line to find and to add if
271 * not present at end of each input file. This attribute
272 * is used in conjunction with fixlastline.
273 * @param crlf the type of new line to add -
274 * cr, mac, lf, unix, crlf, or dos
275 * @since Ant 1.6
276 */
277 public void setEol(FixCRLF.CrLf crlf) {
278 String s = crlf.getValue();
279 if (s.equals("cr") || s.equals("mac")) {
280 eolString = "\r";
281 } else if (s.equals("lf") || s.equals("unix")) {
282 eolString = "\n";
283 } else if (s.equals("crlf") || s.equals("dos")) {
284 eolString = "\r\n";
285 }
286 }
287
288 /**
289 * set the output writer, this is to allow
290 * concat to be used as a nested element
291 * @param outputWriter the output writer
292 * @since Ant 1.6
293 */
294 public void setWriter(Writer outputWriter) {
295 this.outputWriter = outputWriter;
296 }
297
298 /**
299 * set the binary attribute.
300 * if true, concat will concatenate the files
301 * byte for byte. This mode does not allow
302 * any filtering, or other modifications
303 * to the input streams.
304 * The default value is false.
305 * @since ant 1.6.2
306 * @param binary if true, enable binary mode
307 */
308 public void setBinary(boolean binary) {
309 this.binary = binary;
310 }
311
312 /**
313 * This method checks the attributes and performs the concatenation.
314 */
315 private void checkAndExecute() {
316
317 // treat empty nested text as no text
318 sanitizeText();
319
320 // if binary check if incompatible attributes are used
321 if (binary) {
322 if (destinationFile == null) {
323 throw new BuildException(
324 "DestFile attribute is required for binary concatenation");
325 }
326
327 if (textBuffer != null) {
328 throw new BuildException(
329 "Nested text is incompatible with binary concatenation");
330 }
331 if (encoding != null || outputEncoding != null) {
332 throw new BuildException(
333 "Seting input or output encoding is incompatible with binary"
334 + " concatenation");
335 }
336 if (filterChains != null) {
337 throw new BuildException(
338 "Setting filters is incompatible with binary concatenation");
339 }
340 if (fixLastLine) {
341 throw new BuildException(
342 "Setting fixlastline is incompatible with binary concatenation");
343 }
344 if (header != null || footer != null) {
345 throw new BuildException(
346 "Nested header or footer is incompatible with binary concatenation");
347 }
348 }
349
350 if (destinationFile != null && outputWriter != null) {
351 throw new BuildException(
352 "Cannot specify both a destination file and an output writer");
353 }
354
355 // Sanity check our inputs.
356 if (sources.size() == 0 && textBuffer == null) {
357 // Nothing to concatenate!
358 throw new BuildException(
359 "At least one file must be provided, or some text.");
360 }
361
362 // If using filesets, disallow inline text. This is similar to
363 // using GNU 'cat' with file arguments -- stdin is simply
364 // ignored.
365 if (sources.size() > 0 && textBuffer != null) {
366 throw new BuildException(
367 "Cannot include inline text when using filesets.");
368 }
369
370 // Iterate thru the sources - paths, filesets and filelists
371 for (Enumeration e = sources.elements(); e.hasMoreElements();) {
372 Object o = e.nextElement();
373 if (o instanceof Path) {
374 Path path = (Path) o;
375 checkAddFiles(null, path.list());
376
377 } else if (o instanceof FileSet) {
378 FileSet fileSet = (FileSet) o;
379 DirectoryScanner scanner =
380 fileSet.getDirectoryScanner(getProject());
381 checkAddFiles(fileSet.getDir(getProject()),
382 scanner.getIncludedFiles());
383
384 } else if (o instanceof FileList) {
385 FileList fileList = (FileList) o;
386 checkAddFiles(fileList.getDir(getProject()),
387 fileList.getFiles(getProject()));
388 }
389 }
390
391 // check if the files are outofdate
392 if (destinationFile != null && !forceOverwrite
393 && (sourceFiles.size() > 0) && destinationFile.exists()) {
394 boolean outofdate = false;
395 for (int i = 0; i < sourceFiles.size(); ++i) {
396 File file = (File) sourceFiles.elementAt(i);
397 if (file.lastModified() > destinationFile.lastModified()) {
398 outofdate = true;
399 break;
400 }
401 }
402 if (!outofdate) {
403 log(destinationFile + " is up-to-date.", Project.MSG_VERBOSE);
404 return; // no need to do anything
405 }
406 }
407
408 // Do nothing if all the sources are not present
409 // And textBuffer is null
410 if (textBuffer == null && sourceFiles.size() == 0
411 && header == null && footer == null) {
412 log("No existing files and no nested text, doing nothing",
413 Project.MSG_INFO);
414 return;
415 }
416
417 if (binary) {
418 binaryCat();
419 } else {
420 cat();
421 }
422 }
423
424 /**
425 * execute the concat task.
426 */
427 public void execute() {
428 try {
429 checkAndExecute();
430 } finally {
431 resetTask();
432 }
433 }
434
435 /**
436 * Reset state to default.
437 */
438 public void reset() {
439 append = false;
440 forceOverwrite = true;
441 destinationFile = null;
442 encoding = null;
443 outputEncoding = null;
444 fixLastLine = false;
445 sources.removeAllElements();
446 sourceFiles.removeAllElements();
447 filterChains = null;
448 footer = null;
449 header = null;
450 }
451
452 /**
453 * reset the used variables to allow the same task
454 * instance to be used again.
455 */
456 private void resetTask() {
457 sourceFiles.clear();
458 }
459
460 private void checkAddFiles(File base, String[] filenames) {
461 for (int i = 0; i < filenames.length; ++i) {
462 File file = new File(base, filenames[i]);
463 if (!file.exists()) {
464 log("File " + file + " does not exist.", Project.MSG_ERR);
465 continue;
466 }
467 if (destinationFile != null
468 && fileUtils.fileNameEquals(destinationFile, file)) {
469 throw new BuildException("Input file \""
470 + file + "\" "
471 + "is the same as the output file.");
472 }
473 sourceFiles.addElement(file);
474 }
475 }
476
477 /** perform the binary concatenation */
478 private void binaryCat() {
479 log("Binary concatenation of " + sourceFiles.size()
480 + " files to " + destinationFile);
481 FileOutputStream out = null;
482 FileInputStream in = null;
483 byte[] buffer = new byte[8 * 1024];
484 try {
485 try {
486 out = new FileOutputStream(destinationFile);
487 } catch (Exception t) {
488 throw new BuildException(
489 "Unable to open " + destinationFile
490 + " for writing", t);
491 }
492 for (Iterator i = sourceFiles.iterator(); i.hasNext();) {
493 File sourceFile = (File) i.next();
494 try {
495 in = new FileInputStream(sourceFile);
496 } catch (Exception t) {
497 throw new BuildException(
498 "Unable to open input file " + sourceFile,
499 t);
500 }
501 int count = 0;
502 do {
503 try {
504 count = in.read(buffer, 0, buffer.length);
505 } catch (Exception t) {
506 throw new BuildException(
507 "Unable to read from " + sourceFile, t);
508 }
509 try {
510 if (count > 0) {
511 out.write(buffer, 0, count);
512 }
513 } catch (Exception t) {
514 throw new BuildException(
515 "Unable to write to " + destinationFile, t);
516 }
517 } while (count > 0);
518
519 try {
520 in.close();
521 } catch (Exception t) {
522 throw new BuildException(
523 "Unable to close " + sourceFile, t);
524 }
525 in = null;
526 }
527 } finally {
528 if (in != null) {
529 try {
530 in.close();
531 } catch (Throwable t) {
532 // Ignore
533 }
534 }
535 if (out != null) {
536 try {
537 out.close();
538 } catch (Exception ex) {
539 throw new BuildException(
540 "Unable to close " + destinationFile, ex);
541 }
542 }
543 }
544 }
545
546 /** perform the concatenation */
547 private void cat() {
548 OutputStream os = null;
549 Reader reader = null;
550 char[] buffer = new char[BUFFER_SIZE];
551
552 try {
553
554 PrintWriter writer = null;
555
556 if (outputWriter != null) {
557 writer = new PrintWriter(outputWriter);
558 } else {
559 if (destinationFile == null) {
560 // Log using WARN so it displays in 'quiet' mode.
561 os = new LogOutputStream(this, Project.MSG_WARN);
562 } else {
563 // ensure that the parent dir of dest file exists
564 File parent = fileUtils.getParentFile(destinationFile);
565 if (!parent.exists()) {
566 parent.mkdirs();
567 }
568
569 os = new FileOutputStream(destinationFile.getAbsolutePath(),
570 append);
571 }
572
573 if (outputEncoding == null) {
574 writer = new PrintWriter(
575 new BufferedWriter(
576 new OutputStreamWriter(os)));
577 } else {
578 writer = new PrintWriter(
579 new BufferedWriter(
580 new OutputStreamWriter(os, outputEncoding)));
581 }
582 }
583
584 if (header != null) {
585 if (header.getFiltering()) {
586 concatenate(
587 buffer, writer, new StringReader(header.getValue()));
588 } else {
589 writer.print(header.getValue());
590 }
591 }
592
593 if (textBuffer != null) {
594 reader = new StringReader(
595 getProject().replaceProperties(textBuffer.substring(0)));
596 } else {
597 reader = new MultiReader();
598 }
599
600 concatenate(buffer, writer, reader);
601
602 if (footer != null) {
603 if (footer.getFiltering()) {
604 concatenate(
605 buffer, writer, new StringReader(footer.getValue()));
606 } else {
607 writer.print(footer.getValue());
608 }
609 }
610
611 writer.flush();
612 if (os != null) {
613 os.flush();
614 }
615
616 } catch (IOException ioex) {
617 throw new BuildException("Error while concatenating: "
618 + ioex.getMessage(), ioex);
619 } finally {
620 if (reader != null) {
621 try {
622 reader.close();
623 } catch (IOException ignore) {
624 // ignore
625 }
626 }
627 if (os != null) {
628 try {
629 os.close();
630 } catch (IOException ignore) {
631 // ignore
632 }
633 }
634 }
635 }
636
637
638 /** Concatenate a single reader to the writer using buffer */
639 private void concatenate(char[] buffer, Writer writer, Reader in)
640 throws IOException {
641 if (filterChains != null) {
642 ChainReaderHelper helper = new ChainReaderHelper();
643 helper.setBufferSize(BUFFER_SIZE);
644 helper.setPrimaryReader(in);
645 helper.setFilterChains(filterChains);
646 helper.setProject(getProject());
647 in = new BufferedReader(helper.getAssembledReader());
648 }
649
650 while (true) {
651 int nRead = in.read(buffer, 0, buffer.length);
652 if (nRead == -1) {
653 break;
654 }
655 writer.write(buffer, 0, nRead);
656 }
657
658 writer.flush();
659 }
660
661 /**
662 * Treat empty nested text as no text.
663 *
664 * <p>Depending on the XML parser, addText may have been called
665 * for &quot;ignorable whitespace&quot; as well.</p>
666 */
667 private void sanitizeText() {
668 if (textBuffer != null) {
669 if (textBuffer.substring(0).trim().length() == 0) {
670 textBuffer = null;
671 }
672 }
673 }
674
675 /**
676 * sub element points to a file or contains text
677 */
678 public static class TextElement extends ProjectComponent {
679 private String value = "";
680 private boolean trimLeading = false;
681 private boolean trim = false;
682 private boolean filtering = true;
683 private String encoding = null;
684
685 /**
686 * whether to filter the text in this element
687 * or not.
688 *
689 * @param filtering true if the text should be filtered.
690 * the default value is true.
691 */
692 public void setFiltering(boolean filtering) {
693 this.filtering = filtering;
694 }
695
696 /** return the filtering attribute */
697 private boolean getFiltering() {
698 return filtering;
699 }
700
701 /**
702 * The encoding of the text element
703 *
704 * @param encoding the name of the charset used to encode
705 */
706 public void setEncoding(String encoding) {
707 this.encoding = encoding;
708 }
709
710 /**
711 * set the text using a file
712 * @param file the file to use
713 * @throws BuildException if the file does not exist, or cannot be
714 * read
715 */
716 public void setFile(File file) {
717 // non-existing files are not allowed
718 if (!file.exists()) {
719 throw new BuildException("File " + file + " does not exist.");
720 }
721
722 BufferedReader reader = null;
723 try {
724 if (this.encoding == null) {
725 reader = new BufferedReader(new FileReader(file));
726 } else {
727 reader = new BufferedReader(
728 new InputStreamReader(new FileInputStream(file),
729 this.encoding));
730 }
731 value = fileUtils.readFully(reader);
732 } catch (IOException ex) {
733 throw new BuildException(ex);
734 } finally {
735 if (reader != null) {
736 try {
737 reader.close();
738 } catch (Throwable t) {
739 // ignore
740 }
741 }
742 }
743 }
744
745 /**
746 * set the text using inline
747 * @param value the text to place inline
748 */
749 public void addText(String value) {
750 this.value += getProject().replaceProperties(value);
751 }
752
753 /**
754 * s:^\s*:: on each line of input
755 * @param strip if true do the trim
756 */
757 public void setTrimLeading(boolean strip) {
758 this.trimLeading = strip;
759 }
760
761 /**
762 * whether to call text.trim()
763 * @param trim if true trim the text
764 */
765 public void setTrim(boolean trim) {
766 this.trim = trim;
767 }
768
769 /**
770 * @return the text, after possible trimming
771 */
772 public String getValue() {
773 if (value == null) {
774 value = "";
775 }
776 if (value.trim().length() == 0) {
777 value = "";
778 }
779 if (trimLeading) {
780 char[] current = value.toCharArray();
781 StringBuffer b = new StringBuffer(current.length);
782 boolean startOfLine = true;
783 int pos = 0;
784 while (pos < current.length) {
785 char ch = current[pos++];
786 if (startOfLine) {
787 if (ch == ' ' || ch == '\t') {
788 continue;
789 }
790 startOfLine = false;
791 }
792 b.append(ch);
793 if (ch == '\n' || ch == '\r') {
794 startOfLine = true;
795 }
796 }
797 value = b.toString();
798 }
799 if (trim) {
800 value = value.trim();
801 }
802 return value;
803 }
804 }
805
806 /**
807 * This class reads from each of the source files in turn.
808 * The concatentated result can then be filtered as
809 * a single stream.
810 */
811 private class MultiReader extends Reader {
812 private int pos = 0;
813 private Reader reader = null;
814 private int lastPos = 0;
815 private char[] lastChars = new char[eolString.length()];
816 private boolean needAddSeparator = false;
817
818 private Reader getReader() throws IOException {
819 if (reader == null) {
820 log("Concating file " + sourceFiles.elementAt(pos),
821 Project.MSG_VERBOSE);
822 if (encoding == null) {
823 reader = new BufferedReader(
824 new FileReader((File) sourceFiles.elementAt(pos)));
825 } else {
826 // invoke the zoo of io readers
827 reader = new BufferedReader(
828 new InputStreamReader(
829 new FileInputStream(
830 (File) sourceFiles.elementAt(pos)),
831 encoding));
832 }
833 for (int i = 0; i < lastChars.length; ++i) {
834 lastChars[i] = 0;
835 }
836 }
837 return reader;
838 }
839
840 /**
841 * Read a character from the current reader object. Advance
842 * to the next if the reader is finished.
843 * @return the character read, -1 for EOF on the last reader.
844 * @exception IOException - possibly thrown by the read for a reader
845 * object.
846 */
847 public int read() throws IOException {
848 if (needAddSeparator) {
849 int ret = eolString.charAt(lastPos++);
850 if (lastPos >= eolString.length()) {
851 lastPos = 0;
852 needAddSeparator = false;
853 }
854 return ret;
855 }
856
857 while (pos < sourceFiles.size()) {
858 int ch = getReader().read();
859 if (ch == -1) {
860 reader.close();
861 reader = null;
862 if (fixLastLine && isMissingEndOfLine()) {
863 needAddSeparator = true;
864 lastPos = 0;
865 }
866 } else {
867 addLastChar((char) ch);
868 return ch;
869 }
870 pos++;
871 }
872 return -1;
873 }
874
875 /**
876 * Read into the buffer <code>cbuf</code>.
877 * @param cbuf The array to be read into.
878 * @param off The offset.
879 * @param len The length to read.
880 * @exception IOException - possibly thrown by the reads to the
881 * reader objects.
882 */
883 public int read(char[] cbuf, int off, int len)
884 throws IOException {
885
886 int amountRead = 0;
887 while (pos < sourceFiles.size() || (needAddSeparator)) {
888 if (needAddSeparator) {
889 cbuf[off] = eolString.charAt(lastPos++);
890 if (lastPos >= eolString.length()) {
891 lastPos = 0;
892 needAddSeparator = false;
893 pos++;
894 }
895 len--;
896 off++;
897 amountRead++;
898 if (len == 0) {
899 return amountRead;
900 }
901 continue;
902 }
903 int nRead = getReader().read(cbuf, off, len);
904 if (nRead == -1 || nRead == 0) {
905 reader.close();
906 reader = null;
907 if (fixLastLine && isMissingEndOfLine()) {
908 needAddSeparator = true;
909 lastPos = 0;
910 } else {
911 pos++;
912 }
913 } else {
914 if (fixLastLine) {
915 for (int i = nRead;
916 i > (nRead - lastChars.length);
917 --i) {
918 if (i <= 0) {
919 break;
920 }
921 addLastChar(cbuf[off + i - 1]);
922 }
923 }
924 len -= nRead;
925 off += nRead;
926 amountRead += nRead;
927 if (len == 0) {
928 return amountRead;
929 }
930 }
931 }
932 if (amountRead == 0) {
933 return -1;
934 } else {
935 return amountRead;
936 }
937 }
938
939 /**
940 * Close the current reader
941 */
942 public void close() throws IOException {
943 if (reader != null) {
944 reader.close();
945 }
946 }
947 /**
948 * if checking for end of line at end of file
949 * add a character to the lastchars buffer
950 */
951 private void addLastChar(char ch) {
952 for (int i = lastChars.length - 2; i >= 0; --i) {
953 lastChars[i] = lastChars[i + 1];
954 }
955 lastChars[lastChars.length - 1] = ch;
956 }
957
958 /**
959 * return true if the lastchars buffer does
960 * not contain the lineseparator
961 */
962 private boolean isMissingEndOfLine() {
963 for (int i = 0; i < lastChars.length; ++i) {
964 if (lastChars[i] != eolString.charAt(i)) {
965 return true;
966 }
967 }
968 return false;
969 }
970 }
971
972}
973
Note: See TracBrowser for help on using the repository browser.