All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.odysseus.el.ExpressionFactoryImpl Maven / Gradle / Ivy

There is a newer version: 2.2.7
Show newest version
/*
 * Copyright 2006-2009 Odysseus Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.odysseus.el;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.EnumSet;
import java.util.Properties;

import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ExpressionFactory;

import de.odysseus.el.misc.TypeConverter;
import de.odysseus.el.tree.TreeBuilder;
import de.odysseus.el.tree.TreeStore;
import de.odysseus.el.tree.impl.Builder;
import de.odysseus.el.tree.impl.Cache;
import de.odysseus.el.tree.impl.Builder.Feature;

/**
 * Expression factory implementation.
 * 
 * This class is also used as an EL "service provider". The juel-spi jar file specifies this
 * class as el expression factory implementation in
 * META-INF/services/javax.el.ExpressionFactory. Calling
 * {@link ExpressionFactory#newInstance()} will then return an instance of this class, configured as
 * described below.
 * 
 * If no properties are specified at construction time, properties are read from
 * 
    *
  1. * If the file JAVA_HOME/lib/el.properties exists and if it contains property * javax.el.ExpressionFactory whose value is the name of this class, these properties * are taken as default properties.
  2. *
  3. Otherwise, if system property javax.el.ExpressionFactory is set to the name of * this class, the system properties {@link System#getProperties()} are taken as default properties. *
  4. *
  5. * el.properties on your classpath. These properties override the properties from * JAVA_HOME/lib/el.properties or {@link System#getProperties()}.
  6. *
* There are also constructors to explicitly pass in an instance of {@link Properties}. * * Having this, the following properties are read: *
    *
  • * javax.el.cacheSize - cache size (int, default is 1000)
  • *
  • * javax.el.methodInvocations - allow method invocations as in * ${foo.bar(baz)} (boolean, default is false).
  • *
  • * javax.el.nullProperties - resolve null properties as in * ${foo[null]} (boolean, default is false).
  • *
  • * javax.el.varArgs - support function/method calls using varargs (boolean, default is * false).
  • *
* * @author Christoph Beck */ public class ExpressionFactoryImpl extends javax.el.ExpressionFactory { /** * A profile provides a default set of language features that will define the builder's * behavior. A profile can be adjusted using the javax.el.methodInvocations, * javax.el.varArgs and javax.el.nullProperties properties. * * @since 2.2 */ public static enum Profile { /** * JEE5: none */ JEE5(EnumSet.noneOf(Feature.class)), /** * JEE6: javax.el.methodInvocations, javax.el.varArgs. This is the * default profile. */ JEE6(EnumSet.of(Feature.METHOD_INVOCATIONS, Feature.VARARGS)); private final EnumSet features; private Profile(EnumSet features) { this.features = features; } Feature[] features() { return features.toArray(new Feature[features.size()]); } boolean contains(Feature feature) { return features.contains(feature); } } /** * javax.el.methodInvocations */ public static final String PROP_METHOD_INVOCATIONS = "javax.el.methodInvocations"; /** * javax.el.varArgs */ public static final String PROP_VAR_ARGS = "javax.el.varArgs"; /** * javax.el.nullProperties */ public static final String PROP_NULL_PROPERTIES = "javax.el.nullProperties"; /** * javax.el.ignoreReturnType */ public static final String PROP_IGNORE_RETURN_TYPE = "javax.el.ignoreReturnType"; /** * javax.el.cacheSize */ public static final String PROP_CACHE_SIZE = "javax.el.cacheSize"; private final TreeStore store; private final TypeConverter converter; /** * Create a new expression factory using the default builder and cache implementations. The * builder and cache are configured from el.properties (see above). The maximum * cache size will be 1000 unless overridden in el.properties. The builder profile * is {@link Profile#JEE6} (features may be overridden in el.properties). */ public ExpressionFactoryImpl() { this(Profile.JEE6); } /** * Create a new expression factory using the default builder and cache implementations. The * builder and cache are configured from the specified profile and el.properties * (see above). The maximum cache size will be 1000 unless overridden in * el.properties. * * @param profile * builder profile (features may be overridden in el.properties) * * @since 2.2 */ public ExpressionFactoryImpl(Profile profile) { Properties properties = loadProperties("el.properties"); this.store = createTreeStore(1000, profile, properties); this.converter = createTypeConverter(properties); } /** * Create a new expression factory using the default builder and cache implementations. The * builder and cache are configured using the specified properties. The maximum cache size will * be 1000 unless overridden by property javax.el.cacheSize. The builder profile is * {@link Profile#JEE6} (features may be overridden in properties). * * @param properties * used to initialize this factory (may be null) */ public ExpressionFactoryImpl(Properties properties) { this(Profile.JEE6, properties); } /** * Create a new expression factory using the default builder and cache implementations. The * builder and cache are configured using the specified profile and properties. The maximum * cache size will be 1000 unless overridden by property javax.el.cacheSize. * * @param profile * builder profile (individual features may be overridden in properties) * @param properties * used to initialize this factory (may be null) * * @since 2.2 */ public ExpressionFactoryImpl(Profile profile, Properties properties) { this.store = createTreeStore(1000, profile, properties); this.converter = createTypeConverter(properties); } /** * Create a new expression factory using the default builder and cache implementations. The * builder and cache are configured using the specified properties. The maximum cache size will * be 1000 unless overridden by property javax.el.cacheSize. The builder profile is * {@link Profile#JEE6} (individual features may be overridden in properties). * * @param properties * used to initialize this factory (may be null) * @param converter * custom type converter */ public ExpressionFactoryImpl(Properties properties, TypeConverter converter) { this(Profile.JEE6, properties, converter); } /** * Create a new expression factory using the default builder and cache implementations. The * builder and cache are configured using the specified profile and properties. The maximum * cache size will be 1000 unless overridden by property javax.el.cacheSize. * * @param profile * builder profile (individual features may be overridden in properties) * @param properties * used to initialize this factory (may be null) * @param converter * custom type converter * * @since 2.2 */ public ExpressionFactoryImpl(Profile profile, Properties properties, TypeConverter converter) { this.store = createTreeStore(1000, profile, properties); this.converter = converter; } /** * Create a new expression factory. * * @param store * the tree store used to parse and cache parse trees. */ public ExpressionFactoryImpl(TreeStore store) { this(store, TypeConverter.DEFAULT); } /** * Create a new expression factory. * * @param store * the tree store used to parse and cache parse trees. * @param converter * custom type converter */ public ExpressionFactoryImpl(TreeStore store, TypeConverter converter) { this.store = store; this.converter = converter; } private Properties loadDefaultProperties() { String home = System.getProperty("java.home"); String path = home + File.separator + "lib" + File.separator + "el.properties"; File file = new File(path); if (file.exists()) { Properties properties = new Properties(); InputStream input = null; try { properties.load(input = new FileInputStream(file)); } catch (IOException e) { throw new ELException("Cannot read default EL properties", e); } finally { try { input.close(); } catch (IOException e) { // ignore... } } if (getClass().getName().equals(properties.getProperty("javax.el.ExpressionFactory"))) { return properties; } } if (getClass().getName().equals(System.getProperty("javax.el.ExpressionFactory"))) { return System.getProperties(); } return null; } private Properties loadProperties(String path) { Properties properties = new Properties(loadDefaultProperties()); // try to find and load properties InputStream input = null; try { input = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); } catch (SecurityException e) { input = ClassLoader.getSystemResourceAsStream(path); } if (input != null) { try { properties.load(input); } catch (IOException e) { throw new ELException("Cannot read EL properties", e); } finally { try { input.close(); } catch (IOException e) { // ignore... } } } return properties; } private boolean getFeatureProperty(Profile profile, Properties properties, Feature feature, String property) { return Boolean.valueOf(properties.getProperty(property, String.valueOf(profile.contains(feature)))); } /** * Create the factory's tree store. This implementation creates a new tree store using the * default builder and cache implementations. The builder and cache are configured using the * specified properties. The maximum cache size will be as specified unless overridden by * property javax.el.cacheSize. */ protected TreeStore createTreeStore(int defaultCacheSize, Profile profile, Properties properties) { // create builder TreeBuilder builder = null; if (properties == null) { builder = createTreeBuilder(null, profile.features()); } else { EnumSet features = EnumSet.noneOf(Builder.Feature.class); if (getFeatureProperty(profile, properties, Feature.METHOD_INVOCATIONS, PROP_METHOD_INVOCATIONS)) { features.add(Builder.Feature.METHOD_INVOCATIONS); } if (getFeatureProperty(profile, properties, Feature.VARARGS, PROP_VAR_ARGS)) { features.add(Builder.Feature.VARARGS); } if (getFeatureProperty(profile, properties, Feature.NULL_PROPERTIES, PROP_NULL_PROPERTIES)) { features.add(Builder.Feature.NULL_PROPERTIES); } if (getFeatureProperty(profile, properties, Feature.IGNORE_RETURN_TYPE, PROP_IGNORE_RETURN_TYPE)) { features.add(Builder.Feature.IGNORE_RETURN_TYPE); } builder = createTreeBuilder(properties, features.toArray(new Builder.Feature[0])); } // create cache int cacheSize = defaultCacheSize; if (properties != null && properties.containsKey(PROP_CACHE_SIZE)) { try { cacheSize = Integer.parseInt(properties.getProperty(PROP_CACHE_SIZE)); } catch (NumberFormatException e) { throw new ELException("Cannot parse EL property " + PROP_CACHE_SIZE, e); } } Cache cache = cacheSize > 0 ? new Cache(cacheSize) : null; return new TreeStore(builder, cache); } /** * Create the factory's type converter. This implementation takes the * de.odysseus.el.misc.TypeConverter property as the name of a class implementing * the de.odysseus.el.misc.TypeConverter interface. If the property is not set, the * default converter (TypeConverter.DEFAULT) is used. */ protected TypeConverter createTypeConverter(Properties properties) { Class clazz = load(TypeConverter.class, properties); if (clazz == null) { return TypeConverter.DEFAULT; } try { return TypeConverter.class.cast(clazz.newInstance()); } catch (Exception e) { throw new ELException("TypeConverter " + clazz + " could not be instantiated", e); } } /** * Create the factory's builder. This implementation takes the * de.odysseus.el.tree.TreeBuilder property as a name of a class implementing the * de.odysseus.el.tree.TreeBuilder interface. If the property is not set, a plain * de.odysseus.el.tree.impl.Builder is used. If the configured class is a subclass * of de.odysseus.el.tree.impl.Builder and which provides a constructor taking an * array of Builder.Feature, this constructor will be invoked. Otherwise, the * default constructor will be used. */ protected TreeBuilder createTreeBuilder(Properties properties, Feature... features) { Class clazz = load(TreeBuilder.class, properties); if (clazz == null) { return new Builder(features); } try { if (Builder.class.isAssignableFrom(clazz)) { Constructor constructor = clazz.getConstructor(Feature[].class); if (constructor == null) { if (features == null || features.length == 0) { return TreeBuilder.class.cast(clazz.newInstance()); } else { throw new ELException("Builder " + clazz + " is missing constructor (can't pass features)"); } } else { return TreeBuilder.class.cast(constructor.newInstance((Object) features)); } } else { return TreeBuilder.class.cast(clazz.newInstance()); } } catch (Exception e) { throw new ELException("TreeBuilder " + clazz + " could not be instantiated", e); } } private Class load(Class clazz, Properties properties) { if (properties != null) { String className = properties.getProperty(clazz.getName()); if (className != null) { ClassLoader loader; try { loader = Thread.currentThread().getContextClassLoader(); } catch (Exception e) { throw new ELException("Could not get context class loader", e); } try { return loader == null ? Class.forName(className) : loader.loadClass(className); } catch (ClassNotFoundException e) { throw new ELException("Class " + className + " not found", e); } catch (Exception e) { throw new ELException("Class " + className + " could not be instantiated", e); } } } return null; } @Override public final Object coerceToType(Object obj, Class targetType) { return converter.convert(obj, targetType); } @Override public final ObjectValueExpression createValueExpression(Object instance, Class expectedType) { return new ObjectValueExpression(converter, instance, expectedType); } @Override public final TreeValueExpression createValueExpression(ELContext context, String expression, Class expectedType) { return new TreeValueExpression(store, context.getFunctionMapper(), context.getVariableMapper(), converter, expression, expectedType); } @Override public final TreeMethodExpression createMethodExpression(ELContext context, String expression, Class expectedReturnType, Class[] expectedParamTypes) { return new TreeMethodExpression(store, context.getFunctionMapper(), context.getVariableMapper(), converter, expression, expectedReturnType, expectedParamTypes); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy