001: // ============================================================================
002: // $Id: ParseFormat.java,v 1.13 2006/04/26 03:41:38 davidahall Exp $
003: // Copyright (c) 2003-2005 David A. Hall
004: // ============================================================================
005: // The contents of this file are subject to the Common Development and
006: // Distribution License (CDDL), Version 1.0 (the License); you may not use this
007: // file except in compliance with the License. You should have received a copy
008: // of the the License along with this file: if not, a copy of the License is
009: // available from Sun Microsystems, Inc.
010: //
011: // http://www.sun.com/cddl/cddl.html
012: //
013: // From time to time, the license steward (initially Sun Microsystems, Inc.) may
014: // publish revised and/or new versions of the License. You may not use,
015: // distribute, or otherwise make this file available under subsequent versions
016: // of the License.
017: //
018: // Alternatively, the contents of this file may be used under the terms of the
019: // GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
020: // case the provisions of the LGPL are applicable instead of those above. If you
021: // wish to allow use of your version of this file only under the terms of the
022: // LGPL, and not to allow others to use your version of this file under the
023: // terms of the CDDL, indicate your decision by deleting the provisions above
024: // and replace them with the notice and other provisions required by the LGPL.
025: // If you do not delete the provisions above, a recipient may use your version
026: // of this file under the terms of either the CDDL or the LGPL.
027: //
028: // This library is distributed in the hope that it will be useful,
029: // but WITHOUT ANY WARRANTY; without even the implied warranty of
030: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
031: // ============================================================================
032:
033: package net.sf.jga.fn.string;
034:
035: import java.text.DateFormat;
036: import java.text.Format;
037: import java.text.MessageFormat;
038: import java.text.NumberFormat;
039: import java.text.ParseException;
040: import java.util.Date;
041: import net.sf.jga.fn.EvaluationException;
042: import net.sf.jga.fn.UnaryFunctor;
043: import net.sf.jga.fn.adaptor.Identity;
044: import net.sf.jga.fn.arithmetic.ValueOf;
045: import net.sf.jga.fn.property.Cast;
046: import net.sf.jga.fn.property.ConstructUnary;
047: import net.sf.jga.fn.property.GetProperty;
048:
049: /**
050: * Unary Functor that parses a given Format.
051: * <p>
052: * <p>
053: * Copyright © 2003-2005 David A. Hall
054: * @author <a href="mailto:davidahall@users.sourceforge.net">David A. Hall</a>
055: **/
056:
057: public class ParseFormat<R> extends UnaryFunctor<String, R> {
058:
059: static final long serialVersionUID = 6590747028326789147L;
060:
061: // The java.text.Format object that controls parsing.
062: private Format _format;
063:
064: // A functor that converts the object from the type returned by the format's
065: // parse() method to the parameter type
066: private UnaryFunctor<Object, R> _converter;
067:
068: /**
069: * Builds the ParseFormat given a text Format and a functor that can convert
070: * Objects returned by the Format's parse(Object) method and the desired
071: * type of this functor.
072: */
073: protected ParseFormat(Format format, UnaryFunctor<Object, R> conv) {
074: if (format == null) {
075: String msg = "Format must be specified";
076: throw new IllegalArgumentException(msg);
077: }
078:
079: _format = format;
080: _converter = conv;
081: }
082:
083: /**
084: * @return the format used to parse values
085: */
086:
087: public Format getFormat() {
088: return _format;
089: }
090:
091: // UnaryFunctor interface
092:
093: /**
094: * Parses the value from the given string, using the java.text.Format object
095: * passed at construction.
096: * <p>
097: * @param arg formatted string to be parsed
098: * @throws java.text.ParseException when the string cannot be parsed to the
099: * correct type
100: * @return the value that the string represented
101: */
102:
103: public R fn(String arg) {
104:
105: try {
106: Object obj = _format.parseObject(arg);
107: // @SuppressWarnings
108: // This line causes an unchecked cast warning: we can't do anything
109: // about this other than warn the user not to use improper formats.
110: // It would help if java.text.Format was generic, but it isn't. If
111: // the user gives us a converter, we're OK, otherwise all we can do
112: // is cast and hope for the best
113: R val = (_converter == null) ? (R) obj : _converter.fn(obj);
114: return val;
115: } catch (ParseException x) {
116: String msg = "Unable to parse \"{0}\"";
117: Object[] args = new Object[] { arg };
118: throw new EvaluationException(MessageFormat.format(msg,
119: args), x);
120: } catch (ClassCastException x) {
121: String msg = "Unable to convert \"{0}\" to correct type";
122: Object[] args = new Object[] { arg };
123: throw new EvaluationException(MessageFormat.format(msg,
124: args), x);
125: }
126: }
127:
128: /**
129: * Calls the Visitor's <code>visit(ParseFormat)</code> method, if it
130: * implements the nested Visitor interface.
131: */
132: public void accept(net.sf.jga.fn.Visitor v) {
133: if (v instanceof ParseFormat.Visitor)
134: ((ParseFormat.Visitor) v).visit(this );
135: else
136: v.visit(this );
137: }
138:
139: // Object overrides
140:
141: public String toString() {
142: return "ParseFormat[" + _format + "]";
143: }
144:
145: // Acyclic Visitor
146:
147: /**
148: * Interface for classes that may interpret a <b>ParseFormat</b>
149: * predicate.
150: */
151: public interface Visitor extends net.sf.jga.fn.Visitor {
152: public void visit(ParseFormat host);
153: }
154:
155: /**
156: * ParseFormat functor for use with Dates. This class exists
157: * as an implementation detail that works around a limit in the javac
158: * inferencer -- in all substantive ways, this is simply a ParseFormat functor.
159: */
160:
161: static public class Date<T extends java.util.Date> extends
162: ParseFormat<T> {
163: static final long serialVersionUID = 6959869794482290044L;
164:
165: public Date(Class<T> type, DateFormat format) {
166: super (format, (UnaryFunctor<Object, T>)
167: // If we want to return java.util.Date, then we can simply cast it.
168: // Otherwise, we return [ new D(((Date)arg).getTime()) ]
169: // since we know that All Date subclasses have a constructor that
170: // takes a long (and this will break if a Date subclass is used that
171: // does not have such a constructor.
172: // The other possibility is to build a date using a default constructor
173: // and set its time property [ new D().setTime(((Date)arg).getTime()) ]
174: // but that functor is even scarier
175:
176: // @SupressWarnings
177: //
178: ((type.equals(java.util.Date.class)) ? new Cast<Object, java.util.Date>(
179: java.util.Date.class)
180: : new ConstructUnary<Long, T>(Long.TYPE,
181: type)
182: .compose(
183: new GetProperty<java.util.Date, Long>(
184: java.util.Date.class,
185: "Time"))
186: .compose(
187: new Cast<Object, java.util.Date>(
188: java.util.Date.class))));
189: }
190: }
191:
192: /**
193: * ParseFormat functor for use with Numbers. This class exists
194: * as an implementation detail that works around a limit in the javac
195: * inferencer -- in all substantive ways, this is simply a ParseFormat functor.
196: */
197:
198: static public class Number<T extends java.lang.Number> extends
199: ParseFormat<T> {
200: static final long serialVersionUID = -5365953713327551298L;
201:
202: public Number(Class<T> type, NumberFormat format) {
203: super (
204: format, //(UnaryFunctor<Object,R>)
205: new ValueOf<java.lang.Number, T>(type)
206: .compose(new Cast<Object, java.lang.Number>(
207: java.lang.Number.class)));
208: }
209: }
210: }
|