##### 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;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 |  |
     }