/**
* 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.builder.config;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.naming.ConfigurationException;
import org.ajax4jsf.builder.generator.Logger;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.ExtendedBaseRules;
import org.apache.tools.ant.BuildException;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.ext.EntityResolver2;
/**
* Parse builder config file for use with component creation.
*
* @author asmirnov@exadel.com (latest modification by $Author: ishabalov $)
* @version $Revision: 1.1.2.10 $ $Date: 2007/02/20 20:57:58 $
*
*/
public class BuilderConfig implements LoaderHolder {
/**
* Resource path to config file schemas and entities
*/
public static final String COMPONENT_SCHEMA_BASE = "/META-INF/schema/";
/**
* Resource path to config file schemas and entities
*/
public static final String ENTITIES_FOLDER = "entities";
/**
* default URI to dtd in classpath
*/
private static final String COMPONENT_CONFIG_DTD_URI = COMPONENT_SCHEMA_BASE
+ "component-config.dtd";
/**
* root element of configuration file
*/
private static final String GENERATOR_CONFIG_ROOT_ELEMENT = "components";
/**
* PUBLIC Id of configuration schema
*/
private static final String GENERATOR_CONFIG_PUBLIC_ID = "-//AJAX4JSF//CDK Generator config/EN";
/**
* components described in this configuration
*/
private List<ComponentBean> components = new ArrayList<ComponentBean>();
/**
* validators described in this configuration
*/
private List<ValidatorBean> validators = new ArrayList<ValidatorBean>();
/**
* components described in this configuration
*/
private List<ConverterBean> converters = new ArrayList<ConverterBean>();
/**
* renderers described in this configuration
*/
private List<RendererBean> renderers = new ArrayList<RendererBean>();
private List<ListenerBean> listeners = new ArrayList<ListenerBean>();
private List<FunctionBean> functions = new ArrayList<FunctionBean>();
private ClassLoader _loader;
private Logger _log;
/**
* @param project -
* current ant project
*/
public BuilderConfig(ClassLoader loader, Logger log) {
_loader = loader;
_log = log;
}
/**
* Parsing builder configuration file
*
* @param configFile
* @throws ParsingException
*/
public void parseConfig(final File configFile) throws ParsingException {
getLog().info(" Parse config file " + configFile.toString());
Digester digester = new Digester();
digester.setRules(new ExtendedBaseRules());
digester.setValidating(false);
digester.setNamespaceAware(false);
// try {
// URL dtd =
// this.getClass().getClassLoader().getResource(COMPONENT_CONFIG_DTD_URI);
// new
// URL("resource:/com/exadel/vcp/builder/config/component-config.dtd");
getLog()
.debug("Register config DTD as URI " + COMPONENT_CONFIG_DTD_URI);
digester.register(GENERATOR_CONFIG_PUBLIC_ID, COMPONENT_CONFIG_DTD_URI);
// } catch (MalformedURLException e) {
// throw new ParsingException("Malformed URL for internal DTD
// reference",e);
// }
// setup custom entity resolver for handle file-resource path's
// resolve DTD even it not pointed in file, and, for entities - resolve
// it in
// classpath if entity registered in DTD witn path /META-INF/schema
digester.setEntityResolver(new EntityResolver2() {
/*
* (non-Javadoc)
*
* @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
* java.lang.String)
*/
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
getLog().debug(
"Request for entity with systemId " + systemId
+ " and publicId " + publicId);
if (GENERATOR_CONFIG_PUBLIC_ID.equals(publicId)) {
return getDTDSource();
} else if (null != publicId && null != systemId
&& systemId.contains(ENTITIES_FOLDER)) {
int base = systemId.indexOf(ENTITIES_FOLDER);
String entity = COMPONENT_SCHEMA_BASE
+ systemId.substring(base);
getLog().debug(
"attempt to load entity from classpath " + entity);
InputStream entityStream = BuilderConfig.class
.getResourceAsStream(entity);
if (null != entityStream) {
return new InputSource(entityStream);
}
}
return null;
}
public InputSource getExternalSubset(String name, String baseURI)
throws SAXException, IOException {
getLog().debug(
"Request for ExternalSubset with name " + name
+ " and baseURI " + baseURI);
if (GENERATOR_CONFIG_ROOT_ELEMENT.equals(name)) {
return getDTDSource();
}
return null;
}
public InputSource resolveEntity(String name, String publicId,
String baseURI, String systemId) throws SAXException,
IOException {
getLog().debug(
"Request for extended entity with systemId " + systemId
+ " and publicId " + publicId);
getLog().debug(
"additional parameters with name " + name
+ " and baseURI " + baseURI);
if ("[dtd]".equals(name)
&& GENERATOR_CONFIG_PUBLIC_ID.equals(publicId)) {
return getDTDSource();
} else if (null == name
|| (!name.startsWith("[") && !name.startsWith("&"))) {
return resolveEntity(publicId, systemId);
}
return null;
}
/**
* Resolve config DTD from classpath
*
* @return source of config file DTD
*/
private InputSource getDTDSource() {
return new InputSource(BuilderConfig.class
.getResourceAsStream(COMPONENT_CONFIG_DTD_URI));
}
});
// Parsing rules.
// Components
String path = "components/component";
digester.addObjectCreate(path, ComponentBean.class);
digester.addBeanPropertySetter(path + "/name");
digester.addBeanPropertySetter(path + "/family");
digester.addBeanPropertySetter(path + "/classname");
digester.addBeanPropertySetter(path + "/superclass");
digester.addBeanPropertySetter(path + "/description");
digester.addBeanPropertySetter(path + "/displayname");
digester.addBeanPropertySetter(path + "/icon");
// TODO - for superclass, populate from description in config file, if
// exist
digester.addSetProperties(path);
digester.addSetNext(path, "addComponent");
path = "components/renderer";
digester.addObjectCreate(path, RendererBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addRenderer");
path = "components/component/renderer";
digester.addObjectCreate(path, RendererBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setRenderer");
path = "components/component/facet";
digester.addObjectCreate(path, JsfBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addFacet");
path = "components/component/event";
digester.addObjectCreate(path, EventBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addEvent");
// Validators
path = "components/validator";
digester.addObjectCreate(path, ValidatorBean.class);
digester.addBeanPropertySetter(path + "/id");
digester.addBeanPropertySetter(path + "/classname");
digester.addBeanPropertySetter(path + "/superclass");
digester.addBeanPropertySetter(path + "/description");
digester.addBeanPropertySetter(path + "/displayname");
digester.addBeanPropertySetter(path + "/icon");
// TODO - for superclass, populate from description in config file, if
// exist
digester.addSetProperties(path);
digester.addSetNext(path, "addValidator");
path = "components/validator/property";
digester.addObjectCreate(path, PropertyBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addProperty");
// Converters
path = "components/converter";
digester.addObjectCreate(path, ConverterBean.class);
digester.addBeanPropertySetter(path + "/id");
digester.addBeanPropertySetter(path + "/classname");
digester.addBeanPropertySetter(path + "/superclass");
digester.addBeanPropertySetter(path + "/description");
digester.addBeanPropertySetter(path + "/displayname");
digester.addBeanPropertySetter(path + "/icon");
digester.addBeanPropertySetter(path + "/forclass");
// TODO - for superclass, populate from description in config file, if
// exist
digester.addSetProperties(path);
digester.addSetNext(path, "addConverter");
// Functions
path = "components/function";
digester.addObjectCreate(path, FunctionBean.class);
digester.addBeanPropertySetter(path + "/name");
digester.addBeanPropertySetter(path + "/description");
digester.addBeanPropertySetter(path + "/method");
digester.addSetNext(path, "addFunction");
// - Tags & Tag handlers
path = "components/component/tag";
digester.addObjectCreate(path, TagBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setTag");
path = "components/validator/tag";
digester.addObjectCreate(path, TagBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setTag");
path = "components/converter/tag";
digester.addObjectCreate(path, TagBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setTag");
path = "components/component/tag/test";
digester.addObjectCreate(path, TagTestClassHolder.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setTest");
path = "components/component/test";
digester.addObjectCreate(path, TestClassHolder.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setTest");
path = "components/component/taghandler";
digester.addObjectCreate(path, TagHandlerBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setTaghandler");
/*
* path = "components/component/taghandler/test";
* digester.addObjectCreate(path, TestClassHolder.class);
* digester.addBeanPropertySetter(path+"/?");
* digester.addSetProperties(path); digester.addSetNext(path,
* "setTest");
*/
// Properties
path = "components/component/property";
digester.addObjectCreate(path, PropertyBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addProperty");
// path = "components/validator/property";
// digester.addObjectCreate(path, PropertyBean.class);
// digester.addBeanPropertySetter(path+"/?");
// digester.addSetProperties(path);
// digester.addSetNext(path, "addProperty");
path = "components/converter/property";
digester.addObjectCreate(path, PropertyBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addProperty");
path = "*/properties/property";
digester.addObjectCreate(path, PropertyBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addProperty");
// Listeners
path = "components/listener";
digester.addObjectCreate(path, ListenerBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addListener");
path = "components/listener/tag";
digester.addObjectCreate(path, TagBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setTag");
path = "components/listener/taghandler";
digester.addObjectCreate(path, TagHandlerBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "setTaghandler");
path = "components/listener/property";
digester.addObjectCreate(path, PropertyBean.class);
digester.addBeanPropertySetter(path + "/?");
digester.addSetProperties(path);
digester.addSetNext(path, "addProperty");
// Set this config as root.
digester.push(this);
try {
digester.parse(configFile.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
throw new ParsingException("I/O error on parsing config file ", e);
} catch (SAXException e) {
// TODO Auto-generated catch block
throw new ParsingException("SAX Parsing error in config file ", e);
}
// checkComopnentProperties();
// return this;
}
/**
* Check all components for existing and default properties.
*
* @param classpath -
* classpath to find user components, renderers, tags
* @throws ConfigurationException
*/
public void checkComponentProperties() throws ParsingException {
// ClassLoader loader = getProject().createClassLoader(classpath);
// if(null == loader) {
// loader = this.getClass().getClassLoader();
// }
// setLoader(loader);
for (ListenerBean listener : getListeners()) {
try {
Class<?> listenerClass = Class.forName(listener
.getComponentclass(), false, getLoader());
for (ComponentBean bean : getComponents()) {
if (bean.getSuperclass() != null) {
Class<?> componentSClass = Class.forName(bean
.getSuperclass(), false, getLoader());
if (listenerClass.isAssignableFrom(componentSClass)) {
PropertyBean listenerProperty = bean
.getProperty(listener.getName());
if (null == listenerProperty) {
listenerProperty = new PropertyBean();
listenerProperty.setName(listener.getName());
bean.addProperty(listenerProperty);
listenerProperty.setClassname("javax.el.MethodExpression");
}
Map<String, PropertyDescriptor> map =
getPropertyDescriptors(componentSClass);
PropertyDescriptor propertyDescriptor =
map.get(listener.getName());
if (propertyDescriptor != null) {
String componentPropertyName = propertyDescriptor.getPropertyType().getName();
if (!componentPropertyName.equals(listenerProperty.getClassname())) {
_log.error(
String.format("Overriding property type %s with %s for %s.%s",
listenerProperty.getClassname(),
componentPropertyName,
bean.getClassname(),
listener.getName()
));
}
listenerProperty.setClassname(componentPropertyName);
}
// TODO - check existing property for compability with this listener.
listenerProperty.setEl(true);
listenerProperty.setElonly(true);
listenerProperty.setAttachedstate(true);
listenerProperty.setMethodargs(listener
.getEventclass());
listener.addSuitableComponent(bean);
}
}
}
} catch (ClassNotFoundException e) {
throw new BuildException(e);
}
listener.checkProperties();
}
for (Iterator iter = this.getComponents().iterator(); iter.hasNext();) {
ComponentBaseBean component = (ComponentBaseBean) iter.next();
component.checkProperties();
}
for (Iterator iter = this.getValidators().iterator(); iter.hasNext();) {
ComponentBaseBean component = (ComponentBaseBean) iter.next();
component.checkProperties();
}
for (Iterator iter = this.getConverters().iterator(); iter.hasNext();) {
ComponentBaseBean component = (ComponentBaseBean) iter.next();
component.checkProperties();
}
}
private Map<String, PropertyDescriptor> getPropertyDescriptors(Class<?> clazz) {
if (clazz.equals(Object.class)) {
return Collections.emptyMap();
}
Map<String, PropertyDescriptor> m =
new TreeMap<String, PropertyDescriptor>();
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
m.putAll(getPropertyDescriptors(superclass));
}
Class<?>[] interfaces = clazz.getInterfaces();
if (interfaces != null) {
for (Class<?> intrfc : interfaces) {
m.putAll(getPropertyDescriptors(intrfc));
}
}
PropertyDescriptor[] descriptors =
PropertyUtils.getPropertyDescriptors(clazz);
for (PropertyDescriptor propertyDescriptor : descriptors) {
m.put(propertyDescriptor.getName(), propertyDescriptor);
}
return m;
}
/*
* (non-Javadoc)
*
* @see com.exadel.vcp.builder.config.LoaderHolder#getLoader()
*/
public ClassLoader getLoader() {
return _loader;
}
/*
* (non-Javadoc)
*
* @see com.exadel.vcp.builder.config.LoaderHolder#setLoader(java.lang.ClassLoader)
*/
public void setLoader(ClassLoader loader) {
_loader = loader;
}
public Logger getLog() {
return _log;
}
public void addComponent(ComponentBean component) {
this.components.add(component);
component.setParent(this);
}
public void addValidator(ValidatorBean validator) {
this.validators.add(validator);
validator.setParent(this);
}
public void addConverter(ConverterBean converter) {
this.converters.add(converter);
converter.setParent(this);
}
public void addRenderer(RendererBean renderer) {
this.renderers.add(renderer);
renderer.setParent(this);
}
public void addListener(ListenerBean listener) {
this.listeners.add(listener);
listener.setParent(this);
}
public void addFunction(FunctionBean function) {
this.functions.add(function);
function.setParent(this);
}
/*
* (non-Javadoc)
*
* @see java.util.List#clear()
*/
public void clear() {
components.clear();
renderers.clear();
}
/*
* (non-Javadoc)
*
* @see java.util.List#contains(java.lang.Object)
*/
public boolean contains(Object o) {
return components.contains(o);
}
/*
* (non-Javadoc)
*
* @see java.util.List#get(int)
*/
public ComponentBaseBean get(int index) {
return components.get(index);
}
/*
* (non-Javadoc)
*
* @see java.util.List#indexOf(java.lang.Object)
*/
public int indexOf(ComponentBaseBean o) {
return components.indexOf(o);
}
/*
* (non-Javadoc)
*
* @see java.util.List#isEmpty()
*/
public boolean isEmpty() {
return components.isEmpty();
}
/*
* (non-Javadoc)
*
* @see java.util.List#listIterator()
*/
public List<ComponentBean> getComponents() {
return components;
}
public List<ConverterBean> getConverters() {
return converters;
}
public List<ValidatorBean> getValidators() {
return validators;
}
public List<ListenerBean> getListeners() {
return listeners;
}
/**
* @return the renderers
*/
public List<RendererBean> getRenderers() {
return this.renderers;
}
public List<FunctionBean> getFunctions() {
return functions;
}
/*
* (non-Javadoc)
*
* @see java.util.List#remove(int)
*/
public ComponentBaseBean remove(int index) {
return components.remove(index);
}
/*
* (non-Javadoc)
*
* @see java.util.List#remove(java.lang.Object)
*/
public boolean remove(ComponentBaseBean o) {
return components.remove(o);
}
/*
* (non-Javadoc)
*
* @see java.util.List#size()
*/
public int size() {
return components.size();
}
}