All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jsftemplating.layout.facelets.FaceletsLayoutDefinitionReader Maven / Gradle / Ivy

/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://jsftemplating.dev.java.net/cddl1.html or
 * jsftemplating/cddl1.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at jsftemplating/cddl1.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.jsftemplating.layout.facelets;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import javax.xml.parsers.DocumentBuilder;

import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.sun.jsftemplating.layout.LayoutDefinitionException;
import com.sun.jsftemplating.layout.LayoutDefinitionManager;
import com.sun.jsftemplating.layout.SyntaxException;
import com.sun.jsftemplating.layout.descriptors.ComponentType;
import com.sun.jsftemplating.layout.descriptors.LayoutComponent;
import com.sun.jsftemplating.layout.descriptors.LayoutComposition;
import com.sun.jsftemplating.layout.descriptors.LayoutDefine;
import com.sun.jsftemplating.layout.descriptors.LayoutDefinition;
import com.sun.jsftemplating.layout.descriptors.LayoutElement;
import com.sun.jsftemplating.layout.descriptors.LayoutFacet;
import com.sun.jsftemplating.layout.descriptors.LayoutForEach;
import com.sun.jsftemplating.layout.descriptors.LayoutIf;
import com.sun.jsftemplating.layout.descriptors.LayoutInsert;
import com.sun.jsftemplating.layout.descriptors.LayoutStaticText;
import com.sun.jsftemplating.layout.template.BaseProcessingContext;
import com.sun.jsftemplating.layout.template.EventParserCommand;
import com.sun.jsftemplating.layout.template.ProcessingContextEnvironment;
import com.sun.jsftemplating.layout.template.TemplateParser;
import com.sun.jsftemplating.layout.template.TemplateReader;
import com.sun.jsftemplating.util.IncludeInputStream;
import com.sun.jsftemplating.util.LayoutElementUtil;
import com.sun.jsftemplating.util.Util;

import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;


/**
 * @author Jason Lee
 *
 */
public class FaceletsLayoutDefinitionReader {
    private URL url;
    private String key;
    private Document document;
    private int _idNumber;

    public FaceletsLayoutDefinitionReader(String key, URL url) {
	_idNumber = LayoutElementUtil.getStartingIdNumber(null, key);
	InputStream is = null;
	BufferedInputStream bs = null;
	try{
	    this.key = key;
	    this.url = url;

	    DocumentBuilder builder = DbFactory.getInstance();
	    builder.setErrorHandler(new ParsingErrorHandler());
	    is = this.url.openStream();
	    bs = new BufferedInputStream(is);
	    document = builder.parse(new IncludeInputStream(bs));
	} catch (Exception e) {
	    throw new LayoutDefinitionException(e);
	} finally {
	    Util.closeStream(bs);
	    Util.closeStream(is);
	}
    }

    public LayoutDefinition read() throws IOException {
	LayoutDefinition layoutDefinition = new LayoutDefinition(key);
	NodeList nodeList = document.getChildNodes();
	boolean abortProcessing = false;
    DocumentType docType = document.getDoctype();
    if (docType != null) {
        LayoutStaticText stDocType = new LayoutStaticText(layoutDefinition, "",
            "");
        layoutDefinition.addChildLayoutElement(stDocType);
    }
    for (int i = 0; i < nodeList.getLength() && (abortProcessing != true); i++) {
	    abortProcessing = process(layoutDefinition, nodeList.item(i), false);
	}
	return layoutDefinition;
    }

    public boolean process(LayoutElement parent, Node node, boolean nested) throws IOException {
	boolean abortProcessing = false;
	LayoutElement element = null;
	LayoutElement newParent = parent;
	boolean endElement = false;

	String value = node.getNodeValue();
//	TODO:  find out what "name" should be in the ctors
	switch (node.getNodeType()) {
	case Node.TEXT_NODE :
	    if (!value.trim().equals("")) {
		element = new LayoutStaticText(parent, 
			LayoutElementUtil.getGeneratedId(node.getNodeName(), getNextIdNumber()), 
			value);
	    }
	    break;
	case Node.ELEMENT_NODE:
	    element = createComponent(parent, node, nested);
	    if (element instanceof LayoutStaticText) {
		// We have a element node that needs to be static text
		endElement = true;
	    } else if (element instanceof LayoutForEach) {
		newParent = element;
	    } else if (element instanceof LayoutIf) {
		newParent = element;
	    } else if (element instanceof LayoutComponent) {
		nested = true;
		newParent = element;
	    } else if (element instanceof LayoutComposition) {
		abortProcessing = ((LayoutComposition)element).isTrimming(); 
		newParent = element;
	    } else if (element instanceof LayoutDefine) {
		newParent = element;
	    } else if (element instanceof LayoutFacet) {
		newParent = element;
	    } else if (element instanceof LayoutInsert) {
		newParent = element;
	    }
//	    FIXME: Jason, this code may need to be refactored.  I think almost
//	    FIXME: everything should have newParent = element.  The problem comes when
//	    FIXME: you are turning  and  into 2 separate staticText
//	    FIXME: components.  This should be a single component, then it could contain
//	    FIXME: children also.  You may want a to create a component like Woodstock's
//	    FIXME: "markup" component to do this.
	    break;
	default:
	    // just because... :P
	}

	if (element != null) {
	    parent.addChildLayoutElement(element);

	    NodeList nodeList = node.getChildNodes();
	    boolean abortChildProcessing = false;
	    for (int i = 0; i < nodeList.getLength() && (abortChildProcessing != true); i++) {
		abortChildProcessing = process(newParent, nodeList.item(i), nested);
	    }
	    if (abortChildProcessing == true) {
		abortProcessing = abortChildProcessing;
	    }else {

		if (endElement) {
		    String nodeName = node.getNodeName();
		    element = new LayoutStaticText(parent, LayoutElementUtil.getGeneratedId(nodeName, getNextIdNumber()), "");
		    parent.addChildLayoutElement(element);
		}
	    }
	}

	return abortProcessing;
    }
    
    private LayoutComposition processComposition(LayoutElement parent, String attrName, NamedNodeMap attrs, String id, boolean trimming) {
    LayoutComposition lc = new LayoutComposition(parent, id);
    lc.setTrimming(trimming);
    if (trimming) {
	    parent = parent.getLayoutDefinition(); // parent to the LayoutDefinition
	    parent.getChildLayoutElements().clear(); // a ui:composition clears everything outside of it
	}	
	Node fileNameNode = attrs.getNamedItem(attrName);
	String fileName = (fileNameNode != null) ? fileNameNode.getNodeValue() : null;
	lc.setTemplate(fileName);

	return lc;
    }

    private LayoutComponent processComponent(LayoutElement parent, Node node, NamedNodeMap attrs, String id, boolean trimming) {
	if (trimming) {
	    parent = parent.getLayoutDefinition(); // parent to the LayoutDefinition
	    parent.getChildLayoutElements().clear(); // a ui:composition clears everything outside of it
	}	
	LayoutComponent lc = new LayoutComponent(parent, id,
		LayoutDefinitionManager.getGlobalComponentType(null, "event"));
	parent.addChildLayoutElement(lc);
	LayoutComposition comp = processComposition(lc, "template", attrs, id+"_lc", trimming);	

	NodeList nodeList = node.getChildNodes();
	boolean abortChildProcessing = false;
	for (int i = 0; i < nodeList.getLength() && (abortChildProcessing != true); i++) {
	    try {
		abortChildProcessing = process(comp, nodeList.item(i), true);
	    } catch (IOException e) {
		e.printStackTrace();
	    }
	}
	lc.addChildLayoutElement(comp);

	return lc;
    }

/*
This code is not used and does not appear to be correct, it should use FacesContext.getApplication()
    private Application getApplication() {
        ApplicationFactory appFactory = (ApplicationFactory) FactoryFinder
                .getFactory(FactoryFinder.APPLICATION_FACTORY);
        return appFactory.getApplication();
    }

    private Object getElValue(String el) {
        FacesContext context = FacesContext.getCurrentInstance();
        return getApplication()
                .evaluateExpressionGet(context, el, Object.class);
    }
*/

    
    private LayoutElement createComponent(LayoutElement parent, Node node, boolean nested) {
	LayoutElement element = null;
	String nodeName = node.getNodeName();
	NamedNodeMap attrs = node.getAttributes();
	Node nameNode = attrs.getNamedItem("id");
	String id = (nameNode != null) ? nameNode.getNodeValue() :
	    LayoutElementUtil.getGeneratedId(nodeName, getNextIdNumber());

	if ("ui:composition".equals(nodeName)) {
	    element = processComposition(parent, "template", attrs, id, true);
	} else if ("ui:decorate".equals(nodeName)) {
	    element = processComposition(parent, "template", attrs, id, false);
	} else if ("ui:define".equals(nodeName)) {
	    String name = attrs.getNamedItem("name").getNodeValue();
	    element = new LayoutDefine(parent, name);
	} else if ("ui:insert".equals(nodeName)) {
	    LayoutInsert li = new LayoutInsert(parent, id);
	    Node nameAttr = attrs.getNamedItem("name");
	    String name = (nameAttr != null) ? nameAttr.getNodeValue() : null;
	    li.setName(name);
	    element = li;
	// Let these be handled by the else below, and let's see what happens :)
	} else if ("ui:component".equals(nodeName)) {
	    element = processComponent(parent, node, attrs, id, true);
	} else if ("ui:fragment".equals(nodeName)) {
	    element = processComponent(parent, node, attrs, id, false);
	    /*
	    Node bindingAttr = attrs.getNamedItem("binding");
	    if (bindingAttr == null) {
		throw new LayoutDefinitionException("ui:fragment requires a binding attribute");
	    }
	    String bindingEl = bindingAttr.getNodeValue();
	    Object obj = getElValue(bindingEl);
	    if (!(obj instanceof UIComponent)) {
		throw new LayoutDefinitionException("Binding EL must return a UIComponent");
	    }
	    UIComponent comp = (UIComponent) obj;
	    String family = comp.getFamily(); // TODO:  is the correct?
	    System.out.println(LayoutDefinitionManager.getGlobalComponentTypes(null));
	    ComponentType componentType = 
		LayoutDefinitionManager.getGlobalComponentType(null, family);
	    LayoutComponent lc = new LayoutComponent(parent, id, componentType);
    	    addAttributesToComponent(lc, node);
	    lc.setFacetChild(false);
	    lc.setNested(nested);
	    element = lc;
	    */
	} else if ("ui:debug".equals(nodeName)) {
	} else if ("ui:include".equals(nodeName)) {
	    element = processComposition(parent, "src", attrs, id, false);
	} else if ("ui:param".equals(nodeName)) {
	    // Handle "param"
	    Node nameAttNode = attrs.getNamedItem("name");
	    if (nameAttNode == null) {
		throw new SyntaxException("The 'name' attribute is required on 'param'.");
	    }
	    Node valueNode = attrs.getNamedItem("value");
	    if (valueNode == null) {
		throw new SyntaxException("The 'value' attribute is required on 'param'.");
	    }

	    // For now only handle cases where the parent is a LayoutComposition
	    if (!(parent instanceof LayoutComposition)) {
		throw new SyntaxException("<" + nodeName
		    + " name='" + nameAttNode.getNodeValue()
		    + "' value='" + valueNode.getNodeValue()
		    + "'> must be child of a 'composition' element!");
	    }
	    // Set the name=value on the parent LayoutComposition
	    ((LayoutComposition) parent).setParameter(
		nameAttNode.getNodeValue(), valueNode.getNodeValue());
	} else if ("ui:remove".equals(nodeName)) {
	    // Let the element remain null
	} else if ("ui:repeat".equals(nodeName)) {
	} else if ("ui:event".equals(nodeName)) {
	    // per Ken, we need to append "/>" to allow the handler parser code
	    // to end correctly
	    String body = node.getTextContent();
	    body = (body == null) ? "/>" : (body.trim() + "/>");
	    Node type = node.getAttributes().getNamedItem("type");
	    if (type == null) {
		// Ensure type != null
		throw new SyntaxException(
		    "The 'type' attribute is required on 'ui:event'!");
	    }
	    String eventName = type.getNodeValue();
	    InputStream is = new ByteArrayInputStream(body.getBytes());
	    EventParserCommand command = new EventParserCommand();
	    try {
		TemplateParser parser = new TemplateParser(is);
		parser.open();  // Needed to initialize things.
		// Setup the reader...
		TemplateReader reader = new TemplateReader("foo", parser); // TODO: get a real ID
		reader.pushTag("event"); // The tag will be popped at the end
		// Read the handlers...
		command.process(new BaseProcessingContext(), 
			new ProcessingContextEnvironment(reader, parent, true), eventName);
		// Clean up
		parser.close();
	    } catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    } finally {
		if (is != null) {
		    try {
			is.close();
		    } catch (Exception e) {
			// ignore
		    }
		}
	    }
	} else if ("ui:if".equals(nodeName)) {
	    // Handle "if" conditions
	    String condition = attrs.getNamedItem("condition").getNodeValue();
	    element = new LayoutIf(parent, condition);
	} else if ("ui:foreach".equals(nodeName)) {
	    // Handle "foreach" conditions
	    Node valueNode = attrs.getNamedItem("value");
	    if (valueNode == null) {
		throw new SyntaxException("The 'value' property is required on 'foreach'.");
	    }
	    Node varNode = attrs.getNamedItem("var");
	    if (varNode == null) {
		throw new SyntaxException("The 'var' property is required on 'foreach'.");
	    }

	    element = new LayoutForEach(parent, valueNode.getNodeValue(), varNode.getNodeValue());
	} else if ("f:facet".equals(nodeName)) {
	    // FIXME: Need to take NameSpace into account
	    nameNode = attrs.getNamedItem("name");
	    if (nameNode == null) {
		throw new IllegalArgumentException("You must provide a name "
		    + "attribute for all facets!  Parent component is: '"
		    + parent.getUnevaluatedId() + "'.");
	    }
	    LayoutFacet facetElt =  new LayoutFacet(parent, nameNode.getNodeValue());

	    // Determine if this is a facet place holder (i.e. we're defining
	    // a renderer w/ a facet), or if it is a facet value to set on a
	    // containing component.
	    boolean isRendered =
		!LayoutElementUtil.isNestedLayoutComponent(facetElt);
	    facetElt.setRendered(isRendered);
	    element = facetElt;
	} else {
	    LayoutComponent lc = null;
	    ComponentType componentType = null;
	    String nsURI = node.getNamespaceURI();
	    if (nsURI != null) {
		// Do lookup using namespace...
		componentType = LayoutDefinitionManager.getGlobalComponentType(
			null, nsURI + ':' + node.getLocalName());
	    }
	    if (componentType == null) {
		// Try w/o using namespace
		componentType = LayoutDefinitionManager.
			getGlobalComponentType(null, nodeName);
	    }
	    if (componentType == null) {
		String value = node.getNodeValue();
		if (value == null) {
		    value = "";
		}
//		FIXME: This needs to account for beginning and ending tags....
		lc = new LayoutStaticText(parent, id, 
			"<" + nodeName + buildAttributeList(node) + ">");
	    } else {
		lc = new LayoutComponent(parent, id, componentType);
		addAttributesToComponent(lc, node);
	    }
	    lc.setNested(nested);
//	    FIXME: Because of the way pages are composed in facelets, the parent
//	    FIXME: LayoutComponent may not exist in this LD.  In that case it is not
//	    FIXME: a "facet child", but it appears to be according to the following
//	    FIXME: method.  We need a better way to mark children as facets or real
//	    FIXME: children.  This may even require diverging the LD into 1 for
//	    FIXME: components and 1 for pages. :(
	    //LayoutElementUtil.checkForFacetChild(parent, lc);
	    //lc.setFacetChild(false);  This is done by checkForFacetChild(...)
	    element = lc;
	}

	return element;
    }

    private void addAttributesToComponent (LayoutComponent lc, Node node) {
	NamedNodeMap map = node.getAttributes();
	for (int i = 0; i < map.getLength(); i++) {
	    Node attr = map.item(i);
	    lc.addOption(attr.getNodeName(), attr.getNodeValue());
	}
    }

    private String buildAttributeList(Node node) {
	StringBuilder attrs = new StringBuilder();

	NamedNodeMap map = node.getAttributes();
	for (int i = 0; i < map.getLength(); i++) {
	    Node attr = map.item(i);
	    attrs.append(" ")
	    .append(attr.getNodeName())
	    .append("=\"")
	    .append(attr.getNodeValue())
	    .append("\"");
	}

	return attrs.toString();
    }

    /**
     *	

This method returns the next ID number. Calling this method will * increment the id number.

*/ public int getNextIdNumber() { // Make sure we increment the global counter, if appropriate LayoutElementUtil.incHighestId(_idNumber); return _idNumber++; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy