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

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

/*
* 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: XFormRenderer.java 3009 2009-03-20 12:46:39Z drmlipp $
*
* $Log$
* Revision 1.7  2006/12/19 22:38:36  mlipp
* Fixed newline handling.
*
* Revision 1.6  2006/12/19 15:23:46  drmlipp
* Implemented multiple selects.
*
* Revision 1.5  2006/12/15 15:58:02  drmlipp
* Continued.
*
* Revision 1.4  2006/12/13 12:49:33  drmlipp
* Simplified.
*
* Revision 1.3  2006/12/12 14:22:08  drmlipp
* Merged XForms client from branch.
*
* Revision 1.2  2006/12/12 10:14:23  drmlipp
* New taglib for XForms display.
*
* Revision 1.1.2.24  2006/12/11 13:30:19  drmlipp
* Continued with result delivery.
*
* Revision 1.1.2.23  2006/12/08 22:01:57  mlipp
* Minor improvements and adapted to Chiba update.
*
* Revision 1.1.2.22  2006/12/08 15:23:12  drmlipp
* Added submit handling to XForms JSF component.
*
* Revision 1.1.2.21  2006/12/02 22:23:35  mlipp
* Improved.
*
* Revision 1.1.2.20  2006/12/01 16:52:07  drmlipp
* Continued.
*
* Revision 1.1.2.19  2006/11/30 22:02:44  mlipp
* Continued.
*
* Revision 1.1.2.18  2006/11/29 22:03:37  mlipp
* Continued.
*
* Revision 1.1.2.17  2006/11/28 16:44:31  drmlipp
* Started JS based rendering.
*
* Revision 1.1.2.16  2006/11/28 13:08:42  drmlipp
* Started Ajax response handling.
*
* Revision 1.1.2.15  2006/11/27 16:57:33  drmlipp
* Outputting event.
*
* Revision 1.1.2.14  2006/11/27 14:28:33  drmlipp
* Continuing.
*
* Revision 1.1.2.13  2006/11/26 21:16:15  mlipp
* Getting Ajax call back to component.
*
* Revision 1.1.2.12  2006/11/25 22:45:56  mlipp
* Got request through to JSF.
*
* Revision 1.1.2.11  2006/11/24 16:15:42  drmlipp
* Continuing Ajax support.
*
* Revision 1.1.2.10  2006/11/23 23:00:53  mlipp
* Started AJAX handling.
*
* Revision 1.1.2.9  2006/11/16 21:23:21  mlipp
* Internationalized calendar dialog.
*
* Revision 1.1.2.8  2006/11/13 22:50:09  mlipp
* Continuing date/time handling.
*
* Revision 1.1.2.7  2006/11/10 22:29:48  mlipp
* Continuing date handling.
*
* Revision 1.1.2.6  2006/11/10 15:26:29  drmlipp
* Removed unnecessary statement.
*
* Revision 1.1.2.5  2006/11/09 23:16:15  mlipp
* Started response processing.
*
* Revision 1.1.2.4  2006/11/08 12:47:02  drmlipp
* Started using stylesheet.
*
* Revision 1.1.2.3  2006/11/07 16:33:01  drmlipp
* Continuing...
*
* Revision 1.1.2.2  2006/11/06 22:40:22  mlipp
* Continuing...
*
* 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.7  2006/11/05 22:12:06  mlipp
* Added calendar library.
*
* Revision 1.1.2.6  2006/11/03 15:37:07  drmlipp
* Made component a naming container.
*
* Revision 1.1.2.5  2006/11/02 22:07:17  mlipp
* Added client id generation.
*
* Revision 1.1.2.4  2006/11/01 22:28:19  mlipp
* Continuing.
*
* 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:12  drmlipp
* Component for linking stylesheet added.
*
*/
package de.danet.an.util.jsf.xftaglib;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

import javax.faces.application.FacesMessage;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ActionEvent;
import javax.faces.render.Renderer;
import javax.faces.webapp.FacesServlet;
import javax.portlet.RenderResponse;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Templates;
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 javax.xml.transform.stream.StreamSource;

import org.apache.myfaces.renderkit.html.util.AddResource;
import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
import org.chiba.xml.events.ChibaEventNames;
import org.chiba.xml.events.DOMEventNames;
import org.chiba.xml.events.XFormsEventNames;
import org.chiba.xml.xforms.ChibaBean;
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.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;

import de.danet.an.util.XMLUtil;
import de.danet.an.util.jsf.JSFUtil;
import de.danet.an.util.jsf.MyFacesAjaxServlet;
import de.danet.an.util.jsf.taglib.JSLoading;
import de.danet.an.util.jsf.taglib.MyFacesResourceHandler;

/**
* This class provides a renderer for the HtmlLinkCssStylesheet component.
*
* @author Michael Lipp
*/
public class XFormRenderer extends Renderer {

    private static final org.apache.commons.logging.Log logger
        = org.apache.commons.logging.LogFactory.getLog(XFormRenderer.class);

    protected static String L10N_MSGS
        = XFormRenderer.class.getPackage().getName() + ".L10n";

    private static final String DATA_PREFIX = "d_";
    private static final String TRIGGER_PREFIX = "t_";
    private static final String REMOVE_UPLOAD_PREFIX = "ru_";
   
    private static Templates defTemplatesCache = null;
   
    private static Map monthsNamesCache = new HashMap();
    private static Map abbrevMonthsNamesCache = new HashMap ();

    private Templates defTemplates () {
        if (defTemplatesCache == null) {
            try {
                InputStream is = XFormRenderer.class
                    .getResourceAsStream("initialFormData.xsl");
                defTemplatesCache = TransformerFactory.newInstance()
                    .newTemplates(new StreamSource(is));
            } catch (TransformerConfigurationException e) {
                logger.error (e.getMessage(), e);
            } catch (TransformerFactoryConfigurationError e) {
                logger.error (e.getMessage(), e);
            }
           
        }
        return defTemplatesCache;
    }

    private String[] monthsNames(Locale locale) {
        String[] res = (String[])monthsNamesCache.get(locale);
        if (res != null) {
            return res;
        }
        initMonthsNames(locale);
        return (String[])monthsNamesCache.get(locale);
    }
   
    private String[] abbrevMonthsNames(Locale locale) {
        String[] res = (String[])abbrevMonthsNamesCache.get(locale);
        if (res != null) {
            return res;
        }
        initMonthsNames(locale);
        return (String[])abbrevMonthsNamesCache.get(locale);
    }
   
    private void initMonthsNames(Locale locale) {
        Calendar cal = Calendar.getInstance(locale);
        cal.set(Calendar.DATE, 1);
        int months = cal.getActualMaximum(Calendar.MONTH)
            - cal.getActualMinimum(Calendar.MONTH) + 1;
        int minMonth = cal.getActualMinimum(Calendar.MONTH);
        String[] monthsNames = new String[months];
        String[] abbrevMonthsNames = new String[months];
        SimpleDateFormat dfl = new SimpleDateFormat("MMMMM", locale);
        SimpleDateFormat dfs = new SimpleDateFormat("MMM", locale);
        for (int i = 0; i < months; i++) {
            cal.set(Calendar.MONTH, minMonth + i);
            monthsNames[i] = dfl.format(cal.getTime());
            abbrevMonthsNames[i] = dfs.format(cal.getTime());
        }
        monthsNamesCache.put(locale, monthsNames);
        abbrevMonthsNamesCache.put(locale, abbrevMonthsNames);
    }
   
    private String join (String[] strings, String quote, String sep) {
        if (quote == null) {
            quote = "";
        }
        StringBuffer res = new StringBuffer ();
        for (int i = 0; i < strings.length; i++) {
            if (i > 0) {
                res.append(sep);
            }
            res.append(quote);
            res.append(strings[i]);
            res.append(quote);
        }
        return res.toString();
    }
   
    /* (non-Javadoc)
     * @see javax.faces.render.Renderer#encodeBegin
     */
    public void encodeBegin(FacesContext context, UIComponent comp)
        throws IOException {
        super.encodeBegin(context, comp);
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement(HTML.DIV_ELEM, null);
        writer.writeAttribute(HTML.ID_ATTR, comp.getClientId(context), null);
        writer.writeAttribute(HTML.CLASS_ATTR, "portlet-font jxcp", null);
        writer.endElement(HTML.DIV_ELEM);
    }

    /* (non-Javadoc)
     * @see javax.faces.render.Renderer#encodeEnd(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
     */
    public void encodeEnd(FacesContext context, UIComponent component)
        throws IOException {
        super.encodeEnd(context, component);
        XForm comp = (XForm)component;
        ChibaBean chiba = comp.chiba();
        if (logger.isDebugEnabled()) {
            try {
                logger.debug ("Chiba document:\n" + XMLUtil.w3cDomToString
                              (chiba.getXMLContainer().getDocumentElement()));
            } catch (XFormsException e) {
                logger.debug (e.getMessage());
            }
        }
        Object response = context.getExternalContext().getResponse();
        if (response instanceof RenderResponse) {
            try {
                Element form = chiba.getXMLContainer().getDocumentElement();
                NodeList titles
                    = form.getElementsByTagNameNS(XMLUtil.XMLNS_XHTML, "title");
                if (titles.getLength() > 0) {
                    String title = XMLUtil
                        .getFirstLevelTextContent((Element)titles.item(0));
                    ((RenderResponse)response).setTitle(title);
                }
            } catch (XFormsException e) {
                logger.debug (e.getMessage());
            }
        }

        // Ensure availability of JS
        JSLoading.ensureInclusion(context, JSLoading.UTIL);
        JSLoading.ensureInclusion(context, JSLoading.ANCHOR_POSITION);
        JSLoading.ensureInclusion(context, JSLoading.DATE);
        JSLoading.ensureInclusion(context, JSLoading.POPUP_WINDOW);
        JSLoading.ensureInclusion(context, JSLoading.CALENDAR_POPUP);
        AddResource addResource = AddResourceFactory.getInstance(context);
        if (!context.getExternalContext().getRequestMap()
            .containsKey(XFormRenderer.class.getName() + "_SCRIPTS")) {
            addResource.addJavaScriptAtPosition
                (context, AddResource.HEADER_BEGIN,
                 new MyFacesResourceHandler(XFormRenderer.class, "helpers.js"));
            addResource.addJavaScriptAtPosition
                (context, AddResource.HEADER_BEGIN,
                 new MyFacesResourceHandler
                 (XFormRenderer.class, "xforms2html.js"));
            context.getExternalContext().getRequestMap()
                .put(XFormRenderer.class.getName() + "_SCRIPTS", Boolean.TRUE);
        }
        // Add styles
        addResource.addStyleSheet
            (context, AddResource.HEADER_BEGIN,
             new MyFacesResourceHandler(XFormRenderer.class, "xforms.css"));

        // Write individual JS
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement(HTML.SCRIPT_ELEM, null);
        writer.writeAttribute
            (HTML.SCRIPT_TYPE_ATTR, HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT, null);

        String jsPrefix = JavascriptUtils
            .getValidJavascriptName(comp.getClientId(context),false);
        writer.write("window." + jsPrefix + "baseData={\n");
        writer.write("jsPrefix: \"" + jsPrefix + "\",\n");
        writer.write("elementIdPrefix: \""
                     + comp.getClientId(context)
                     + NamingContainer.SEPARATOR_CHAR + "\",\n");
        String urlPrefix = addResource.getResourceUri
            (context, new MyFacesResourceHandler(JSLoading.class, ""));
        writer.write("resourcePrefix: \"" + urlPrefix + "\",\n");
        writer.write("dataPrefix: \"" + DATA_PREFIX + "\",\n");
        writer.write("triggerPrefix: \"" + TRIGGER_PREFIX + "\",\n");
        writer.write("removeUploadPrefix: \"" + REMOVE_UPLOAD_PREFIX + "\",\n");
        Locale locale = context.getViewRoot().getLocale();
        ResourceBundle l10n = ResourceBundle.getBundle
            ("de.danet.an.util.jsf.xftaglib.L10n",
             locale, XFormRenderer.class.getClassLoader());
        writer.write("dateFormat: \"" + l10n.getString("dateFormat") + "\",\n");
        writer.write
            ("dateTimeFormat: \"" + l10n.getString("dateTimeFormat") + "\",\n");
        writer.write("monthsNames: new Array("
                     + join(monthsNames(locale), "\"", ",") + "),\n");
        writer.write("abbrevMonthsNames: new Array("
                + join(abbrevMonthsNames(locale), "\"", ",") + "),\n");
        writer.write("todayLabel: \"" + l10n.getString("todayLabel") + "\",\n");
       
        String path
            = MyFacesAjaxServlet.getAjaxActionUrl(context, component);
        if (path.indexOf("?") < 0) {
            path = path + "?";
        } else {
            path = path + "&";
        }
        writer.write("ajaxBaseUrl: \"" + path + "\"\n");
        writer.write("};\n");
       
        String guiData = null;
        try {
            Transformer t;
            t = defTemplates().newTransformer();
            StringWriter sw = new StringWriter();
            t.transform(new DOMSource(chiba.getXMLContainer()),
                        new StreamResult(sw));
            guiData = sw.toString();
        } catch (TransformerConfigurationException e) {
            logger.error (e.getMessage(), e);
        } catch (TransformerFactoryConfigurationError e) {
            logger.error (e.getMessage(), e);
        } catch (TransformerException e) {
            logger.error (e.getMessage(), e);
        } catch (XFormsException e) {
            logger.error (e.getMessage(), e);
        }
       
        guiData = guiData.replaceAll("\\r", "");
        guiData = guiData.replaceAll("\\\\", "\\\\\\\\");
        guiData = guiData.replaceAll("\"", "\\\\\"");
        guiData = guiData.replaceAll("\\n", "\\\\n");
       
        writer.write
            ("(new JxcpXFormBuilder(window." + jsPrefix + "baseData))"
             + ".buildUI(document.getElementById(\""
             + comp.getClientId(context) + "\"), \"" + guiData + "\");");
        writer.endElement(HTML.SCRIPT_ELEM);
    }

    /* (non-Javadoc)
     * @see javax.faces.render.Renderer#decode
     */
    public void decode(FacesContext context, UIComponent component) {
        super.decode(context, component);
        String clientId = component.getClientId(context);
        ChibaBean chiba = ((XForm)component).chiba();
        Map paramMap = context.getExternalContext().getRequestParameterMap();
        String ajaxCallTo
            = (String)paramMap.get(MyFacesAjaxServlet.AJAX_CALL_TO);
        if (ajaxCallTo != null && ajaxCallTo.equals(clientId)) {
            handleAjaxRequest (context, component, paramMap);
            return;
        }
        String clientBaseId = clientId + NamingContainer.SEPARATOR_CHAR;
        String trigger = null;
        Map paramValuesMap = context
            .getExternalContext().getRequestParameterValuesMap();
        try {
            for (Iterator i = paramValuesMap.entrySet().iterator();
                 i.hasNext();) {
                Map.Entry entry = (Map.Entry)i.next();
                String key = (String)entry.getKey();
                if (!key.startsWith(clientBaseId)) {
                    continue;
                }
                String controlId = key.substring(clientBaseId.length());
                if (controlId.startsWith(DATA_PREFIX)) {
                    String[] values = (String[])entry.getValue();
                    String value = values[0];
                    for (int j = 1; j < values.length; j++) {
                        value += " " + values[j];
                    }
                    chiba.updateControlValue
                        (controlId.substring(DATA_PREFIX.length()), value);
                    continue;
                }
                if (controlId.startsWith(TRIGGER_PREFIX)) {
                    trigger = controlId.substring(TRIGGER_PREFIX.length());
                    continue;
                }
            }
            if (trigger != null) {
                handleTrigger(component, chiba, trigger);
            }
        } catch (XFormsException e) {
            logger.warn(e.getMessage(), e);
        }
    }

    /**
     * Used to get result from InvokeSubmissionHandler.
     */
    static ThreadLocal submitAction = new ThreadLocal();
    static ThreadLocal submitResult = new ThreadLocal();
   
    /**
     * @param component
     * @param chiba
     * @param trigger
     * @throws XFormsException
     */
    private void handleTrigger
        (UIComponent component, ChibaBean chiba, String trigger)
        throws XFormsException {
        final List events = new ArrayList ();
        EventListener listener = new EventListener () {
            public void handleEvent(Event evt) {
                if (logger.isDebugEnabled()) {
                    logger.debug
                        ("Event of type \"" + evt.getType()
                         + "\" targeted at:\n"
                         + XMLUtil.w3cDomToString((Element)evt.getTarget()));
                }
                events.add(evt);
            }
        };
        EventTarget doc = null;
        try {
            doc = (EventTarget)
                chiba.getXMLContainer().getDocumentElement();
            doc.addEventListener
                (XFormsEventNames.SUBMIT_ERROR, listener, true);
            submitAction.set(null);
            submitResult.set(null);
            chiba.dispatch(trigger, DOMEventNames.ACTIVATE);
        } finally {
            if (doc != null) {
                doc.removeEventListener
                    (XFormsEventNames.SUBMIT_ERROR, listener, true);
            }
        }
        if (events.size() > 0) {
            JSFUtil.addMessage
                (FacesMessage.SEVERITY_ERROR, L10N_MSGS, "submitError", null);
        }
        if (submitResult.get() != null) {
            ((XForm)component).queueEvent(new ActionEvent(component));
            ((XForm)component).setUpdatedInstanceData
                ((String)submitAction.get(), (Node)submitResult.get());
            submitAction.set(null);
            submitResult.set(null);
        }
    }
   
    private void handleAjaxRequest
        (FacesContext context, UIComponent component, Map paramMap) {
        ChibaBean chiba = ((XForm)component).chiba();
        String controlId = (String)paramMap.get("controlId");
        String value = (String)paramMap.get("value");
        final List events = new ArrayList ();
        EventListener listener = new EventListener () {
            public void handleEvent(Event evt) {
                if (logger.isDebugEnabled()) {
                    logger.debug
                        ("Event of type \"" + evt.getType()
                         + "\" targeted at:\n"
                         + XMLUtil.w3cDomToString((Element)evt.getTarget()));
                }
                events.add(evt);
            }
        };
        EventTarget doc = null;
        try {
            doc = (EventTarget)
                chiba.getXMLContainer().getDocumentElement();
            doc.addEventListener
                (ChibaEventNames.STATE_CHANGED, listener, true);
            doc.addEventListener
                (ChibaEventNames.PROTOTYPE_CLONED, listener, true);
            doc.addEventListener
                (ChibaEventNames.ID_GENERATED, listener, true);
            doc.addEventListener
                (ChibaEventNames.ITEM_INSERTED, listener, true);
            doc.addEventListener
                (ChibaEventNames.ITEM_DELETED, listener, true);
            doc.addEventListener
                (ChibaEventNames.INDEX_CHANGED, listener, true);
            doc.addEventListener
                (ChibaEventNames.SWITCH_TOGGLED, listener, true);
           
            chiba.updateControlValue (controlId, value);
        } catch (XFormsException e) {
            logger.warn(e.getMessage(), e);
        } finally {
            if (doc != null) {
                doc.removeEventListener
                    (ChibaEventNames.STATE_CHANGED, listener, true);
                doc.removeEventListener
                    (ChibaEventNames.PROTOTYPE_CLONED, listener, true);
                doc.removeEventListener
                    (ChibaEventNames.ID_GENERATED, listener, true);
                doc.removeEventListener
                    (ChibaEventNames.ITEM_INSERTED, listener, true);
                doc.removeEventListener
                    (ChibaEventNames.ITEM_DELETED, listener, true);
                doc.removeEventListener
                    (ChibaEventNames.INDEX_CHANGED, listener, true);
                doc.removeEventListener
                    (ChibaEventNames.SWITCH_TOGGLED, listener, true);
            }
        }
        HttpServletResponse response
            = (HttpServletResponse)context.getExternalContext().getResponse();
        response.setContentType("text/xml; charset=UTF-8");
        try {
            Document resDoc = DocumentBuilderFactory.newInstance()
                .newDocumentBuilder().newDocument();
            Element eventsNode = resDoc.createElement("events");
            resDoc.appendChild(eventsNode);
            for (Iterator i = events.iterator(); i.hasNext();) {
                Event evt = (Event)i.next();
                Element eventNode = resDoc.createElement("event");
                eventNode.setAttribute("type", evt.getType());
                if (evt.getTarget() instanceof Node) {
                    eventNode.appendChild
                        (resDoc.importNode((Node)evt.getTarget(), true));
                }
                eventsNode.appendChild(eventNode);
            }
           
            // Transform to text
            Transformer t = TransformerFactory.newInstance().newTransformer();
            StringWriter sw = new StringWriter();
            t.transform(new DOMSource(resDoc), new StreamResult(sw));

            // Send as response
            response.getWriter().write(sw.toString());
            response.getWriter().close ();
        } catch (IOException e) {
            logger.error (e.getMessage(), e);
        } catch (ParserConfigurationException e) {
            logger.error (e.getMessage(), e);
        } catch (TransformerException e) {
            logger.error (e.getMessage(), e);
        }
        context.responseComplete();
    }
   
}
TOP

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

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.