blob: 1d42db6dec9df086d8a8a4baa5f305334bf2bab3 [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.model;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.digester.Digester;
import org.apache.commons.lang.StringUtils;
import org.apache.myfaces.buildtools.maven2.plugin.builder.io.XmlWriter;
/**
* Base class for various metadata (model) classes.
* <p>
* This holds metadata common to all model classes that represent a JSF entity,
* eg a component, converter, validator, renderer. In all these cases, the class has a
* type, a parent and optionally a list of implemented interfaces.
* <p>
* Instances of this type are intended primarily to map to entries in generated
* JSP TLD files, faces-config.xml files and similar. The "class" attribute on
* this class contains the name of the java class that implements that entity.
* <p>
* However there are also instances of this class that represent "abstract"
* entities that are not directly referenced by TLD or faces-config files; they
* exist to represent ancestor classes of the concrete entities.
* <p>
* The instances are created via annotations on Java classes. Therefore each
* ClassMeta has a "sourceClass" property that records the name of the annotated
* class. In simple cases, "class" and "sourceClass" are the same, ie the JSF entity
* <i>is</i> the class that holds the relevant annotations. But this is not true in
* the case of "code generation"; here the class property references the actual JSF
* entity (the generated class).
* <p>
* As described in the documentation for the parent attribute, the ClassMeta objects
* describe a java inheritance tree, but it is not quite the same as the real java
* inheritance hierarchy for the annotated java classes. The ClassMeta data omits
* any classes in the hierarchy that are not annotated, as they are not relevant
* for the purposes of metadata inheritance.
*/
public class ClassMeta
{
private String _xmlElementName;
private String _className;
private String _parentClassName;
private List _interfaceClassNames = new ArrayList();
private String _modelId;
private String _sourceClassName;
private String _sourceClassParentClassName;
/**
* Add digester rules to repopulate an instance of this type from an xml
* file.
*/
public static void addXmlRules(Digester digester, String prefix)
{
digester.addBeanPropertySetter(prefix + "/modelId");
digester.addBeanPropertySetter(prefix + "/className");
digester.addBeanPropertySetter(prefix + "/parentClassName");
digester.addBeanPropertySetter(prefix + "/sourceClassName");
digester.addBeanPropertySetter(prefix + "/sourceClassParentClassName");
digester.addCallMethod(prefix + "/interfaces/interface",
"addInterfaceClassName", 1);
digester.addCallParam(prefix + "/interfaces/interface", 0, "name");
}
/**
* Constructor.
*
* Param xmlElementName is the name of the xml element that is created
* when method writeXml is invoked.
*/
protected ClassMeta(String xmlElementName)
{
_xmlElementName = xmlElementName;
}
/**
* Write the properties of this instance out as xml.
* <p>
* The name of the xml element that is created to hold the properties
* was specified when the constructor was called.
* <p>
* Subclasses that want to output their own properties should not
* override this method. Instead, they should override writeXmlSimple
* (and in rare cases writeXmlComplex).
* <p>
* Having two write methods (writeXmlSimple/writeXmlComplex) gives some basic
* control over the order in which data is written to xml, in order to make
* the generated xml look nice. Any properties written in writeXmlSimple will
* appear in the output file before properties written by writeXmlComplex.
* Therefore, properties which are "easily read" should be written out in
* a writeXmlSimple method. Data which has large CDATA blocks, or complicated
* nested structure should be written out in a writeXmlComplex method so that
* the "simple" stuff can be easily read and is not buried in the middle of
* the harder-to-read output.
*/
protected void writeXml(XmlWriter out)
{
out.beginElement(_xmlElementName);
writeXmlSimple(out);
writeXmlComplex(out);
out.endElement(_xmlElementName);
}
/**
* Write this model out as xml.
* <p>
* Subclasses that wish to write out properties as xml should override
* this method, call the super implementation, then call methods on the
* XmlWriter object to output their data.
*/
protected void writeXmlSimple(XmlWriter out)
{
out.writeElement("modelId", _modelId);
out.writeElement("className", _className);
out.writeElement("parentClassName", _parentClassName);
out.writeElement("sourceClassName", _sourceClassName);
out.writeElement("sourceClassParentClassName", _sourceClassParentClassName);
// Interface data is a little complex, and could possibly be placed in the
// writeXmlComplex method. But except for very complicated inheritance
// hierarchies is is still only going to be a few lines and it logically
// belongs with the className/parentClassName/etc data. So it is written here
// along with the other "simple" data.
if (!_interfaceClassNames.isEmpty())
{
out.beginElement("interfaces");
for (Iterator i = _interfaceClassNames.iterator(); i.hasNext();)
{
String name = (String) i.next();
out.beginElement("interface");
out.writeAttr("name", name);
out.endElement("interface");
}
out.endElement("interfaces");
}
}
/**
* See documentation for writeXml and writeXmlSimple methods.
*/
protected void writeXmlComplex(XmlWriter out)
{
// no complex data to write for this class.
}
/**
* Merge any inheritable data from the specified "other" instance into
* the metadata held by this instance.
*/
protected void merge(ClassMeta other)
{
// There is nothing to merge between two ClassMeta objects;
// none of the properties on this class are inheritable.
}
/**
* Indicates which "group" of metadata this class belongs to.
* <p>
* Projects can inherit metadata from other projects, in which case
* all the ClassMeta objects end up in one big collection. But for
* some purposes it is necessary to iterate over the objects belonging
* to only one project (eg when generating components). This return
* value can be tested to check which "group" (project) a particular
* instance belongs to.
*/
public String getModelId()
{
return _modelId;
}
public void setModelId(String modelId)
{
this._modelId = modelId;
}
/**
* The fully-qualified name of the JSF entity class, ie the class that actually
* implements a Component, Converter, Validator, Renderer, etc.
* <p>
* The specified class may be a hand-written one, or one created via code generation.
*/
public String getClassName()
{
return _className;
}
public void setClassName(String className)
{
_className = className;
}
/**
* Utility method to return just the packagename part of the className
* attribute.
*/
public String getPackageName()
{
return StringUtils.substring(getClassName(), 0, StringUtils.lastIndexOf(getClassName(), '.'));
}
/**
* The nearest relevant (annotated) ancestor class of the class that this metadata was
* extracted from.
* <p>
* For example, when a class is marked as a Component class, then this will
* refer to the nearest ancestor class that is also marked as a Component
* class. Note that this is "consistent with" the actual Java class hierarchy for
* annotated classes that were used to create this data, but skips java classes
* that were not annotated (and are therefore irrelevant for metadata purposes).
* <p>
* The value of this attribute will match the className attribute of another ClassMeta
* object. Following the chain of ParentClassName links (plus Interface links) gives all
* the ClassMeta objects needed to determine the set of metadata properties for a JSF
* entity.
*/
public String getParentClassName()
{
return _parentClassName;
}
public void setParentClassName(String className)
{
_parentClassName = className;
}
/**
* The list of relevant interface classes.
* <p>
* For example, when a class is marked as a Component class, then this will
* refer to the list of interfaces which that class implements that are also
* marked as a component.
*/
public List getInterfaceClassNames()
{
return _interfaceClassNames;
}
public void setInterfaceClassNames(List classNames)
{
_interfaceClassNames = classNames;
}
public void addInterfaceClassName(String name)
{
_interfaceClassNames.add(name);
}
/**
* Return the className of the real java class from which this metadata was gathered.
* <p>
* This is mostly used for documentation. However when generating code in "template mode",
* this is used to locate the original class in order to find the source code to copy.
* It is also used for some reason in MakeComponentsMojo when determining whether to
* generate a class or not - this is probably wrong.
*/
public String getSourceClassName()
{
return _sourceClassName;
}
public void setSourceClassName(String sourceClassName)
{
this._sourceClassName = sourceClassName;
}
/**
* Return the real java parent class of the class from which this metadata
* was gathered (see classSource property).
* <p>
* This value is usually the same as property parentClassName. However if
* the parent of the annotated class is not itself annotated, then this
* property still points to the real java parent (which will not have a
* ClassMeta object representing it) while property parentClass will point
* to the nearest superclass that does have an annotation (and does have a
* corresponding ClassMeta object).
* <p>
* This information is needed for code generation.
*/
public void setSourceClassParentClassName(String sourceClassParentClassName)
{
this._sourceClassParentClassName = sourceClassParentClassName;
}
public String getSourceClassParentClassName()
{
if (_sourceClassParentClassName == null)
{
//return the parent class name instead.
return getParentClassName();
}
else
{
return _sourceClassParentClassName;
}
}
}