source: josm/trunk/src/org/openstreetmap/josm/data/preferences/PreferencesReader.java@ 10087

Last change on this file since 10087 was 9978, checked in by Don-vip, 8 years ago

sonar - remove useless initializations

  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.preferences;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.BufferedReader;
7import java.io.File;
8import java.io.IOException;
9import java.io.InputStream;
10import java.io.Reader;
11import java.nio.charset.StandardCharsets;
12import java.nio.file.Files;
13import java.util.ArrayList;
14import java.util.Collections;
15import java.util.LinkedHashMap;
16import java.util.List;
17import java.util.Map;
18import java.util.SortedMap;
19import java.util.TreeMap;
20
21import javax.xml.XMLConstants;
22import javax.xml.stream.XMLInputFactory;
23import javax.xml.stream.XMLStreamConstants;
24import javax.xml.stream.XMLStreamException;
25import javax.xml.stream.XMLStreamReader;
26import javax.xml.transform.stream.StreamSource;
27import javax.xml.validation.Schema;
28import javax.xml.validation.SchemaFactory;
29import javax.xml.validation.Validator;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.io.CachedFile;
33import org.xml.sax.SAXException;
34
35/**
36 * Loads preferences from XML.
37 */
38public class PreferencesReader {
39
40 private static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
41
42 private final SortedMap<String, Setting<?>> settings = new TreeMap<>();
43 private XMLStreamReader parser;
44 private int version;
45 private Reader reader;
46 private File file;
47
48 private final boolean defaults;
49
50 /**
51 * Constructs a new {@code PreferencesReader}.
52 * @param file the file
53 * @param defaults true when reading from the cache file for default preferences,
54 * false for the regular preferences config file
55 * @throws IOException if any I/O error occurs
56 * @throws XMLStreamException if any XML stream error occurs
57 */
58 public PreferencesReader(File file, boolean defaults) throws IOException, XMLStreamException {
59 this.defaults = defaults;
60 this.reader = null;
61 this.file = file;
62 }
63
64 /**
65 * Constructs a new {@code PreferencesReader}.
66 * @param reader the {@link Reader}
67 * @param defaults true when reading from the cache file for default preferences,
68 * false for the regular preferences config file
69 * @throws XMLStreamException if any XML stream error occurs
70 */
71 public PreferencesReader(Reader reader, boolean defaults) throws XMLStreamException {
72 this.defaults = defaults;
73 this.reader = reader;
74 this.file = null;
75 }
76
77 /**
78 * Validate the XML.
79 * @param f the file
80 * @throws IOException if any I/O error occurs
81 * @throws SAXException if any SAX error occurs
82 */
83 public static void validateXML(File f) throws IOException, SAXException {
84 try (BufferedReader in = Files.newBufferedReader(f.toPath(), StandardCharsets.UTF_8)) {
85 validateXML(in);
86 }
87 }
88
89 /**
90 * Validate the XML.
91 * @param in the {@link Reader}
92 * @throws IOException if any I/O error occurs
93 * @throws SAXException if any SAX error occurs
94 */
95 public static void validateXML(Reader in) throws IOException, SAXException {
96 try (CachedFile cf = new CachedFile("resource://data/preferences.xsd"); InputStream xsdStream = cf.getInputStream()) {
97 Schema schema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new StreamSource(xsdStream));
98 Validator validator = schema.newValidator();
99 validator.validate(new StreamSource(in));
100 }
101 }
102
103 /**
104 * Return the parsed preferences as a settings map
105 * @return the parsed preferences as a settings map
106 */
107 public SortedMap<String, Setting<?>> getSettings() {
108 return settings;
109 }
110
111 /**
112 * Return the version from the XML root element.
113 * (Represents the JOSM version when the file was written.)
114 * @return the version
115 */
116 public int getVersion() {
117 return version;
118 }
119
120 public void parse() throws XMLStreamException, IOException {
121 if (reader != null) {
122 this.parser = XMLInputFactory.newInstance().createXMLStreamReader(reader);
123 doParse();
124 } else {
125 try (BufferedReader in = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
126 this.parser = XMLInputFactory.newInstance().createXMLStreamReader(in);
127 doParse();
128 }
129 }
130 }
131
132 private void doParse() throws XMLStreamException {
133 int event = parser.getEventType();
134 while (true) {
135 if (event == XMLStreamConstants.START_ELEMENT) {
136 String topLevelElementName = defaults ? "preferences-defaults" : "preferences";
137 String localName = parser.getLocalName();
138 if (!topLevelElementName.equals(localName)) {
139 throw new XMLStreamException(
140 tr("Expected element ''{0}'', but got ''{1}''", topLevelElementName, localName),
141 parser.getLocation());
142 }
143 try {
144 version = Integer.parseInt(parser.getAttributeValue(null, "version"));
145 } catch (NumberFormatException e) {
146 if (Main.isDebugEnabled()) {
147 Main.debug(e.getMessage());
148 }
149 }
150 parseRoot();
151 } else if (event == XMLStreamConstants.END_ELEMENT) {
152 return;
153 }
154 if (parser.hasNext()) {
155 event = parser.next();
156 } else {
157 break;
158 }
159 }
160 parser.close();
161 }
162
163 private void parseRoot() throws XMLStreamException {
164 while (true) {
165 int event = parser.next();
166 if (event == XMLStreamConstants.START_ELEMENT) {
167 String localName = parser.getLocalName();
168 switch(localName) {
169 case "tag":
170 Setting setting;
171 if (defaults && isNil()) {
172 setting = new StringSetting(null);
173 } else {
174 String value = parser.getAttributeValue(null, "value");
175 if (value == null) {
176 throw new XMLStreamException(tr("value expected"), parser.getLocation());
177 }
178 setting = new StringSetting(value);
179 }
180 if (defaults) {
181 setting.setTime(Math.round(Double.parseDouble(parser.getAttributeValue(null, "time"))));
182 }
183 settings.put(parser.getAttributeValue(null, "key"), setting);
184 jumpToEnd();
185 break;
186 case "list":
187 case "lists":
188 case "maps":
189 parseToplevelList();
190 break;
191 default:
192 throwException("Unexpected element: "+localName);
193 }
194 } else if (event == XMLStreamConstants.END_ELEMENT) {
195 return;
196 }
197 }
198 }
199
200 private void jumpToEnd() throws XMLStreamException {
201 while (true) {
202 int event = parser.next();
203 if (event == XMLStreamConstants.START_ELEMENT) {
204 jumpToEnd();
205 } else if (event == XMLStreamConstants.END_ELEMENT) {
206 return;
207 }
208 }
209 }
210
211 private void parseToplevelList() throws XMLStreamException {
212 String key = parser.getAttributeValue(null, "key");
213 Long time = null;
214 if (defaults) {
215 time = Math.round(Double.parseDouble(parser.getAttributeValue(null, "time")));
216 }
217 String name = parser.getLocalName();
218
219 List<String> entries = null;
220 List<List<String>> lists = null;
221 List<Map<String, String>> maps = null;
222 if (defaults && isNil()) {
223 Setting setting;
224 switch (name) {
225 case "lists":
226 setting = new ListListSetting(null);
227 break;
228 case "maps":
229 setting = new MapListSetting(null);
230 break;
231 default:
232 setting = new ListSetting(null);
233 break;
234 }
235 setting.setTime(time);
236 settings.put(key, setting);
237 jumpToEnd();
238 } else {
239 while (true) {
240 int event = parser.next();
241 if (event == XMLStreamConstants.START_ELEMENT) {
242 String localName = parser.getLocalName();
243 switch(localName) {
244 case "entry":
245 if (entries == null) {
246 entries = new ArrayList<>();
247 }
248 entries.add(parser.getAttributeValue(null, "value"));
249 jumpToEnd();
250 break;
251 case "list":
252 if (lists == null) {
253 lists = new ArrayList<>();
254 }
255 lists.add(parseInnerList());
256 break;
257 case "map":
258 if (maps == null) {
259 maps = new ArrayList<>();
260 }
261 maps.add(parseMap());
262 break;
263 default:
264 throwException("Unexpected element: "+localName);
265 }
266 } else if (event == XMLStreamConstants.END_ELEMENT) {
267 break;
268 }
269 }
270 Setting setting;
271 if (entries != null) {
272 setting = new ListSetting(Collections.unmodifiableList(entries));
273 } else if (lists != null) {
274 setting = new ListListSetting(Collections.unmodifiableList(lists));
275 } else if (maps != null) {
276 setting = new MapListSetting(Collections.unmodifiableList(maps));
277 } else {
278 switch (name) {
279 case "lists":
280 setting = new ListListSetting(Collections.<List<String>>emptyList());
281 break;
282 case "maps":
283 setting = new MapListSetting(Collections.<Map<String, String>>emptyList());
284 break;
285 default:
286 setting = new ListSetting(Collections.<String>emptyList());
287 break;
288 }
289 }
290 if (defaults) {
291 setting.setTime(time);
292 }
293 settings.put(key, setting);
294 }
295 }
296
297 private List<String> parseInnerList() throws XMLStreamException {
298 List<String> entries = new ArrayList<>();
299 while (true) {
300 int event = parser.next();
301 if (event == XMLStreamConstants.START_ELEMENT) {
302 if ("entry".equals(parser.getLocalName())) {
303 entries.add(parser.getAttributeValue(null, "value"));
304 jumpToEnd();
305 } else {
306 throwException("Unexpected element: "+parser.getLocalName());
307 }
308 } else if (event == XMLStreamConstants.END_ELEMENT) {
309 break;
310 }
311 }
312 return Collections.unmodifiableList(entries);
313 }
314
315 private Map<String, String> parseMap() throws XMLStreamException {
316 Map<String, String> map = new LinkedHashMap<>();
317 while (true) {
318 int event = parser.next();
319 if (event == XMLStreamConstants.START_ELEMENT) {
320 if ("tag".equals(parser.getLocalName())) {
321 map.put(parser.getAttributeValue(null, "key"), parser.getAttributeValue(null, "value"));
322 jumpToEnd();
323 } else {
324 throwException("Unexpected element: "+parser.getLocalName());
325 }
326 } else if (event == XMLStreamConstants.END_ELEMENT) {
327 break;
328 }
329 }
330 return Collections.unmodifiableMap(map);
331 }
332
333 /**
334 * Check if the current element is nil (meaning the value of the setting is null).
335 * @return true, if the current element is nil
336 * @see <a href="https://msdn.microsoft.com/en-us/library/2b314yt2(v=vs.85).aspx">Nillable Attribute on MS Developer Network</a>
337 */
338 private boolean isNil() {
339 String nil = parser.getAttributeValue(XSI_NS, "nil");
340 return "true".equals(nil) || "1".equals(nil);
341 }
342
343 /**
344 * Throw RuntimeException with line and column number.
345 *
346 * Only use this for errors that should not be possible after schema validation.
347 * @param msg the error message
348 */
349 private void throwException(String msg) {
350 throw new RuntimeException(msg + tr(" (at line {0}, column {1})",
351 parser.getLocation().getLineNumber(), parser.getLocation().getColumnNumber()));
352 }
353}
Note: See TracBrowser for help on using the repository browser.