Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.file / org.gvsig.fmap.dal.file.csv / src / main / java / org / gvsig / fmap / dal / store / csv / virtualrows / RandomAccessFileReader.java @ 45948

History | View | Annotate | Download (34.8 KB)

1
package org.gvsig.fmap.dal.store.csv.virtualrows;
2

    
3
import java.io.BufferedReader;
4
import java.io.File;
5
import java.io.IOException;
6
import java.io.RandomAccessFile;
7
import java.io.Reader;
8
import java.io.UncheckedIOException;
9
import java.nio.ByteBuffer;
10
import java.nio.CharBuffer;
11
import java.nio.channels.Channels;
12
import java.nio.charset.Charset;
13
import java.util.Iterator;
14
import java.util.NoSuchElementException;
15
import java.util.Spliterator;
16
import java.util.Spliterators;
17
import java.util.function.Predicate;
18
import java.util.stream.Stream;
19
import java.util.stream.StreamSupport;
20
import org.apache.commons.io.FilenameUtils;
21
import org.apache.commons.io.IOUtils;
22
import org.gvsig.tools.ToolsLocator;
23
import org.gvsig.tools.i18n.I18nManager;
24
import org.gvsig.tools.library.impl.DefaultLibrariesInitializer;
25
import org.gvsig.tools.observer.Observable;
26
import org.gvsig.tools.task.SimpleTaskStatus;
27
import org.gvsig.tools.task.TaskStatus;
28
import org.gvsig.tools.task.TaskStatusManager;
29

    
30
/**
31
 *
32
 * @author gvSIG Team
33
 */
34
public class RandomAccessFileReader extends Reader {
35

    
36
    public static final Predicate<String> FILTER_NONE = (String t) -> false;
37

    
38
    protected static final int INDEX_HEADER_FILESIZE = 0;
39
    protected static final int INDEX_HEADER_INDEXCREATIONCOST = 1;
40

    
41
    protected RandomAccessFile raf;
42
    protected Reader reader;
43
    protected long currentPosition;
44
    protected final Charset charset;
45
    protected long lastModified;
46

    
47
    public RandomAccessFileReader(File f, String charsetName) throws IOException {
48
        this(new RandomAccessFile(f, "r"), Charset.forName(charsetName));
49
        this.lastModified = f.lastModified();
50
    }
51

    
52
    public RandomAccessFileReader(File f, Charset charset) throws IOException {
53
        this(new RandomAccessFile(f, "r"), charset);
54
        this.lastModified = f.lastModified();
55
    }
56

    
57
    public RandomAccessFileReader(RandomAccessFile raf, String charsetName) throws IOException {
58
        this(raf, Charset.forName(charsetName));
59
        this.lastModified = -1;
60
    }
61

    
62
    public RandomAccessFileReader(RandomAccessFile raf, Charset charset) throws IOException {
63
        this.charset = charset;
64
        this.raf = raf;
65
        this.reader = null;
66
        this.currentPosition = 0;
67
        this.lastModified = -1;
68
    }
69

    
70
    public Charset getCharset() {
71
        return this.charset;
72
    }
73
    
74
    @Override
75
    public int read(char[] cbuf, int off, int len) throws IOException {
76
        if (this.reader == null) {
77
            this.createReader();
78
        }
79
        int n = this.reader.read(cbuf, off, len);
80
        if (n > 0) {
81
            // Update current position (bytes) adding the read characters.
82
            CharBuffer charBuffer = CharBuffer.wrap(cbuf, off, len);
83
            ByteBuffer byteBuffer = this.charset.encode(charBuffer);
84
            this.currentPosition += byteBuffer.limit();
85
        }
86
        return n;
87
    }
88

    
89
    protected void createReader() {
90
        this.reader = Channels.newReader(this.raf.getChannel(), this.charset.name());
91
    }
92

    
93
//    protected InputStream is;
94
//    protected void createReader() {
95
//        if( this.is==null ) {
96
//            this.is = new InputStream() {
97
//                @Override
98
//                public int read() throws IOException {
99
//                    return raf.read();
100
//                }
101
//            };
102
//        }
103
//        this.reader = new InputStreamReader(this.is, charset);
104
//    }
105
    @Override
106
    public void close() throws IOException {
107
//        IOUtils.closeQuietly(this.is);
108
        IOUtils.closeQuietly(this.reader);
109
        IOUtils.closeQuietly(this.raf);
110
    }
111

    
112
    public long getFilePointer() throws IOException {
113
        return this.raf.getFilePointer();
114
    }
115

    
116
    public long getCurrentPosition() {
117
        return this.currentPosition;
118
    }
119

    
120
    public void rewind() throws IOException {
121
        this.raf.seek(0);
122
        this.reader = null;
123
        this.currentPosition = 0;
124
    }
125

    
126
    public void seek(long position) throws IOException {
127
        this.raf.seek(position);
128
        this.reader = null;
129
        this.currentPosition = position;
130
    }
131

    
132
    public String readLine() throws IOException {
133
        StringBuilder buffer = new StringBuilder();
134
        int c = -1;
135
        boolean eol = false;
136

    
137
        while (!eol) {
138
            switch (c = this.read()) {
139
                case -1:
140
                case '\n':
141
                    eol = true;
142
                    break;
143
                case '\r':
144
                    eol = true;
145
                    long cur = raf.getFilePointer();
146
                    if ((raf.read()) != '\n') {
147
                        raf.seek(cur);
148
                    }
149
                    break;
150
                default:
151
                    buffer.append((char) c);
152
                    break;
153
            }
154
        }
155
        if ((c == -1) && (buffer.length() == 0)) {
156
            return null;
157
        }
158
        return buffer.toString();
159
    }
160

    
161
    public long countLines(Predicate<String> filter, SimpleTaskStatus status) throws IOException {
162
        if (raf.length() == 0) {
163
            return 0;
164
        }
165
        long savedpos = this.getCurrentPosition();
166
        long count = -1;
167
        if (status != null) {
168
            I18nManager i18n = ToolsLocator.getI18nManager();
169
            status.message(i18n.getTranslation("_Calculating_number_of_lines"));
170
            status.setIndeterminate();
171
        }
172
        BufferedReader breader = new BufferedReader(this, 10240);
173
        try {
174
            String line;
175
            count = 0;
176
            while ((line = breader.readLine()) != null) {
177
                if (status != null) {
178
                    if (status.isCancellationRequested()) {
179
                        return -1;
180
                    }
181
//                    status.incrementCurrentValue();
182
                    if((count % 1000) == 0){
183
                        status.setCurValue(count);
184
                    }
185
                }
186
                if (filter.test(line)) {
187
                    continue;
188
                }
189
                count++;
190
            }
191
            status.setCurValue(count);
192
            if (status != null) {
193
                status.message("");
194
                status.setIndeterminate();
195
            }
196
        } finally {
197
            this.seek(savedpos);
198
        }
199
        return count;
200
    }
201

    
202
    public boolean isRecomemendedTheRecreationOfTheLinesIndex(File index) {
203
        RandomAccessFileIndex line_idx = null;
204
        try {
205
            if (this.lastModified > 0 && this.lastModified > index.lastModified()) {
206
                return true;
207
            }
208
            line_idx = new RandomAccessFileIndex();
209
            line_idx.open(index);
210
            if (this.raf.length() != line_idx.getHeader(INDEX_HEADER_FILESIZE)) {
211
                return true;
212
            }
213
            long creationCost = line_idx.getHeader(INDEX_HEADER_FILESIZE);
214
            if (creationCost < 2000) { // < 2 sec.
215
                return true;
216
            }
217
            if (line_idx.get(-1) == 0) {
218
                // if last index == 0, index is corrupt
219
                return true;
220
            }
221
//            FIXME: isValidIndexOfLines not full implemented 
222
//            Podria comprobarse que  una muestra de 4 o 5 bloques de datos
223
//            repartidas por el fichero tengan el checksum correcto
224
            return false;
225
        } catch (IOException ex) {
226
            return true;
227
        } finally {
228
            IOUtils.closeQuietly(line_idx);
229
        }
230
    }
231

    
232
    public RandomAccessFileIndex createOrOpenIndexOfLines(File index, Predicate<String> filter, SimpleTaskStatus status) throws IOException {
233
        return this.createOrOpenIndexOfLines(index, false, filter, status);
234
    }
235

    
236
    public RandomAccessFileIndex createOrOpenIndexOfLines(File index, boolean safe, Predicate<String> filter, SimpleTaskStatus status) throws IOException {
237
        if (this.isRecomemendedTheRecreationOfTheLinesIndex(index)) {
238
            return this.createIndexOfLines(index, safe, filter, status);
239
        }
240
        return new RandomAccessFileIndex(index);
241
    }
242

    
243
    public RandomAccessFileIndex createIndexOfLines(File index, Predicate<String> filter, SimpleTaskStatus status) throws IOException {
244
        return this.createIndexOfLines(index, false, filter, status);
245
    }
246

    
247
    public RandomAccessFileIndex createIndexOfLines(File index, boolean safe, Predicate<String> filter, SimpleTaskStatus status) throws IOException {
248
        long countLines = this.countLines(filter, status);
249
        if (countLines < 1) {
250
            return null;
251
        }
252
        RandomAccessFileIndex line_idx = new RandomAccessFileIndex();
253
        line_idx.create(index, countLines);
254

    
255
        long savedpos = this.getCurrentPosition();
256
        try {
257
            if (status != null) {
258
                I18nManager i18n = ToolsLocator.getI18nManager();
259
                status.push();
260
                status.message(i18n.getTranslation("_Creating_the_index_of_the_lines"));
261
                status.setRangeOfValues(0, line_idx.size64());
262
                status.setCurValue(0);
263
            }
264
            long t1 = System.currentTimeMillis();
265
            String line;
266
            int lineno = 0;
267
            long position = 0;
268
//            line_idx.set(lineno++, position);
269
            if (safe) {
270
                // Don't use buffered reader, slow and safe calculate position
271
                int x = (int) (countLines / 100);
272
                while (lineno < countLines) { //true ) {
273
                    line = this.readLine();
274
                    if (line == null) {
275
                        break;
276
                    }
277
                    if (filter.test(line)) {
278
                        continue;
279
                    }
280
                    line_idx.set(lineno++, position);
281
                    if (status != null) {
282
                        if (status.isCancellationRequested()) {
283
                            status.cancel();
284
                            return null;
285
                        }
286
//                        status.incrementCurrentValue();
287
                        if((lineno % x) == 0){
288
                            status.setCurValue(lineno);
289
                        }
290
                    }
291
                    position = this.getCurrentPosition();
292
//                    line_idx.set(lineno++, position);
293
                }
294
                status.setCurValue(lineno);
295
            } else {
296
                // Use buffered reader, fast and unsafe calculate position.
297
                while (lineno < countLines) {
298
                    this.seek(position);
299
                    MyBufferedReader breader = new MyBufferedReader(this, 10240);
300
                    line = breader.readLine();
301
                    if (line == null) {
302
                        break;
303
                    }
304
                    if (filter.test(line)) {
305
                        continue;
306
                    }
307
                    line_idx.set(lineno++, position);
308
                    if (status != null) {
309
                        if (status.isCancellationRequested()) {
310
                            status.cancel();
311
                            return null;
312
                        }
313
                        status.incrementCurrentValue();
314
                    }
315
                    CharBuffer charBuffer = null;
316
                    // ? Y si hay un \r\n ?
317
                    if(breader.isSkipLf()){
318
                        charBuffer = CharBuffer.wrap(line + "\r\n");
319
                    } else {
320
                        charBuffer = CharBuffer.wrap(line + "\n");
321
                    }
322
                    ByteBuffer byteBuffer = this.charset.encode(charBuffer);
323
                    position += byteBuffer.limit();
324

    
325
//                    line_idx.set(lineno++, position);
326
                }
327
            }
328
            long t2 = System.currentTimeMillis();
329
            line_idx.setHeader(INDEX_HEADER_FILESIZE, this.raf.length());
330
            line_idx.setHeader(INDEX_HEADER_INDEXCREATIONCOST, t2 - t1);
331
            if (status != null) {
332
                status.message("");
333
                status.pop();
334
            }
335
            return line_idx;
336
        } finally {
337
            this.seek(savedpos);
338
        }
339
    }
340

    
341
    public long getLastLinesIndexCreationCost(RandomAccessFileIndex index) {
342
        return index.getHeader(INDEX_HEADER_INDEXCREATIONCOST);
343
    }
344

    
345
    public static void main(String[] args) throws Exception {
346
        new DefaultLibrariesInitializer().fullInitialize();
347

    
348
        String fname;
349
        fname = "/home/jjdelcerro/Descargas/test/origen_coordenadas.csv";
350
//        fname = "/home/jjdelcerro/Descargas/test/esp_poblaciones.csv";
351
//        fname = "/home/jjdelcerro/Descargas/test/esp_provincias.csv";
352
//        fname = "/home/jjdelcerro/Descargas/test/sigpac.csv";
353

    
354
        File data_file = new File(fname);
355
        File idx_file = new File(FilenameUtils.removeExtension(data_file.getAbsolutePath()) + ".idx");
356

    
357
        final TaskStatusManager taskStatusManager = ToolsLocator.getTaskStatusManager();
358
        taskStatusManager.addObserver((Observable observable, Object notification) -> {
359
            TaskStatus status = taskStatusManager.getRunningTaskStatusMostRecent();
360
//            System.out.print("\033[?25l\r");
361
//            if( status!=null && status.isRunning() ) {
362
//                System.out.print("\033[?25l\r");
363
//                System.out.print(status.getTitle()+ " - " + status.getLabel());
364
//                System.out.print("\033[K\033[?12l\033[?25h");
365
//            }
366
//            System.out.flush();
367
        });
368
        SimpleTaskStatus status = taskStatusManager.createDefaultSimpleTaskStatus(data_file.getName());
369
        status.add();
370

    
371
        RandomAccessFileReader reader = new RandomAccessFileReader(data_file, "UTF-8");
372
        System.out.println("Index '" + idx_file.getName() + "', is creation recomended: " + reader.isRecomemendedTheRecreationOfTheLinesIndex(idx_file));
373
        RandomAccessFileIndex lines_idx = reader.createOrOpenIndexOfLines(idx_file, FILTER_NONE, status);
374

    
375
        for (int linenumber = 0; linenumber < lines_idx.size(); linenumber++) {
376
            long lineoffset = lines_idx.get(linenumber);
377
            reader.seek(lineoffset);
378
            MyBufferedReader breader = new MyBufferedReader(reader, 10240);
379
            String line = breader.readLine();
380
            if (linenumber < 100) {
381
                System.out.println(String.format("%6d/%d: %s", lineoffset, linenumber, line));
382
            } else if (linenumber == 100) {
383
                System.out.println("More records...");
384
            }
385
        }
386

    
387
        System.out.println("------------------------------------");
388

    
389
        for (int linenumber = lines_idx.size() - 1; linenumber >= 0; linenumber--) {
390
            long lineoffset = lines_idx.get(linenumber);
391
            reader.seek(lineoffset);
392
            MyBufferedReader breader = new MyBufferedReader(reader, 10240);
393
            String line = breader.readLine();
394
            if (linenumber < 100) {
395
                System.out.println(String.format("%6d/%d: %s", lineoffset, linenumber, line));
396
            } else if (linenumber == 100) {
397
                System.out.println("More records...");
398
            }
399
        }
400

    
401
    }
402

    
403
    public static class MyBufferedReader extends BufferedReader {
404

    
405
        private Reader in;
406

    
407
        private char cb[];
408
        private int nChars, nextChar;
409

    
410
        private static final int INVALIDATED = -2;
411
        private static final int UNMARKED = -1;
412
        private int markedChar = UNMARKED;
413
        private int readAheadLimit = 0;
414
        /* Valid only when markedChar > 0 */
415

    
416
        /**
417
         * If the next character is a line feed, skip it
418
         */
419
        private boolean skipLF = false;
420

    
421
        /**
422
         * The skipLF flag when the mark was set
423
         */
424
        private boolean markedSkipLF = false;
425

    
426
        private static int defaultCharBufferSize = 8192;
427
        private static int defaultExpectedLineLength = 80;
428

    
429
        /**
430
         * Creates a buffering character-input stream that uses an input buffer
431
         * of the specified size.
432
         *
433
         * @param in A Reader
434
         * @param sz Input-buffer size
435
         *
436
         * @exception IllegalArgumentException If {@code sz <= 0}
437
         */
438
        public MyBufferedReader(Reader in, int sz) {
439
            super(in);
440
            if (sz <= 0) {
441
                throw new IllegalArgumentException("Buffer size <= 0");
442
            }
443
            this.in = in;
444
            cb = new char[sz];
445
            nextChar = nChars = 0;
446
        }
447

    
448
        /**
449
         * Creates a buffering character-input stream that uses a default-sized
450
         * input buffer.
451
         *
452
         * @param in A Reader
453
         */
454
        public MyBufferedReader(Reader in) {
455
            this(in, defaultCharBufferSize);
456
        }
457

    
458
        /**
459
         * Checks to make sure that the stream has not been closed
460
         */
461
        private void ensureOpen() throws IOException {
462
            if (in == null) {
463
                throw new IOException("Stream closed");
464
            }
465
        }
466

    
467
        /**
468
         * Fills the input buffer, taking the mark into account if it is valid.
469
         */
470
        private void fill() throws IOException {
471
            int dst;
472
            if (markedChar <= UNMARKED) {
473
                /* No mark */
474
                dst = 0;
475
            } else {
476
                /* Marked */
477
                int delta = nextChar - markedChar;
478
                if (delta >= readAheadLimit) {
479
                    /* Gone past read-ahead limit: Invalidate mark */
480
                    markedChar = INVALIDATED;
481
                    readAheadLimit = 0;
482
                    dst = 0;
483
                } else {
484
                    if (readAheadLimit <= cb.length) {
485
                        /* Shuffle in the current buffer */
486
                        System.arraycopy(cb, markedChar, cb, 0, delta);
487
                        markedChar = 0;
488
                        dst = delta;
489
                    } else {
490
                        /* Reallocate buffer to accommodate read-ahead limit */
491
                        char ncb[] = new char[readAheadLimit];
492
                        System.arraycopy(cb, markedChar, ncb, 0, delta);
493
                        cb = ncb;
494
                        markedChar = 0;
495
                        dst = delta;
496
                    }
497
                    nextChar = nChars = delta;
498
                }
499
            }
500

    
501
            int n;
502
            do {
503
                n = in.read(cb, dst, cb.length - dst);
504
            } while (n == 0);
505
            if (n > 0) {
506
                nChars = dst + n;
507
                nextChar = dst;
508
            }
509
        }
510

    
511
        /**
512
         * Reads a single character.
513
         *
514
         * @return The character read, as an integer in the range 0 to 65535
515
         * (<tt>0x00-0xffff</tt>), or -1 if the end of the stream has been
516
         * reached
517
         * @exception IOException If an I/O error occurs
518
         */
519
        public int read() throws IOException {
520
            synchronized (lock) {
521
                ensureOpen();
522
                for (;;) {
523
                    if (nextChar >= nChars) {
524
                        fill();
525
                        if (nextChar >= nChars) {
526
                            return -1;
527
                        }
528
                    }
529
                    if (skipLF) {
530
                        skipLF = false;
531
                        if (cb[nextChar] == '\n') {
532
                            nextChar++;
533
                            continue;
534
                        }
535
                    }
536
                    return cb[nextChar++];
537
                }
538
            }
539
        }
540

    
541
        /**
542
         * Reads characters into a portion of an array, reading from the
543
         * underlying stream if necessary.
544
         */
545
        private int read1(char[] cbuf, int off, int len) throws IOException {
546
            if (nextChar >= nChars) {
547
                /* If the requested length is at least as large as the buffer, and
548
               if there is no mark/reset activity, and if line feeds are not
549
               being skipped, do not bother to copy the characters into the
550
               local buffer.  In this way buffered streams will cascade
551
               harmlessly. */
552
                if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
553
                    return in.read(cbuf, off, len);
554
                }
555
                fill();
556
            }
557
            if (nextChar >= nChars) {
558
                return -1;
559
            }
560
            if (skipLF) {
561
                skipLF = false;
562
                if (cb[nextChar] == '\n') {
563
                    nextChar++;
564
                    if (nextChar >= nChars) {
565
                        fill();
566
                    }
567
                    if (nextChar >= nChars) {
568
                        return -1;
569
                    }
570
                }
571
            }
572
            int n = Math.min(len, nChars - nextChar);
573
            System.arraycopy(cb, nextChar, cbuf, off, n);
574
            nextChar += n;
575
            return n;
576
        }
577

    
578
        /**
579
         * Reads characters into a portion of an array.
580
         *
581
         * <p>
582
         * This method implements the general contract of the corresponding
583
         * <code>{@link Reader#read(char[], int, int) read}</code> method of the
584
         * <code>{@link Reader}</code> class. As an additional convenience, it
585
         * attempts to read as many characters as possible by repeatedly
586
         * invoking the <code>read</code> method of the underlying stream. This
587
         * iterated <code>read</code> continues until one of the following
588
         * conditions becomes true: <ul>
589
         *
590
         * <li> The specified number of characters have been read,
591
         *
592
         * <li> The <code>read</code> method of the underlying stream returns
593
         * <code>-1</code>, indicating end-of-file, or
594
         *
595
         * <li> The <code>ready</code> method of the underlying stream returns
596
         * <code>false</code>, indicating that further input requests would
597
         * block.
598
         *
599
         * </ul> If the first <code>read</code> on the underlying stream returns
600
         * <code>-1</code> to indicate end-of-file then this method returns
601
         * <code>-1</code>. Otherwise this method returns the number of
602
         * characters actually read.
603
         *
604
         * <p>
605
         * Subclasses of this class are encouraged, but not required, to attempt
606
         * to read as many characters as possible in the same fashion.
607
         *
608
         * <p>
609
         * Ordinarily this method takes characters from this stream's character
610
         * buffer, filling it from the underlying stream as necessary. If,
611
         * however, the buffer is empty, the mark is not valid, and the
612
         * requested length is at least as large as the buffer, then this method
613
         * will read characters directly from the underlying stream into the
614
         * given array. Thus redundant <code>BufferedReader</code>s will not
615
         * copy data unnecessarily.
616
         *
617
         * @param cbuf Destination buffer
618
         * @param off Offset at which to start storing characters
619
         * @param len Maximum number of characters to read
620
         *
621
         * @return The number of characters read, or -1 if the end of the stream
622
         * has been reached
623
         *
624
         * @exception IOException If an I/O error occurs
625
         */
626
        public int read(char cbuf[], int off, int len) throws IOException {
627
            synchronized (lock) {
628
                ensureOpen();
629
                if ((off < 0) || (off > cbuf.length) || (len < 0)
630
                        || ((off + len) > cbuf.length) || ((off + len) < 0)) {
631
                    throw new IndexOutOfBoundsException();
632
                } else if (len == 0) {
633
                    return 0;
634
                }
635

    
636
                int n = read1(cbuf, off, len);
637
                if (n <= 0) {
638
                    return n;
639
                }
640
                while ((n < len) && in.ready()) {
641
                    int n1 = read1(cbuf, off + n, len - n);
642
                    if (n1 <= 0) {
643
                        break;
644
                    }
645
                    n += n1;
646
                }
647
                return n;
648
            }
649
        }
650

    
651
        /**
652
         * Reads a line of text. A line is considered to be terminated by any
653
         * one of a line feed ('\n'), a carriage return ('\r'), or a carriage
654
         * return followed immediately by a linefeed.
655
         *
656
         * @param ignoreLF If true, the next '\n' will be skipped
657
         *
658
         * @return A String containing the contents of the line, not including
659
         * any line-termination characters, or null if the end of the stream has
660
         * been reached
661
         *
662
         * @see java.io.LineNumberReader#readLine()
663
         *
664
         * @exception IOException If an I/O error occurs
665
         */
666
        String readLine(boolean ignoreLF) throws IOException {
667
            StringBuilder s = null;
668
            int startChar;
669

    
670
            synchronized (lock) {
671
                ensureOpen();
672
                boolean omitLF = ignoreLF || skipLF;
673

    
674
                bufferLoop:
675
                for (;;) {
676

    
677
                    if (nextChar >= nChars) {
678
                        fill();
679
                    }
680
                    if (nextChar >= nChars) {
681
                        /* EOF */
682
                        if (s != null && s.length() > 0) {
683
                            return s.toString();
684
                        } else {
685
                            return null;
686
                        }
687
                    }
688
                    boolean eol = false;
689
                    char c = 0;
690
                    int i;
691

    
692
                    /* Skip a leftover '\n', if necessary */
693
                    if (omitLF && (cb[nextChar] == '\n')) {
694
                        nextChar++;
695
                    }
696
                    skipLF = false;
697
                    omitLF = false;
698

    
699
                    charLoop:
700
                    for (i = nextChar; i < nChars; i++) {
701
                        c = cb[i];
702
                        if ((c == '\n') || (c == '\r')) {
703
                            eol = true;
704
                            break charLoop;
705
                        }
706
                    }
707

    
708
                    startChar = nextChar;
709
                    nextChar = i;
710

    
711
                    if (eol) {
712
                        String str;
713
                        if (s == null) {
714
                            str = new String(cb, startChar, i - startChar);
715
                        } else {
716
                            s.append(cb, startChar, i - startChar);
717
                            str = s.toString();
718
                        }
719
                        nextChar++;
720
                        if (c == '\r') {
721
                            skipLF = true;
722
                        }
723
                        return str;
724
                    }
725

    
726
                    if (s == null) {
727
                        s = new StringBuilder(defaultExpectedLineLength);
728
                    }
729
                    s.append(cb, startChar, i - startChar);
730
                }
731
            }
732
        }
733

    
734
        /**
735
         * Reads a line of text. A line is considered to be terminated by any
736
         * one of a line feed ('\n'), a carriage return ('\r'), or a carriage
737
         * return followed immediately by a linefeed.
738
         *
739
         * @return A String containing the contents of the line, not including
740
         * any line-termination characters, or null if the end of the stream has
741
         * been reached
742
         *
743
         * @exception IOException If an I/O error occurs
744
         *
745
         * @see java.nio.file.Files#readAllLines
746
         */
747
        public String readLine() throws IOException {
748
            return readLine(false);
749
        }
750

    
751
        /**
752
         * Skips characters.
753
         *
754
         * @param n The number of characters to skip
755
         *
756
         * @return The number of characters actually skipped
757
         *
758
         * @exception IllegalArgumentException If <code>n</code> is negative.
759
         * @exception IOException If an I/O error occurs
760
         */
761
        public long skip(long n) throws IOException {
762
            if (n < 0L) {
763
                throw new IllegalArgumentException("skip value is negative");
764
            }
765
            synchronized (lock) {
766
                ensureOpen();
767
                long r = n;
768
                while (r > 0) {
769
                    if (nextChar >= nChars) {
770
                        fill();
771
                    }
772
                    if (nextChar >= nChars) /* EOF */ {
773
                        break;
774
                    }
775
                    if (skipLF) {
776
                        skipLF = false;
777
                        if (cb[nextChar] == '\n') {
778
                            nextChar++;
779
                        }
780
                    }
781
                    long d = nChars - nextChar;
782
                    if (r <= d) {
783
                        nextChar += r;
784
                        r = 0;
785
                        break;
786
                    } else {
787
                        r -= d;
788
                        nextChar = nChars;
789
                    }
790
                }
791
                return n - r;
792
            }
793
        }
794

    
795
        /**
796
         * Tells whether this stream is ready to be read. A buffered character
797
         * stream is ready if the buffer is not empty, or if the underlying
798
         * character stream is ready.
799
         *
800
         * @exception IOException If an I/O error occurs
801
         */
802
        public boolean ready() throws IOException {
803
            synchronized (lock) {
804
                ensureOpen();
805

    
806
                /*
807
             * If newline needs to be skipped and the next char to be read
808
             * is a newline character, then just skip it right away.
809
                 */
810
                if (skipLF) {
811
                    /* Note that in.ready() will return true if and only if the next
812
                 * read on the stream will not block.
813
                     */
814
                    if (nextChar >= nChars && in.ready()) {
815
                        fill();
816
                    }
817
                    if (nextChar < nChars) {
818
                        if (cb[nextChar] == '\n') {
819
                            nextChar++;
820
                        }
821
                        skipLF = false;
822
                    }
823
                }
824
                return (nextChar < nChars) || in.ready();
825
            }
826
        }
827

    
828
        /**
829
         * Tells whether this stream supports the mark() operation, which it
830
         * does.
831
         */
832
        public boolean markSupported() {
833
            return true;
834
        }
835

    
836
        /**
837
         * Marks the present position in the stream. Subsequent calls to reset()
838
         * will attempt to reposition the stream to this point.
839
         *
840
         * @param readAheadLimit Limit on the number of characters that may be
841
         * read while still preserving the mark. An attempt to reset the stream
842
         * after reading characters up to this limit or beyond may fail. A limit
843
         * value larger than the size of the input buffer will cause a new
844
         * buffer to be allocated whose size is no smaller than limit. Therefore
845
         * large values should be used with care.
846
         *
847
         * @exception IllegalArgumentException If {@code readAheadLimit < 0}
848
         * @exception IOException If an I/O error occurs
849
         */
850
        public void mark(int readAheadLimit) throws IOException {
851
            if (readAheadLimit < 0) {
852
                throw new IllegalArgumentException("Read-ahead limit < 0");
853
            }
854
            synchronized (lock) {
855
                ensureOpen();
856
                this.readAheadLimit = readAheadLimit;
857
                markedChar = nextChar;
858
                markedSkipLF = skipLF;
859
            }
860
        }
861

    
862
        /**
863
         * Resets the stream to the most recent mark.
864
         *
865
         * @exception IOException If the stream has never been marked, or if the
866
         * mark has been invalidated
867
         */
868
        public void reset() throws IOException {
869
            synchronized (lock) {
870
                ensureOpen();
871
                if (markedChar < 0) {
872
                    throw new IOException((markedChar == INVALIDATED)
873
                            ? "Mark invalid"
874
                            : "Stream not marked");
875
                }
876
                nextChar = markedChar;
877
                skipLF = markedSkipLF;
878
            }
879
        }
880

    
881
        public void close() throws IOException {
882
            synchronized (lock) {
883
                if (in == null) {
884
                    return;
885
                }
886
                try {
887
                    in.close();
888
                } finally {
889
                    in = null;
890
                    cb = null;
891
                }
892
            }
893
        }
894

    
895
        /**
896
         * Returns a {@code Stream}, the elements of which are lines read from
897
         * this {@code BufferedReader}. The {@link Stream} is lazily populated,
898
         * i.e., read only occurs during the
899
         * <a href="../util/stream/package-summary.html#StreamOps">terminal
900
         * stream operation</a>.
901
         *
902
         * <p>
903
         * The reader must not be operated on during the execution of the
904
         * terminal stream operation. Otherwise, the result of the terminal
905
         * stream operation is undefined.
906
         *
907
         * <p>
908
         * After execution of the terminal stream operation there are no
909
         * guarantees that the reader will be at a specific position from which
910
         * to read the next character or line.
911
         *
912
         * <p>
913
         * If an {@link IOException} is thrown when accessing the underlying
914
         * {@code BufferedReader}, it is wrapped in an {@link
915
         * UncheckedIOException} which will be thrown from the {@code Stream}
916
         * method that caused the read to take place. This method will return a
917
         * Stream if invoked on a BufferedReader that is closed. Any operation
918
         * on that stream that requires reading from the BufferedReader after it
919
         * is closed, will cause an UncheckedIOException to be thrown.
920
         *
921
         * @return a {@code Stream<String>} providing the lines of text
922
         * described by this {@code BufferedReader}
923
         *
924
         * @since 1.8
925
         */
926
        public Stream<String> lines() {
927
            Iterator<String> iter = new Iterator<String>() {
928
                String nextLine = null;
929

    
930
                @Override
931
                public boolean hasNext() {
932
                    if (nextLine != null) {
933
                        return true;
934
                    } else {
935
                        try {
936
                            nextLine = readLine();
937
                            return (nextLine != null);
938
                        } catch (IOException e) {
939
                            throw new UncheckedIOException(e);
940
                        }
941
                    }
942
                }
943

    
944
                @Override
945
                public String next() {
946
                    if (nextLine != null || hasNext()) {
947
                        String line = nextLine;
948
                        nextLine = null;
949
                        return line;
950
                    } else {
951
                        throw new NoSuchElementException();
952
                    }
953
                }
954
            };
955
            return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
956
                    iter, Spliterator.ORDERED | Spliterator.NONNULL), false);
957
        }
958
        
959
        public boolean isSkipLf() {
960
            return this.skipLF;
961
        }
962

    
963
    }
964

    
965
}