blob: e622f15927ff9fdff1738fcbe1f881c0359acbe5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.buildtools.maven2.plugin.builder;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.AttributeHolder;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.AttributeMeta;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.BehaviorMeta;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.ComponentMeta;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.ConverterMeta;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.FaceletTagMeta;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.MethodSignatureMeta;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.Model;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.ModelUtils;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.PropertyHolder;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.PropertyMeta;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.TagMeta;
import org.apache.myfaces.buildtools.maven2.plugin.builder.model.ValidatorMeta;
/**
*/
public class Flattener
{
private final Log log = LogFactory.getLog(Flattener.class);
private Model model;
private Set flattened = new HashSet();
public Flattener(Model model)
{
this.model = model;
}
/**
* Flatten the specified model.
* <p>
* In the flattened representation, each model object directly contains the
* data that it inherits from its parents, so that the getter methods return
* all available metadata, not just the data that was defined directly on
* that item.
*/
public void flatten()
{
flattenComponentProperties();
flattenValidatorProperties();
flattenConverterProperties();
flattenBehaviorProperties();
flattenTagAttributes();
flattenFaceletTagAttributes();
}
private void flattenComponentProperties()
{
List components = model.getComponents();
for (Iterator i = components.iterator(); i.hasNext();)
{
ComponentMeta comp = (ComponentMeta) i.next();
flattenComponent(comp);
}
}
private void flattenComponent(ComponentMeta component)
{
if (flattened.contains(component))
{
// already done
return;
}
String parentClassName = component.getParentClassName();
if (parentClassName != null)
{
ComponentMeta parent = model
.findComponentByClassName(parentClassName);
if (parent != null)
{
flattenComponent(parent);
component.merge(parent);
}
else
{
//How to manage a component that its
//parent class is not a real component?
//Use UIComponent instead and log a warn
//so if needed we can fix this.
log.warn("Component:"+component.getClassName()+
" without a parent defined as component, using " +
"UIComponent");
parent = model
.findComponentByClassName("javax.faces.component.UIComponent");
flattenComponent(parent);
component.merge(parent);
}
}
List interfaceClassNames = component.getInterfaceClassNames();
for (Iterator i = interfaceClassNames.iterator(); i.hasNext();)
{
String ifaceClassName = (String) i.next();
ComponentMeta iface = model
.findComponentByClassName(ifaceClassName);
flattenComponent(iface);
component.merge(iface);
}
flattened.add(component);
}
private void flattenValidatorProperties()
{
List validator = model.getValidators();
for (Iterator i = validator.iterator(); i.hasNext();)
{
ValidatorMeta val = (ValidatorMeta) i.next();
flattenValidator(val);
}
}
private void flattenValidator(ValidatorMeta validator)
{
if (flattened.contains(validator))
{
// already done
return;
}
String parentClassName = validator.getParentClassName();
if (parentClassName != null)
{
ValidatorMeta parent = model
.findValidatorByClassName(parentClassName);
if (parent != null)
{
flattenValidator(parent);
validator.merge(parent);
}
else
{
//How to manage a validator that its
//parent class is not a real validator?
//Ans: no problem, do nothing.
}
}
flattened.add(validator);
}
private void flattenConverterProperties()
{
List converter = model.getConverters();
for (Iterator i = converter.iterator(); i.hasNext();)
{
ConverterMeta val = (ConverterMeta) i.next();
flattenConverter(val);
}
}
private void flattenConverter(ConverterMeta converter)
{
if (flattened.contains(converter))
{
// already done
return;
}
String parentClassName = converter.getParentClassName();
if (parentClassName != null)
{
ConverterMeta parent = model
.findConverterByClassName(parentClassName);
if (parent != null)
{
flattenConverter(parent);
converter.merge(parent);
}
else
{
//How to manage a validator that its
//parent class is not a real validator?
//Ans: no problem, do nothing.
}
}
flattened.add(converter);
}
private void flattenTagAttributes()
{
List tags = model.getTags();
for (Iterator i = tags.iterator(); i.hasNext();)
{
TagMeta val = (TagMeta) i.next();
flattenTag(val);
}
}
/**
* This method allows component tag classes to be
* used with tags, so it is possible to inherit attribute
* for component tags. But tags cannot inherit
* attributes from other tags.
*
*/
private void flattenTag(TagMeta tag)
{
if (flattened.contains(tag))
{
// already done
return;
}
if (tag.getSourceClassParentClassName() == null)
{
//No need to scan
return;
}
ComponentMeta component = model.findComponentByTagClassName(
tag.getSourceClassParentClassName());
if (null != component)
{
Collection propertyList = component.getPropertyList();
for (Iterator it = propertyList.iterator(); it.hasNext();)
{
PropertyMeta property = (PropertyMeta) it.next();
//Just add all non tag excluded properties.
if (!property.isTagExcluded().booleanValue())
{
AttributeMeta attribute = new AttributeMeta();
if (property.isMethodExpression())
{
attribute.setClassName("javax.el.MethodExpression");
MethodSignatureMeta sig = property.getMethodBindingSignature();
attribute.setDeferredMethodSignature(
sig.getReturnType()+" myMethod("+sig.getParameterTypesAsString()+")");
}
else if (null == property.isRtexprvalue() || !property.isRtexprvalue().booleanValue())
{
attribute.setDeferredValueType(property.getClassName());
attribute.setClassName("javax.el.ValueExpression");
}
else
{
//rtexprvalue = true, set className as expected
attribute.setClassName(property.getClassName());
}
attribute.setRtexprvalue(property.isRtexprvalue());
attribute.setDescription(property.getDescription());
attribute.setLongDescription(property.getLongDescription());
attribute.setName(property.getJspName());
attribute.setRequired(property.isRequired());
//just add attribute to tag
AttributeMeta attributeInTag = tag.getAttribute(attribute.getName());
if (attributeInTag != null)
{
//attributeInTag takes precedence, so
//we have to merge and copy
attribute.merge(attributeInTag);
attributeInTag.copy(attribute);
}
else
{
//Add it to tag
tag.addAttribute(attribute);
}
}
}
}
flattened.add(tag);
}
/**
* @since 1.0.4
**/
private void flattenFaceletTagAttributes()
{
List faceletTags = model.getFaceletTags();
for (Iterator i = faceletTags.iterator(); i.hasNext();)
{
FaceletTagMeta val = (FaceletTagMeta) i.next();
flattenFaceletTag(val);
}
}
/**
* @since 1.0.6
**/
private void flattenBehaviorProperties()
{
List behavior = model.getBehaviors();
for (Iterator i = behavior.iterator(); i.hasNext();)
{
BehaviorMeta val = (BehaviorMeta) i.next();
flattenBehavior(val);
}
}
/**
* @since 1.0.6
**/
private void flattenBehavior(BehaviorMeta behavior)
{
if (flattened.contains(behavior))
{
// already done
return;
}
String parentClassName = behavior.getParentClassName();
if (parentClassName != null)
{
BehaviorMeta parent = model
.findBehaviorByClassName(parentClassName);
if (parent != null)
{
flattenBehavior(parent);
behavior.merge(parent);
}
else
{
//How to manage a validator that its
//parent class is not a real validator?
//Ans: no problem, do nothing.
}
}
flattened.add(behavior);
}
/**
* This method allows component facelet tag classes to be
* used with component and jsp tags, so it is possible to
* inherit attribute for component. But facelet tags can inherit
* attributes from other facelet tags.
*
* @since 1.0.4
*
*/
private void flattenFaceletTag(FaceletTagMeta faceletTag)
{
if (flattened.contains(faceletTag))
{
// already done
return;
}
if (faceletTag.getSourceClassParentClassName() == null)
{
//No need to scan
return;
}
// In order of precedence
// 1. Merge base parent tag handler
String parentClassName = faceletTag.getParentClassName();
if (parentClassName != null)
{
FaceletTagMeta parent = model
.findFaceletTagByClassName(parentClassName);
if (parent != null)
{
flattenFaceletTag(parent);
faceletTag.merge(parent);
}
else
{
//do nothing.
}
}
//1.1 Mark all attributes on the facelets tag hierarchy as faceletsOnly
for (Iterator it = faceletTag.attributes(); it.hasNext();)
{
AttributeMeta a = (AttributeMeta) it.next();
a.setFaceletsOnly(Boolean.TRUE);
}
// 2. Merge from tag class
if (faceletTag.getTagClass() != null)
{
TagMeta tag = model.findTagByClassName(faceletTag.getTagClass());
if (null != tag)
{
if (tag.getLongDescription() != null)
{
faceletTag.setLongDescription(
tag.getLongDescription()+"<p>"+faceletTag.getLongDescription()+"</p>");
}
if (tag.getDescription() != null)
{
faceletTag.setDescription(
tag.getDescription()+" "+faceletTag.getDescription());
}
ModelUtils.mergeAttributes(faceletTag, tag);
}
}
// 3. Merge from converter/validator/component class
if (faceletTag.getConverterClass() != null)
{
ConverterMeta converter = model.findConverterByClassName(
faceletTag.getConverterClass());
if (null != converter)
{
if (converter.getLongDescription() != null)
{
faceletTag.setLongDescription(
converter.getLongDescription()+"<p>"+faceletTag.getLongDescription()+"</p>");
}
if (converter.getDescription() != null)
{
faceletTag.setDescription(
converter.getDescription()+" "+faceletTag.getDescription());
}
addOrMergePropertiesToAttributeHolder(faceletTag, converter);
}
}
if (faceletTag.getValidatorClass() != null)
{
ValidatorMeta validator = model.findValidatorByClassName(
faceletTag.getValidatorClass());
if (null != validator)
{
if (validator.getLongDescription() != null)
{
faceletTag.setLongDescription(
validator.getLongDescription()+"<p>"+faceletTag.getLongDescription()+"</p>");
}
if (validator.getDescription() != null)
{
faceletTag.setDescription(
validator.getDescription()+" "+faceletTag.getDescription());
}
addOrMergePropertiesToAttributeHolder(faceletTag, validator);
}
}
if (faceletTag.getBehaviorClass() != null)
{
BehaviorMeta behavior = model.findBehaviorByClassName(
faceletTag.getBehaviorClass());
if (null != behavior)
{
if (behavior.getLongDescription() != null)
{
faceletTag.setLongDescription(
behavior.getLongDescription()+"<p>"+faceletTag.getLongDescription()+"</p>");
}
if (behavior.getDescription() != null)
{
faceletTag.setDescription(
behavior.getDescription()+" "+faceletTag.getDescription());
}
addOrMergePropertiesToAttributeHolder(faceletTag, behavior);
}
}
if (faceletTag.getComponentClass() != null)
{
ComponentMeta component = model.findComponentByClassName(
faceletTag.getComponentClass());
if (null != component)
{
if (component.getLongDescription() != null)
{
faceletTag.setLongDescription(
component.getLongDescription()+"<p>"+faceletTag.getLongDescription()+"</p>");
}
if (component.getDescription() != null)
{
faceletTag.setDescription(
component.getDescription()+" "+faceletTag.getDescription());
}
addOrMergePropertiesToAttributeHolder(faceletTag, component);
}
}
flattened.add(faceletTag);
}
/**
* @since 1.0.4
**/
private void addOrMergePropertiesToAttributeHolder(AttributeHolder attributeHolder, PropertyHolder propertyHolder)
{
Collection propertyList = propertyHolder.getProperties().values();
for (Iterator it = propertyList.iterator(); it.hasNext();)
{
PropertyMeta property = (PropertyMeta) it.next();
//Just add all non tag excluded properties.
if (!property.isTagExcluded().booleanValue())
{
AttributeMeta attribute = new AttributeMeta();
if (property.isMethodExpression())
{
attribute.setClassName("javax.el.MethodExpression");
MethodSignatureMeta sig = property.getMethodBindingSignature();
attribute.setDeferredMethodSignature(
sig.getReturnType()+" myMethod("+sig.getParameterTypesAsString()+")");
}
else if (null == property.isRtexprvalue() || !property.isRtexprvalue().booleanValue())
{
attribute.setDeferredValueType(property.getClassName());
attribute.setClassName("javax.el.ValueExpression");
}
else
{
//rtexprvalue = true, set className as expected
attribute.setClassName(property.getClassName());
}
attribute.setRtexprvalue(property.isRtexprvalue());
attribute.setDescription(property.getDescription());
attribute.setLongDescription(property.getLongDescription());
attribute.setName(property.getJspName());
attribute.setRequired(property.isRequired());
attribute.setFaceletsOnly(property.isFaceletsOnly());
//just add attribute to tag
AttributeMeta attributeInTag = attributeHolder.getAttribute(attribute.getName());
if (attributeInTag != null)
{
//attributeInTag takes precedence, so
//we have to merge and copy
attribute.merge(attributeInTag);
attributeInTag.copy(attribute);
}
else
{
//Add it to tag
attributeHolder.addAttribute(attribute);
}
}
}
}
}