Coverage Report - org.yaml.snakeyaml.reader.StreamReader
 
Classes in this File Line Coverage Branch Coverage Complexity
StreamReader
100%
82/82
97%
45/46
2.75
 
 1  
 /**
 2  
  * Copyright (c) 2008-2011, http://www.snakeyaml.org
 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  
 package org.yaml.snakeyaml.reader;
 18  
 
 19  
 import java.io.IOException;
 20  
 import java.io.Reader;
 21  
 import java.nio.charset.Charset;
 22  
 import java.util.regex.Matcher;
 23  
 import java.util.regex.Pattern;
 24  
 
 25  
 import org.yaml.snakeyaml.error.Mark;
 26  
 import org.yaml.snakeyaml.error.YAMLException;
 27  
 import org.yaml.snakeyaml.scanner.Constant;
 28  
 
 29  
 /**
 30  
  * Reader: checks if characters are in allowed range, adds '\0' to the end.
 31  
  */
 32  
 public class StreamReader {
 33  
     // NON_PRINTABLE changed from PyYAML: \uFFFD excluded because Java returns
 34  
     // it in case of data corruption
 35  1
     final static Pattern NON_PRINTABLE = Pattern
 36  
             .compile("[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFC]");
 37  
     private String name;
 38  
     private final Reader stream;
 39  4073
     private int pointer = 0;
 40  4073
     private boolean eof = true;
 41  
     private String buffer;
 42  4073
     private int index = 0;
 43  4073
     private int line = 0;
 44  4073
     private int column = 0;
 45  
     private char[] data;
 46  
 
 47  2619
     public StreamReader(String stream) {
 48  2619
         this.name = "<string>";
 49  2619
         this.buffer = ""; // to set length to 0
 50  2619
         checkPrintable(stream);
 51  2618
         this.buffer = stream + "\0";
 52  2618
         this.stream = null;
 53  2618
         this.eof = true;
 54  2618
         this.data = null;
 55  2618
     }
 56  
 
 57  1454
     public StreamReader(Reader reader) {
 58  1454
         this.name = "<reader>";
 59  1454
         this.buffer = "";
 60  1454
         this.stream = reader;
 61  1454
         this.eof = false;
 62  1454
         this.data = new char[1024];
 63  1454
         this.update();
 64  1452
     }
 65  
 
 66  
     void checkPrintable(CharSequence data) {
 67  2620
         Matcher em = NON_PRINTABLE.matcher(data);
 68  2620
         if (em.find()) {
 69  1
             int position = this.index + this.buffer.length() - this.pointer + em.start();
 70  1
             throw new ReaderException(name, position, em.group().charAt(0),
 71  
                     "special characters are not allowed");
 72  
         }
 73  2619
     }
 74  
 
 75  
     /**
 76  
      * Checks <code>chars</chars> for the non-printable characters.
 77  
      * 
 78  
      * @param chars
 79  
      *            the array where to search.
 80  
      * @param begin
 81  
      *            the beginning index, inclusive.
 82  
      * @param end
 83  
      *            the ending index, exclusive.
 84  
      * @throws ReaderException
 85  
      *             if <code>chars</code> contains non-printable character(s).
 86  
      */
 87  
     void checkPrintable(final char[] chars, final int begin, final int end) {
 88  1758039
         for (int i = begin; i < end; i++) {
 89  1691728
             final char c = chars[i];
 90  
 
 91  1691728
             if ((c >= '\u0020' && c <= '\u007E') || c == '\n' || c == '\r' || c == '\t'
 92  
                     || c == '\u0085' || (c >= '\u00A0' && c <= '\uD7FF')
 93  
                     || (c >= '\uE000' && c <= '\uFFFC')) {
 94  8189
                 continue;
 95  
             }
 96  
 
 97  2115
             int position = this.index + this.buffer.length() - this.pointer + i;
 98  2115
             throw new ReaderException(name, position, c, "special characters are not allowed");
 99  
         }
 100  66311
     }
 101  
 
 102  
     public Mark getMark() {
 103  1578792
         return new Mark(name, this.index, this.line, this.column, this.buffer, this.pointer);
 104  
     }
 105  
 
 106  
     public void forward() {
 107  1409292
         forward(1);
 108  1409289
     }
 109  
 
 110  
     /**
 111  
      * read the next length characters and move the pointer.
 112  
      * 
 113  
      * @param length
 114  
      */
 115  
     public void forward(int length) {
 116  1796236
         if (this.pointer + length + 1 >= this.buffer.length()) {
 117  5666
             update();
 118  
         }
 119  1796233
         char ch = 0;
 120  3774345
         for (int i = 0; i < length; i++) {
 121  1978112
             ch = this.buffer.charAt(this.pointer);
 122  1978112
             this.pointer++;
 123  1978112
             this.index++;
 124  1978112
             if (Constant.LINEBR.has(ch) || (ch == '\r' && buffer.charAt(pointer) != '\n')) {
 125  266329
                 this.line++;
 126  266329
                 this.column = 0;
 127  1711783
             } else if (ch != '\uFEFF') {
 128  1711783
                 this.column++;
 129  
             }
 130  
         }
 131  1796233
     }
 132  
 
 133  
     public char peek() {
 134  4262486
         return this.buffer.charAt(this.pointer);
 135  
     }
 136  
 
 137  
     /**
 138  
      * Peek the next index-th character
 139  
      * 
 140  
      * @param index
 141  
      * @return
 142  
      */
 143  
     public char peek(int index) {
 144  3757909
         if (this.pointer + index + 1 > this.buffer.length()) {
 145  497
             update();
 146  
         }
 147  3757909
         return this.buffer.charAt(this.pointer + index);
 148  
     }
 149  
 
 150  
     /**
 151  
      * peek the next length characters
 152  
      * 
 153  
      * @param length
 154  
      * @return
 155  
      */
 156  
     public String prefix(int length) {
 157  687726
         if (this.pointer + length >= this.buffer.length()) {
 158  427
             update();
 159  
         }
 160  687726
         if (this.pointer + length > this.buffer.length()) {
 161  321
             return this.buffer.substring(this.pointer);
 162  
         }
 163  687405
         return this.buffer.substring(this.pointer, this.pointer + length);
 164  
     }
 165  
 
 166  
     /**
 167  
      * prefix(length) immediately followed by forward(length)
 168  
      */
 169  
     public String prefixForward(int length) {
 170  555811
         final String prefix = prefix(length);
 171  555811
         this.pointer += length;
 172  555811
         this.index += length;
 173  
         // prefix never contains new line characters
 174  555811
         this.column += length;
 175  555811
         return prefix;
 176  
     }
 177  
 
 178  
     private void update() {
 179  8044
         if (!this.eof) {
 180  4253
             this.buffer = buffer.substring(this.pointer);
 181  4253
             this.pointer = 0;
 182  
             try {
 183  4253
                 int converted = this.stream.read(data);
 184  4252
                 if (converted > 0) {
 185  
                     /*
 186  
                      * Let's create StringBuilder manually. Anyway str1 + str2
 187  
                      * generates new StringBuilder(str1).append(str2).toSting()
 188  
                      * Giving correct capacity to the constructor prevents
 189  
                      * unnecessary operations in appends.
 190  
                      */
 191  2890
                     checkPrintable(data, 0, converted);
 192  2886
                     this.buffer = new StringBuilder(buffer.length() + converted).append(buffer)
 193  
                             .append(data, 0, converted).toString();
 194  
                 } else {
 195  1362
                     this.eof = true;
 196  1362
                     this.buffer += "\0";
 197  
                 }
 198  1
             } catch (IOException ioe) {
 199  1
                 throw new YAMLException(ioe);
 200  4248
             }
 201  
         }
 202  8039
     }
 203  
 
 204  
     public int getColumn() {
 205  1622553
         return column;
 206  
     }
 207  
 
 208  
     public Charset getEncoding() {
 209  4
         return Charset.forName(((UnicodeReader) this.stream).getEncoding());
 210  
     }
 211  
 
 212  
     public int getIndex() {
 213  1392914
         return index;
 214  
     }
 215  
 
 216  
     public int getLine() {
 217  846960
         return line;
 218  
     }
 219  
 }