/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.velocity;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.log.Log;
import org.apache.velocity.util.introspection.Info;
import org.apache.velocity.util.introspection.SecureIntrospectorImpl;
import org.apache.velocity.util.introspection.SecureUberspector;
import org.apache.velocity.util.introspection.VelMethod;
import org.apache.velocity.util.introspection.VelPropertyGet;
import org.openflexo.antar.binding.BindingDefinition;
import org.openflexo.antar.binding.BindingDefinition.BindingDefinitionType;
import org.openflexo.foundation.ontology.EditionPatternInstance;
import org.openflexo.foundation.viewpoint.binding.ViewPointDataBinding;
import org.openflexo.logging.FlexoLogger;
public class FlexoVelocityIntrospector extends SecureUberspector {
private static final Logger logger = FlexoLogger.getLogger(FlexoVelocityIntrospector.class.getPackage().getName());
private static class FlexoSecureIntrospectorImpl extends SecureIntrospectorImpl {
public FlexoSecureIntrospectorImpl(String[] badClasses, String[] badPackages, Log log) {
super(badClasses, badPackages, log);
}
public void clearCache() {
getIntrospectorCache().clear();
}
}
private RuntimeServices runtimeServices;
@Override
public void init() {
String[] badPackages = runtimeServices.getConfiguration().getStringArray(RuntimeConstants.INTROSPECTOR_RESTRICT_PACKAGES);
String[] badClasses = runtimeServices.getConfiguration().getStringArray(RuntimeConstants.INTROSPECTOR_RESTRICT_CLASSES);
introspector = new FlexoSecureIntrospectorImpl(badClasses, badPackages, log);
}
private FlexoSecureIntrospectorImpl getIntrospector() {
return (FlexoSecureIntrospectorImpl) introspector;
}
@Override
public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) throws Exception {
if (obj == null) {
return null;
}
// Allow to use the 'magic' methods provided by Velocity on array (ex. get(x))
if (obj instanceof Object[]) {
return super.getMethod(obj, methodName, args, i);
}
Class objClass = obj.getClass();
Method m = introspector.getMethod(objClass, methodName, args);
if (m != null) {
// Fix a bug in security manager of JDK1.5 where access to a public method of a non-visible inherited class is refused although
// it
// shouldn't (e.g, the method length() on StringBuilder is inherited from AbstractStringBuilder which has a package visibility)
try {
m.setAccessible(true);
} catch (SecurityException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "Security exception for method: " + objClass + "." + methodName, e);
}
}
return m != null ? new VelMethodImpl(m) : null;
}
// if it's an array
if (obj instanceof Class) {
m = introspector.getMethod((Class<?>) obj, methodName, args);
if (m != null) {
return new VelMethodImpl(m);
}
}
if (obj instanceof EditionPatternInstance) {
EditionPatternInstance epi = (EditionPatternInstance) obj;
ViewPointDataBinding vpdb = VPBindingEvaluator.buildBindingForMethodAndParams(methodName, args);
vpdb.setOwner(epi.getPattern());
vpdb.setBindingDefinition(new BindingDefinition(methodName, Object.class, BindingDefinitionType.GET, false));
if (vpdb.isValid()) {
return new VPBindingEvaluator(vpdb, epi);
}
}
if (logger.isLoggable(Level.INFO)) {
logger.info("Method '" + methodName + "' could not be found on " + objClass.getName() + " called in " + i.getTemplateName()
+ " at line " + i.getLine() + " column " + i.getColumn());
}
return null;
}
@Override
public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception {
VelPropertyGet get = super.getPropertyGet(obj, identifier, i);
if (get == null) {
if (obj instanceof EditionPatternInstance) {
EditionPatternInstance epi = (EditionPatternInstance) obj;
ViewPointDataBinding vpdb = new ViewPointDataBinding(identifier);
vpdb.setOwner(epi.getPattern());
vpdb.setBindingDefinition(new BindingDefinition(identifier, Object.class, BindingDefinitionType.GET, false));
if (vpdb.isValid()) {
return new VPBindingEvaluator(vpdb, epi);
}
}
get = getPropertyGetForClass(obj.getClass(), identifier, i);
if (get == null && obj instanceof Class) {
get = getPropertyGetForClass((Class<?>) obj, identifier, i);
}
}
return get;
}
private VelPropertyGet getPropertyGetForClass(Class<?> klass, String identifier, Info i) {
try {
return new FlexoVelocityPropertyGet(klass, identifier);
} catch (NoSuchFieldException e) {
if (logger.isLoggable(Level.INFO)) {
logger.info("Field '" + identifier + "' could not be found on " + klass.getName() + " called in " + i.getTemplateName()
+ " at line " + i.getLine() + " column " + i.getColumn());
}
} catch (SecurityException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "Security exception for field: " + klass + "." + identifier, e);
}
}
return null;
}
public void clearCache() {
getIntrospector().clearCache();
}
@Override
public void setRuntimeServices(RuntimeServices rs) {
super.setRuntimeServices(rs);
this.runtimeServices = rs;
}
}