/**
* 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.generator;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.ajax4jsf.builder.config.BuilderConfig;
import org.ajax4jsf.builder.config.ComponentBean;
import org.ajax4jsf.builder.config.RendererBean;
import org.ajax4jsf.builder.config.TagBean;
import org.ajax4jsf.builder.config.TagHandlerBean;
import org.ajax4jsf.templatecompiler.builder.CompilationContext;
import org.ajax4jsf.templatecompiler.builder.CompilationException;
import org.ajax4jsf.templatecompiler.builder.TemplateCompiler;
import org.ajax4jsf.templatecompiler.elements.RootElement;
import org.ajax4jsf.templatecompiler.elements.TemplateElement;
import org.ajax4jsf.templatecompiler.elements.vcp.FResourceTemplateElement;
import org.ajax4jsf.templatecompiler.elements.vcp.HeaderResourceElement;
import org.ajax4jsf.templatecompiler.elements.vcp.HeaderScriptsElement;
import org.ajax4jsf.templatecompiler.elements.vcp.HeaderStylesElement;
/**
* @author Nick Belaevski
* @since 3.3.0
*/
public class ResourcesConfigParser {
private JSFGeneratorConfiguration config;
private Logger log;
private File templates;
private ResourcesConfigGeneratorBean resourcesConfigGeneratorBean;
private Map<String, ResourcesConfigGeneratorBean> componentResourcesMap;
public ResourcesConfigParser(JSFGeneratorConfiguration config, Logger log) {
super();
this.config = config;
this.log = log;
this.resourcesConfigGeneratorBean = new ResourcesConfigGeneratorBean();
this.componentResourcesMap = new HashMap<String, ResourcesConfigGeneratorBean>();
}
public File getTemplates() {
return templates;
}
public void setTemplates(File templates) {
this.templates = templates;
}
private void addResources(ResourcesConfigGeneratorBean configBean,
TemplateElement templateElement, String packageName,
RendererBean renderer, BuilderConfig builderConfig)
throws ClassNotFoundException {
if (templateElement instanceof FResourceTemplateElement) {
FResourceTemplateElement resourceTemplateElement = (FResourceTemplateElement) templateElement;
String name = resourceTemplateElement.getName();
configBean.addResource(name, packageName, null, true);
} else if (templateElement instanceof RootElement) {
RootElement rootElement = (RootElement) templateElement;
CompilationContext compilationContext = rootElement
.getComponentBean();
addResource(configBean, renderer, builderConfig, compilationContext);
} else if (templateElement instanceof HeaderResourceElement) {
HeaderResourceElement resourceElement = (HeaderResourceElement) templateElement;
ResourceType type = null;
if (templateElement instanceof HeaderScriptsElement) {
type = ResourceType.SCRIPT;
} else if (templateElement instanceof HeaderStylesElement) {
type = ResourceType.STYLE;
}
String[] paths = resourceElement.getResourcePaths();
if (paths != null) {
for (int i = 0; i < paths.length; i++) {
String string = paths[i];
configBean.addResource(string, packageName, type, false);
}
}
}
ArrayList<TemplateElement> subElements = templateElement
.getSubElements();
for (TemplateElement element : subElements) {
addResources(configBean, element, packageName, renderer,
builderConfig);
}
}
private String getResourceKey(String packageName, Object resource) {
try {
Class<? extends Object> resourceClass = resource.getClass();
Method method = resourceClass.getMethod("getKey");
String resourceKey = (String) method.invoke(resource);
if (resourceKey != null && !resourceKey.equals(resourceClass.getName())) {
ClassLoader loader = config.getClassLoader();
if (loader.getResource(resourceKey) == null) {
String packagePath = packageName.replace('.', '/');
if (packagePath.length() != 0) {
packagePath += "/";
}
if (loader.getResource(packagePath + resourceKey) == null) {
resourceKey = null;
}
} else {
if (!resourceKey.startsWith("/")) {
resourceKey = "/" + resourceKey;
}
}
}
return resourceKey;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
private String addResource(ResourcesConfigGeneratorBean configBean,
RendererBean renderer, BuilderConfig builderConfig,
CompilationContext compilationContext)
throws ClassNotFoundException {
String packageName;
String classname = renderer.getClassname();
int idx = classname.lastIndexOf('.');
if (idx != -1) {
packageName = classname.substring(0, idx);
} else {
packageName = "";
}
ClassLoader loader = builderConfig.getLoader();
Class<?> cl = null;
try {
cl = loader.loadClass(classname);
} catch (ClassNotFoundException e) {
String superclass = compilationContext.getBaseclassPackageName()
+ "." + compilationContext.getBaseclassName();
if (superclass != null) {
cl = loader.loadClass(superclass);
}
}
if (cl != null) {
try {
GetResourceInterceptor interceptor = new GetResourceInterceptor(builderConfig.getLoader());
Object instance = AbstractClassStubBuilder.buildStub(cl,
interceptor, builderConfig.getLoader());
//clear list - some resources could be requested during class/instance initialization
Object[] result = null;
Method method = null;
Set<String> locatedResources = new HashSet<String>();
Class<?> cl1 = instance.getClass();
while (cl1 != null && method == null) {
try {
method = cl1.getDeclaredMethod("getStyles");
} catch (NoSuchMethodException e) {
cl1 = cl1.getSuperclass();
}
}
if (method != null) {
method.setAccessible(true);
result = (Object[]) method.invoke(instance);
if (result instanceof Object[]) {
for (Object object : result) {
String resourceName = interceptor.getResourceName(object);
if (resourceName == null) {
resourceName = getResourceKey(packageName, object);
}
if (resourceName != null) {
locatedResources.add(resourceName);
configBean.addResource(resourceName, packageName, ResourceType.STYLE,
false);
}
}
}
}
method = null;
cl1 = instance.getClass();
while (cl1 != null && method == null) {
try {
method = cl1.getDeclaredMethod("getScripts");
} catch (NoSuchMethodException e) {
cl1 = cl1.getSuperclass();
}
}
if (method != null) {
method.setAccessible(true);
result = (Object[]) method.invoke(instance);
if (result instanceof Object[]) {
for (Object object : result) {
String resourceName = interceptor.getResourceName(object);
if (resourceName == null) {
resourceName = getResourceKey(packageName, object);
}
if (resourceName != null) {
locatedResources.add(resourceName);
configBean.addResource(resourceName, packageName, ResourceType.SCRIPT,
false);
}
}
}
List<String> list = interceptor.getList();
if (list != null) {
list.removeAll(locatedResources);
for (String resourceName : list) {
log.warn("Error detecting resource type: " + resourceName);
configBean.addResource(resourceName, packageName, null,
false);
}
}
}
} catch (SecurityException e) {
builderConfig.getLog().error(e.getMessage(), e);
} catch (IllegalArgumentException e) {
builderConfig.getLog().error(e.getMessage(), e);
} catch (IllegalAccessException e) {
builderConfig.getLog().error(e.getMessage(), e);
} catch (InvocationTargetException e) {
builderConfig.getLog().error(e.getMessage(), e);
}
}
return packageName;
}
private void addResources(ResourcesConfigGeneratorBean configBean,
RendererBean renderer, BuilderConfig builderConfig)
throws CompilationException, IOException, ClassNotFoundException {
if (null != renderer) {
CompilationContext compilationContext = new RendererCompilationContext(
log, config.getClassLoader(), config);
if (renderer.isGenerate()) {
String templateString = renderer.getTemplate();
if (templateString != null) {
File template;
if (null != getTemplates()) {
template = new File(getTemplates(), templateString);
} else {
template = new File(templateString);
}
TemplateCompiler templateCompiler = new TemplateCompiler();
InputStream templateStream = new FileInputStream(template);
templateCompiler.processing(templateStream,
compilationContext);
TemplateElement root = compilationContext.getTree();
String classname = renderer.getClassname();
String packageName;
int idx = classname.lastIndexOf('.');
if (idx != -1) {
packageName = classname.substring(0, idx);
} else {
packageName = "";
}
addResources(configBean, root, packageName, renderer,
builderConfig);
}
} else {
addResource(configBean, renderer, builderConfig,
compilationContext);
}
}
}
public void parse(BuilderConfig config) throws CompilationException, IOException, ClassNotFoundException {
List<ComponentBean> components = config.getComponents();
for (ComponentBean componentBean : components) {
ResourcesConfigGeneratorBean localBeanInstance = new ResourcesConfigGeneratorBean();
RendererBean rendererBean = componentBean.getRenderer();
addResources(localBeanInstance, rendererBean, config);
String componentName = null;
TagBean tag = componentBean.getTag();
if (tag != null) {
componentName = tag.getName();
}
if (componentName == null) {
TagHandlerBean taghandler = componentBean.getTaghandler();
if (taghandler != null) {
componentName = taghandler.getName();
}
}
if (componentName != null) {
this.componentResourcesMap.put(componentName, localBeanInstance);
}
this.resourcesConfigGeneratorBean.merge(localBeanInstance);
}
List<RendererBean> renderers = config.getRenderers();
for (RendererBean rendererBean : renderers) {
addResources(this.resourcesConfigGeneratorBean, rendererBean, config);
}
}
public ResourcesConfigGeneratorBean getResourcesConfigGeneratorBean() {
return resourcesConfigGeneratorBean;
}
public Map<String, ResourcesConfigGeneratorBean> getComponentResourcesMap() {
return componentResourcesMap;
}
}
class GetResourceInterceptor implements MethodInterceptor {
private static final Class<?>[] SIGNATURE = new Class<?>[] { String.class };
private ClassLoader classLoader;
private List<String> list = new ArrayList<String>();
private Map<String, Object> resources = new HashMap<String, Object>();
private Map<Object, String> resourcesInverse = new IdentityHashMap<Object, String>();
public GetResourceInterceptor(ClassLoader loader) {
this.classLoader = loader;
}
public List<String> getList() {
return list;
}
public String getResourceName(Object resource) {
return resourcesInverse.get(resource);
}
public void clearList() {
list.clear();
}
public Object intercept(Object instance, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
if ("getResource".equals(method.getName())
&& Arrays.equals(SIGNATURE, method.getParameterTypes())) {
String resourceName = (String) args[0];
list.add(resourceName);
Object resource = resources.get(resourceName);
if (resource == null) {
Class<?> returnType = method.getReturnType();
if (returnType.isInterface()) {
Object interfaceStub = AbstractClassStubBuilder.buildInterfaceStub(returnType, classLoader);
resources.put(resourceName, interfaceStub);
resourcesInverse.put(interfaceStub, resourceName);
return interfaceStub;
}
}
return null;
} else {
return methodProxy.invokeSuper(instance, args);
}
}
}