001: // ============================================================================
002: // $Id: ParserContext.java,v 1.5 2006/12/16 16:48:57 davidahall Exp $
003: // Copyright (c) 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.parser;
034:
035: import java.lang.reflect.Field;
036: import java.lang.reflect.Method;
037: import java.lang.reflect.Modifier;
038: import java.math.BigDecimal;
039: import java.math.BigInteger;
040: import java.text.MessageFormat;
041: import java.util.ArrayList;
042: import java.util.HashMap;
043: import java.util.List;
044: import java.util.Map;
045: import net.sf.jga.fn.UnaryFunctor;
046: import net.sf.jga.fn.arithmetic.ValueOf;
047:
048: /**
049: * Configuration settings for a JFXGParser.
050: * <p>
051: * Copyright © 2005 David A. Hall
052: */
053:
054: class ParserContext {
055: // Maps the argument names to their declared classes (declared to the entry point that
056: // defines them: the names are bound to the types given to the parseGenerator, parseUnary,
057: // and parseBinary methods)
058: private Map<String, Class> _arguments = new HashMap<String, Class>();
059:
060: // Imported classes, keyed by the classname within the package (or an alias)
061: private Map<String, Class> _imports = new HashMap<String, Class>();
062:
063: // Imported packages
064: private List<String> _packages = new ArrayList<String>();
065:
066: // Imported static methods, keyed by the method name (or an alias). Multiple methods
067: // may be keyed to the same name
068: private Map<String, List<Method>> _methods = new HashMap<String, List<Method>>();
069:
070: // Imported static members.
071: private Map<String, Field> _fields = new HashMap<String, Field>();
072:
073: // The object to which "this" refers (the parser won't reference itself directly)
074: private Object _this Obj;
075:
076: // True if decimal strings without a 'D' or 'F' are to be interpreted as BigDecimal
077: // constants
078: private boolean _undecoratedDecimal;
079:
080: ParserContext() {
081: initializePromotionRules();
082: }
083:
084: /**
085: * Returns a copy of this context.
086: */
087: ParserContext copy() {
088: ParserContext peer = new ParserContext();
089: peer._arguments.putAll(_arguments);
090: peer._imports.putAll(_imports);
091: peer._methods.putAll(_methods);
092: peer._fields.putAll(_fields);
093:
094: peer._packages.addAll(_packages);
095:
096: peer._this Obj = _this Obj;
097: peer._undecoratedDecimal = _undecoratedDecimal;
098:
099: for (Class fromtype : _promotionRules.keySet()) {
100: HashMap<Class, UnaryFunctor> ruleset = _promotionRules
101: .get(fromtype);
102: for (Class totype : ruleset.keySet()) {
103: UnaryFunctor fn = ruleset.get(totype);
104: peer.addPromotionRule(fromtype, totype, fn);
105: }
106: }
107:
108: return peer;
109: }
110:
111: /**
112: * Declares a named argument to the functor being built, and its type.
113: */
114: public void declareArgument(String argName, Class argType) {
115: _arguments.put(argName, argType);
116: }
117:
118: /**
119: * Returns the type of the named argument, or null if the name is unrecognized
120: */
121: public Class getTypeForName(String name) {
122: return _arguments.get(name);
123: }
124:
125: /**
126: * Imports the given class into the parser. The class will not need to be
127: * fully qualified for use in expressions. This will replace any existing
128: * imported class that happens to have the same name.
129: */
130: public void importClass(Class clasz) {
131: importClass(ParserUtils.getSimpleName(clasz), clasz);
132: }
133:
134: /**
135: * Imports the given class into the parser under the given alias. The alias
136: * may be used in place of the fully qualified name of the class hereafter.
137: * This will replace any existing imported class associated with the alias.
138: */
139: public void importClass(String alias, Class clasz) {
140: if (alias != null && clasz != null)
141: _imports.put(alias, clasz);
142: }
143:
144: /**
145: * Removes information about the class imported under the given name.
146: */
147: public void deportClass(String alias) {
148: if (alias != null)
149: _imports.remove(alias);
150: }
151:
152: /**
153: * Returns the class associated with the given alias
154: */
155: public Class getImportedClass(String alias) {
156: Class klass = _imports.get(alias);
157: if (klass != null)
158: return klass;
159:
160: List<Class> potentials = new ArrayList<Class>();
161: for (String pkgName : _packages) {
162: try {
163: potentials.add(Class.forName(pkgName + "." + alias));
164: } catch (Exception x) {
165: // There's no way to know that a class exists, other than to try and
166: // load it. Exceptions for flow control are the only option, here.
167: }
168: }
169:
170: if (potentials.size() == 1) {
171: return potentials.get(0);
172: }
173:
174: return null;
175: }
176:
177: /**
178: * Imports all classes in the given package into the parser.
179: */
180: public void importPackage(String pkgName) {
181: if (!_packages.contains(pkgName)) {
182: _packages.add(pkgName);
183: }
184: }
185:
186: /**
187: * Removes information about the package imported under the given name.
188: */
189: public void deportPackage(String pkgName) {
190: _packages.remove(pkgName);
191: }
192:
193: /**
194: * Imports all of the static public methods and members in the given class.
195: */
196: public void importStatics(Class clasz) {
197: // First, do the fields
198: Field[] fields = clasz.getFields();
199: for (int i = 0; i < fields.length; ++i) {
200: if (Modifier.isStatic(fields[i].getModifiers()))
201: importField(fields[i]);
202: }
203:
204: // next, do the methods
205: Method[] methods = clasz.getMethods();
206: for (int i = 0; i < methods.length; ++i) {
207: if (Modifier.isStatic(methods[i].getModifiers()))
208: importMethod(methods[i]);
209: }
210: }
211:
212: /**
213: * Imports the given static member. The member will not need to be qualified
214: * with its classname for use in expressions.
215: * @throws java.lang.NoSuchFieldException if the named field does not exist
216: * @throws java.lang.IllegalArgumentException if the named field is not static
217: */
218: public void importField(Class clasz, String name)
219: throws NoSuchFieldException {
220: importField(clasz.getField(name));
221: }
222:
223: /**
224: * Imports the given static member. The member will not need to be qualified
225: * with its classname for use in expressions.
226: * @throws java.lang.IllegalArgumentException if the field is not static
227: */
228: public void importField(Field field)
229: throws IllegalArgumentException {
230: if (!Modifier.isStatic(field.getModifiers())) {
231: String msg = "Cannot import non-static field {0}";
232: throw new IllegalArgumentException(MessageFormat.format(
233: msg, new Object[] { field }));
234: }
235:
236: _fields.put(field.getName(), field);
237: }
238:
239: /**
240: * Returns the imported field with the given name, or null if no such field has
241: * been imported.
242: */
243: public Field getImportedField(String name) {
244: return _fields.get(name);
245: }
246:
247: /**
248: * Imports the given static method(s).
249: * @throws java.lang.NoSuchMethodException if the named method does not exist, or if
250: * it/they is/are not static
251: */
252: public void importMethod(Class clasz, String name)
253: throws NoSuchMethodException {
254: Method[] methods = clasz.getMethods();
255: int numImported = 0;
256: for (int i = 0; i < methods.length; ++i) {
257: if (name.equals(methods[i].getName())
258: && Modifier.isStatic(methods[i].getModifiers())) {
259: importMethod(name, methods[i]);
260: ++numImported;
261: }
262: }
263:
264: if (numImported == 0) {
265: String msg = "No non-static method {0} found in class {1}";
266: Object[] args = new Object[] { name, clasz.getName() };
267: throw new NoSuchMethodException(MessageFormat.format(msg,
268: args));
269: }
270: }
271:
272: /**
273: * Imports a static method into the parser. The method will not need to be
274: * qualified with its classname for use in expressions. Multiple methods
275: * may be imported with the same name: the first best fit will be used when
276: * the method name is used.
277: * @throws java.lang.NoSuchMethodException if the named method does not exist
278: * @throws java.lang.IllegalArgumentException if the named method is not static
279: */
280: public void importMethod(Method meth) {
281: importMethod(meth.getName(), meth);
282: }
283:
284: /**
285: * Imports a static method into the parser under the given name. The method
286: * will not need to be qualified with its classname for use in expressions.
287: * Multiple methods may be imported with the same name: the first best fit
288: * will be used when the method name is used.
289: * @throws java.lang.IllegalArgumentException if the method is not static
290: */
291: public void importMethod(String name, Method meth) {
292: if (!Modifier.isStatic(meth.getModifiers())) {
293: String msg = "Cannot import non-static method {0}";
294: throw new IllegalArgumentException(MessageFormat.format(
295: msg, new Object[] { meth }));
296: }
297:
298: List<Method> methodList = _methods.get(name);
299: if (methodList == null) {
300: methodList = new ArrayList<Method>();
301: _methods.put(name, methodList);
302: }
303: methodList.add(meth);
304: }
305:
306: /**
307: * Returns a list of methods with the given name
308: */
309: public Method[] getImportedMethods(String name) {
310: List<Method> methodList = _methods.get(name);
311: if (methodList == null)
312: return new Method[0];
313: else
314: return methodList.toArray(new Method[0]);
315: }
316:
317: /**
318: * Binds the object to which 'this' refers
319: */
320: public void bindThis(Object this Binding) {
321: _this Obj = this Binding;
322: }
323:
324: /**
325: * Returns the current object to which 'this' refers
326: */
327: protected Object getBoundObject() {
328: return _this Obj;
329: }
330:
331: /**
332: * Enables/Disables the interpretation of undecorated decimal literals
333: * as BigDecimals. When true, an undecorated number containing a
334: * decimal, for example <tt>1.50</tt> is interpreted as a BigDecimal
335: * literal. When false, the same token is interpreted as a Double, as
336: * is the case in standard java
337: */
338: public void setUndecoratedDecimal(boolean flag) {
339: _undecoratedDecimal = flag;
340: }
341:
342: /**
343: * When true, an undecorated number containing a
344: * decimal, for example <tt>1.50</tt> is interpreted as a BigDecimal
345: * literal. When false, the same token is interpreted as a Double, as
346: * is the case in standard java
347: */
348: public boolean isUndecoratedDecimal() {
349: return _undecoratedDecimal;
350: }
351:
352: /**
353: * Registers a type-coercion rule that will promote values of the from type to
354: * values of the to type for use in expressions.
355: */
356: public void addPromotionRule(Class fromType, Class toType,
357: UnaryFunctor converter) {
358: HashMap<Class, UnaryFunctor> ruleset = _promotionRules
359: .get(fromType);
360: if (ruleset == null) {
361: ruleset = new HashMap<Class, UnaryFunctor>();
362: _promotionRules.put(fromType, ruleset);
363: }
364:
365: ruleset.put(toType, converter);
366: }
367:
368: /**
369: * Removes the type-coercion rule (if any) that would have promoted values of the
370: * from type to values of the to type for use in expressions.
371: */
372: public void removePromotionRule(Class fromType, Class toType) {
373: HashMap<Class, UnaryFunctor> ruleset = _promotionRules
374: .get(fromType);
375: if (ruleset != null)
376: ruleset.remove(toType);
377: }
378:
379: /**
380: * Returns a type-coercion rule that will promote values of the from type to
381: * values of the to type for use in expressions, if any such rule exists.
382: * Returns null if no such rule exists.
383: */
384: public UnaryFunctor getPromotionRule(Class fromType, Class toType) {
385: HashMap<Class, UnaryFunctor> ruleset = _promotionRules
386: .get(fromType);
387: if (ruleset == null)
388: return null;
389:
390: return ruleset.get(toType);
391: }
392:
393: private HashMap<Class, HashMap<Class, UnaryFunctor>> _promotionRules = new HashMap();
394:
395: private UnaryFunctor toShort = new ValueOf(Short.class);
396:
397: private UnaryFunctor toInteger = new ValueOf(Integer.class);
398: private UnaryFunctor toLong = new ValueOf(Long.class);
399: private UnaryFunctor toFloat = new ValueOf(Float.class);
400: private UnaryFunctor toDouble = new ValueOf(Double.class);
401: private UnaryFunctor toDecimal = new ValueOf(BigDecimal.class);
402: private UnaryFunctor toBigInt = new ValueOf(BigInteger.class);
403:
404: private void initializePromotionRules() {
405: addPromotionRule(Byte.class, Short.class, toShort);
406: addPromotionRule(Byte.class, Integer.class, toInteger);
407: addPromotionRule(Byte.class, Long.class, toLong);
408: addPromotionRule(Byte.class, BigInteger.class, toBigInt);
409: addPromotionRule(Byte.class, Double.class, toDouble);
410: addPromotionRule(Byte.class, Float.class, toFloat);
411: addPromotionRule(Byte.class, BigDecimal.class, toDecimal);
412:
413: addPromotionRule(Short.class, Integer.class, toInteger);
414: addPromotionRule(Short.class, Long.class, toLong);
415: addPromotionRule(Short.class, BigInteger.class, toBigInt);
416: addPromotionRule(Short.class, Double.class, toDouble);
417: addPromotionRule(Short.class, Float.class, toFloat);
418: addPromotionRule(Short.class, BigDecimal.class, toDecimal);
419:
420: addPromotionRule(Integer.class, Long.class, toLong);
421: addPromotionRule(Integer.class, BigInteger.class, toBigInt);
422: addPromotionRule(Integer.class, Float.class, toFloat);
423: addPromotionRule(Integer.class, Double.class, toDouble);
424: addPromotionRule(Integer.class, BigDecimal.class, toDecimal);
425:
426: addPromotionRule(Long.class, BigInteger.class, toBigInt);
427: addPromotionRule(Long.class, Float.class, toFloat);
428: addPromotionRule(Long.class, Double.class, toDouble);
429: addPromotionRule(Long.class, BigDecimal.class, toDecimal);
430:
431: addPromotionRule(BigInteger.class, Float.class, toFloat);
432: addPromotionRule(BigInteger.class, Double.class, toDouble);
433: addPromotionRule(BigInteger.class, BigDecimal.class, toDecimal);
434:
435: addPromotionRule(Float.class, Double.class, toDouble);
436: addPromotionRule(Float.class, BigDecimal.class, toDecimal);
437:
438: addPromotionRule(Double.class, BigDecimal.class, toDecimal);
439: }
440: }
|