1 | /* $Id: XmlInputStream.java 19907 2012-12-30 13:06:01Z closettop_nightlybuild $ | |
2 | ***************************************************************************** | |
3 | * Copyright (c) 2009-2012 Contributors - see below | |
4 | * All rights reserved. This program and the accompanying materials | |
5 | * are made available under the terms of the Eclipse Public License v1.0 | |
6 | * which accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: | |
10 | * tfmorris | |
11 | * euluis | |
12 | ***************************************************************************** | |
13 | * | |
14 | * Some portions of this file was previously release using the BSD License: | |
15 | */ | |
16 | ||
17 | // Copyright (c) 1996-2006 The Regents of the University of California. All | |
18 | // Rights Reserved. Permission to use, copy, modify, and distribute this | |
19 | // software and its documentation without fee, and without a written | |
20 | // agreement is hereby granted, provided that the above copyright notice | |
21 | // and this paragraph appear in all copies. This software program and | |
22 | // documentation are copyrighted by The Regents of the University of | |
23 | // California. The software program and documentation are supplied "AS | |
24 | // IS", without any accompanying services from The Regents. The Regents | |
25 | // does not warrant that the operation of the program will be | |
26 | // uninterrupted or error-free. The end-user understands that the program | |
27 | // was developed for research purposes and is advised not to rely | |
28 | // exclusively on the program for any reason. IN NO EVENT SHALL THE | |
29 | // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, | |
30 | // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, | |
31 | // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF | |
32 | // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF | |
33 | // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY | |
34 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
35 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE | |
36 | // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF | |
37 | // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, | |
38 | // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
39 | ||
40 | package org.argouml.persistence; | |
41 | ||
42 | import java.io.BufferedInputStream; | |
43 | import java.io.IOException; | |
44 | import java.io.InputStream; | |
45 | import java.util.HashMap; | |
46 | import java.util.Iterator; | |
47 | import java.util.Map; | |
48 | import java.util.logging.Level; | |
49 | import java.util.logging.Logger; | |
50 | ||
51 | ||
52 | /** | |
53 | * A BufferInputStream that is aware of XML structure. | |
54 | * It searches for the first occurrence of a named tag | |
55 | * and reads only the data (inclusively) from that tag | |
56 | * to the matching end tag or it can search for the first | |
57 | * occurrence of a named tag and read on the child tags. | |
58 | * The tag is not expected to be an empty tag. | |
59 | * <p> | |
60 | * TODO: This is hardwired to assume a fixed single byte | |
61 | * character encoding. It needs to be updated to handle different | |
62 | * encodings, including multi-byte encodings. - tfm 20070607 | |
63 | * | |
64 | * @author Bob Tarling | |
65 | */ | |
66 | class XmlInputStream extends BufferedInputStream { | |
67 | ||
68 | private boolean xmlStarted; | |
69 | private boolean inTag; | |
70 | private StringBuffer currentTag = new StringBuffer(); | |
71 | private boolean endStream; | |
72 | private String tagName; | |
73 | private String endTagName; | |
74 | private Map attributes; | |
75 | private boolean childOnly; | |
76 | private int instanceCount; | |
77 | //private EventListenerList listenerList = new EventListenerList(); | |
78 | ||
79 | /** | |
80 | * Logger. | |
81 | */ | |
82 | private static final Logger LOG = | |
83 | Logger.getLogger(XmlInputStream.class.getName()); | |
84 | ||
85 | /** | |
86 | * Construct a new XmlInputStream. | |
87 | * | |
88 | * @param inStream the input stream to wrap. | |
89 | * @param theTag the tag name from which to start reading | |
90 | * @param theLength the expected length of the input stream | |
91 | * @param theEventSpacing the number of characters to read before | |
92 | * firing a progress event. | |
93 | */ | |
94 | public XmlInputStream( | |
95 | InputStream inStream, | |
96 | String theTag, | |
97 | long theLength, | |
98 | long theEventSpacing) { | |
99 | super(inStream); | |
100 | tagName = theTag; | |
101 | endTagName = '/' + theTag; | |
102 | attributes = null; | |
103 | childOnly = false; | |
104 | } | |
105 | ||
106 | /** | |
107 | * Reopen a stream that has already reached the end | |
108 | * of an XML fragment. | |
109 | * | |
110 | * @param theTag the tag name | |
111 | * @param attribs the attributes | |
112 | * @param child child only | |
113 | */ | |
114 | public synchronized void reopen( | |
115 | String theTag, | |
116 | Map attribs, | |
117 | boolean child) { | |
118 | endStream = false; | |
119 | xmlStarted = false; | |
120 | inTag = false; | |
121 | tagName = theTag; | |
122 | endTagName = '/' + theTag; | |
123 | attributes = attribs; | |
124 | childOnly = child; | |
125 | } | |
126 | ||
127 | /** | |
128 | * Reopen a stream that has already reached the end | |
129 | * of an XML fragment. | |
130 | * | |
131 | * @param theTag the tag name | |
132 | */ | |
133 | public synchronized void reopen(String theTag) { | |
134 | endStream = false; | |
135 | xmlStarted = false; | |
136 | inTag = false; | |
137 | tagName = theTag; | |
138 | endTagName = '/' + theTag; | |
139 | attributes = null; | |
140 | childOnly = false; | |
141 | } | |
142 | ||
143 | /* | |
144 | * @see java.io.InputStream#read() | |
145 | */ | |
146 | public synchronized int read() throws IOException { | |
147 | ||
148 |
1
1. read : negated conditional → NO_COVERAGE |
if (!xmlStarted) { |
149 |
1
1. read : removed call to org/argouml/persistence/XmlInputStream::skipToTag → NO_COVERAGE |
skipToTag(); |
150 | xmlStarted = true; | |
151 | } | |
152 |
1
1. read : negated conditional → NO_COVERAGE |
if (endStream) { |
153 |
1
1. read : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return -1; |
154 | } | |
155 | int ch = super.read(); | |
156 | endStream = isLastTag(ch); | |
157 |
1
1. read : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return ch; |
158 | } | |
159 | ||
160 | /* | |
161 | * @see java.io.InputStream#read(byte[], int, int) | |
162 | */ | |
163 | public synchronized int read(byte[] b, int off, int len) | |
164 | throws IOException { | |
165 | ||
166 |
1
1. read : negated conditional → NO_COVERAGE |
if (!xmlStarted) { |
167 |
1
1. read : removed call to org/argouml/persistence/XmlInputStream::skipToTag → NO_COVERAGE |
skipToTag(); |
168 | xmlStarted = true; | |
169 | } | |
170 |
1
1. read : negated conditional → NO_COVERAGE |
if (endStream) { |
171 |
1
1. read : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return -1; |
172 | } | |
173 | ||
174 | int cnt; | |
175 |
3
1. read : changed conditional boundary → NO_COVERAGE 2. read : Changed increment from 1 to -1 → NO_COVERAGE 3. read : negated conditional → NO_COVERAGE |
for (cnt = 0; cnt < len; ++cnt) { |
176 | int read = read(); | |
177 |
1
1. read : negated conditional → NO_COVERAGE |
if (read == -1) { |
178 | break; | |
179 | } | |
180 |
1
1. read : Replaced integer addition with subtraction → NO_COVERAGE |
b[cnt + off] = (byte) read; |
181 | } | |
182 | ||
183 |
2
1. read : changed conditional boundary → NO_COVERAGE 2. read : negated conditional → NO_COVERAGE |
if (cnt > 0) { |
184 |
1
1. read : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return cnt; |
185 | } | |
186 |
1
1. read : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return -1; |
187 | } | |
188 | ||
189 | ||
190 | ||
191 | /** | |
192 | * Determines if the character is the last character of the last tag of | |
193 | * interest. | |
194 | * Every character read after the first tag of interest should be passed | |
195 | * through this method in order. | |
196 | * | |
197 | * @param ch the character to test. | |
198 | * @return true if this is the end of the last tag. | |
199 | */ | |
200 | private boolean isLastTag(int ch) { | |
201 |
1
1. isLastTag : negated conditional → NO_COVERAGE |
if (ch == '<') { |
202 | inTag = true; | |
203 |
1
1. isLastTag : removed call to java/lang/StringBuffer::setLength → NO_COVERAGE |
currentTag.setLength(0); |
204 |
1
1. isLastTag : negated conditional → NO_COVERAGE |
} else if (ch == '>') { |
205 | inTag = false; | |
206 | String tag = currentTag.toString(); | |
207 |
1
1. isLastTag : negated conditional → NO_COVERAGE |
if (tag.equals(endTagName) |
208 | // TODO: The below is not strictly correct, but should | |
209 | // cover the case we deal with. Using a real XML parser | |
210 | // would be better. | |
211 | // Look for XML document has just a single root element | |
212 |
2
1. isLastTag : Replaced integer subtraction with addition → NO_COVERAGE 2. isLastTag : negated conditional → NO_COVERAGE |
|| (currentTag.charAt(currentTag.length() - 1) == '/' |
213 |
1
1. isLastTag : negated conditional → NO_COVERAGE |
&& tag.startsWith(tagName) |
214 |
1
1. isLastTag : negated conditional → NO_COVERAGE |
&& tag.indexOf(' ') == tagName.indexOf(' '))) { |
215 |
1
1. isLastTag : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return true; |
216 | } | |
217 |
1
1. isLastTag : negated conditional → NO_COVERAGE |
} else if (inTag) { |
218 | currentTag.append((char) ch); | |
219 | } | |
220 |
1
1. isLastTag : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return false; |
221 | } | |
222 | ||
223 | /** | |
224 | * Keep on reading an input stream until a specific | |
225 | * sequence of characters has ben read. | |
226 | * This method assumes there is at least one match. | |
227 | * | |
228 | * @throws IOException | |
229 | */ | |
230 | private void skipToTag() throws IOException { | |
231 | char[] searchChars = tagName.toCharArray(); | |
232 | int i; | |
233 | boolean found; | |
234 | while (true) { | |
235 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (!childOnly) { |
236 |
1
1. skipToTag : removed call to org/argouml/persistence/XmlInputStream::mark → NO_COVERAGE |
mark(1000); |
237 | } | |
238 | // Keep reading till we get the left bracket of an opening tag | |
239 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
while (realRead() != '<') { |
240 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (!childOnly) { |
241 |
1
1. skipToTag : removed call to org/argouml/persistence/XmlInputStream::mark → NO_COVERAGE |
mark(1000); |
242 | } | |
243 | } | |
244 | found = true; | |
245 | // Compare each following character to see | |
246 | // that it matches the tag we want | |
247 |
3
1. skipToTag : changed conditional boundary → NO_COVERAGE 2. skipToTag : Changed increment from 1 to -1 → NO_COVERAGE 3. skipToTag : negated conditional → NO_COVERAGE |
for (i = 0; i < tagName.length(); ++i) { |
248 | int c = realRead(); | |
249 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (c != searchChars[i]) { |
250 | found = false; | |
251 | break; | |
252 | } | |
253 | } | |
254 | int terminator = realRead(); | |
255 | // We also want to match with the right bracket of the tag or | |
256 | // some other terminator | |
257 |
2
1. skipToTag : negated conditional → NO_COVERAGE 2. skipToTag : negated conditional → NO_COVERAGE |
if (found && !isNameTerminator((char) terminator)) { |
258 | found = false; | |
259 | } | |
260 | ||
261 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (found) { |
262 | // We've found the matching tag but do we have | |
263 | // the correct instance with matching attributes? | |
264 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (attributes != null) { |
265 | Map attributesFound = new HashMap(); | |
266 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (terminator != '>') { |
267 | attributesFound = readAttributes(); | |
268 | } | |
269 | // Search all attributes found to those expected. | |
270 | // If any don't match then turn off the found flag | |
271 | // so that we search for the next matching tag. | |
272 | Iterator it = attributes.entrySet().iterator(); | |
273 |
2
1. skipToTag : negated conditional → NO_COVERAGE 2. skipToTag : negated conditional → NO_COVERAGE |
while (found && it.hasNext()) { |
274 | Map.Entry pair = (Map.Entry) it.next(); | |
275 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (!pair.getValue().equals( |
276 | attributesFound.get(pair.getKey()))) { | |
277 | found = false; | |
278 | } | |
279 | } | |
280 | } | |
281 | } | |
282 | ||
283 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (found) { |
284 |
2
1. skipToTag : changed conditional boundary → NO_COVERAGE 2. skipToTag : negated conditional → NO_COVERAGE |
if (instanceCount < 0) { |
285 | found = false; | |
286 |
1
1. skipToTag : Replaced integer addition with subtraction → NO_COVERAGE |
++instanceCount; |
287 | } | |
288 | } | |
289 | ||
290 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (found) { |
291 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
if (childOnly) { |
292 | // Read the name of the child tag | |
293 | // and then reset read position | |
294 | // back to that child tag. | |
295 |
1
1. skipToTag : removed call to org/argouml/persistence/XmlInputStream::mark → NO_COVERAGE |
mark(1000); |
296 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
while (realRead() != '<') { |
297 | /* do nothing */ | |
298 | } | |
299 | tagName = ""; | |
300 | char ch = (char) realRead(); | |
301 |
1
1. skipToTag : negated conditional → NO_COVERAGE |
while (!isNameTerminator(ch)) { |
302 | tagName += ch; | |
303 | ch = (char) realRead(); | |
304 | } | |
305 | endTagName = "/" + tagName; | |
306 | LOG.log(Level.INFO, "Start tag = {0}", tagName); | |
307 | LOG.log(Level.INFO, "End tag = {0}", endTagName); | |
308 | } | |
309 |
1
1. skipToTag : removed call to org/argouml/persistence/XmlInputStream::reset → NO_COVERAGE |
reset(); |
310 | return; | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | private boolean isNameTerminator(char ch) { | |
316 |
4
1. isNameTerminator : negated conditional → NO_COVERAGE 2. isNameTerminator : negated conditional → NO_COVERAGE 3. isNameTerminator : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE 4. isNameTerminator : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return (ch == '>' || Character.isWhitespace(ch)); |
317 | } | |
318 | ||
319 | /** | |
320 | * Having read the inputstream up until the tag name. | |
321 | * This method continues to read the contents of the tag to | |
322 | * retrieve any attribute names and values. | |
323 | * @return a map of name value pairs. | |
324 | * @throws IOException | |
325 | */ | |
326 | private Map readAttributes() throws IOException { | |
327 | Map attributesFound = new HashMap(); | |
328 | int character; | |
329 |
1
1. readAttributes : negated conditional → NO_COVERAGE |
while ((character = realRead()) != '>') { |
330 |
1
1. readAttributes : negated conditional → NO_COVERAGE |
if (!Character.isWhitespace((char) character)) { |
331 | StringBuffer attributeName = new StringBuffer(); | |
332 | attributeName.append((char) character); | |
333 |
2
1. readAttributes : negated conditional → NO_COVERAGE 2. readAttributes : negated conditional → NO_COVERAGE |
while ((character = realRead()) != '=' |
334 | && !Character.isWhitespace((char) character)) { | |
335 | attributeName.append((char) character); | |
336 | } | |
337 | // Skip any whitespace till we should be on an equals sign. | |
338 |
1
1. readAttributes : negated conditional → NO_COVERAGE |
while (Character.isWhitespace((char) character)) { |
339 | character = realRead(); | |
340 | } | |
341 |
1
1. readAttributes : negated conditional → NO_COVERAGE |
if (character != '=') { |
342 | throw new IOException( | |
343 | "Expected = sign after attribute " | |
344 | + attributeName); | |
345 | } | |
346 | // Skip any whitespace till we should be on a quote symbol. | |
347 | int quoteSymbol = realRead(); | |
348 |
1
1. readAttributes : negated conditional → NO_COVERAGE |
while (Character.isWhitespace((char) quoteSymbol)) { |
349 | quoteSymbol = realRead(); | |
350 | } | |
351 |
2
1. readAttributes : negated conditional → NO_COVERAGE 2. readAttributes : negated conditional → NO_COVERAGE |
if (quoteSymbol != '"' && quoteSymbol != '\'') { |
352 | throw new IOException( | |
353 | "Expected \" or ' around attribute value after " | |
354 | + "attribute " + attributeName); | |
355 | } | |
356 | StringBuffer attributeValue = new StringBuffer(); | |
357 |
1
1. readAttributes : negated conditional → NO_COVERAGE |
while ((character = realRead()) != quoteSymbol) { |
358 | attributeValue.append((char) character); | |
359 | } | |
360 | attributesFound.put( | |
361 | attributeName.toString(), | |
362 | attributeValue.toString()); | |
363 | } | |
364 | } | |
365 |
1
1. readAttributes : mutated return of Object value for org/argouml/persistence/XmlInputStream::readAttributes to ( if (x != null) null else throw new RuntimeException ) → NO_COVERAGE |
return attributesFound; |
366 | } | |
367 | ||
368 | ||
369 | ||
370 | /** | |
371 | * The close method is overridden to prevent some class out of | |
372 | * our control from closing the stream (such as a SAX parser). | |
373 | * Use realClose() to finally close the stream for real. | |
374 | * @throws IOException to satisfy ancestor but will never happen. | |
375 | */ | |
376 | public void close() throws IOException { | |
377 | } | |
378 | ||
379 | /** | |
380 | * Really close the input. | |
381 | * | |
382 | * @throws IOException if an I/O error occurs. | |
383 | */ | |
384 | public void realClose() throws IOException { | |
385 |
1
1. realClose : removed call to java/io/BufferedInputStream::close → NO_COVERAGE |
super.close(); |
386 | } | |
387 | ||
388 | private int realRead() throws IOException { | |
389 | int read = super.read(); | |
390 |
1
1. realRead : negated conditional → NO_COVERAGE |
if (read == -1) { |
391 | throw new IOException("Tag " + tagName + " not found"); | |
392 | } | |
393 |
1
1. realRead : replaced return of integer sized value with (x == 0 ? 1 : 0) → NO_COVERAGE |
return read; |
394 | } | |
395 | ||
396 | } | |
Mutations | ||
148 |
1.1 |
|
149 |
1.1 |
|
152 |
1.1 |
|
153 |
1.1 |
|
157 |
1.1 |
|
166 |
1.1 |
|
167 |
1.1 |
|
170 |
1.1 |
|
171 |
1.1 |
|
175 |
1.1 2.2 3.3 |
|
177 |
1.1 |
|
180 |
1.1 |
|
183 |
1.1 2.2 |
|
184 |
1.1 |
|
186 |
1.1 |
|
201 |
1.1 |
|
203 |
1.1 |
|
204 |
1.1 |
|
207 |
1.1 |
|
212 |
1.1 2.2 |
|
213 |
1.1 |
|
214 |
1.1 |
|
215 |
1.1 |
|
217 |
1.1 |
|
220 |
1.1 |
|
235 |
1.1 |
|
236 |
1.1 |
|
239 |
1.1 |
|
240 |
1.1 |
|
241 |
1.1 |
|
247 |
1.1 2.2 3.3 |
|
249 |
1.1 |
|
257 |
1.1 2.2 |
|
261 |
1.1 |
|
264 |
1.1 |
|
266 |
1.1 |
|
273 |
1.1 2.2 |
|
275 |
1.1 |
|
283 |
1.1 |
|
284 |
1.1 2.2 |
|
286 |
1.1 |
|
290 |
1.1 |
|
291 |
1.1 |
|
295 |
1.1 |
|
296 |
1.1 |
|
301 |
1.1 |
|
309 |
1.1 |
|
316 |
1.1 2.2 3.3 4.4 |
|
329 |
1.1 |
|
330 |
1.1 |
|
333 |
1.1 2.2 |
|
338 |
1.1 |
|
341 |
1.1 |
|
348 |
1.1 |
|
351 |
1.1 2.2 |
|
357 |
1.1 |
|
365 |
1.1 |
|
385 |
1.1 |
|
390 |
1.1 |
|
393 |
1.1 |