001: package org.apache.velocity.tools.generic;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.lang.reflect.Array;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.Locale;
029:
030: /**
031: * <p>Utility class for easy parsing of String values held in a Map.</p>
032: * <p><pre>
033: * Template example(s):
034: * $parser.foo -> bar
035: * $parser.getNumber('baz') -> 12.6
036: * $parser.getInt('baz') -> 12
037: * $parser.getNumbers('baz') -> [12.6]
038: *
039: * Toolbox configuration:
040: * <tool>
041: * <key>parser</key>
042: * <class>org.apache.velocity.generic.ValueParser</class>
043: * </tool>
044: * </pre></p>
045: *
046: * <p>This comes in very handy when parsing parameters.</p>
047: *
048: * @author Nathan Bubna
049: * @version $Revision: 497988 $ $Date: 2007-01-19 14:48:13 -0800 (Fri, 19 Jan 2007) $
050: * @since VelocityTools 1.2
051: */
052: public class ValueParser {
053: private Map source = null;
054: private String delimiter = ",";
055:
056: public ValueParser() {
057: }
058:
059: public ValueParser(Map source) {
060: setSource(source);
061: }
062:
063: protected void setSource(Map source) {
064: this .source = source;
065: }
066:
067: protected Map getSource() {
068: if (source == null) {
069: throw new NullPointerException(
070: "You must set a Map source for values to be parsed.");
071: }
072: return this .source;
073: }
074:
075: /**
076: * Sets the delimiter used for separating values in a single String value.
077: * The default delimiter is a comma.
078: *
079: * @since VelocityTools 1.3
080: * @see #parseStringList
081: */
082: protected final void setStringsDelimiter(String delimiter) {
083: this .delimiter = delimiter;
084: }
085:
086: /**
087: * Returns the delimiter used for separating values in a single String value.
088: * The default delimiter is a comma.
089: *
090: * @since VelocityTools 1.3
091: * @see #parseStringList
092: */
093: protected final String getStringsDelimiter() {
094: return this .delimiter;
095: }
096:
097: // ----------------- public parsing methods --------------------------
098:
099: /**
100: * Convenience method for checking whether a certain parameter exists.
101: *
102: * @param key the parameter's key
103: * @return <code>true</code> if a parameter exists for the specified
104: * key; otherwise, returns <code>false</code>.
105: */
106: public boolean exists(String key) {
107: return (getString(key) != null);
108: }
109:
110: /**
111: * Convenience method for use in Velocity templates.
112: * This allows for easy "dot" access to parameters.
113: *
114: * e.g. $params.foo instead of $params.getString('foo')
115: *
116: * @param key the parameter's key
117: * @return parameter matching the specified key or
118: * <code>null</code> if there is no matching
119: * parameter
120: */
121: public Object get(String key) {
122: return getString(key);
123: }
124:
125: /**
126: * @param key the parameter's key
127: * @return parameter matching the specified key or
128: * <code>null</code> if there is no matching
129: * parameter
130: */
131: public String getString(String key) {
132: Object value = getSource().get(key);
133: if (value == null) {
134: return null;
135: }
136:
137: if (value instanceof Collection) {
138: Collection values = (Collection) value;
139: if (!values.isEmpty()) {
140: // take the next available value
141: value = values.iterator().next();
142: }
143: } else if (value.getClass().isArray()) {
144: if (Array.getLength(value) > 0) {
145: // take the first value
146: value = Array.get(value, 0);
147: }
148: }
149: return String.valueOf(value);
150: }
151:
152: /**
153: * @param key the desired parameter's key
154: * @param alternate The alternate value
155: * @return parameter matching the specified key or the
156: * specified alternate String if there is no matching
157: * parameter
158: */
159: public String getString(String key, String alternate) {
160: String s = getString(key);
161: return (s != null) ? s : alternate;
162: }
163:
164: /**
165: * @param key the desired parameter's key
166: * @return a {@link Boolean} object for the specified key or
167: * <code>null</code> if no matching parameter is found
168: */
169: public Boolean getBoolean(String key) {
170: String s = getString(key);
171: return (s != null) ? parseBoolean(s) : null;
172: }
173:
174: /**
175: * @param key the desired parameter's key
176: * @param alternate The alternate boolean value
177: * @return boolean value for the specified key or the
178: * alternate boolean is no value is found
179: */
180: public boolean getBoolean(String key, boolean alternate) {
181: Boolean bool = getBoolean(key);
182: return (bool != null) ? bool.booleanValue() : alternate;
183: }
184:
185: /**
186: * @param key the desired parameter's key
187: * @param alternate the alternate {@link Boolean}
188: * @return a {@link Boolean} for the specified key or the specified
189: * alternate if no matching parameter is found
190: */
191: public Boolean getBoolean(String key, Boolean alternate) {
192: Boolean bool = getBoolean(key);
193: return (bool != null) ? bool : alternate;
194: }
195:
196: /**
197: * @param key the desired parameter's key
198: * @return a {@link Integer} for the specified key or
199: * <code>null</code> if no matching parameter is found
200: */
201: public Integer getInteger(String key) {
202: Number num = getNumber(key);
203: if (num == null || num instanceof Integer) {
204: return (Integer) num;
205: }
206: return new Integer(num.intValue());
207: }
208:
209: /**
210: * @param key the desired parameter's key
211: * @param alternate The alternate Integer
212: * @return an Integer for the specified key or the specified
213: * alternate if no matching parameter is found
214: */
215: public Integer getInteger(String key, Integer alternate) {
216: Integer num = getInteger(key);
217: if (num == null) {
218: return alternate;
219: }
220: return num;
221: }
222:
223: /**
224: * @param key the desired parameter's key
225: * @return a {@link Double} for the specified key or
226: * <code>null</code> if no matching parameter is found
227: */
228: public Double getDouble(String key) {
229: Number num = getNumber(key);
230: if (num == null || num instanceof Double) {
231: return (Double) num;
232: }
233: return new Double(num.intValue());
234: }
235:
236: /**
237: * @param key the desired parameter's key
238: * @param alternate The alternate Double
239: * @return an Double for the specified key or the specified
240: * alternate if no matching parameter is found
241: */
242: public Double getDouble(String key, Double alternate) {
243: Double num = getDouble(key);
244: if (num == null) {
245: return alternate;
246: }
247: return num;
248: }
249:
250: /**
251: * @param key the desired parameter's key
252: * @return a {@link Number} for the specified key or
253: * <code>null</code> if no matching parameter is found
254: */
255: public Number getNumber(String key) {
256: String s = getString(key);
257: if (s == null || s.length() == 0) {
258: return null;
259: }
260: try {
261: return parseNumber(s);
262: } catch (Exception e) {
263: //there is no Number with that key
264: return null;
265: }
266: }
267:
268: /**
269: * @param key the desired parameter's key
270: * @return a {@link Locale} for the specified key or
271: * <code>null</code> if no matching parameter is found
272: */
273: public Locale getLocale(String key) {
274: String s = getString(key);
275: if (s == null || s.length() == 0) {
276: return null;
277: }
278: return parseLocale(s);
279: }
280:
281: /**
282: * @param key the desired parameter's key
283: * @param alternate The alternate Number
284: * @return a Number for the specified key or the specified
285: * alternate if no matching parameter is found
286: */
287: public Number getNumber(String key, Number alternate) {
288: Number n = getNumber(key);
289: return (n != null) ? n : alternate;
290: }
291:
292: /**
293: * @param key the desired parameter's key
294: * @param alternate The alternate int value
295: * @return the int value for the specified key or the specified
296: * alternate value if no matching parameter is found
297: */
298: public int getInt(String key, int alternate) {
299: Number n = getNumber(key);
300: return (n != null) ? n.intValue() : alternate;
301: }
302:
303: /**
304: * @param key the desired parameter's key
305: * @param alternate The alternate double value
306: * @return the double value for the specified key or the specified
307: * alternate value if no matching parameter is found
308: */
309: public double getDouble(String key, double alternate) {
310: Number n = getNumber(key);
311: return (n != null) ? n.doubleValue() : alternate;
312: }
313:
314: /**
315: * @param key the desired parameter's key
316: * @param alternate The alternate Locale
317: * @return a Locale for the specified key or the specified
318: * alternate if no matching parameter is found
319: */
320: public Locale getLocale(String key, Locale alternate) {
321: Locale l = getLocale(key);
322: return (l != null) ? l : alternate;
323: }
324:
325: /**
326: * @param key the key for the desired parameter
327: * @return an array of String objects containing all of the values
328: * associated with the given key, or <code>null</code>
329: * if the no values are associated with the given key
330: */
331: public String[] getStrings(String key) {
332: Object value = getSource().get(key);
333: if (value == null) {
334: return null;
335: }
336:
337: String[] strings = null;
338: if (value instanceof Collection) {
339: Collection values = (Collection) value;
340: if (!values.isEmpty()) {
341: strings = new String[values.size()];
342: int index = 0;
343: for (Iterator i = values.iterator(); i.hasNext();) {
344: strings[index++] = String.valueOf(i.next());
345: }
346: }
347: } else if (value.getClass().isArray()) {
348: strings = new String[Array.getLength(value)];
349: for (int i = 0; i < strings.length; i++) {
350: strings[i] = String.valueOf(Array.get(value, i));
351: }
352: } else {
353: strings = parseStringList(String.valueOf(value));
354: }
355: return strings;
356: }
357:
358: /**
359: * @param key the key for the desired parameter
360: * @return an array of Boolean objects associated with the given key.
361: */
362: public Boolean[] getBooleans(String key) {
363: String[] strings = getStrings(key);
364: if (strings == null) {
365: return null;
366: }
367:
368: Boolean[] bools = new Boolean[strings.length];
369: for (int i = 0; i < strings.length; i++) {
370: if (strings[i] != null && strings[i].length() > 0) {
371: bools[i] = parseBoolean(strings[i]);
372: }
373: }
374: return bools;
375: }
376:
377: /**
378: * @param key the key for the desired parameter
379: * @return an array of Number objects associated with the given key,
380: * or <code>null</code> if Numbers are not associated with it.
381: */
382: public Number[] getNumbers(String key) {
383: String[] strings = getStrings(key);
384: if (strings == null) {
385: return null;
386: }
387:
388: Number[] nums = new Number[strings.length];
389: try {
390: for (int i = 0; i < nums.length; i++) {
391: if (strings[i] != null && strings[i].length() > 0) {
392: nums[i] = parseNumber(strings[i]);
393: }
394: }
395: } catch (NumberFormatException nfe) {
396: return null;
397: }
398: return nums;
399: }
400:
401: /**
402: * @param key the key for the desired parameter
403: * @return an array of int values associated with the given key,
404: * or <code>null</code> if numbers are not associated with it.
405: */
406: public int[] getInts(String key) {
407: String[] strings = getStrings(key);
408: if (strings == null) {
409: return null;
410: }
411:
412: int[] ints = new int[strings.length];
413: try {
414: for (int i = 0; i < ints.length; i++) {
415: if (strings[i] != null && strings[i].length() > 0) {
416: ints[i] = parseNumber(strings[i]).intValue();
417: }
418: }
419: } catch (NumberFormatException nfe) {
420: return null;
421: }
422: return ints;
423: }
424:
425: /**
426: * @param key the key for the desired parameter
427: * @return an array of double values associated with the given key,
428: * or <code>null</code> if numbers are not associated with it.
429: */
430: public double[] getDoubles(String key) {
431: String[] strings = getStrings(key);
432: if (strings == null) {
433: return null;
434: }
435:
436: double[] doubles = new double[strings.length];
437: try {
438: for (int i = 0; i < doubles.length; i++) {
439: if (strings[i] != null && strings[i].length() > 0) {
440: doubles[i] = parseNumber(strings[i]).doubleValue();
441: }
442: }
443: } catch (NumberFormatException nfe) {
444: return null;
445: }
446: return doubles;
447: }
448:
449: /**
450: * @param key the key for the desired parameter
451: * @return an array of Locale objects associated with the given key,
452: * or <code>null</code> if Locales are not associated with it.
453: */
454: public Locale[] getLocales(String key) {
455: String[] strings = getStrings(key);
456: if (strings == null) {
457: return null;
458: }
459:
460: Locale[] locs = new Locale[strings.length];
461: for (int i = 0; i < locs.length; i++) {
462: if (strings[i] != null && strings[i].length() > 0) {
463: locs[i] = parseLocale(strings[i]);
464: }
465: }
466: return locs;
467: }
468:
469: // --------------------------- protected methods ------------------
470:
471: /**
472: * Converts a parameter value into a {@link Number}
473: * This is used as the base for all numeric parsing methods. So,
474: * sub-classes can override to allow for customized number parsing.
475: * (e.g. to handle fractions, compound numbers, etc.)
476: *
477: * @param value the string to be parsed
478: * @return the value as a {@link Number}
479: */
480: protected Number parseNumber(String value)
481: throws NumberFormatException {
482: if (value.indexOf('.') >= 0) {
483: return new Double(value);
484: }
485: return new Long(value);
486: }
487:
488: /**
489: * Converts a parameter value into a {@link Boolean}
490: * Sub-classes can override to allow for customized boolean parsing.
491: * (e.g. to handle "Yes/No" or "T/F")
492: *
493: * @param value the string to be parsed
494: * @return the value as a {@link Boolean}
495: */
496: protected Boolean parseBoolean(String value) {
497: return Boolean.valueOf(value);
498: }
499:
500: /**
501: * Converts a single String value into an array of Strings by splitting
502: * it on the tool's set delimiter. The default delimiter is a comma.
503: *
504: * @since VelocityTools 1.3
505: */
506: protected String[] parseStringList(String value) {
507: if (value.indexOf(this .delimiter) < 0) {
508: return new String[] { value };
509: }
510: return value.split(this .delimiter);
511: }
512:
513: /**
514: * Converts a String value into a Locale.
515: *
516: * @since VelocityTools 1.3
517: */
518: protected Locale parseLocale(String value) {
519: if (value.indexOf("_") < 0) {
520: return new Locale(value);
521: }
522:
523: String[] params = value.split("_");
524: if (params.length == 2) {
525: return new Locale(params[0], params[1]);
526: } else if (params.length == 3) {
527: return new Locale(params[0], params[1], params[2]);
528: } else {
529: // there's only 3 possible params, so this must be invalid
530: return null;
531: }
532: }
533:
534: }
|