Author: gvanmatre
Date: Fri Oct 27 20:13:24 2006
New Revision: 468624
URL: http://svn.apache.org/viewvc?view=rev&rev=468624
Log:
This utility rips through JSP Tag Library Descriptors converting them into Clay
configuration beans. The larger goal (SHALE-322) is to lessen the verbose
configuration for each custom component library by using the TLD as a source of
base metadata. This utility only focuses on extracting component definitions.
The converters, validators, and listeners will need to be extracted using
another method.
Added:
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/utils/TldDigester.java
(with props)
shale/framework/trunk/shale-clay/src/test/java/org/apache/shale/clay/utils/TldDigesterTestCase.java
(with props)
Modified:
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java
shale/framework/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties
Modified:
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java
URL:
http://svn.apache.org/viewvc/shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java?view=diff&rev=468624&r1=468623&r2=468624
==============================================================================
---
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java
(original)
+++
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/parser/Node.java
Fri Oct 27 20:13:24 2006
@@ -352,12 +352,7 @@
Iterator ni = node.getChildren().iterator();
while (ni.hasNext()) {
Node child = (Node) ni.next();
- if (child.getName() != null && child.getName().equals(name)) {
- nodes.add(child);
- }
- if (!child.getChildren().isEmpty()) {
- findNodesByName(child, name, nodes);
- }
+ findNodesByName(child, name, nodes);
}
}
Added:
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/utils/TldDigester.java
URL:
http://svn.apache.org/viewvc/shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/utils/TldDigester.java?view=auto&rev=468624
==============================================================================
---
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/utils/TldDigester.java
(added)
+++
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/utils/TldDigester.java
Fri Oct 27 20:13:24 2006
@@ -0,0 +1,410 @@
+/*
+ * 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.shale.clay.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.faces.webapp.UIComponentTag;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.shale.clay.config.beans.AttributeBean;
+import org.apache.shale.clay.config.beans.ComponentBean;
+import org.apache.shale.clay.parser.Node;
+import org.apache.shale.clay.parser.Parser;
+import org.apache.shale.util.Messages;
+
+/**
+ * <p>Rips through JSP Tag Library Descriptors converting them into Clay
configuration beans.</p>
+ */
+public class TldDigester {
+
+ /**
+ * <p>
+ * Commons logger utility class instance.
+ * </p>
+ */
+ private static Log log;
+ static {
+ log = LogFactory.getLog(TldDigester.class);
+ }
+
+ /**
+ * <p>
+ * Message resources for this class.
+ * </p>
+ */
+ private static Messages messages = new Messages(
+ "org.apache.shale.clay.Bundle",
TldDigester.class.getClassLoader());
+
+ /**
+ * <p>Returns an array of <code>URL</code>'s that represent the
+ * target TLD documents. The parameter <code>tldResources</code>
+ * is a comma delimited value list of tld documents.</p>
+ *
+ * @param tldResources paths to the TLD's within the jars
+ * @return array of TLD URL's
+ * @throws IOException error locating the resource
+ */
+ private URL[] findUrls(String tldResources) throws IOException {
+ StringTokenizer tokenizer = new StringTokenizer(tldResources, ", ");
+
+ List tmp = new ArrayList();
+
+ ClassLoader classloader =
Thread.currentThread().getContextClassLoader();
+ if (classloader == null) {
+ classloader = this.getClass().getClassLoader();
+ }
+
+ while (tokenizer.hasMoreTokens()) {
+ String tldResource = tokenizer.nextToken().trim();
+ if (tldResources.length() == 0) {
+ continue;
+ }
+
+ for (Enumeration ui = classloader.getResources(tldResource);
ui.hasMoreElements();) {
+ URL url = (URL) ui.nextElement();
+ if (url != null) {
+ tmp.add(url);
+ }
+ }
+ }
+
+ URL[] urls = new URL[tmp.size()];
+ tmp.toArray(urls);
+
+ return urls;
+ }
+
+ /**
+ * <p>Loads the <code>targetURL</code> into a
<code>StringBuffer</code>.</p>
+ * @param targetURL TLD document
+ * @return content of the TLD document
+ * @throws IOException error reading template
+ */
+ private StringBuffer load(URL targetURL) throws IOException {
+
+ StringBuffer buff = new StringBuffer();
+ BufferedReader in = null;
+
+ try {
+
+ in = new BufferedReader(new
InputStreamReader(targetURL.openStream()));
+ while (in.ready()) {
+ buff.append(in.readLine());
+ }
+
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
+
+ return buff;
+
+ }
+
+ /**
+ * <p>Parsers the TLD document using the clay markup parser.</p>
+ * @param document TLD
+ * @return root <code>taglib</code> document node.
+ */
+ private Node parse(StringBuffer document) {
+ Parser parser = new Parser();
+ List roots = parser.parse(document);
+ for (Iterator ri = roots.iterator(); ri.hasNext();) {
+ Node node = (Node) ri.next();
+ if (node.isWellFormed() && node.getName().equals("taglib")) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <p>Finds a node with a matching <code>name</code>
+ * starting at the <code>root</code>.</p>
+ *
+ * @param root starting node
+ * @param name target node name
+ * @return sibling node with a matching name
+ */
+ private Node findNode(Node root, String name) {
+ List nodes = root.getNodesByName(name);
+ if (nodes.size() > 0) {
+ return (Node) nodes.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * <p>Returns the node's text value.</p>
+ * @param root starting node
+ * @param name target node name
+ * @return text of the node between the starting and ending nodes
+ */
+ private String getValue(Node root, String name) {
+ String value = null;
+ Node node = findNode(root, name);
+ if (node == null) {
+ return null;
+ }
+ for (Iterator ni = node.getChildren().iterator(); ni.hasNext();) {
+ Node child = (Node) node.getChildren().get(0);
+ if (!child.isWellFormed() && !isNodeWhitespace(child)) {
+ value = child.getToken().getRawText();
+ break;
+ }
+ }
+
+
+ return value;
+ }
+
+ /**
+ * <p>Test the value of the node and returns <code>true</code> if
+ * the value is only whitespace.</p>
+ *
+ * @param node markup node
+ * @return <code>true</code> if value of the node is only whitespace
+ */
+ private boolean isNodeWhitespace(Node node) {
+ StringBuffer document = node.getToken().getDocument();
+ for (int i = node.getToken().getBeginOffset();
+ i < node.getToken().getEndOffset(); i++) {
+ char c = document.charAt(i);
+ if (!Character.isWhitespace(c)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * <p>Creates an [EMAIL PROTECTED] AttributeBean} from a markup [EMAIL
PROTECTED] Node}.</p>
+ * @param node markup node
+ * @return attribute config bean
+ */
+ private AttributeBean createAttributeConfig(Node node) {
+ String name = getValue(node, "name");
+ if (name.equals("jsfid")) {
+ return null;
+ }
+
+ AttributeBean bean = new AttributeBean();
+ bean.setName(name);
+ if (name.startsWith("action")
+ || (name.indexOf("validator") > -1)
+ || (name.indexOf("Validator") > -1)
+ || name.equals("valueChangeListener")) {
+ bean.setBindingType(AttributeBean.BINDING_TYPE_METHOD);
+ } else {
+ bean.setBindingType(AttributeBean.BINDING_TYPE_VALUE);
+ }
+
+ //bean.setDescription(getValue(node, "description"));
+
+ return bean;
+ }
+
+ /**
+ * <p>Creates a [EMAIL PROTECTED] ComponentBean} from a markup [EMAIL
PROTECTED] Node}.
+ * Adds all child "attribute" [EMAIL PROTECTED] Node}s as [EMAIL
PROTECTED] ComponentBean}
+ * [EMAIL PROTECTED] AttributeBean}s.</p>
+ *
+ * @param node markup node
+ * @param prefix namespace prefix
+ * @return config bean that represents the TLD tag
+ */
+ private ComponentBean createComponentConfig(Node node, String prefix) {
+ StringBuffer name = new StringBuffer(prefix);
+ name.append(":").append(getValue(node, "name"));
+
+ String tagClazz = getValue(node, "tag-class");
+
+ UIComponentTag tag = null;
+ try {
+ Class clazz = Class.forName(tagClazz);
+ Object t = clazz.newInstance();
+ if (t instanceof UIComponentTag) {
+ tag = (UIComponentTag) t;
+ }
+ } catch (ClassNotFoundException e) {
+ log.error(messages.getMessage("tag.load.error",
+ new Object[] {name.toString(), tagClazz}), e);
+ return null;
+ } catch (InstantiationException e) {
+ log.error(messages.getMessage("tag.load.error",
+ new Object[] {name.toString(), tagClazz}), e);
+ return null;
+ } catch (IllegalAccessException e) {
+ log.error(messages.getMessage("tag.load.error",
+ new Object[] {name.toString(), tagClazz}), e);
+ return null;
+ }
+ if (tag == null) {
+ log.error(messages.getMessage("tag.load.error",
+ new Object[] {name.toString(), tagClazz}));
+ return null;
+ }
+
+ ComponentBean bean = new ComponentBean();
+
+ bean.setJsfid(name.toString());
+ bean.setComponentType(tag.getComponentType());
+ //bean.setDescription(getValue(node, "description"));
+
+ List attributes = node.getNodesByName("attribute");
+ for (Iterator ai = attributes.iterator(); ai.hasNext();) {
+ Node attrNode = (Node) ai.next();
+ AttributeBean attr = createAttributeConfig(attrNode);
+ if (attr != null) {
+ bean.addAttribute(attr);
+ }
+ }
+ return bean;
+ }
+
+ /**
+ * <p>Converts the TLD into an array of [EMAIL PROTECTED] ComponentBean}
+ * starting at the "taglib" root node. Each "tag" will be
+ * converted into a [EMAIL PROTECTED] Clay} configuration bean.</p>
+ *
+ * @param root TLD taglib root node
+ * @return Clay config beans that acts as a stand-in for the TLD
+ */
+ private ComponentBean[] convert(Node root) {
+ List beans = new ArrayList();
+
+ String shortName = getValue(root, "short-name");
+
+ List tags = root.getNodesByName("tag");
+ for (Iterator ti = tags.iterator(); ti.hasNext();) {
+ Node node = (Node) ti.next();
+ ComponentBean bean = createComponentConfig(node, shortName);
+ if (bean != null) {
+ beans.add(bean);
+ }
+ }
+
+ ComponentBean[] configBeans = new ComponentBean[beans.size()];
+ beans.toArray(configBeans);
+ return configBeans;
+ }
+
+ /**
+ * <p>Converts TLD files located in jars into [EMAIL PROTECTED] Clay}'s
+ * metadata. The tag handlers are loaded and the
+ * <code>componentType</code> extracted.</p>
+ *
+ * @param tldResources comma separated list of tag library descriptors.
+ * The fully qualified class path is required.
+ * @return array of config beans
+ * @throws IOException error loading the tld file
+ */
+ public TldDefinition[] digest(String tldResources) throws IOException {
+ List defs = new ArrayList();
+
+ URL[] urls = findUrls(tldResources);
+ StringBuffer document = null;
+ for (int i = 0; i < urls.length; i++) {
+ document = load(urls[i]);
+ Node root = parse(document);
+ defs.add(createTldDefinition(root, convert(root)));
+ document.setLength(0);
+ }
+
+ TldDefinition[] configDefs = new TldDefinition[defs.size()];
+ defs.toArray(configDefs);
+
+ return configDefs;
+ }
+
+ /**
+ * <p>Factory method that creates a structure to hold information
+ * about the tag library descriptor.</p>
+ *
+ * @param root TLD document root
+ * @param configBeans POJO beans describing the tag handler information
+ * @return a structure representing the TLD
+ */
+ private TldDefinition createTldDefinition(final Node root, final
ComponentBean[] configBeans) {
+ final String uri = getValue(root, "uri");
+ final String shortname = getValue(root, "short-name");
+ final String tlibVersion = getValue(root, "tlib-version");
+ final String jspVersion = getValue(root, "jsp-version");
+
+ return new TldDefinition() {
+ public ComponentBean[] getConfigBeans() {
+ return configBeans;
+ }
+ public String getUri() {
+ return uri;
+ }
+ public String getShortname() {
+ return shortname;
+ }
+ public String getTlibVersion() {
+ return tlibVersion;
+ }
+ public String getJspVersion() {
+ return jspVersion;
+ }
+ };
+ }
+
+ /**
+ * <p>Top-level interfaces that describes a TLD.</p>
+ */
+ public static interface TldDefinition {
+ /**
+ * @return Configuration beans that describe the attributes of the
component
+ */
+ ComponentBean[] getConfigBeans();
+
+ /**
+ * @return the TLD's uri
+ */
+ String getUri();
+
+ /**
+ * @return the TLD's short name
+ */
+ String getShortname();
+
+ /**
+ * @return tag library version
+ */
+ String getTlibVersion();
+
+ /**
+ * @return JavaServer Pages version
+ */
+ String getJspVersion();
+
+ }
+}
Propchange:
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/utils/TldDigester.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
shale/framework/trunk/shale-clay/src/main/java/org/apache/shale/clay/utils/TldDigester.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Modified:
shale/framework/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties
URL:
http://svn.apache.org/viewvc/shale/framework/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties?view=diff&rev=468624&r1=468623&r2=468624
==============================================================================
---
shale/framework/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties
(original)
+++
shale/framework/trunk/shale-clay/src/main/resources/org/apache/shale/clay/Bundle.properties
Fri Oct 27 20:13:24 2006
@@ -156,4 +156,7 @@
clay.null.tagUtils=The utility managed bean
"org.apache.shale.TAG_UTILITY_BEAN" registered in the core shale jar cannot be
resolved.
#org.apache.shale.clay.taglib.SymbolTag
-clayparent.notfound=The symbol tag must be nested under a clay tag.
\ No newline at end of file
+clayparent.notfound=The symbol tag must be nested under a clay tag.
+
+#org.apache.shale.clay.utils.TldParserUtil
+tag.load.error=Unable to load tag handler "{0}" with the class name of "{1}".
\ No newline at end of file
Added:
shale/framework/trunk/shale-clay/src/test/java/org/apache/shale/clay/utils/TldDigesterTestCase.java
URL:
http://svn.apache.org/viewvc/shale/framework/trunk/shale-clay/src/test/java/org/apache/shale/clay/utils/TldDigesterTestCase.java?view=auto&rev=468624
==============================================================================
---
shale/framework/trunk/shale-clay/src/test/java/org/apache/shale/clay/utils/TldDigesterTestCase.java
(added)
+++
shale/framework/trunk/shale-clay/src/test/java/org/apache/shale/clay/utils/TldDigesterTestCase.java
Fri Oct 27 20:13:24 2006
@@ -0,0 +1,84 @@
+/*
+ * 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.shale.clay.utils;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.apache.shale.clay.config.beans.AttributeBean;
+import org.apache.shale.clay.config.beans.ComponentBean;
+import org.apache.shale.clay.utils.TldDigester.TldDefinition;
+
+public class TldDigesterTestCase extends TestCase {
+
+ // Construct a new instance of this test case.
+ public TldDigesterTestCase(String name) {
+ super(name);
+ }
+
+ // Return the tests included in this test case.
+ public static Test suite() {
+ return (new TestSuite(TldDigesterTestCase.class));
+ }
+
+
+ public void testDigestClayTld() throws Exception {
+ TldDigester stripper = new TldDigester();
+ TldDefinition[] tldDefs = stripper.digest("META-INF/shale-clay.tld");
+ assertNotNull(tldDefs);
+ assertEquals(1, tldDefs.length);
+
+ assertEquals("tlib-version", "1.0", tldDefs[0].getTlibVersion());
+ assertEquals("jsp-version", "1.2", tldDefs[0].getJspVersion());
+ assertEquals("uri", "http://shale.apache.org/clay",
tldDefs[0].getUri());
+ assertEquals("short-name", "clay", tldDefs[0].getShortname());
+
+ ComponentBean[] beans = tldDefs[0].getConfigBeans();
+ assertNotNull(beans);
+
+ assertEquals("lenght", 1, beans.length);
+ assertEquals("jsfid", "clay:clay", beans[0].getJsfid());
+ assertEquals("componentType", "org.apache.shale.clay.component.Clay",
beans[0].getComponentType());
+
+ assertEquals("size", 4, beans[0].getAttributes().size());
+
+ AttributeBean attr = beans[0].getAttribute("rendered");
+ assertNotNull("attr", attr);
+ assertEquals("rendered", "rendered", attr.getName());
+ assertEquals("bindingType", AttributeBean.BINDING_TYPE_VALUE,
attr.getBindingType());
+ //assertEquals("If false, this component will not be rendered.",
attr.getDescription());
+
+ attr = beans[0].getAttribute("id");
+ assertNotNull("attr", attr);
+ assertEquals("id", attr.getName());
+ assertEquals("bindingType", AttributeBean.BINDING_TYPE_VALUE,
attr.getBindingType());
+
+ attr = beans[0].getAttribute("shapeValidator");
+ assertNotNull("attr", attr);
+ assertEquals("shapeValidator", "shapeValidator", attr.getName());
+ assertEquals("bindingType", AttributeBean.BINDING_TYPE_METHOD,
attr.getBindingType());
+
+ attr = beans[0].getAttribute("managedBeanName");
+ assertNotNull("attr", attr);
+ assertEquals("managedBeanName", "managedBeanName", attr.getName());
+ assertEquals("bindingType", AttributeBean.BINDING_TYPE_VALUE,
attr.getBindingType());
+
+ }
+
+}
Propchange:
shale/framework/trunk/shale-clay/src/test/java/org/apache/shale/clay/utils/TldDigesterTestCase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
shale/framework/trunk/shale-clay/src/test/java/org/apache/shale/clay/utils/TldDigesterTestCase.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL