/* * $Id$ * * 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.richfaces.cdk.templatecompiler; import static org.richfaces.cdk.apt.CacheType.NON_JAVA_SOURCES; import java.io.File; import java.io.FileNotFoundException; import java.text.MessageFormat; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.richfaces.cdk.Cache; import org.richfaces.cdk.CdkException; import org.richfaces.cdk.FileManager; import org.richfaces.cdk.Logger; import org.richfaces.cdk.ModelBuilder; import org.richfaces.cdk.Source; import org.richfaces.cdk.Sources; import org.richfaces.cdk.apt.LibraryCache; import org.richfaces.cdk.model.ClassName; import org.richfaces.cdk.model.ComponentLibrary; import org.richfaces.cdk.model.EventName; import org.richfaces.cdk.model.FacesId; import org.richfaces.cdk.model.MethodSignature; import org.richfaces.cdk.model.PropertyBase; import org.richfaces.cdk.model.PropertyModel; import org.richfaces.cdk.model.RenderKitModel; import org.richfaces.cdk.model.RendererModel; import org.richfaces.cdk.model.SimpleVisitor; import org.richfaces.cdk.templatecompiler.model.Attribute; import org.richfaces.cdk.templatecompiler.model.ClientBehavior; import org.richfaces.cdk.templatecompiler.model.CompositeInterface; import org.richfaces.cdk.templatecompiler.model.ImportAttributes; import org.richfaces.cdk.templatecompiler.model.Template; import org.richfaces.cdk.util.Strings; import org.richfaces.cdk.xmlconfig.FragmentParser; import org.richfaces.cdk.xmlconfig.JAXB; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.inject.Inject; /** * <p class="changed_added_4_0"> * </p> * * @author asmirnov@exadel.com */ public class RendererTemplateParser implements ModelBuilder { private static final Pattern PARAMETERS_STRING_PATTERN = Pattern.compile("^(\\S+)\\s+(\\S+)\\s*\\(([^\\)]*)\\)$", Pattern.COMMENTS); private static final Pattern COMMA_SEPARATED_PATTERN = Pattern.compile("\\s*,\\s*", Pattern.COMMENTS); private ComponentLibrary library; private JAXB jaxbBinding; private Logger log; private FileManager sources; private FragmentParser fragmentParser; @Inject @Cache(NON_JAVA_SOURCES) public LibraryCache nonJavaCache; /** * <p class="changed_added_4_0"> * </p> * * @param library * @param jaxbBinding * @param log * @param sources */ @Inject public RendererTemplateParser(ComponentLibrary library, JAXB jaxbBinding, Logger log, @Source(Sources.RENDERER_TEMPLATES) FileManager sources, FragmentParser fragmentParser) { this.library = library; this.jaxbBinding = jaxbBinding; this.log = log; this.sources = sources; this.fragmentParser = fragmentParser; } /* * (non-Javadoc) * * @see org.richfaces.cdk.ModelBuilder#build() */ @Override public void build() throws CdkException { Iterable<File> sourceFiles = this.sources.getFiles(); for (File file : sourceFiles) { if (nonJavaCache.storedBefore(file.lastModified())) { build(file); } } } public void build(final File file) throws CdkException { log.debug("RendererTemplateParser.build"); final String absolutePath = file.getAbsolutePath(); log.debug(" - file = " + absolutePath); try { RendererModel existedModel = library.accept(new SimpleVisitor<RendererModel, String>() { @Override public RendererModel visitRender(RendererModel model, String absolutePath) { Template template = model.getTemplate(); if (null != template) { if (absolutePath.equals(template.getTemplatePath())) { throw new AlreadyProcessedException(); } } else if (null != model.getTemplatePath()) { try { if (file.equals(sources.getFile(model.getTemplatePath()))) { return model; } } catch (FileNotFoundException e) { throw new CdkException("Template file defined in Renderer not found: " + model.getTemplatePath(), e); } } return null; } }, absolutePath); Template template = parseTemplate(file); mergeTemplateIntoModel(template, existedModel); } catch (AlreadyProcessedException e) { log.warn("Template " + absolutePath + "was already processed"); } } protected RendererModel mergeTemplateIntoModel(Template template, RendererModel renderer) throws CdkException { CompositeInterface compositeInterface = template.getInterface(); if (renderer == null) { renderer = new RendererModel(); RenderKitModel renderKit = library.addRenderKit(compositeInterface.getRenderKitId()); renderKit.getRenderers().add(renderer); } renderer.setTemplate(template); setRendererType(compositeInterface, renderer); setFamily(compositeInterface, renderer); setRendererClass(compositeInterface, renderer); setRendererBaseClass(compositeInterface, renderer); Boolean rendersChildren = compositeInterface.getRendersChildren(); if (rendersChildren != null) { renderer.setRendersChildren(rendersChildren); } List<ImportAttributes> attributesImports = compositeInterface.getAttributesImports(); for (ImportAttributes attributesImport : attributesImports) { String importURI = attributesImport.getSource(); Collection<PropertyBase> properties; try { properties = fragmentParser.parseProperties(importURI); if (properties != null) { renderer.getAttributes().addAll(properties); } } catch (FileNotFoundException e) { throw new CdkException("File for import not found", e); } } List<Attribute> templateAttributes = compositeInterface.getAttributes(); for (Attribute templateAttribute : templateAttributes) { renderer.getAttributes().add(buildProperty(templateAttribute)); } return renderer; } private PropertyModel buildProperty(Attribute templateAttribute) { PropertyModel rendererProperty = new PropertyModel(); rendererProperty.setName(templateAttribute.getName()); rendererProperty.setDefaultValue(templateAttribute.getDefaultValue()); rendererProperty.setDescription(templateAttribute.getShortDescription()); rendererProperty.setDisplayName(templateAttribute.getDisplayName()); Set<EventName> eventNamesSet = convertBehaviorsToEvents(templateAttribute.getClientBehaviors()); rendererProperty.getEventNames().addAll(eventNamesSet); Boolean required = templateAttribute.getRequired(); if (null != required) { rendererProperty.setRequired(required); } MethodSignature parsedSignature = parseSignature(templateAttribute.getMethodSignature()); rendererProperty.setSignature(parsedSignature); rendererProperty.setType(templateAttribute.getType()); return rendererProperty; } private Set<EventName> convertBehaviorsToEvents(Collection<ClientBehavior> clientBehaviors) { Set<EventName> result = Sets.newLinkedHashSet(); for (ClientBehavior clientBehavior : clientBehaviors) { EventName eventName = new EventName(); eventName.setName(clientBehavior.getEvent()); eventName.setDefaultEvent(clientBehavior.isDefaultEvent()); result.add(eventName); } return result; } private MethodSignature parseSignature(String signatureString) { List<ClassName> parameters = Lists.newArrayList(); MethodSignature signature = null; if (!Strings.isEmpty(signatureString)) { Matcher parametersStringMatcher = PARAMETERS_STRING_PATTERN.matcher(signatureString); if (!parametersStringMatcher.find()) { throw new IllegalArgumentException(MessageFormat.format("Signature string {0} cannot be parsed!", signatureString)); } signature = new MethodSignature(); signature.setReturnType(ClassName.parseName(parametersStringMatcher.group(1))); String parametersString = parametersStringMatcher.group(3); if (parametersString.trim().length() != 0) { String[] parameterStrings = COMMA_SEPARATED_PATTERN.split(parametersString); for (String parameter : parameterStrings) { String trimmedParameter = parameter.trim(); parameters.add(ClassName.parseName(trimmedParameter)); } signature.setParameters(parameters); } } return signature; } private void setRendererClass(CompositeInterface compositeInterface, RendererModel renderer) { ClassName javaClass = compositeInterface.getJavaClass(); if (null != javaClass) { renderer.setRendererClass(javaClass); } } private void setRendererBaseClass(CompositeInterface compositeInterface, RendererModel renderer) { ClassName baseClass = compositeInterface.getBaseClass(); if (null != baseClass) { renderer.setBaseClass(baseClass); } } private void setFamily(CompositeInterface compositeInterface, RendererModel renderer) { FacesId componentFamily = compositeInterface.getComponentFamily(); if (null != componentFamily) { renderer.setFamily(componentFamily); } } private void setRendererType(CompositeInterface compositeInterface, RendererModel renderer) { FacesId rendererType = compositeInterface.getRendererType(); if (null != rendererType) { renderer.setId(rendererType); } } protected Template parseTemplate(File file) throws CdkException { try { Template template = jaxbBinding.unmarshal(file, "http://jboss.org/schema/richfaces/cdk/cdk-template.xsd", Template.class); template.setTemplatePath(file.getAbsolutePath()); return template; } catch (FileNotFoundException e) { throw new CdkException("Template file not found " + file.getAbsolutePath(), e); } } }