001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.util.string;
018:
019: import java.text.DecimalFormat;
020: import java.text.DecimalFormatSymbols;
021: import java.text.NumberFormat;
022: import java.text.ParseException;
023: import java.util.Locale;
024:
025: import org.apache.wicket.IClusterable;
026: import org.apache.wicket.util.time.Duration;
027: import org.apache.wicket.util.time.Time;
028:
029: /**
030: * Holds an immutable String value and optionally a Locale, with methods to
031: * convert to various types. Also provides some handy parsing methods and a
032: * variety of static factory methods.
033: * <p>
034: * Objects can be constructed directly from Strings or by using the valueOf()
035: * static factory methods. The repeat() static factory methods provide a way of
036: * generating a String value that repeats a given char or String a number of
037: * times.
038: * <p>
039: * Conversions to a wide variety of types can be found in the to*() methods. A
040: * generic conversion can be achieved with to(Class).
041: * <P>
042: * The beforeFirst(), afterFirst(), beforeLast() and afterLast() methods are
043: * handy for parsing things like paths and filenames.
044: *
045: * @author Jonathan Locke
046: */
047: public class StringValue implements IClusterable {
048: private static final long serialVersionUID = 1L;
049:
050: /** Locale to be used for formatting and parsing. */
051: private final Locale locale;
052:
053: /** The underlying string. */
054: private final String text;
055:
056: /**
057: * @param times
058: * Number of times to repeat character
059: * @param c
060: * Character to repeat
061: * @return Repeated character string
062: */
063: public static StringValue repeat(final int times, final char c) {
064: final AppendingStringBuffer buffer = new AppendingStringBuffer(
065: times);
066:
067: for (int i = 0; i < times; i++) {
068: buffer.append(c);
069: }
070:
071: return valueOf(buffer);
072: }
073:
074: /**
075: * @param times
076: * Number of times to repeat string
077: * @param s
078: * String to repeat
079: * @return Repeated character string
080: */
081: public static StringValue repeat(final int times, final String s) {
082: final AppendingStringBuffer buffer = new AppendingStringBuffer(
083: times);
084:
085: for (int i = 0; i < times; i++) {
086: buffer.append(s);
087: }
088:
089: return valueOf(buffer);
090: }
091:
092: /**
093: * Converts the given input to an instance of StringValue.
094: *
095: * @param value
096: * Double precision value
097: * @return String value formatted with one place after decimal
098: */
099: public static StringValue valueOf(final double value) {
100: return valueOf(value, Locale.getDefault());
101: }
102:
103: /**
104: * Converts the given input to an instance of StringValue.
105: *
106: * @param value
107: * Double precision value
108: * @param places
109: * Number of places after decimal
110: * @param locale
111: * Locale to be used for formatting
112: * @return String value formatted with the given number of places after
113: * decimal
114: */
115: public static StringValue valueOf(final double value,
116: final int places, final Locale locale) {
117: if (Double.isNaN(value) || Double.isInfinite(value)) {
118: return valueOf("N/A");
119: } else {
120: final DecimalFormat format = new DecimalFormat("#."
121: + repeat(places, '#'), new DecimalFormatSymbols(
122: locale));
123: return valueOf(format.format(value));
124: }
125: }
126:
127: /**
128: * Converts the given input to an instance of StringValue.
129: *
130: * @param value
131: * Double precision value
132: * @param locale
133: * Locale to be used for formatting
134: * @return String value formatted with one place after decimal
135: */
136: public static StringValue valueOf(final double value,
137: final Locale locale) {
138: return valueOf(value, 1, locale);
139: }
140:
141: /**
142: * Converts the given input to an instance of StringValue.
143: *
144: * @param object
145: * An object
146: * @return String value for object
147: */
148: public static StringValue valueOf(final Object object) {
149: return valueOf(Strings.toString(object));
150: }
151:
152: /**
153: * Converts the given input to an instance of StringValue.
154: *
155: * @param object
156: * An object
157: * @param locale
158: * Locale to be used for formatting
159: * @return String value for object
160: */
161: public static StringValue valueOf(final Object object,
162: final Locale locale) {
163: return valueOf(Strings.toString(object), locale);
164: }
165:
166: /**
167: * Converts the given input to an instance of StringValue.
168: *
169: * @param string
170: * A string
171: * @return String value for string
172: */
173: public static StringValue valueOf(final String string) {
174: return new StringValue(string);
175: }
176:
177: /**
178: * Converts the given input to an instance of StringValue.
179: *
180: * @param string
181: * A string
182: * @param locale
183: * Locale to be used for formatting
184: * @return String value for string
185: */
186: public static StringValue valueOf(final String string,
187: final Locale locale) {
188: return new StringValue(string, locale);
189: }
190:
191: /**
192: * Converts the given input to an instance of StringValue.
193: *
194: * @param buffer
195: * A string buffer
196: * @return String value
197: */
198: public static StringValue valueOf(final AppendingStringBuffer buffer) {
199: return valueOf(buffer.toString());
200: }
201:
202: /**
203: * Private constructor to force use of static factory methods.
204: *
205: * @param text
206: * The text for this string value
207: */
208: protected StringValue(final String text) {
209: this .text = text;
210: this .locale = Locale.getDefault();
211: }
212:
213: /**
214: * Private constructor to force use of static factory methods.
215: *
216: * @param text
217: * The text for this string value
218: * @param locale
219: * the locale for formatting and parsing
220: */
221: protected StringValue(final String text, final Locale locale) {
222: this .text = text;
223: this .locale = locale;
224: }
225:
226: /**
227: * Gets the substring after the first occurence given char.
228: *
229: * @param c
230: * char to scan for
231: * @return the substring
232: */
233: public final String afterFirst(final char c) {
234: return Strings.afterFirst(text, c);
235: }
236:
237: /**
238: * Gets the substring after the last occurence given char.
239: *
240: * @param c
241: * char to scan for
242: * @return the substring
243: */
244: public final String afterLast(final char c) {
245: return Strings.afterLast(text, c);
246: }
247:
248: /**
249: * Gets the substring before the first occurence given char.
250: *
251: * @param c
252: * char to scan for
253: * @return the substring
254: */
255: public final String beforeFirst(final char c) {
256: return Strings.beforeFirst(text, c);
257: }
258:
259: /**
260: * Gets the substring before the last occurence given char.
261: *
262: * @param c
263: * char to scan for
264: * @return the substring
265: */
266: public final String beforeLast(final char c) {
267: return Strings.afterLast(text, c);
268: }
269:
270: /**
271: * Replaces on this text.
272: *
273: * @param searchFor
274: * What to search for
275: * @param replaceWith
276: * What to replace with
277: * @return This string value with searchFor replaces with replaceWith
278: */
279: public final CharSequence replaceAll(final CharSequence searchFor,
280: final CharSequence replaceWith) {
281: return Strings.replaceAll(text, searchFor, replaceWith);
282: }
283:
284: /**
285: * Converts this StringValue to a given type.
286: *
287: * @param type
288: * The type to convert to
289: * @return The converted value
290: * @throws StringValueConversionException
291: */
292: public final Object to(final Class type)
293: throws StringValueConversionException {
294: if (type == String.class) {
295: return toString();
296: }
297:
298: if (type == Integer.TYPE || type == Integer.class) {
299: return toInteger();
300: }
301:
302: if (type == Long.TYPE || type == Long.class) {
303: return toLongObject();
304: }
305:
306: if (type == Boolean.TYPE || type == Boolean.class) {
307: return toBooleanObject();
308: }
309:
310: if (type == Double.TYPE || type == Double.class) {
311: return toDoubleObject();
312: }
313:
314: if (type == Character.TYPE || type == Character.class) {
315: return toCharacter();
316: }
317:
318: if (type == Time.class) {
319: return toTime();
320: }
321:
322: if (type == Duration.class) {
323: return toDuration();
324: }
325:
326: throw new StringValueConversionException("Cannot convert '"
327: + toString() + "'to type " + type);
328: }
329:
330: /**
331: * Convert this text to a boolean.
332: *
333: * @return This string value as a boolean
334: * @throws StringValueConversionException
335: */
336: public final boolean toBoolean()
337: throws StringValueConversionException {
338: return Strings.isTrue(text);
339: }
340:
341: /**
342: * Convert to primitive types, returning default value if text is null.
343: *
344: * @param defaultValue
345: * the default value to return of text is null
346: * @return the converted text as a primitive or the default if text is null
347: * @throws StringValueConversionException
348: */
349: public final boolean toBoolean(final boolean defaultValue)
350: throws StringValueConversionException {
351: return (text == null) ? defaultValue : toBoolean();
352: }
353:
354: /**
355: * Convert this text to a boolean and convert unchecked
356: * NumberFormatExceptions to checked.
357: *
358: * @return Converted text
359: * @throws StringValueConversionException
360: */
361: public final Boolean toBooleanObject()
362: throws StringValueConversionException {
363: return Strings.toBoolean(text);
364: }
365:
366: /**
367: * Convert this text to a char.
368: *
369: * @return This string value as a character
370: * @throws StringValueConversionException
371: */
372: public final char toChar() throws StringValueConversionException {
373: return Strings.toChar(text);
374: }
375:
376: /**
377: * Convert to primitive types, returning default value if text is null.
378: *
379: * @param defaultValue
380: * the default value to return of text is null
381: * @return the converted text as a primitive or the default if text is null
382: * @throws StringValueConversionException
383: */
384: public final char toChar(final char defaultValue)
385: throws StringValueConversionException {
386: return (text == null) ? defaultValue : toChar();
387: }
388:
389: /**
390: * Convert this text to a Character and convert unchecked
391: * NumberFormatExceptions to checked.
392: *
393: * @return Converted text
394: * @throws StringValueConversionException
395: */
396: public final Character toCharacter()
397: throws StringValueConversionException {
398: return new Character(toChar());
399: }
400:
401: /**
402: * Convert this text to a double and convert unchecked
403: * NumberFormatExceptions to checked.
404: *
405: * @return Converted text
406: * @throws StringValueConversionException
407: */
408: public final double toDouble()
409: throws StringValueConversionException {
410: try {
411: return NumberFormat.getNumberInstance(this .locale).parse(
412: text).doubleValue();
413: } catch (ParseException e) {
414: throw new StringValueConversionException(
415: "Unable to convert '" + text
416: + "' to a double value", e);
417: }
418: }
419:
420: /**
421: * Convert to primitive types, returning default value if text is null.
422: *
423: * @param defaultValue
424: * the default value to return of text is null
425: * @return the converted text as a primitive or the default if text is null
426: * @throws StringValueConversionException
427: */
428: public final double toDouble(final double defaultValue)
429: throws StringValueConversionException {
430: return (text == null) ? defaultValue : toDouble();
431: }
432:
433: /**
434: * Convert this text to a Double and convert unchecked
435: * NumberFormatExceptions to checked.
436: *
437: * @return Converted text
438: * @throws StringValueConversionException
439: */
440: public final Double toDoubleObject()
441: throws StringValueConversionException {
442: return new Double(toDouble());
443: }
444:
445: /**
446: * Convert this text to a Duration instance and convert unchecked
447: * NumberFormatExceptions to checked.
448: *
449: * @return Converted text
450: * @throws StringValueConversionException
451: */
452: public final Duration toDuration()
453: throws StringValueConversionException {
454: return Duration.valueOf(text, this .locale);
455: }
456:
457: /**
458: * Convert to primitive types, returning default value if text is null.
459: *
460: * @param defaultValue
461: * the default value to return of text is null
462: * @return the converted text as a primitive or the default if text is null
463: * @throws StringValueConversionException
464: */
465: public final Duration toDuration(final Duration defaultValue)
466: throws StringValueConversionException {
467: return (text == null) ? defaultValue : toDuration();
468: }
469:
470: /**
471: * Convert this text to an int and convert unchecked NumberFormatExceptions
472: * to checked.
473: *
474: * @return Converted text
475: * @throws StringValueConversionException
476: */
477: public final int toInt() throws StringValueConversionException {
478: try {
479: return Integer.parseInt(text);
480: } catch (NumberFormatException e) {
481: throw new StringValueConversionException(
482: "Unable to convert '" + text + "' to an int value",
483: e);
484: }
485: }
486:
487: /**
488: * Convert to primitive types, returning default value if text is null.
489: *
490: * @param defaultValue
491: * the default value to return of text is null
492: * @return the converted text as a primitive or the default if text is null
493: * @throws StringValueConversionException
494: */
495: public final int toInt(final int defaultValue)
496: throws StringValueConversionException {
497: return (text == null) ? defaultValue : toInt();
498: }
499:
500: /**
501: * Convert this text to an Integer and convert unchecked
502: * NumberFormatExceptions to checked.
503: *
504: * @return Converted text
505: * @throws StringValueConversionException
506: */
507: public final Integer toInteger()
508: throws StringValueConversionException {
509: try {
510: return new Integer(text);
511: } catch (NumberFormatException e) {
512: throw new StringValueConversionException(
513: "Unable to convert '" + text
514: + "' to an Integer value", e);
515: }
516: }
517:
518: /**
519: * Convert this text to a long and convert unchecked NumberFormatExceptions
520: * to checked.
521: *
522: * @return Converted text
523: * @throws StringValueConversionException
524: */
525: public final long toLong() throws StringValueConversionException {
526: try {
527: return Long.parseLong(text);
528: } catch (NumberFormatException e) {
529: throw new StringValueConversionException(
530: "Unable to convert '" + text + "' to a long value",
531: e);
532: }
533: }
534:
535: /**
536: * Convert to primitive types, returning default value if text is null.
537: *
538: * @param defaultValue
539: * the default value to return of text is null
540: * @return the converted text as a primitive or the default if text is null
541: * @throws StringValueConversionException
542: */
543: public final long toLong(final long defaultValue)
544: throws StringValueConversionException {
545: return (text == null) ? defaultValue : toLong();
546: }
547:
548: /**
549: * Convert this text to a Long and convert unchecked NumberFormatExceptions
550: * to checked.
551: *
552: * @return Converted text
553: * @throws StringValueConversionException
554: */
555: public final Long toLongObject()
556: throws StringValueConversionException {
557: try {
558: return new Long(text);
559: } catch (NumberFormatException e) {
560: throw new StringValueConversionException(
561: "Unable to convert '" + text + "' to a Long value",
562: e);
563: }
564: }
565:
566: /**
567: * Convert to object types, returning null if text is null.
568: *
569: * @return converted
570: * @throws StringValueConversionException
571: */
572: public final Boolean toOptionalBoolean()
573: throws StringValueConversionException {
574: return (text == null) ? null : toBooleanObject();
575: }
576:
577: /**
578: * Convert to object types, returning null if text is null.
579: *
580: * @return converted
581: * @throws StringValueConversionException
582: */
583: public final Character toOptionalCharacter()
584: throws StringValueConversionException {
585: return (text == null) ? null : toCharacter();
586: }
587:
588: /**
589: * Convert to object types, returning null if text is null.
590: *
591: * @return converted
592: * @throws StringValueConversionException
593: */
594: public final Double toOptionalDouble()
595: throws StringValueConversionException {
596: return (text == null) ? null : toDoubleObject();
597: }
598:
599: /**
600: * Convert to object types, returning null if text is null.
601: *
602: * @return converted
603: * @throws StringValueConversionException
604: */
605: public final Duration toOptionalDuration()
606: throws StringValueConversionException {
607: return (text == null) ? null : toDuration();
608: }
609:
610: /**
611: * Convert to object types, returning null if text is null.
612: *
613: * @return converted
614: * @throws StringValueConversionException
615: */
616: public final Integer toOptionalInteger()
617: throws StringValueConversionException {
618: return (text == null) ? null : toInteger();
619: }
620:
621: /**
622: * Convert to object types, returning null if text is null.
623: *
624: * @return converted
625: * @throws StringValueConversionException
626: */
627: public final Long toOptionalLong()
628: throws StringValueConversionException {
629: return (text == null) ? null : toLongObject();
630: }
631:
632: /**
633: * Convert to object types, returning null if text is null.
634: *
635: * @return converted
636: */
637: public final String toOptionalString() {
638: return text;
639: }
640:
641: /**
642: * Convert to object types, returning null if text is null.
643: *
644: * @return converted
645: * @throws StringValueConversionException
646: */
647: public final Time toOptionalTime()
648: throws StringValueConversionException {
649: return (text == null) ? null : toTime();
650: }
651:
652: /**
653: * @return The string value
654: */
655: public final String toString() {
656: return text;
657: }
658:
659: /**
660: * Convert to primitive types, returning default value if text is null.
661: *
662: * @param defaultValue
663: * the default value to return of text is null
664: * @return the converted text as a primitive or the default if text is null
665: */
666: public final String toString(final String defaultValue) {
667: return (text == null) ? defaultValue : text;
668: }
669:
670: /**
671: * Convert this text to a time instance and convert unchecked
672: * NumberFormatExceptions to checked.
673: *
674: * @return Converted text
675: * @throws StringValueConversionException
676: */
677: public final Time toTime() throws StringValueConversionException {
678: try {
679: return Time.valueOf(text);
680: } catch (ParseException e) {
681: throw new StringValueConversionException(
682: "Unable to convert '" + text + "' to a Time value",
683: e);
684: }
685: }
686:
687: /**
688: * Convert to primitive types, returning default value if text is null.
689: *
690: * @param defaultValue
691: * the default value to return of text is null
692: * @return the converted text as a primitive or the default if text is null
693: * @throws StringValueConversionException
694: */
695: public final Time toTime(final Time defaultValue)
696: throws StringValueConversionException {
697: return (text == null) ? defaultValue : toTime();
698: }
699: }
|