Package org.apache.shale.clay.parser.builder

Source Code of org.apache.shale.clay.parser.builder.Builder

/*
* 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.
*/

/*
* $Id: Builder.java 467434 2006-10-24 18:48:48Z gvanmatre $
*/
package org.apache.shale.clay.parser.builder;

import java.util.Iterator;
import java.util.Map;

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.config.beans.ConfigBean;
import org.apache.shale.clay.config.beans.ConfigBeanFactory;
import org.apache.shale.clay.config.beans.ElementBean;
import org.apache.shale.clay.config.beans.SymbolBean;
import org.apache.shale.clay.parser.Node;
import org.apache.shale.clay.parser.Token;
import org.apache.shale.util.Messages;

/**
* <p>
* The abstract document node converter handles building meta components,
* {@link org.apache.shale.clay.config.beans.ComponentBean}, for a parsed html
* document fragment. The {@link org.apache.shale.clay.parser.Parser} loads the
* HTML document into a tree structure of
* {@link org.apache.shale.clay.parser.Node}. Each node in the parsed document
* tree is mapped to a <code>Builder</code> by a a subclass of
* {@link org.apache.shale.clay.parser.builder.chain.BuilderRuleContext}.
* </p>
*/
public abstract class Builder {

    /**
     * <p>
     * Message resources for this class.
     * </p>
     */
    protected static Messages messages = new Messages(
            "org.apache.shale.clay.Bundle", Builder.class
            .getClassLoader());

    /**
     * <p>
     * Common Logger utility class.
     * </p>
     */
    private static Log log;
    static {
        log = LogFactory.getLog(Builder.class);
    }

    /**
     * <p>
     * This method returns <code>true</code> if the faces component that it
     * builds allows children but the default is <code>false</code>.
     * </p>
     *
     * @return <code>false</code>
     */
    public boolean isChildrenAllowed() {
        return false;
    }

    /**
     * <p>
     * Returns a <code>jsfid</code> for the component.
     * </p>
     *
     * @param node markup
     * @return jsfid
     */
    protected abstract String getJsfid(Node node);

    /**
     * <p>
     * Returns the faces component type registered in the faces configuration or the
     * fully qualified class name for <code>ActionListeners</code> and
     * <code>ValueChangeListeners</code>.
     * </p>
     *
     * @param node markup
     * @return component type
     */
    protected abstract String getComponentType(Node node);

    /**
     * <p>
     * An integer value used to order {@link ElementBean}'s in the
     * <code>children</code> collection of a {@link ComponentBean}. When read
     * for the configuration files by the
     * {@link org.apache.shale.clay.config.ClayConfigureListener}, the
     * <code>renderId</code> is the <strong>"signature"</strong> when
     * resolving inheritance and overrides. Used here, the component structure
     * must take on the form of the HTML document.
     * </p>
     */
    private static int renderId = 0;

    /**
     * <p>
     * Returns the next generated <code>renderId</code>.
     * <p>
     *
     * @return unique id
     */
    protected synchronized int getRenderId() {
        return renderId++;
    }

    /**
     * <p>
     * Returns <code>true</code> if the builder handles converting the node's
     * children or <code>false</code> if it's handled by the parent. The
     * <code>isBodyAllowed</code> attribute can also
     * be used to override the default option. This flag allows the HTML to be
     * mocked up but the body ignored when converted into a
     * {@link ComponentBean} graph.
     * <p>
     *
     * @param node markup
     * @param target child config bean
     * @return <code>false</code> if the node's children should be ignored
     */
    protected boolean getBuildNodeBody(Node node, ElementBean target) {
        return (target != null && !target.getIsBodyAllowed());
    }

    /**
     * <p>
     * Called to being building a {@link ElementBean} from a {@link Node}. This
     * follows the JSF pattern used by the Components and Renders.
     * </p>
     *
     * @param node markup
     * @param target child config bean
     * @param root parent config bean
     */
    protected void encodeBegin(Node node, ElementBean target,
            ComponentBean root) {

        assignNode(node, target);
    }

    /**
     * <p>
     * Factory method that creates a {@link AttributeBean} from the <code>original</code>
     * replacing the <code>value</code>.
     * </p>
     *
     * @param original attribute being cloned
     * @param value attribute property value override
     * @param target owner of the attribute
     * @return cloned original with the value overridden
     */
    protected AttributeBean createAttribute(AttributeBean original, String value, ComponentBean target) {
        AttributeBean attr = new AttributeBean();
        attr.setName(original.getName());
        attr.setValue(value);
        attr.setBindingType(original.getBindingType());
        target.addAttribute(attr);
        return attr;
    }




    /**
     * <p>
     * Recursively builds the clay meta component data from the parse
     * document of {@link Node}'s. A similar design pattern found in JSF.
     * </p>
     *
     * @param node markup
     * @param target child config bean
     * @param root parent config bean
     */
    protected void encodeChildren(Node node, ElementBean target,
            ComponentBean root) {

        if (!getBuildNodeBody(node, target)) {
            Iterator ci = node.getChildren().iterator();
            while (ci.hasNext()) {
                Node child = (Node) ci.next();
                Builder childRenderer = getBuilder(child);

                ElementBean targetChild = childRenderer.createElement(child);
                root.addChild(targetChild);
                //if the child component allows children, pass it to the render as
                // the root, otherwise, add the child to the current root
                if (childRenderer.isChildrenAllowed()) {
                    childRenderer.encode(child, targetChild, targetChild);
                } else {
                    childRenderer.encode(child, targetChild, root);
                }
            }
        }

    }

    /**
     * <p>
     * This call is invoked for any final processing.
     * </p>
     *
     * @param node markup
     * @param target child config bean
     * @param root parent config bean
     */
    protected void encodeEnd(Node node, ElementBean target,
            ComponentBean root) {

    }

    /**
     * <p>
     * Factory method that creates a {@link ElementBean} from a {@link Node}.
     * </p>
     *
     * @param node markup
     * @return target config bean from the markup node
     */
    public ElementBean createElement(Node node) {

        ElementBean target = new ElementBean();
        target.setJsfid(getJsfid(node));
        if (!node.isComment() && node.isStart()) {
            String jsfid = (String) node.getAttributes().get("jsfid");
            if (jsfid != null) {
               target.setJsfid(jsfid);
            }
        }
        target.setComponentType(getComponentType(node));
        target.setRenderId(getRenderId());

        return target;
    }

    /**
     * <p>
     * The call that begins the conversion of a {@link Node} to a
     * {@link ComponentBean}. Each element in the html document will be
     * converted into a Clay meta component.
     * </p>
     *
     * @param node markup
     * @param target child config bean
     * @param root parent config bean
     */
    public void encode(Node node, ElementBean target, ComponentBean root) {

        if (log.isDebugEnabled()) {
            log.debug(messages.getMessage("encode.begin", new Object[] {node}));
        }

        encodeBegin(node, target, root);
        encodeChildren(node, target, root);
        encodeEnd(node, target, root);

        if (log.isDebugEnabled()) {
            log.debug(messages.getMessage("encode.end", new Object[] {node}));
        }

    }


    /**
     * <p>
     * This method resolves the <code>jsfid</code> attribute for an HTML
     * element to a component definition in the XML configuration files.
     * </p>
     *
     * @param node markup
     * @param target child config bean
     */
    protected void assignNode(Node node, ElementBean target) {
        String id = (String) node.getAttributes().get("id");
        if (id == null) {
            id = (String) node.getAttributes().get("name");
        }

        target.setId(id);

        // look to see if this node should be bound to a component
        if (target.getJsfid() != null) {
            // lookup the ConfigBean that handles the id
            ConfigBean config = ConfigBeanFactory.findConfig(target.getJsfid());
            // disconnect component type
            target.setComponentType(null);


            try {
               //assign the parent
               config.assignParent(target);
               // resolve inheritance
               config.realizingInheritance(target);
            } catch (RuntimeException e) {
                log.error(e);
                throw new RuntimeException(
                        messages.getMessage("parser.unresolved",
                        new Object[] {node.getToken(), node.getToken().getRawText()}));
            }

            // if the inheritance is broken, toggle back to the default
            if (target.getComponentType() == null) {
                target.setComponentType(this.getComponentType(node));
            }

        }

        // HTML attributes will override the declarative component
        assignAttributes(node, target);

    }

    /**
     * <p>
     * This method applies the HTML attribute overrides to the declarative
     * component, an object representation of the XML configuration.
     * </p>
     *
     * @param node markup
     * @param target child config bean
     */
    protected void assignAttributes(Node node, ComponentBean target) {
        // override with html attributes

        Iterator ai = node.getAttributes().entrySet().iterator();
        next: while (ai.hasNext()) {
            Map.Entry e = (Map.Entry) ai.next();

            if (e.getKey().equals("id")
                || e.getKey().equals("jsfid")
                || e.getKey().equals("allowbody")
                || e.getKey().equals("facetname")) {

                continue next;
            }

            AttributeBean original = null;
            Token valueToken = (Token) e.getValue();
            if (valueToken != null) {
                original = target.getAttribute((String) e.getKey());
                if (original != null) {
                    createAttribute(original, valueToken.getRawText(), target);
                } else  {
                    //any token that is not an attribute in the target becomes a symbol
                    StringBuffer identifier = new StringBuffer((String) e.getKey());
                    identifier.insert(0, '@');
                    SymbolBean symbol = new SymbolBean();
                    symbol.setName(identifier.toString());
                    symbol.setValue(valueToken.getRawText());
                    target.addSymbol(symbol);
                }
            }
        }

        if (node.getAttributes().containsKey("allowbody")) {
           target.setAllowBody((String) node.getAttributes().get("allowbody"));
        }

        if (node.getAttributes().containsKey("facetname")) {
            target.setFacetName((String) node.getAttributes().get("facetname"));
        }


    }

    /**
     * <p>Returns the {@link org.apache.shale.clay.parser.builder.Builder} that
     * is assigned the task of converting the html node to a corresponding component
     * metadata used to construct a JSF resource.</p>
     *
     * @param node markup node
     * @return builder that maps markup to config beans
     */
    public Builder getBuilder(Node node) {
        return BuilderFactory.getRenderer(node);
    }

}
TOP

Related Classes of org.apache.shale.clay.parser.builder.Builder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.