2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Coverage Report - org.apache.shale.clay.utils.TldDigester
 
Classes in this File Line Coverage Branch Coverage Complexity
TldDigester
88% 
100% 
2.714
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to you under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.shale.clay.utils;
 19  
 
 20  
 import java.io.BufferedReader;
 21  
 import java.io.IOException;
 22  
 import java.io.InputStreamReader;
 23  
 import java.net.URL;
 24  
 import java.util.ArrayList;
 25  
 import java.util.Enumeration;
 26  
 import java.util.Iterator;
 27  
 import java.util.List;
 28  
 import java.util.StringTokenizer;
 29  
 
 30  
 import javax.faces.webapp.UIComponentTag;
 31  
 
 32  
 import org.apache.commons.logging.Log;
 33  
 import org.apache.commons.logging.LogFactory;
 34  
 
 35  
 import org.apache.shale.clay.config.beans.AttributeBean;
 36  
 import org.apache.shale.clay.config.beans.ComponentBean;
 37  
 import org.apache.shale.clay.parser.Node;
 38  
 import org.apache.shale.clay.parser.Parser;
 39  
 import org.apache.shale.util.Messages;
 40  
 
 41  
 /**
 42  
  * <p>Rips through JSP Tag Library Descriptors converting them into Clay configuration beans.</p>
 43  
  */
 44  1
 public class TldDigester {
 45  
 
 46  
     /**
 47  
      * <p>
 48  
      * Commons logger utility class instance.
 49  
      * </p>
 50  
      */
 51  
     private static Log log;
 52  
     static {
 53  2
         log = LogFactory.getLog(TldDigester.class);
 54  
     }
 55  
 
 56  
     /**
 57  
      * <p>
 58  
      * Message resources for this class.
 59  
      * </p>
 60  
      */
 61  1
     private static Messages messages = new Messages(
 62  
             "org.apache.shale.clay.Bundle", TldDigester.class.getClassLoader());
 63  
 
 64  
     /**
 65  
      * <p>Returns an array of <code>URL</code>'s that represent the
 66  
      * target TLD documents.  The parameter <code>tldResources</code>
 67  
      * is a comma delimited value list of tld documents.</p>
 68  
      *
 69  
      * @param tldResources paths to the TLD's within the jars
 70  
      * @return array of TLD URL's
 71  
      * @throws IOException  error locating the resource
 72  
      */
 73  
     private URL[] findUrls(String tldResources) throws IOException {
 74  1
         StringTokenizer tokenizer = new StringTokenizer(tldResources, ", ");
 75  
 
 76  1
         List tmp = new ArrayList();
 77  
 
 78  1
         ClassLoader classloader = Thread.currentThread().getContextClassLoader();
 79  1
         if (classloader == null) {
 80  0
             classloader = this.getClass().getClassLoader();
 81  
         }
 82  
 
 83  2
         while (tokenizer.hasMoreTokens()) {
 84  1
             String tldResource = tokenizer.nextToken().trim();
 85  1
             if (tldResources.length() == 0) {
 86  0
                continue;
 87  
             }
 88  
 
 89  1
             for (Enumeration ui = classloader.getResources(tldResource); ui.hasMoreElements();) {
 90  1
                 URL url = (URL) ui.nextElement();
 91  1
                 if (url != null) {
 92  1
                     tmp.add(url);
 93  
                 }
 94  1
             }
 95  1
         }
 96  
 
 97  1
         URL[] urls = new URL[tmp.size()];
 98  1
         tmp.toArray(urls);
 99  
 
 100  1
         return urls;
 101  
     }
 102  
 
 103  
     /**
 104  
      * <p>Loads the <code>targetURL</code> into a <code>StringBuffer</code>.</p>
 105  
      * @param targetURL TLD document
 106  
      * @return content of the TLD document
 107  
      * @throws IOException error reading template
 108  
      */
 109  
     private StringBuffer load(URL targetURL) throws IOException {
 110  
 
 111  1
         StringBuffer buff = new StringBuffer();
 112  1
         BufferedReader in = null;
 113  
 
 114  
         try {
 115  
 
 116  1
             in = new BufferedReader(new InputStreamReader(targetURL.openStream()));
 117  101
             while (in.ready()) {
 118  100
                buff.append(in.readLine());
 119  100
             }
 120  
 
 121  
         } finally {
 122  1
            if (in != null) {
 123  1
               in.close();
 124  1
            }
 125  0
         }
 126  
 
 127  1
         return buff;
 128  
 
 129  
     }
 130  
 
 131  
     /**
 132  
      * <p>Parsers the TLD document using the clay markup parser.</p>
 133  
      * @param document TLD
 134  
      * @return root <code>taglib</code> document node.
 135  
      */
 136  
     private Node parse(StringBuffer document) {
 137  1
        Parser parser = new Parser();
 138  1
        List roots = parser.parse(document);
 139  1
        for (Iterator ri = roots.iterator(); ri.hasNext();) {
 140  4
           Node node = (Node) ri.next();
 141  4
           if (node.isWellFormed() && node.getName().equals("taglib")) {
 142  1
              return node;
 143  
           }
 144  3
        }
 145  0
        return null;
 146  
     }
 147  
 
 148  
     /**
 149  
      * <p>Finds a node with a matching <code>name</code>
 150  
      * starting at the <code>root</code>.</p>
 151  
      *
 152  
      * @param root starting node
 153  
      * @param name target node name
 154  
      * @return sibling node with a matching name
 155  
      */
 156  
     private Node findNode(Node root, String name) {
 157  14
         List nodes = root.getNodesByName(name);
 158  14
         if (nodes.size() > 0) {
 159  14
            return (Node) nodes.get(0);
 160  
         }
 161  0
         return null;
 162  
     }
 163  
 
 164  
     /**
 165  
      * <p>Returns the node's text value.</p>
 166  
      * @param root starting node
 167  
      * @param name target node name
 168  
      * @return text of the node between the starting and ending nodes
 169  
      */
 170  
     private String getValue(Node root, String name) {
 171  14
         String value = null;
 172  14
         Node node = findNode(root, name);
 173  14
         if (node == null) {
 174  0
             return null;
 175  
         }
 176  14
         for (Iterator ni = node.getChildren().iterator(); ni.hasNext();) {
 177  14
             Node child = (Node) node.getChildren().get(0);
 178  14
             if (!child.isWellFormed() && !isNodeWhitespace(child)) {
 179  14
                 value = child.getToken().getRawText();
 180  14
                 break;
 181  
             }
 182  0
         }
 183  
 
 184  
 
 185  14
         return value;
 186  
     }
 187  
 
 188  
     /**
 189  
      * <p>Test the value of the node and returns <code>true</code> if
 190  
      * the value is only whitespace.</p>
 191  
      *
 192  
      * @param node markup node
 193  
      * @return <code>true</code> if value of the node is only whitespace
 194  
      */
 195  
     private boolean isNodeWhitespace(Node node) {
 196  14
         StringBuffer document = node.getToken().getDocument();
 197  14
         for (int i = node.getToken().getBeginOffset();
 198  14
              i < node.getToken().getEndOffset(); i++) {
 199  14
            char c = document.charAt(i);
 200  14
            if (!Character.isWhitespace(c)) {
 201  14
                return false;
 202  
            }
 203  
         }
 204  0
         return true;
 205  
     }
 206  
 
 207  
     /**
 208  
      * <p>Creates an {@link AttributeBean} from a markup {@link Node}.</p>
 209  
      * @param node markup node
 210  
      * @return attribute config bean
 211  
      */
 212  
     private AttributeBean createAttributeConfig(Node node) {
 213  5
         String name = getValue(node, "name");
 214  5
         if (name.equals("jsfid")) {
 215  1
             return null;
 216  
         }
 217  
 
 218  4
         AttributeBean bean = new AttributeBean();
 219  4
         bean.setName(name);
 220  4
         if (name.startsWith("action")
 221  
                || (name.indexOf("validator") > -1)
 222  
                || (name.indexOf("Validator") > -1)
 223  
                || name.equals("valueChangeListener")) {
 224  1
             bean.setBindingType(AttributeBean.BINDING_TYPE_METHOD);
 225  1
         } else {
 226  3
             bean.setBindingType(AttributeBean.BINDING_TYPE_VALUE);
 227  
         }
 228  
 
 229  
         //bean.setDescription(getValue(node, "description"));
 230  
 
 231  4
         return bean;
 232  
     }
 233  
 
 234  
     /**
 235  
      * <p>Creates a {@link ComponentBean} from a markup {@link Node}.
 236  
      * Adds all child "attribute" {@link Node}s as {@link ComponentBean}
 237  
      * {@link AttributeBean}s.</p>
 238  
      *
 239  
      * @param node markup node
 240  
      * @param prefix namespace prefix
 241  
      * @return config bean that represents the TLD tag
 242  
      */
 243  
     private ComponentBean createComponentConfig(Node node, String prefix) {
 244  2
         StringBuffer name = new StringBuffer(prefix);
 245  2
         name.append(":").append(getValue(node, "name"));
 246  
 
 247  2
         String tagClazz = getValue(node, "tag-class");
 248  
 
 249  2
         UIComponentTag tag = null;
 250  
         try {
 251  2
             Class clazz = Class.forName(tagClazz);
 252  2
             Object t = clazz.newInstance();
 253  2
             if (t instanceof UIComponentTag) {
 254  1
                 tag = (UIComponentTag) t;
 255  
             }
 256  0
         } catch (ClassNotFoundException e) {
 257  0
             log.error(messages.getMessage("tag.load.error",
 258  
                     new Object[] {name.toString(), tagClazz}), e);
 259  0
             return null;
 260  0
         } catch (InstantiationException e) {
 261  0
             log.error(messages.getMessage("tag.load.error",
 262  
                     new Object[] {name.toString(), tagClazz}), e);
 263  0
             return null;
 264  0
         } catch (IllegalAccessException e) {
 265  0
             log.error(messages.getMessage("tag.load.error",
 266  
                     new Object[] {name.toString(), tagClazz}), e);
 267  0
             return null;
 268  2
         }
 269  2
         if (tag == null) {
 270  1
             log.error(messages.getMessage("tag.load.error",
 271  
                     new Object[] {name.toString(), tagClazz}));
 272  1
             return null;
 273  
         }
 274  
 
 275  1
         ComponentBean bean = new ComponentBean();
 276  
 
 277  1
         bean.setJsfid(name.toString());
 278  1
         bean.setComponentType(tag.getComponentType());
 279  
         //bean.setDescription(getValue(node, "description"));
 280  
 
 281  1
         List attributes = node.getNodesByName("attribute");
 282  1
         for (Iterator ai = attributes.iterator(); ai.hasNext();) {
 283  5
             Node attrNode = (Node) ai.next();
 284  5
             AttributeBean attr = createAttributeConfig(attrNode);
 285  5
             if (attr != null) {
 286  4
                 bean.addAttribute(attr);
 287  
             }
 288  5
         }
 289  1
         return bean;
 290  
     }
 291  
 
 292  
     /**
 293  
      * <p>Converts the TLD into an array of {@link ComponentBean}
 294  
      * starting at the "taglib" root node.  Each "tag" will be
 295  
      * converted into a {@link Clay} configuration bean.</p>
 296  
      *
 297  
      * @param root TLD taglib root node
 298  
      * @return Clay config beans that acts as a stand-in for the TLD
 299  
      */
 300  
     private ComponentBean[] convert(Node root) {
 301  1
         List beans = new ArrayList();
 302  
 
 303  1
         String shortName = getValue(root, "short-name");
 304  
 
 305  1
         List tags = root.getNodesByName("tag");
 306  1
         for (Iterator ti = tags.iterator(); ti.hasNext();) {
 307  2
             Node node = (Node) ti.next();
 308  2
             ComponentBean bean = createComponentConfig(node, shortName);
 309  2
             if (bean != null) {
 310  1
                beans.add(bean);
 311  
             }
 312  2
         }
 313  
 
 314  1
         ComponentBean[] configBeans = new ComponentBean[beans.size()];
 315  1
         beans.toArray(configBeans);
 316  1
         return configBeans;
 317  
     }
 318  
 
 319  
     /**
 320  
      * <p>Converts TLD files located in jars into {@link Clay}'s
 321  
      * metadata.  The tag handlers are loaded and the
 322  
      * <code>componentType</code> extracted.</p>
 323  
      *
 324  
      * @param tldResources comma separated list of tag library descriptors.
 325  
      *        The fully qualified class path is required.
 326  
      * @return array of config beans
 327  
      * @throws IOException error loading the tld file
 328  
      */
 329  
     public TldDefinition[] digest(String tldResources) throws IOException {
 330  1
         List defs = new ArrayList();
 331  
 
 332  1
         URL[] urls = findUrls(tldResources);
 333  1
         StringBuffer document = null;
 334  2
         for (int i = 0; i < urls.length; i++) {
 335  1
             document = load(urls[i]);
 336  1
             Node root = parse(document);
 337  1
             defs.add(createTldDefinition(root, convert(root)));
 338  1
             document.setLength(0);
 339  
         }
 340  
 
 341  1
         TldDefinition[] configDefs = new TldDefinition[defs.size()];
 342  1
         defs.toArray(configDefs);
 343  
 
 344  1
         return configDefs;
 345  
     }
 346  
 
 347  
     /**
 348  
      * <p>Factory method that creates a structure to hold information
 349  
      * about the tag library descriptor.</p>
 350  
      *
 351  
      * @param root TLD document root
 352  
      * @param configBeans POJO beans describing the tag handler information
 353  
      * @return a structure representing the TLD
 354  
      */
 355  
     private TldDefinition createTldDefinition(final Node root, final ComponentBean[] configBeans) {
 356  1
        final String uri = getValue(root, "uri");
 357  1
        final String shortname = getValue(root, "short-name");
 358  1
        final String tlibVersion = getValue(root, "tlib-version");
 359  1
        final String jspVersion = getValue(root, "jsp-version");
 360  
 
 361  1
        return new TldDefinition() {
 362  
            public ComponentBean[] getConfigBeans() {
 363  1
                return configBeans;
 364  
            }
 365  
            public String getUri() {
 366  1
               return uri;
 367  
            }
 368  
            public String getShortname() {
 369  1
                return shortname;
 370  
            }
 371  
            public String getTlibVersion() {
 372  1
                return tlibVersion;
 373  
            }
 374  1
            public String getJspVersion() {
 375  1
                return jspVersion;
 376  
            }
 377  
        };
 378  
     }
 379  
 
 380  
     /**
 381  
      * <p>Top-level interfaces that describes a TLD.</p>
 382  
      */
 383  1
     public static interface TldDefinition {
 384  
        /**
 385  
         * @return Configuration beans that describe the attributes of the component
 386  
         */
 387  
        ComponentBean[] getConfigBeans();
 388  
 
 389  
        /**
 390  
         * @return the TLD's uri
 391  
         */
 392  
        String getUri();
 393  
 
 394  
        /**
 395  
         * @return the TLD's short name
 396  
         */
 397  
        String getShortname();
 398  
 
 399  
        /**
 400  
         * @return tag library version
 401  
         */
 402  
        String getTlibVersion();
 403  
 
 404  
        /**
 405  
         * @return JavaServer Pages version
 406  
         */
 407  
        String getJspVersion();
 408  
 
 409  
     }
 410  
 }