source: josm/trunk/src/com/kitfox/svg/xml/XMLParseUtil.java@ 17333

Last change on this file since 17333 was 17333, checked in by Don-vip, 3 years ago

see #20129 - Fix typos and misspellings in the code (patch by gaben)

  • Property svn:eol-style set to native
File size: 11.2 KB
Line 
1/*
2 * SVG Salamander
3 * Copyright (c) 2004, Mark McKay
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
9 *
10 * - Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
32 * projects can be found at http://www.kitfox.com
33 *
34 * Created on February 18, 2004, 1:49 PM
35 */
36
37package com.kitfox.svg.xml;
38
39import java.awt.Toolkit;
40import java.lang.reflect.Method;
41import java.util.Collections;
42import java.util.HashMap;
43import java.util.Iterator;
44import java.util.LinkedList;
45import java.util.Map;
46import java.util.logging.Level;
47import java.util.logging.Logger;
48import java.util.regex.Matcher;
49import java.util.regex.Pattern;
50
51import com.kitfox.svg.SVGConst;
52
53/**
54 * @author Mark McKay
55 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
56 */
57public class XMLParseUtil
58{
59 static final Matcher fpMatch = Pattern.compile("([-+]?((\\d*\\.\\d+)|(\\d+))([eE][+-]?\\d+)?)(\\%|in|cm|mm|pt|pc|px|em|ex)?").matcher("");
60 static final Matcher intMatch = Pattern.compile("[-+]?\\d+").matcher("");
61 static final Matcher quoteMatch = Pattern.compile("^'|'$").matcher("");
62
63 /**
64 * A reference to {@link Map#ofEntries(Map.Entry[])} available since Java 9
65 */
66 static final Method mapOfEntries = mapOfEntriesMethod();
67
68 private static Method mapOfEntriesMethod() {
69 try {
70 return Map.class.getMethod("ofEntries", Map.Entry[].class);
71 } catch (NoSuchMethodException e) {
72 return null;
73 }
74 }
75
76 /** Creates a new instance of XMLParseUtil */
77 private XMLParseUtil()
78 {
79 }
80
81 public static String[] parseStringList(String list)
82 {
83 final Matcher matchWs = Pattern.compile("[^\\s]+").matcher("");
84 matchWs.reset(list);
85
86 LinkedList<String> matchList = new LinkedList<>();
87 while (matchWs.find())
88 {
89 matchList.add(matchWs.group());
90 }
91
92 String[] retArr = new String[matchList.size()];
93 return matchList.toArray(retArr);
94 }
95
96 public static double parseDouble(String val)
97 {
98 return findDouble(val);
99 }
100
101 /**
102 * Searches the given string for the first floating point number it contains,
103 * parses and returns it.
104 */
105 public synchronized static double findDouble(String val)
106 {
107 if (val == null) return 0;
108
109 fpMatch.reset(val);
110 try
111 {
112 if (!fpMatch.find()) return 0;
113 }
114 catch (StringIndexOutOfBoundsException e)
115 {
116 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
117 "XMLParseUtil: regex parse problem: '" + val + "'", e);
118 }
119
120 val = fpMatch.group(1);
121 //System.err.println("Parsing " + val);
122
123 double retVal = 0;
124 try
125 {
126 retVal = Double.parseDouble(val);
127
128 float pixPerInch;
129 try {
130 pixPerInch = Toolkit.getDefaultToolkit().getScreenResolution();
131 }
132 catch (NoClassDefFoundError err)
133 {
134 //Default value for headless X servers
135 pixPerInch = 72;
136 }
137 final float inchesPerCm = .3936f;
138 final String units = fpMatch.group(6);
139
140 if ("%".equals(units)) retVal /= 100;
141 else if ("in".equals(units))
142 {
143 retVal *= pixPerInch;
144 }
145 else if ("cm".equals(units))
146 {
147 retVal *= inchesPerCm * pixPerInch;
148 }
149 else if ("mm".equals(units))
150 {
151 retVal *= inchesPerCm * pixPerInch * .1f;
152 }
153 else if ("pt".equals(units))
154 {
155 retVal *= (1f / 72f) * pixPerInch;
156 }
157 else if ("pc".equals(units))
158 {
159 retVal *= (1f / 6f) * pixPerInch;
160 }
161 }
162 catch (Exception e)
163 {}
164 return retVal;
165 }
166
167 /**
168 * Scans an input string for double values. For each value found, places
169 * in a list. This method regards any characters not part of a floating
170 * point value to be separators. Thus this will parse whitespace separated,
171 * comma separated, and many other separation schemes correctly.
172 */
173 public synchronized static double[] parseDoubleList(String list)
174 {
175 if (list == null) return null;
176
177 fpMatch.reset(list);
178
179 LinkedList<Double> doubList = new LinkedList<>();
180 while (fpMatch.find())
181 {
182 String val = fpMatch.group(1);
183 doubList.add(Double.valueOf(val));
184 }
185
186 double[] retArr = new double[doubList.size()];
187 Iterator<Double> it = doubList.iterator();
188 int idx = 0;
189 while (it.hasNext())
190 {
191 retArr[idx++] = it.next().doubleValue();
192 }
193
194 return retArr;
195 }
196
197 /**
198 * Searches the given string for the first floating point number it contains,
199 * parses and returns it.
200 */
201 public synchronized static float findFloat(String val)
202 {
203 if (val == null) return 0f;
204
205 fpMatch.reset(val);
206 if (!fpMatch.find()) return 0f;
207
208 val = fpMatch.group(1);
209 //System.err.println("Parsing " + val);
210
211 float retVal = 0f;
212 try
213 {
214 retVal = Float.parseFloat(val);
215 String units = fpMatch.group(6);
216 if ("%".equals(units)) retVal /= 100;
217 }
218 catch (Exception e)
219 {}
220 return retVal;
221 }
222
223 public synchronized static float[] parseFloatList(String list)
224 {
225 if (list == null) return null;
226
227 fpMatch.reset(list);
228
229 LinkedList<Float> floatList = new LinkedList<>();
230 while (fpMatch.find())
231 {
232 String val = fpMatch.group(1);
233 floatList.add(Float.valueOf(val));
234 }
235
236 float[] retArr = new float[floatList.size()];
237 Iterator<Float> it = floatList.iterator();
238 int idx = 0;
239 while (it.hasNext())
240 {
241 retArr[idx++] = it.next().floatValue();
242 }
243
244 return retArr;
245 }
246
247 /**
248 * Searches the given string for the first integer point number it contains,
249 * parses and returns it.
250 */
251 public static int findInt(String val)
252 {
253 if (val == null) return 0;
254
255 intMatch.reset(val);
256 if (!intMatch.find()) return 0;
257
258 val = intMatch.group();
259
260 int retVal = 0;
261 try
262 { retVal = Integer.parseInt(val); }
263 catch (Exception e)
264 {}
265 return retVal;
266 }
267
268 public static int[] parseIntList(String list)
269 {
270 if (list == null) return null;
271
272 intMatch.reset(list);
273
274 LinkedList<Integer> intList = new LinkedList<>();
275 while (intMatch.find())
276 {
277 String val = intMatch.group();
278 intList.add(Integer.valueOf(val));
279 }
280
281 int[] retArr = new int[intList.size()];
282 Iterator<Integer> it = intList.iterator();
283 int idx = 0;
284 while (it.hasNext())
285 {
286 retArr[idx++] = it.next().intValue();
287 }
288
289 return retArr;
290 }
291
292 /**
293 * The input string represents a ratio. Can either be specified as a
294 * double number on the range of [0.0 1.0] or as a percentage [0% 100%]
295 */
296 public static double parseRatio(String val)
297 {
298 if (val == null || val.equals("")) return 0.0;
299
300 if (val.charAt(val.length() - 1) == '%')
301 {
302 parseDouble(val.substring(0, val.length() - 1));
303 }
304 return parseDouble(val);
305 }
306
307 public static NumberWithUnits parseNumberWithUnits(String val)
308 {
309 if (val == null) return null;
310
311 return new NumberWithUnits(val);
312 }
313
314 /**
315 * Takes a CSS style string and returns a hash of them.
316 * @param styleString - A CSS formatted string of styles. Eg,
317 * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
318 * @return the map with the added styles
319 */
320 public static Map<String, String> parseStyle(String styleString) {
321 final Map<String, String> map = new HashMap<>();
322 final Pattern patSemi = Pattern.compile(";");
323
324 // com.kitfox.svg.xml.StyleAttribute 58,595 (3.6%) 1,992,230 B (1.4%) n/a
325 patSemi.splitAsStream(styleString)
326 .filter(style -> !style.isEmpty())
327 .forEach(style -> {
328 int colon = style.indexOf(':');
329 if (colon == -1) return;
330 String key = style.substring(0, colon).trim().intern();
331 String value = quoteMatch.reset(style.substring(colon + 1).trim()).replaceAll("").intern();
332 map.put(key, value);
333 });
334 return toUnmodifiableMap(map);
335 }
336
337 /**
338 * Returns an unmodifiable map for the given map.
339 * Makes use of {@link Collections#emptyMap()} and {@link Collections#singletonMap} and {@link Map#ofEntries(Map.Entry[])} to save memory.
340 *
341 * @param map the map for which an unmodifiable map is to be returned
342 * @param <K> the type of keys maintained by this map
343 * @param <V> the type of mapped values
344 * @return an unmodifiable map
345 * @see <a href="https://dzone.com/articles/preventing-your-java-collections-from-wasting-memo">
346 * How to Prevent Your Java Collections From Wasting Memory</a>
347 */
348 @SuppressWarnings("unchecked")
349 public static <K, V> Map<K, V> toUnmodifiableMap(Map<K, V> map) {
350 if (map == null || map.isEmpty()) {
351 return Collections.emptyMap();
352 } else if (map.size() == 1) {
353 final Map.Entry<K, V> entry = map.entrySet().iterator().next();
354 return Collections.singletonMap(entry.getKey(), entry.getValue());
355 } else if (mapOfEntries != null) {
356 try {
357 // Java 9: use Map.ofEntries(...)
358 return (Map<K, V>) mapOfEntries.invoke(null, new Object[]{map.entrySet().toArray(new Map.Entry[0])});
359 } catch (Exception ignore) {
360 }
361 }
362 return Collections.unmodifiableMap(map);
363 }
364
365}
Note: See TracBrowser for help on using the repository browser.