Package de.danet.an.util.jsf.xftaglib

Source Code of de.danet.an.util.jsf.xftaglib.XForm

/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2005 Danet GmbH (www.danet.de), BU BTS.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* $Id: XForm.java 3009 2009-03-20 12:46:39Z drmlipp $
*
* $Log$
* Revision 1.2  2006/12/12 10:14:23  drmlipp
* New taglib for XForms display.
*
* Revision 1.1.2.8  2006/12/11 13:30:19  drmlipp
* Continued with result delivery.
*
* Revision 1.1.2.7  2006/12/10 21:53:03  mlipp
* Insert instance in first existing model.
*
* Revision 1.1.2.6  2006/12/08 22:01:57  mlipp
* Minor improvements and adapted to Chiba update.
*
* Revision 1.1.2.5  2006/12/08 15:23:12  drmlipp
* Added submit handling to XForms JSF component.
*
* Revision 1.1.2.4  2006/12/08 08:51:43  drmlipp
* Moved generation of xforms:instance node to XForm.
*
* Revision 1.1.2.3  2006/12/07 23:17:13  mlipp
* Restructured XForm component's properties.
*
* Revision 1.1.2.2  2006/11/28 13:08:42  drmlipp
* Started Ajax response handling.
*
* Revision 1.1.2.1  2006/11/06 16:51:09  drmlipp
* Separated JSF taglib from util jar and XForm taglib from util taglib.
*
* Revision 1.1.2.6  2006/11/03 15:37:07  drmlipp
* Made component a naming container.
*
* Revision 1.1.2.5  2006/10/31 16:07:17  drmlipp
* Seeing the form...
*
* Revision 1.1.2.4  2006/10/30 09:41:33  drmlipp
* Added form attribute.
*
* Revision 1.1.2.3  2006/10/28 21:56:12  mlipp
* Output via ChibaBean.
*
* Revision 1.1.2.2  2006/10/27 15:25:53  drmlipp
* Continuing.
*
* Revision 1.1.2.1  2006/10/26 21:01:01  mlipp
* Started XForm tag.
*
* Revision 1.2  2006/09/29 12:32:09  drmlipp
* Consistently using WfMOpen as projct name now.
*
* Revision 1.1  2006/09/14 13:34:11  drmlipp
* Component for linking stylesheet added.
*
*/
package de.danet.an.util.jsf.xftaglib;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.faces.FacesException;
import javax.faces.component.ActionSource;
import javax.faces.component.NamingContainer;
import javax.faces.component.UICommand;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.portlet.PortletSession;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.chiba.xml.xforms.ChibaBean;
import org.chiba.xml.xforms.config.Config;
import org.chiba.xml.xforms.config.XFormsConfigException;
import org.chiba.xml.xforms.exception.XFormsException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import de.danet.an.util.XMLUtil;

/**
* This class provides a component that causes a given XForm
* to be included in the page.
*
* @author Michael Lipp
*
*/
public class XForm extends UICommand
    implements NamingContainer, ActionSource {

    private static final org.apache.commons.logging.Log logger
        = org.apache.commons.logging.LogFactory.getLog(XForm.class);
   
    public static String COMPONENT_FAMILY = "de.danet.bts.jsf.taglib";
    public static String COMPONENT_TYPE = "de.danet.bts.XForm";

    private static String CHIBA_BEANS_ATTR_PREFIX = "CHIBA_BEAN_";
    /** ChibaBeans are not serializable and may therefore not be put
     * in the session context. We therefore store a key into this cache
     * instead */
    private static Map chibaBeansCache = null;

    private Object form;
    private InstanceDataModel value;
    private String submitAction = null;
    private Node updatedInstanceData = null;

    /* (non-Javadoc)
     * @see javax.faces.component.UIComponent#getFamily()
     */
    public String getFamily() {
        return COMPONENT_FAMILY;
    }

    public Object saveState(FacesContext context) {
        Object[] values = new Object[3];
        values[0] = super.saveState(context);
        values[1] = form;
        values[2] = value;
        return ((Object) (values));
    }

    public void restoreState(FacesContext context, Object state) {
        Object[] values = (Object[])state;
        super.restoreState(context, values[0]);
        form = values[1];
        value = (InstanceDataModel)values[2];
    }
   
    public void setForm(Object value) {
        form = (Object)value;
    }

    public Object getForm() {
        if (form != null) {
            return form;
        }
        ValueBinding vb = getValueBinding("form");
        return vb != null ? (Object)vb.getValue(getFacesContext()) : null;
    }
   
    public void setValue(Object value) {
        value = (InstanceDataModel)value;
    }

    public Object getValue() {
        if (value != null) {
            return value;
        }
        ValueBinding vb = getValueBinding("value");
        return vb != null
            ? (InstanceDataModel)vb.getValue(getFacesContext()) : null;
    }

    /**
     * This class provides a session binding listener that removes a
     * ChibaBean from the global cache when it is no longer referenced
     * from the session.
     *
     * @author Michael Lipp
     */
    public static class ChibaBeanKeyHolder
        implements HttpSessionBindingListener, Serializable {
        private String key;
   
        public ChibaBeanKeyHolder (String key) {
            this.key = key;
        }

        /* (non-Javadoc)
         * @see javax.servlet.http.HttpSessionBindingListener#valueBound
         */
        public void valueBound(HttpSessionBindingEvent arg0) {
        }

        /* (non-Javadoc)
         * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound
         */
        public void valueUnbound(HttpSessionBindingEvent arg0) {
            ChibaBean chiba = (ChibaBean)chibaBeansCache.remove(key);
            try {
                chiba.shutdown();
            } catch (XFormsException e) {
                logger.debug(e.getMessage(), e);
            }
        }
    }

    public ChibaBean chiba() {
        if (chibaBeansCache == null) {
            chibaBeansCache = new HashMap ();
            InputStream is
                = XForm.class.getResourceAsStream("chiba-config.xml");
            try {
                Config.getInstance(is);
            } catch (XFormsConfigException e) {
                throw (IllegalStateException)
                    (new IllegalStateException (e.getMessage())).initCause(e);
            }
        }
        String key = null;
        Object sessionObject
            = FacesContext.getCurrentInstance()
                .getExternalContext().getSession(true);
        if (sessionObject instanceof PortletSession) {
            key = ((PortletSession)sessionObject).getId();
        } else {
            key = ((HttpSession)sessionObject).getId();
        }
        key += "/" + getId() + "/" + ((InstanceDataModel)getValue()).getId();
        if (chibaBeansCache.containsKey(key)) {
            return (ChibaBean)chibaBeansCache.get(key);
        }
        ChibaBean chiba = setupChibaBean();
       
        chibaBeansCache.put(key, chiba);
        if (sessionObject instanceof PortletSession) {
            ((PortletSession)sessionObject).setAttribute
                (CHIBA_BEANS_ATTR_PREFIX + getId(),
                 new ChibaBeanKeyHolder (key));
        } else if (sessionObject instanceof HttpSession) {
            ((HttpSession)sessionObject).setAttribute
                (CHIBA_BEANS_ATTR_PREFIX + getId(),
                 new ChibaBeanKeyHolder (key));
        }

        return chiba;
    }

    /**
     * Setup a new ChibaBean for the given form and instance data.
     * @return
     */
    private ChibaBean setupChibaBean() {
        Element form = setupBasicForm();
        insertInstanceData (form);
        if (logger.isDebugEnabled()) {
            try {
                StringWriter sw = new StringWriter();
                Transformer t;
                t = TransformerFactory.newInstance().newTransformer();
                t.transform(new DOMSource(form), new StreamResult(sw));
                logger.debug ("Initializing Chiba with:\n" + sw.toString());
            } catch (TransformerConfigurationException e) {
                logger.debug(e.getMessage(), e);
            } catch (TransformerFactoryConfigurationError e) {
                logger.debug(e.getMessage(), e);
            } catch (TransformerException e) {
                logger.debug(e.getMessage(), e);
            }
        }
        ChibaBean chiba = new ChibaBean ();
        try {
            chiba.setXMLContainer (form);
            chiba.init();
        } catch (XFormsException e) {
            throw new FacesException(e.getMessage(), e);
        }
        return chiba;
    }

    /**
     * Setup the basic form.
     * @return the form propertiy if given as W3C DOM node, else the
     * parsed form definition string.
     */
    private Element setupBasicForm() {
        Element form = null;
        Object formProperty = getForm ();
        if (formProperty instanceof Document) {
            form = ((Document)formProperty).getDocumentElement();
        } else if (formProperty instanceof Element) {
            form = (Element)formProperty;
        } else if (formProperty instanceof String) {
            DocumentBuilder db;
            try {
                db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                form = db.parse
                    (new InputSource(new StringReader((String)formProperty)))
                    .getDocumentElement();
            } catch (ParserConfigurationException e) {
                throw (IllegalStateException)
                    new IllegalStateException (e.getMessage()).initCause(e);
            } catch (SAXException e) {
                throw (IllegalArgumentException)
                    new IllegalArgumentException (e.getMessage()).initCause(e);
            } catch (IOException e) {
                throw (IllegalStateException)
                    new IllegalStateException (e.getMessage()).initCause(e);
            }
        } else {
            throw new FacesException
                ("Form attribute must be of type String or W3C node.");
        }
        return form;
    }
   
    /**
     * Insert the instance data into the basic form.
     * @param form the basic form
     */
    void insertInstanceData (Element form) {
        // get xhtml:head element
        NodeList headEls
            = form.getElementsByTagNameNS(XMLUtil.XMLNS_XHTML, "head");
        if (headEls.getLength() != 1) {
            throw new IllegalArgumentException
                ("The form must include exactly one xhtml:head element.");
        }
        Element head = (Element)headEls.item(0);
        // find or insert xforms:model
        Element model = null;
        NodeList modelEls
            = head.getElementsByTagNameNS(XMLUtil.XMLNS_XFORMS, "model");
        if (modelEls.getLength() > 0) {
            model = (Element)modelEls.item(0);
        } else {
            model = form.getOwnerDocument()
                .createElementNS(XMLUtil.XMLNS_XFORMS, "xforms:model");
            head.appendChild(model);
        }
        // insert instance data in model
        InstanceDataModel idm = (InstanceDataModel)getValue();
        Element instanceData = (Element)
            form.getOwnerDocument().importNode(idm.getInstanceData(), true);
        Element instance = form.getOwnerDocument()
            .createElementNS(XMLUtil.XMLNS_XFORMS, "instance");
        instance.setAttributeNS(XMLUtil.XMLNS_NS, "xmlns", "");
        instance.appendChild(instanceData);
        model.insertBefore(instance, model.getFirstChild());
        // add submission
        Element submission = form.getOwnerDocument()
            .createElementNS(XMLUtil.XMLNS_XFORMS, "submission");
        submission.setAttribute("id", "action");
        submission.setAttribute("action", "invoke:application");
        submission.setAttribute("method", "");
        submission.setAttribute("replace", "none");
        model.insertBefore(submission, instance.getNextSibling());
        // find existing bindings
        NodeList bindEls = model
            .getElementsByTagNameNS(XMLUtil.XMLNS_XFORMS, "bind");
        Set knownBinds = new HashSet ();
        for (int i = 0; i < bindEls.getLength(); i++) {
            knownBinds.add (((Element)bindEls.item(i)).getAttribute("id"));
        }
        // add provided bindings with lower precedence
        if (idm.getBindings() != null) {
            for (Element child = (Element)idm.getBindings().getFirstChild();
                 child != null; child = (Element)child.getNextSibling()) {
                String id = child.getAttribute("id");
                if (!knownBinds.contains(id)) {
                    Node bind = form.getOwnerDocument().importNode(child, true);
                    model.insertBefore(bind, submission.getNextSibling());
                }
            }
        }
    }

    /**
     * @param updatedInstanceData The updatedInstanceData to set.
     */
    public void setUpdatedInstanceData
        (String submitAction, Node updatedInstanceData) {
        this.submitAction = submitAction;
        this.updatedInstanceData = updatedInstanceData;
    }

    /* (non-Javadoc)
     * @see javax.faces.component.UIComponentBase#processUpdates
     */
    public void processUpdates(FacesContext context) {
        super.processUpdates(context);
        ((InstanceDataModel)getValue()).setSubmitAction(submitAction);
        ((InstanceDataModel)getValue()).setInstanceData(updatedInstanceData);
    }
   
   
}
TOP

Related Classes of de.danet.an.util.jsf.xftaglib.XForm

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.