Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
InterruptedIOException |
|
| 3.7777777777777777;3.778 | ||||
XmiInputStream |
|
| 3.7777777777777777;3.778 |
1 | /* $Id: XmiInputStream.java 17832 2010-01-12 19:02:29Z linus $ | |
2 | ***************************************************************************** | |
3 | * Copyright (c) 2009 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 | ***************************************************************************** | |
12 | * | |
13 | * Some portions of this file was previously release using the BSD License: | |
14 | */ | |
15 | ||
16 | // Copyright (c) 1996-2007 The Regents of the University of California. All | |
17 | // Rights Reserved. Permission to use, copy, modify, and distribute this | |
18 | // software and its documentation without fee, and without a written | |
19 | // agreement is hereby granted, provided that the above copyright notice | |
20 | // and this paragraph appear in all copies. This software program and | |
21 | // documentation are copyrighted by The Regents of the University of | |
22 | // California. The software program and documentation are supplied "AS | |
23 | // IS", without any accompanying services from The Regents. The Regents | |
24 | // does not warrant that the operation of the program will be | |
25 | // uninterrupted or error-free. The end-user understands that the program | |
26 | // was developed for research purposes and is advised not to rely | |
27 | // exclusively on the program for any reason. IN NO EVENT SHALL THE | |
28 | // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, | |
29 | // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, | |
30 | // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF | |
31 | // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF | |
32 | // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY | |
33 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
34 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE | |
35 | // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF | |
36 | // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, | |
37 | // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
38 | ||
39 | package org.argouml.persistence; | |
40 | ||
41 | import java.io.BufferedInputStream; | |
42 | import java.io.IOException; | |
43 | import java.io.InputStream; | |
44 | import java.util.StringTokenizer; | |
45 | ||
46 | import org.argouml.persistence.AbstractFilePersister.ProgressMgr; | |
47 | ||
48 | /** | |
49 | * A BufferInputStream that is aware of XML structure. | |
50 | * It can search for the first occurrence of a named tag | |
51 | * and reads only the data (inclusively) from that tag | |
52 | * to the matching end tag or it can search for the first | |
53 | * occurrence of a named tag and read on the child tags. | |
54 | * The tag is not expected to be an empty tag. | |
55 | * @author Bob Tarling | |
56 | */ | |
57 | class XmiInputStream extends BufferedInputStream { | |
58 | ||
59 | private String tagName; | |
60 | private String endTagName; | |
61 | private String attributes; | |
62 | private boolean extensionFound; | |
63 | private boolean endFound; | |
64 | private boolean parsingExtension; | |
65 | private boolean readingName; | |
66 | ||
67 | private XmiExtensionParser xmiExtensionParser; | |
68 | ||
69 | private StringBuffer stringBuffer; | |
70 | private String type; | |
71 | ||
72 | /** | |
73 | * The number of bytes to be read between each progress | |
74 | * event. | |
75 | */ | |
76 | private long eventSpacing; | |
77 | ||
78 | /** | |
79 | * The number of characters read so far. | |
80 | */ | |
81 | private long readCount; | |
82 | ||
83 | /** | |
84 | * The expected stream length. | |
85 | */ | |
86 | private ProgressMgr progressMgr; | |
87 | ||
88 | /** | |
89 | * Construct a new XmlInputStream. | |
90 | * | |
91 | * @param inputStream the input stream to wrap. | |
92 | * @param extParser the parser to call to read any | |
93 | * XMI.extension elements | |
94 | * @param spacing the number of characters to read before | |
95 | * firing a progress event. | |
96 | * @param prgrssMgr the progress manager | |
97 | */ | |
98 | public XmiInputStream( | |
99 | InputStream inputStream, | |
100 | XmiExtensionParser extParser, | |
101 | long spacing, | |
102 | ProgressMgr prgrssMgr) { | |
103 | 0 | super(inputStream); |
104 | 0 | eventSpacing = spacing; |
105 | 0 | xmiExtensionParser = extParser; |
106 | 0 | progressMgr = prgrssMgr; |
107 | 0 | } |
108 | ||
109 | ||
110 | /* | |
111 | * @see java.io.InputStream#read() | |
112 | */ | |
113 | @Override | |
114 | public synchronized int read() throws IOException { | |
115 | ||
116 | 0 | if (endFound) { |
117 | 0 | extensionFound = false; |
118 | 0 | parsingExtension = false; |
119 | 0 | endFound = false; |
120 | 0 | readingName = false; |
121 | 0 | tagName = null; |
122 | 0 | endTagName = null; |
123 | } | |
124 | ||
125 | 0 | int ch = super.read(); |
126 | ||
127 | 0 | if (parsingExtension) { |
128 | 0 | stringBuffer.append((char) ch); |
129 | } | |
130 | // else { | |
131 | // TODO: Only progress when reading standard XMI | |
132 | // extension parsers will continue progression. | |
133 | 0 | ++readCount; |
134 | 0 | if (progressMgr != null && readCount == eventSpacing) { |
135 | try { | |
136 | 0 | readCount = 0; |
137 | 0 | progressMgr.nextPhase(); |
138 | 0 | } catch (InterruptedException e) { |
139 | 0 | throw new InterruptedIOException(e); |
140 | 0 | } |
141 | } | |
142 | // } | |
143 | ||
144 | 0 | if (xmiExtensionParser != null) { |
145 | 0 | if (readingName) { |
146 | 0 | if (isNameTerminator((char) ch)) { |
147 | 0 | readingName = false; |
148 | 0 | if (parsingExtension && endTagName == null) { |
149 | 0 | endTagName = "/" + tagName; |
150 | 0 | } else if (tagName.equals("XMI.extension")) { |
151 | 0 | extensionFound = true; |
152 | 0 | } else if (tagName.equals(endTagName)) { |
153 | 0 | endFound = true; |
154 | 0 | xmiExtensionParser.parse(type, stringBuffer.toString()); |
155 | 0 | stringBuffer.delete(0, stringBuffer.length()); |
156 | } | |
157 | } else { | |
158 | 0 | tagName += (char) ch; |
159 | } | |
160 | } | |
161 | ||
162 | 0 | if (extensionFound) { |
163 | 0 | if (ch == '>') { |
164 | 0 | extensionFound = false; |
165 | 0 | callExtensionParser(); |
166 | } else { | |
167 | 0 | attributes += (char) ch; |
168 | } | |
169 | } | |
170 | ||
171 | 0 | if (ch == '<') { |
172 | 0 | readingName = true; |
173 | 0 | tagName = ""; |
174 | } | |
175 | } | |
176 | 0 | return ch; |
177 | } | |
178 | ||
179 | private void callExtensionParser() { | |
180 | 0 | String label = null; |
181 | 0 | String extender = null; |
182 | 0 | for (StringTokenizer st = new StringTokenizer(attributes, " ="); |
183 | 0 | st.hasMoreTokens(); ) { |
184 | 0 | String attributeType = st.nextToken(); |
185 | 0 | if (attributeType.equals("xmi.extender")) { |
186 | 0 | extender = st.nextToken(); |
187 | 0 | extender = extender.substring(1, extender.length() - 1); |
188 | } | |
189 | 0 | if (attributeType.equals("xmi.label")) { |
190 | 0 | label = st.nextToken(); |
191 | 0 | label = label.substring(1, label.length() - 1); |
192 | } | |
193 | 0 | } |
194 | 0 | if ("ArgoUML".equals(extender)) { |
195 | 0 | type = label; |
196 | 0 | stringBuffer = new StringBuffer(); |
197 | 0 | parsingExtension = true; |
198 | 0 | endTagName = null; |
199 | } | |
200 | 0 | } |
201 | ||
202 | /* | |
203 | * @see java.io.InputStream#read(byte[], int, int) | |
204 | */ | |
205 | @Override | |
206 | public synchronized int read(byte[] b, int off, int len) | |
207 | throws IOException { | |
208 | ||
209 | int cnt; | |
210 | 0 | for (cnt = 0; cnt < len; ++cnt) { |
211 | 0 | int read = read(); |
212 | 0 | if (read == -1) { |
213 | 0 | break; |
214 | } | |
215 | 0 | b[cnt + off] = (byte) read; |
216 | } | |
217 | ||
218 | 0 | if (cnt > 0) { |
219 | 0 | return cnt; |
220 | } | |
221 | 0 | return -1; |
222 | } | |
223 | ||
224 | ||
225 | private boolean isNameTerminator(char ch) { | |
226 | 0 | return (ch == '>' || Character.isWhitespace(ch)); |
227 | } | |
228 | ||
229 | /** | |
230 | * The close method is overridden to prevent some class out of | |
231 | * our control from closing the stream (such as a SAX parser). | |
232 | * Use realClose() to finally close the stream for real. | |
233 | * @throws IOException to satisfy ancestor but will never happen. | |
234 | */ | |
235 | @Override | |
236 | public void close() throws IOException { | |
237 | 0 | } |
238 | ||
239 | /** | |
240 | * Really close the input. | |
241 | * | |
242 | * @throws IOException if an I/O error occurs. | |
243 | */ | |
244 | public void realClose() throws IOException { | |
245 | 0 | super.close(); |
246 | 0 | } |
247 | } | |
248 | ||
249 | class InterruptedIOException extends IOException { | |
250 | ||
251 | private static final long serialVersionUID = 5654808047803205851L; | |
252 | ||
253 | private InterruptedException cause; | |
254 | ||
255 | 0 | public InterruptedIOException(InterruptedException theCause) { |
256 | 0 | cause = theCause; |
257 | 0 | } |
258 | ||
259 | public InterruptedException getInterruptedException() { | |
260 | 0 | return cause; |
261 | } | |
262 | } |