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


Reply via email to