de.betterform.agent.betty.AppletProcessor.java Source code

Java tutorial

Introduction

Here is the source code for de.betterform.agent.betty.AppletProcessor.java

Source

/*
 * Copyright (c) 2012. betterFORM Project - http://www.betterform.de
 * Licensed under the terms of BSD License
 */

package de.betterform.agent.betty;

import de.betterform.xml.config.Config;
import de.betterform.xml.events.BetterFormEventNames;
import de.betterform.xml.events.XMLEvent;
import de.betterform.xml.xforms.AbstractProcessorDecorator;
import de.betterform.xml.xforms.XFormsProcessor;
import de.betterform.xml.xforms.exception.XFormsException;
import de.betterform.xml.xforms.ui.AbstractFormControl;
import de.betterform.xml.xforms.ui.Repeat;
import de.betterform.xml.xforms.ui.Upload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.w3c.dom.events.Event;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URI;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * AppletAdapter links a Java applet to the betterForm XForms processor. It's
 * responsible for steering the interaction with the processor and provides
 * methods for configuring the processor, loading forms and manage its
 * lifecycle.
 *
 * @author Joern Turner
 * @version $Id: AppletProcessor.java 3351 2008-07-19 19:54:12Z joern $
 */
public class AppletProcessor extends AbstractProcessorDecorator {
    private static final Log LOG = LogFactory.getLog(AppletProcessor.class);

    private Betty betty;
    private ClassLoader contextClassLoader;
    //    private EventTarget root;
    private String uploadDir;
    private List responseSequence = new ArrayList();
    private List deferredSelectors = new ArrayList();
    private StringBuffer initialEventBuffer = new StringBuffer();
    private boolean isReady = false;
    //    private XFormsProcessor xformsProcessor;

    /**
     * Creates a new applet adapter.
     */
    public AppletProcessor() {
        super();
    }

    public void setReady() {
        this.isReady = true;
        this.betty.javascriptEval(initialEventBuffer.toString());
        this.initialEventBuffer = null;
    }

    /**
     * Sets the context class loader.
     * <p/>
     * This is needed to ensure that the right classloader is used for parsing
     * operations.
     *
     * @param contextClassLoader the context class loader.
     */
    public void setContextClassLoader(ClassLoader contextClassLoader) {
        this.contextClassLoader = contextClassLoader;
    }

    /**
     * Sets the betterform applet.
     *
     * @param betty the betterform applet.
     */
    public void setBetterFormApplet(Betty betty) {
        this.betty = betty;
    }

    /**
     * Sets the upload directory.
     *
     * @param uploadDir the upload directory.
     */
    public void setUploadDir(String uploadDir) {
        this.uploadDir = uploadDir;
    }

    @Override
    protected boolean isEventUsed(String eventName) {
        return true;
    }

    public void init() throws XFormsException {
        try {
            //            ensureContextClassLoader();
            addEventListeners();
            /*
            Eventlistener for the events below MUST be registered always and cannot be optimized as in WebProcessor.
            They are used to update the client state and are not intended for form authors.
             */
            this.root.addEventListener(BetterFormEventNames.STATE_CHANGED, this, true);
            this.root.addEventListener(BetterFormEventNames.PROTOTYPE_CLONED, this, true);
            this.root.addEventListener(BetterFormEventNames.ID_GENERATED, this, true);
            this.root.addEventListener(BetterFormEventNames.ITEM_INSERTED, this, true);
            this.root.addEventListener(BetterFormEventNames.ITEM_DELETED, this, true);
            this.root.addEventListener(BetterFormEventNames.INDEX_CHANGED, this, true);
            this.root.addEventListener(BetterFormEventNames.SWITCH_TOGGLED, this, true);
            this.root.addEventListener(BetterFormEventNames.SCRIPT_ACTION, this, true);
            this.root.addEventListener(BetterFormEventNames.AVT_CHANGED, this, true);

            this.configuration = Config.getInstance();

            this.xformsProcessor.init();
            //            this.root = (EventTarget) this.xformsProcessor.getXForms();
        } catch (Exception e) {
            throw new XFormsException(e);
        }
    }

    @Override
    protected boolean isDebugOn() {
        return true;//todo - configurable somehow
    }

    public void executeHandler() throws XFormsException {
        try {
            // [1] check for load request
            //            String uri = (String) this.context.get(XFormsProcessor.LOAD_URI);
            String uri = (String) getContextParam(XFormsProcessor.LOAD_URI);

            //            String target = (String) this.context.get(XFormsProcessor.LOAD_TARGET);
            String target = (String) getContextParam(XFormsProcessor.LOAD_TARGET);

            //            this.context.remove(XFormsProcessor.LOAD_URI);
            removeContextParam(XFormsProcessor.LOAD_URI);

            //            this.context.remove(XFormsProcessor.LOAD_TARGET);
            removeContextParam(XFormsProcessor.LOAD_TARGET);

            if (uri != null) {
                if (target != null && target.equals("new")) {
                    // load url in new window and *don't* terminate
                    this.betty.getAppletContext().showDocument(new URL(uri), "_blank");
                } else {
                    // load url in same window annd *do* terminate
                    this.betty.getAppletContext().showDocument(new URL(uri), "_self");
                    return;
                }
            }

            // todo: [2] check for submission response !!!
        } catch (Exception e) {
            throw new XFormsException(e);
        }
    }

    public void shutdown() throws XFormsException {
        removeEventListeners();
        if (this.root != null) {
            this.root.removeEventListener(BetterFormEventNames.STATE_CHANGED, this, true);
            this.root.removeEventListener(BetterFormEventNames.PROTOTYPE_CLONED, this, true);
            this.root.removeEventListener(BetterFormEventNames.ID_GENERATED, this, true);
            this.root.removeEventListener(BetterFormEventNames.ITEM_INSERTED, this, true);
            this.root.removeEventListener(BetterFormEventNames.ITEM_DELETED, this, true);
            this.root.removeEventListener(BetterFormEventNames.INDEX_CHANGED, this, true);
            this.root.removeEventListener(BetterFormEventNames.SWITCH_TOGGLED, this, true);
            this.root.removeEventListener(BetterFormEventNames.SCRIPT_ACTION, this, true);
            this.root.removeEventListener(BetterFormEventNames.AVT_CHANGED, this, true);
        }
    }

    // implementation of EventListener

    /**
     * This method is called whenever an event occurs of the type for which the
     * <code>EventListener</code> interface was registered.
     *
     * @param event The <code>Event</code> contains contextual information about
     *              the event. It also contains the <code>stopPropagation</code> and
     *              <code>preventDefault</code> methods which are used in determining the
     *              event's flow and default action.
     */
    public void handleEvent(Event event) {
        ensureContextClassLoader();

        if (event instanceof XMLEvent) {
            try {
                //                Object contextInfo = ((XMLEvent) event).getContextInfo();
                //                String eventType = event.getType();
                Element targetElement = (Element) event.getTarget();
                String targetId = targetElement.getAttribute("id");

                XMLEvent xmlEvent = (XMLEvent) event;
                LOG.debug("AppletProcessor handling " + event.getType().toString());
                StringBuffer eventToEvaluate = new StringBuffer("fluxProcessor.applyChanges('");
                //eventToEvaluate.append(event.getType());
                eventToEvaluate.append(targetId);
                eventToEvaluate.append("',[{");
                eventToEvaluate.append("eventType:'");
                eventToEvaluate.append(event.getType());
                eventToEvaluate.append("'");

                Map defaultInfo = (Map) xmlEvent.getContextInfo();
                if (defaultInfo != null && defaultInfo.size() != 0) {
                    LOG.info("adding contextInfo");
                    LOG.info("defaultInfo " + defaultInfo);

                    eventToEvaluate.append(",");
                    // build the list of contextInfo properties
                    Iterator it = defaultInfo.keySet().iterator();
                    while (it.hasNext()) {
                        String key = (String) it.next();
                        String value = "" + defaultInfo.get(key);
                        //append
                        if (key.contains("-")) {
                            key = key.replace("-", "_");
                        }
                        eventToEvaluate.append(key + ":'");
                        eventToEvaluate.append(value + "");
                        eventToEvaluate.append("'");
                        if (it.hasNext()) {
                            eventToEvaluate.append(",");
                        }

                    }
                }

                eventToEvaluate.append("}]);");
                LOG.debug("string to eval: " + eventToEvaluate.toString());

                if (isReady) {
                    //pass for execution
                    this.betty.javascriptEval(eventToEvaluate.toString());
                } else {
                    this.initialEventBuffer.append(eventToEvaluate);
                }
            } catch (Exception e) {
                e.printStackTrace();
                this.xformsProcessor.handleEventException(e);
            }

        }

    }
    /*
            if (eventType.equals(BetterFormEventNames.BETTERFORM_REFRESH_DONE)) {
                for (int i = 0; i < this.responseSequence.size(); i++) {
                    call.append(this.responseSequence.get(i));
                }
                for (int i = 0; i < this.deferredSelectors.size(); i++) {
                    call.append(this.deferredSelectors.get(i));
                }
                this.betty.javascriptEval(call.toString());
                this.responseSequence.clear();
                this.deferredSelectors.clear();
                return;
            }
    */

    // update api for applet

    /**
     * Dispatches an event into the internal DOM.
     *
     * @param id        the id of the target element.
     * @param eventType the type of event to be fired.
     * @throws XFormsException if any error occurred during event dispatching.
     */
    public boolean dispatch(String id, String eventType) throws XFormsException {
        try {
            ensureContextClassLoader();
            return this.xformsProcessor.dispatch(id, eventType);
        } catch (Exception e) {
            throw new XFormsException("failed to dispatch event '" + eventType + "' to target '" + id + "'", e);
        }
    }

    public boolean dispatch(String targetId, String eventType, Object info, boolean bubbles, boolean cancelable)
            throws XFormsException {
        try {
            ensureContextClassLoader();
            return this.xformsProcessor.dispatch(targetId, eventType, info, bubbles, cancelable);
        } catch (Exception e) {
            throw new XFormsException("failed to dispatch event '" + eventType + "' to target '" + targetId + "'",
                    e);
        }

    }

    /**
     * Updates a control's value in the internal DOM.
     *
     * @param id    the id of the control.
     * @param value the new value of the control.
     * @throws XFormsException if any error occurred during value updating.
     */
    public void setValue(String id, String value) throws XFormsException {
        try {
            ensureContextClassLoader();

            AbstractFormControl element = (AbstractFormControl) this.xformsProcessor.lookup(id);
            if (element instanceof Upload) {
                setValue(id, value, "", "");
            } else {
                this.xformsProcessor.setControlValue(id, value);
            }
        } catch (Exception e) {
            throw new XFormsException("failed to update value at '" + id + "'", e);
        }
    }

    /**
     * Updates an upload's value in the internal DOM. <P> The destination
     * parameter will be interpeted relative to the form base and is used as
     * follows: <ol> <li>If it is <code>null</code> or empty, the file will be
     * uploaded to the form base.</li> <li>If it denotes a non-existing file, a
     * corresponding directory will be created and the file will be uploaded to
     * that directory.</li> <li>If it denotes an existing directory, the file
     * will be uploaded to that directory.</li> <li>If it denotes an existing
     * file, the existing file will be overwritten with the uploaded file. Thus,
     * the name of the uploaded file is dropped.</li> </ol> Caution: The latter
     * option is a potential security risk !
     *
     * @param id          the id of the upload control
     * @param value       the absolute name of the file to be uploaded
     * @param type        the optional file mediatype
     * @param destination the optional file destination
     * @throws XFormsException if any error occurred during file uploading.
     */
    public void setValue(String id, String value, String type, String destination) throws XFormsException {
        try {
            ensureContextClassLoader();

            File source = new File(value);
            String mediatype = type == null ? "" : type;
            String filename = source.getName();
            //System.out.println("upload: " + source + ", mediatype='" + mediatype + "'");

            if (this.xformsProcessor.isFileUpload(id, "anyURI")) {
                // get base uri
                URI baseURI = new URI((String) this.xformsProcessor.getContextParam(XFormsProcessor.BASE_URI));
                File baseFile = new File(baseURI);
                if (!baseFile.isDirectory()) {
                    baseURI = baseFile.getParentFile().toURI();
                }

                // get target
                File target;
                if (destination != null && destination.length() > 0) {
                    target = new File(baseURI.resolve(destination));
                } else {
                    target = new File(baseURI.resolve(this.uploadDir));
                }

                // check directory
                if (!target.exists()) {
                    target.mkdirs();
                }
                if (target.isDirectory()) {
                    target = new File(target.getAbsolutePath(), filename);
                }

                // copy file
                //System.out.println("upload: copying to " + target);
                FileInputStream fis = new FileInputStream(source);
                FileOutputStream fos = new FileOutputStream(target);
                byte[] buf = new byte[1024];
                int i;
                while ((i = fis.read(buf)) != -1) {
                    fos.write(buf, 0, i);
                }
                fis.close();
                fos.close();

                // generate relative uri
                URI generated = baseURI.relativize(target.toURI());
                //System.out.println("upload: generated uri " + generated);

                // update control
                this.xformsProcessor.setUploadValue(id, mediatype, filename,
                        generated.toString().getBytes("UTF-8"));
            } else {
                // read binary data into memory
                byte[] data = new byte[(int) source.length()];
                //System.out.println("upload: inlining " + data.length + " bytes");

                FileInputStream stream = new FileInputStream(source);
                stream.read(data);
                stream.close();

                // update control
                this.xformsProcessor.setUploadValue(id, mediatype, filename, data);
            }
        } catch (Exception e) {
            throw new XFormsException("failed to upload at '" + id + "'", e);
        }
    }

    /**
     * Updates a repeat's index.
     *
     * @param id    the id of the repeat.
     * @param index the new repeat index.
     * @throws XFormsException if any error occurred during repeat index
     *                         updating.
     */
    public void setRepeatIndex(String id, int index) throws XFormsException {
        try {
            ensureContextClassLoader();

            Repeat repeat = (Repeat) this.xformsProcessor.lookup(id);
            repeat.setIndex(index);
        } catch (Exception e) {
            throw new XFormsException("failed to set repeat index at '" + id + "'", e);
        }
    }

    @Override
    protected boolean eventOptimizationIsDisabled() {
        return true;
    }
    // helper

    private void ensureContextClassLoader() {
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                Thread.currentThread().setContextClassLoader(contextClassLoader);
                return null;
            }
        });
    }

    private String escape(String string) {
        if (string == null) {
            return string;
        }

        StringBuffer buffer = new StringBuffer(string.length());
        char c;
        for (int index = 0; index < string.length(); index++) {
            c = string.charAt(index);
            switch (c) {
            case '\n':
                buffer.append('\\').append('n');
                break;
            case '\r':
                buffer.append('\\').append('r');
                break;
            case '\t':
                buffer.append('\\').append('t');
                break;
            case '\'':
                buffer.append('\\').append('\'');
                break;
            case '\"':
                buffer.append('\\').append('\"');
                break;
            default:
                buffer.append(c);
                break;
            }
        }

        return buffer.toString();
    }
}

// end of class