/**
* License Agreement.
*
* Rich Faces - Natural Ajax for Java Server Faces (JSF)
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.ajax4jsf.templatecompiler.builder;
import java.beans.PropertyDescriptor;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.ajax4jsf.builder.config.ClassVisitor;
import org.ajax4jsf.builder.config.ClassWalkingLogic;
import org.ajax4jsf.builder.model.JavaPrimitive;
import org.ajax4jsf.templatecompiler.elements.A4JRendererElementsFactory;
import org.ajax4jsf.templatecompiler.elements.ElementsFactory;
import org.ajax4jsf.templatecompiler.elements.TemplateElement;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.w3c.dom.Node;
/**
* Component Beam.
*
* @author ayukhovich@exadel.com (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.2 $ $Date: 2007/02/26 20:48:42 $
*
*/
public abstract class AbstractCompilationContext implements CompilationContext {
private static final Log log = LogFactory.getLog(AbstractCompilationContext.class);
final private static String VCPBODY = "VCPBODY";
final private static String regexComponent = "(.*)" + VCPBODY + "(.*)"
+ VCPBODY + "(.*)";
final private static Pattern patternComponent = Pattern.compile(
regexComponent, Pattern.UNIX_LINES + Pattern.DOTALL);
private final static String DEFAULT_BASE_CLASS = "org.ajax4jsf.renderkit.RendererBase";
private String packageName;
private String className;
private String baseClassName;
private ArrayList<String> declarations;
private ArrayList<String> imports;
private String[] EncodeBegin;
private String[] EncodeEnd;
private String[] EncodeChildren;
private Map<String, Map<String, PropertyDescriptor>> resolvedProperties =
new HashMap<String, Map<String,PropertyDescriptor>>();
private HashMap<String, Class<?>> variables = new HashMap<String, Class<?>>();
private static String[] defaultImports = new String[] {
"java.util.Iterator", "java.util.Collection",
"java.util.Map",
"java.io.IOException", "javax.faces.component.UIComponent",
"javax.faces.context.FacesContext",
"javax.faces.context.ResponseWriter",
"org.ajax4jsf.renderkit.ComponentsVariableResolver",
"org.ajax4jsf.renderkit.ComponentVariables" };
private TemplateElement tree;
private List<ElementsFactory> elementFactories = new ArrayList<ElementsFactory>();
/**
* Name of UIComponent class for this template - can be used for get properties and methods by introspection.
*/
private String componentClass;
/**
* Ant Task related classloader for loat UIComponent and Renderer classes for introspection.
*/
private ClassLoader classLoader;
public AbstractCompilationContext() {
this.baseClassName = DEFAULT_BASE_CLASS;
this.declarations = new ArrayList<String>();
this.imports = new ArrayList<String>();
this.imports.addAll(Arrays.asList(defaultImports));
// Init default elements factory.
this.elementFactories.add(new A4JRendererElementsFactory());
}
/**
* @param loader
* @throws CompilationException
*/
public AbstractCompilationContext(ClassLoader loader) throws CompilationException {
this();
this.classLoader = loader;
setDefaultVariables();
}
public void addElementsFactory(ElementsFactory factory) {
elementFactories.add(0, factory);
}
/**
* @param componentClass
* The componentClass to set.
* @throws CompilationException
*/
public void setComponentClass(String componentClass)
throws CompilationException {
this.componentClass = componentClass;
addVariable("component", componentClass);
}
/**
* set a package name
*/
public void setPackageName(final String packageName) {
this.packageName = packageName;
}
/**
* set a class name
*/
public void setClassName(final String className) {
this.className = className;
}
/**
*
* @param fullClassName
*/
public void setFullClassName(final String fullClassName) {
int dotIndex = fullClassName.lastIndexOf(".");
String tempPackageName = "";
String tempClassName = fullClassName;
if (dotIndex != -1) {
tempPackageName = fullClassName.substring(0, dotIndex);
tempClassName = fullClassName.substring(dotIndex + 1);
} // if
setPackageName(tempPackageName);
setClassName(tempClassName);
}
public void setBaseclass(String baseclassName) throws CompilationException {
this.baseClassName = baseclassName;
addVariable("this", baseclassName);
}
// ------
/**
* @return package name
*/
public String getPackageName() {
return this.packageName;
}
/**
* @return base class package name
*/
public String getBaseclassPackageName() {
String packageName = null;
int dotIndex = this.baseClassName.lastIndexOf(".");
if (dotIndex != -1) {
packageName = this.baseClassName.substring(0, dotIndex);
}
return packageName;
}
/**
* @return class name
*/
public String getClassName() {
return this.className;
}
/**
* @return base class package name
*/
public String getBaseclassName() {
String className = null;
if ((this.baseClassName != null) && (this.baseClassName.length() != 0)) {
int dotIndex = this.baseClassName.lastIndexOf(".");
if (dotIndex != -1) {
className = this.baseClassName.substring(dotIndex + 1);
} else {
className = this.baseClassName;
}
}
return className;
}
/**
* @return full class name with package name
*/
public String getFullClassName() {
StringBuffer buf = new StringBuffer();
if ((this.packageName != null) && (this.packageName.length() != 0)) {
buf.append(this.packageName);
buf.append('.');
}
buf.append(this.className);
return buf.toString();
}
/**
* @return base class package name
*/
public String getFullBaseclass() {
return this.baseClassName;
}
/**
* @return
*/
public String getComponentFileName() {
return getFullClassName().replace('.', '/');
}
/**
* @return Returns the componentClass.
*/
public String getComponentClass() {
String returnStr = this.componentClass;
// if ( (componentClass != null) && ( !
// componentClass.endsWith(".class")) ) {
// returnStr = componentClass + ".class";
// }
return returnStr;
}
/**
* return class loader
*/
public ClassLoader getClassLoader() {
return this.classLoader;
}
/**
* @return string array contain declarations
*/
public String[] getDeclarations() {
return this.declarations.toArray(new String[0]);
}
/**
* @return string array contain declarations
*/
public String[] getImports() {
return this.imports.toArray(new String[0]);
}
public void addToImport(String className) {
this.imports.add(className);
}
public void addToDeclaration(String declaration) {
this.declarations.add(declaration);
}
/**
* Add variable with type String
*
* @param variableName
* @throws CompilationException
*/
public void addVariable(String variableName) throws CompilationException {
addVariable(variableName, "java.lang.String");
}
public void addVariable(String variableName, Class<?> clazz) {
this.variables.put(variableName, clazz);
}
public void addVariable(String variableName, String typeName)
throws CompilationException {
try {
Class<?> clazz = loadClass(typeName);
this.variables.put(variableName, clazz);
} catch (ClassNotFoundException e) {
// error("Error create variable "+variableName+" with type "+typeName, e);
throw new CompilationException("Error create renderer variable "+variableName+" with type "+typeName, e);
}
}
public boolean containsVariable(String variableName) {
return this.variables.containsKey(variableName);
}
public Class<?> getVariableType(String variableName) {
return this.variables.get(variableName);
}
public Class<?> loadClass(String className) throws ClassNotFoundException {
Class<?> clazz = null;
try {
if (JavaPrimitive.isPrimitive(className)) {
clazz = JavaPrimitive.forName(className);
} else {
clazz = this.classLoader.loadClass(className);
}
} catch (ClassNotFoundException e) {
throw e;
} catch (Throwable e) {
log.error(e.getLocalizedMessage(), e);
}
if (null == clazz) {
throw new ClassNotFoundException(className);
}
return clazz;
}
public void setDefaultVariables() throws CompilationException {
addVariable("component", "javax.faces.component.UIComponent");
addVariable("context", "javax.faces.context.FacesContext");
addVariable("writer", "javax.faces.context.ResponseWriter");
// addLocalVariable("component", "javax.faces.component.UIComponent" );
addVariable("variables",
"org.ajax4jsf.renderkit.ComponentVariables");
}
public Class<?> getMethodReturnedClass(Class<?> clazz, String methodName,
Class<?>[] parametersTypes) throws NoSuchMethodException {
Class<?> returnedType = null;
log.debug("class : " + clazz.getName() + "\n\t method : "
+ methodName + "\n\t paramTypes : "
+ Arrays.asList(parametersTypes).toString());
Method method = MethodUtils.getMatchingAccessibleMethod(clazz,
methodName, parametersTypes);
if (null != method) {
returnedType = method.getReturnType();
log.debug("Method found, return type : "
+ returnedType.getName());
return returnedType;
} else {
throw new NoSuchMethodException(clazz + "#" + methodName + "(" + Arrays.toString(parametersTypes) + ")");
}
}
public PropertyDescriptor getPropertyDescriptor(Class<?> clazz,
String propertyName) {
Map<String, PropertyDescriptor> descriptors =
resolvedProperties.get(clazz.getName());
if (descriptors == null) {
descriptors = resolveProperties(clazz);
resolvedProperties.put(clazz.getName(), descriptors);
}
return descriptors.get(propertyName);
}
private Map<String, PropertyDescriptor> resolveProperties(Class<?> clazz) {
final Map<String, PropertyDescriptor> descriptors =
new HashMap<String, PropertyDescriptor>();
new ClassWalkingLogic(clazz).walk(new ClassVisitor() {
public void visit(Class<?> clazz) {
PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(clazz);
for (PropertyDescriptor descriptor : pds) {
descriptors.put(descriptor.getName(), descriptor);
}
}
});
return descriptors;
}
/**
*
*/
public String[] getEncodeBegin() {
return this.EncodeBegin;
}
/**
*
*/
public String[] getEncodeChild() {
return this.EncodeChildren;
}
/**
*
*/
public String[] getEncodeEnd() {
return this.EncodeEnd;
}
public void setCode(String code) {
Matcher matcher = patternComponent.matcher(code);
String strEncodeBegin;
String strEncodeChildren;
String strEncodeEnd;
if (matcher.find()) {
strEncodeBegin = matcher.group(1);
strEncodeChildren = matcher.group(2);
strEncodeEnd = matcher.group(3);
} else {
strEncodeBegin = "";
strEncodeChildren = "";
strEncodeEnd = code;
}
if ((strEncodeBegin != null) && (strEncodeBegin.length() != 0)) {
this.EncodeBegin = strEncodeBegin.split(";\n");
}
if ((strEncodeChildren != null) && (strEncodeChildren.length() != 0)) {
this.EncodeChildren = strEncodeChildren.split(";\n");
}
if ((strEncodeEnd != null) && (strEncodeEnd.length() != 0)) {
this.EncodeEnd = strEncodeEnd.split(";\n");
}
}
/**
* @return the tree
*/
public TemplateElement getTree() {
return this.tree;
}
/**
* @param tree the tree to set
*/
public void setTree(TemplateElement tree) {
this.tree = tree;
}
/* (non-Javadoc)
* @see org.ajax4jsf.templatecompiler.builder.CompilationContext#getProcessor(org.w3c.dom.Node)
*/
public TemplateElement getProcessor(Node nodeElement) throws CompilationException {
for (Iterator<ElementsFactory> iter = elementFactories.listIterator(); iter.hasNext();) {
ElementsFactory factory = iter.next();
TemplateElement processor = factory.getProcessor(nodeElement, this);
if(null != processor){
return processor;
}
}
return null;
}
/* (non-Javadoc)
* @see org.ajax4jsf.templatecompiler.builder.CompilationContext#processTemplate(java.lang.String, org.apache.velocity.VelocityContext)
*/
public String processTemplate(String name, VelocityContext context) throws CompilationException {
StringWriter out = new StringWriter();
try {
getTemplate(name).merge(context, out);
} catch (ResourceNotFoundException e) {
throw new CompilationException(e.getLocalizedMessage());
} catch (ParseErrorException e) {
throw new CompilationException(e.getLocalizedMessage());
} catch (Exception e) {
throw new CompilationException(e.getLocalizedMessage());
}
return out.toString();
}
public Map<String, Map<String, PropertyDescriptor>> getResolvedProperties() {
return resolvedProperties;
}
}