JSF Project Common Metadata Framework Design Overview
NOTE: Currently much of what is discussed in this document is marked internal or internal.provisional. This is due to the current lack of resources to adequately finish things off. Many of the classes and interfaces in
org.eclipse.jst.jsf.common.metadata.internal, and the extension-points described below, should eventually become API. Help wanted to complete the effort.
The Metadata Framework has been designed to flexibly allow tools developers to add metadata and query for it on anything for which they may need metadata. It makes use of EMF models to allow a great deal of extensibility, while keeping things as simple and as generic as possible.
The Metadata EMF Model
* A model is made up of entities that represent the objects that are being decorated. A model is itself an entity.
* An entity can have 0-N child entities.
* A trait describes a named piece of metadata on an entity. An entity can have 0-N traits.
* A trait has a value, and the value itself is defined as an EObject. Since the value type can be described by another EMF model, this allows for arbitrary complexity of the trait values. As long as the extended model can be located and loaded, a model should be loadable.
* To allow for reusability of traits and entities amongst entities within the same model and from without, a model can define EntityGroups. These are groups of entities and traits that will be included at the same level as where the entity defined the IncludeEntityGroup.
Figure 1: The Metadata ECore Model
Domains, Sources and Translators
A domain of metadata conceptually defines the entities for which metadata is being applied. In the initial releases of this framework, only the TaglibraryDomain has been defined. A taglibrary metadata model has a taglibrary entity as the root of the model with tag entities as children, and tag attribute entities as children of the tag entities. A domain is defined through the
org.eclipse.jst.jsf.common.DomainLoadingStrategies extension-point. There is currently no validation support for domains.
Consider a particular tag library for a moment, and how tools would use metadata. Some metadata is defined by the tag library itself, some might be supplied in the jar that contains the tag library in the form of JSR-276 metadata, and some may be supplied specifically for a particular tool in which the tag library is being used. In some cases, the metadata may even be duplicated from these sources.
The Metadata Framework uses an extension-point
org.eclipse.jst.jsf.common.domainSourceModelTypes to define the sources of metadata for a particular domain. This extension-point defines a locator that is used to find these sources of metadata, and an ordinal to describe it's relative importance used by the domainLoadingStrategy when loading a model from the various model sources. The IMetaDataLocator that is supplied with this extension is responsible for locating the source model for a specified URI.
The Eclipse tools that make use of the metadata expect to use entities and traits. Metadata in forms other than "standard metadata model" form, can be automatically translated while loading the model by making use of a translator defined using
org.eclipse.jst.jsf.common.domainSourceModelTypeTranslators. The translators job is to convert the source metadata into entities and traits. More than one translator can be defined against a model source type. This is to allow tools developers to create new tools from metadata that is already available.
If you are supplying metadata in the "standard form" of entities and traits, you can register that metadata file directly using the
org.eclipse.jst.jsf.common.standardMetaDataFiles extension-point. If no locator is provided by the extension, the file is presumed to be relative to the defining plugin.
Figure 2. Sample MetaData File Snippet for JSF HTML Tag Library
<?xml version="1.0" encoding="UTF-8"?> <md:metadatamodel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:md="http://org.eclipse.jst.jsf.common.metadata/metadata.ecore" xmlns:mdt="http://org.eclipse.jst.jsf.common.metadata/metadataTraitTypes.ecore" xmlns:cnst="http://org.eclipse.jst.jsf.core/constraints.ecore" xmlns:qe="http://org.eclipse.jsf.pagedesigner/QuickEditTabSections.ecore" id="http://java.sun.com/jsf/html" type="tagFile"> <entity id="column" type="tag"> <include-entity-group id="basic-jsf-html-attributes"/> <trait id="quick-edit-tab"> <value xsi:type="qe:QuickEditTabSections"> <section id="id"/> </value> </trait> </entity> <entity id="commandButton" type="tag"> <include-entity-group id="basic-jsf-html-attributes"/> <include-entity-group id="common-jsf-html-attributes"/> <include-entity-group id="common-html-attributes" uri="HTML"/> <include-entity-group id="common-language-attributes" uri="HTML"/> <include-entity-group id="common-core-attributes" uri="HTML"/> <include-entity-group id="common-event-attributes" uri="HTML"/> <trait id="quick-edit-tab"> <value xsi:type="qe:QuickEditTabSections"> <section id="id"/> <section id="value"/> <section id="action"/> <section id="actionListener"/> <section id="type"/> <section id="style"/> <section id="section.general.commandButton.listener" type="SECTION"/> </value> </trait> <trait id="containment-constraint"> <value xsi:type="cnst:ContainsTagConstraint"> <set-generator> <algorithm>xpath</algorithm> <expression>ancestor::*</expression> </set-generator> <satisfies-set> <tagId> <uri>http://java.sun.com/jsf/core</uri> <name>view</name> </tagId> <tagId> <uri>http://java.sun.com/jsf/html</uri> <name>form</name> </tagId> </satisfies-set> </value> </trait> <entity id="type"> <trait id="attribute-value-runtime-type"> <value>org.eclipse.jst.jsf.core.attributevalues.StringType</value> </trait> <trait id="valid-values"> <value xsi:type="mdt:ListOfValues"> <item>button</item> <item>reset</item> <item>submit</item> </value> </trait> <trait id="default-value"> <value>submit</value> </trait> <trait id="category"> <value>%property.category.jsf</value> </trait> </entity> etc....
Loading a Model
The framework, when called upon, will load a single merged model from all of the located sources. The
DomainLoadingStrategy class, is responsible for determining the available sources and sorting them before loading and merging.
IMetaDataModelMergeAssistant class will begin merging the metadata sources into a single merged model. It will recurse through the source model and look up entities by id from the merged model. If not found, it will copy the source entity, and all of its children and traits into the merged model. If the entity was located, the source entities children are recursed through in the same manner. Traits are also merged recursively. However, trait values are never merged; the trait value is set by the first merged trait.
After all entities and traits are merged, the included entity groups are then processed. Copies of the contents of the entityGroups are merged into the places where they were included. Because entity groups are merged last, any previously defined traits on an entity that are also defined by an included entityGroup, will behave as overrides of the entityGroup's trait value.
A trait contains a reference to the source model provider instance from which it was merged. This allows a mechanism to access resources from the location where the trait was defined like images, classes, NLS strings, etc.
Sources of metadata for a particular uri and project can be dynamic; as the classpath changes, sources of metadata can come and go. When this occurs, the model should be reloaded. The framework should do this automatically. This design and implementation of this is currently incomplete.
Querying a Model
org.eclipse.jst.jsf.common.metadata.query.TaglibDomainMetaDataQueryHelper is the public API for accessing all metadata currently. Since metadata sources can come from the classpath (TLD's are one example), models for a given URI are specific to a project. So, to query for a model or an entity or trait, one must first establish the
ITaglibDomainMetaDataModelContext using a static call on the helper.
Static method calls in the helper are used find entities and traits after establising the model context. Simple entity and trait visitor classes have been created to locate the first found instance by id. For more complicated querying requirements, you may supply your own
ITraitQueryVisitor instances to the helper.
Simple Example for Querying Metadata
ITaglibDomainMetaDataModelContext modelContext = TaglibDomainMetaDataQueryHelper.createMetaDataModelContext(myProject, taglibUri); Trait trait = TaglibDomainMetaDataQueryHelper.getEntity(modelContext, tagAttributeEntityKey, traitKey); if (trait != null) EObject traitValue = trait.getValue(); ...
Entities are located relative to the starting context. The if you are trying to locate the child entity of a child entity from the current starting point, the key to pass would be the child id's separated by "/"'s.
Entity tagAttrEntity = TaglibDomainMetaDataQueryHelper.getEntity(model, "aTag/aAttr");
see JavaServer Faces Tooling Developer Guide in the Eclipse Help System