001: /*
002: * Copyright 2006, 2007 Odysseus Software GmbH
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package de.odysseus.el.tree.impl;
017:
018: import java.io.PrintWriter;
019: import java.util.EnumSet;
020:
021: import javax.el.ELException;
022:
023: import de.odysseus.el.misc.LocalMessages;
024: import de.odysseus.el.tree.NodePrinter;
025: import de.odysseus.el.tree.Tree;
026: import de.odysseus.el.tree.TreeBuilder;
027:
028: import de.odysseus.el.tree.impl.Scanner.ScanException;
029: import de.odysseus.el.tree.impl.Parser.ParseException;
030: import de.odysseus.el.util.SimpleContext;
031:
032: /**
033: * Tree builder.
034: *
035: * @author Christoph Beck
036: */
037: public class Builder implements TreeBuilder {
038: private static final long serialVersionUID = 1L;
039:
040: /**
041: * Feature enumeration type.
042: */
043: public static enum Feature {
044: /**
045: * Method invocations as in <code>${foo.bar(1)}</code> are a <em>JUEL</em> extension.
046: * The method to be invoked is resolved at evaluation just like properties. That is,
047: * a specialized resolver will have to be provided at evaluation time.
048: */
049: METHOD_INVOCATIONS,
050: /**
051: * For some reason we don't understand, the specification does not allow to resolve
052: * <code>null</code> property values. E.g. <code>${map[key]}</code> will always
053: * return <code>null</code> if <code>key</code> evaluates to <code>null</code>.
054: * Enabling this feature will allow <em>JUEL</em> to pass <code>null</code> to
055: * the property resolvers just like any other property value.
056: */
057: NULL_PROPERTIES
058: }
059:
060: private final EnumSet<Feature> features;
061:
062: public Builder() {
063: this .features = EnumSet.noneOf(Feature.class);
064: }
065:
066: public Builder(Feature... features) {
067: if (features == null || features.length == 0) {
068: this .features = EnumSet.noneOf(Feature.class);
069: } else if (features.length == 1) {
070: this .features = EnumSet.of(features[0]);
071: } else {
072: Feature[] rest = new Feature[features.length - 1];
073: for (int i = 1; i < features.length; i++) {
074: rest[i - 1] = features[i];
075: }
076: this .features = EnumSet.of(features[0], rest);
077: }
078: }
079:
080: /**
081: * Parse an integer literal.
082: * @param string string to parse
083: * @return <code>Long.valueOf(string)</code>
084: */
085: protected Number parseInteger(String string)
086: throws NumberFormatException {
087: return Long.valueOf(string);
088: }
089:
090: /**
091: * Parse a floating point literal.
092: * @param string string to parse
093: * @return <code>Double.valueOf(string)</code>
094: */
095: protected Number parseFloat(String string)
096: throws NumberFormatException {
097: return Double.valueOf(string);
098: }
099:
100: /**
101: * @return <code>true</code> iff the specified feature is supported.
102: */
103: public boolean isEnabled(Feature feature) {
104: return features.contains(feature);
105: }
106:
107: /**
108: * Parse expression.
109: */
110: public Tree build(String expression) throws ELException {
111: try {
112: return new Parser(this , expression).tree();
113: } catch (ScanException e) {
114: throw new ELException(LocalMessages.get("error.build",
115: expression, e.getMessage()));
116: } catch (ParseException e) {
117: throw new ELException(LocalMessages.get("error.build",
118: expression, e.getMessage()));
119: }
120: }
121:
122: @Override
123: public boolean equals(Object obj) {
124: if (obj == null || obj.getClass() != getClass()) {
125: return false;
126: }
127: return features.equals(((Builder) obj).features);
128: }
129:
130: @Override
131: public int hashCode() {
132: return getClass().hashCode();
133: }
134:
135: /**
136: * Dump out abstract syntax tree for a given expression
137: *
138: * @param args array with one element, containing the expression string
139: */
140: public static void main(String[] args) {
141: if (args.length != 1) {
142: System.err.println("usage: java " + Builder.class.getName()
143: + " <expression string>");
144: System.exit(1);
145: }
146: PrintWriter out = new PrintWriter(System.out);
147: Tree tree = null;
148: try {
149: tree = new Builder(Feature.METHOD_INVOCATIONS)
150: .build(args[0]);
151: } catch (ELException e) {
152: System.out.println(e.getMessage());
153: System.exit(0);
154: }
155: NodePrinter.dump(out, tree.getRoot());
156: if (!tree.getFunctionNodes().iterator().hasNext()
157: && !tree.getIdentifierNodes().iterator().hasNext()) {
158: out.print(">> ");
159: try {
160: out.println(tree.getRoot().getValue(null,
161: new SimpleContext(), null));
162: } catch (ELException e) {
163: out.println(e.getMessage());
164: }
165: }
166: out.flush();
167: }
168: }
|