de.betterform.xml.xforms.action.AbstractAction.java Source code

Java tutorial

Introduction

Here is the source code for de.betterform.xml.xforms.action.AbstractAction.java

Source

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

package de.betterform.xml.xforms.action;

import de.betterform.xml.dom.DOMUtil;
import de.betterform.xml.events.DOMEventNames;
import de.betterform.xml.events.XFormsEventNames;
import de.betterform.xml.events.XMLEventConstants;
import de.betterform.xml.ns.NamespaceConstants;
import de.betterform.xml.xforms.XFormsConstants;
import de.betterform.xml.xforms.XFormsElement;
import de.betterform.xml.xforms.exception.XFormsException;
import de.betterform.xml.xforms.model.Model;
import de.betterform.xml.xforms.model.bind.Binding;
import de.betterform.xml.xforms.model.bind.BindingResolver;
import de.betterform.xml.xforms.ui.Group;
import de.betterform.xml.xforms.ui.Item;
import de.betterform.xml.xforms.ui.RepeatItem;
import de.betterform.xml.xpath.impl.saxon.XPathCache;
import de.betterform.xml.xpath.impl.saxon.XPathUtil;
import net.sf.saxon.om.NodeInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;

import java.util.List;

/**
 * Base class for all action implementations.
 *
 * @author Ulrich Nicolas Lissé
 * @version $Id: AbstractAction.java 3501 2008-08-28 17:48:27Z joern $
 */
public abstract class AbstractAction extends XFormsElement implements EventListener, XFormsAction {
    private static final Log LOGGER = LogFactory.getLog(AbstractAction.class);

    protected int position = 1;

    /**
     * The id of the containing repeat item, if any.
     */
    protected String repeatItemId;

    /**
     * The event type by which this action is triggered.
     */
    protected String eventType;

    /**
     * the event target of the event
     */
    protected String eventTargetId;

    /**
     * the event phase when the event is triggered
     */
    protected String phase;
    protected String propagate;
    protected String defaultAction;

    /**
     * Creates an action implementation.
     *
     * @param element the element.
     * @param model   the context model.
     */
    public AbstractAction(Element element, Model model) {
        super(element, model);
    }

    // repeat stuff

    /**
     * Sets the id of the repeat item this element is contained in.
     *
     * @param repeatItemId the id of the repeat item this element is contained
     *                     in.
     */
    public void setRepeatItemId(String repeatItemId) throws XFormsException {
        this.repeatItemId = repeatItemId;
    }

    /**
     * Returns the id of the repeat item this element is contained in.
     *
     * @return the id of the repeat item this element is contained in.
     */
    public String getRepeatItemId() {
        return this.repeatItemId;
    }

    /**
     * Checks wether this element is repeated.
     *
     * @return <code>true</code> if this element is contained in a repeat item,
     *         <code>false</code> otherwise.
     */
    public boolean isRepeated() {
        return this.repeatItemId != null;
    }

    // lifecycle methods

    /**
     * Performs element init.
     *
     * @throws XFormsException if any error occurred during init.
     */
    public void init() throws XFormsException {
        if (getLogger().isTraceEnabled()) {
            getLogger().trace(this + " init");
        }

        initializeAction();
    }

    /**
     * Performs element disposal.
     *
     * @throws XFormsException if any error occurred during disposal.
     */
    public void dispose() throws XFormsException {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug(this + " dispose");
        }

        disposeAction();
    }

    // lifecycle template methods

    /**
     * Initializes this action.
     */
    private final void initializeAction() throws XFormsException {
        if (!(getParentObject() instanceof XFormsAction)) {
            String event = this.element.getAttributeNS(NamespaceConstants.XMLEVENTS_NS,
                    XMLEventConstants.EVENT_ATTRIBUTE);
            if (event.length() == 0) {
                // default if nothing other is specified
                event = DOMEventNames.ACTIVATE;
            }
            this.eventType = event;
            String observer = this.element.getAttributeNS(NamespaceConstants.XMLEVENTS_NS, "observer");

            this.eventTargetId = this.element.getAttributeNS(NamespaceConstants.XMLEVENTS_NS, "target");
            this.phase = this.element.getAttributeNS(NamespaceConstants.XMLEVENTS_NS, "phase");
            if (phase.equals("")) {
                this.phase = "default";
            }
            this.propagate = this.element.getAttributeNS(NamespaceConstants.XMLEVENTS_NS, "propagate");
            if (propagate.equals("")) {
                this.propagate = "continue";
            }
            this.defaultAction = this.element.getAttributeNS(NamespaceConstants.XMLEVENTS_NS, "defaultAction");
            if (defaultAction.equals("")) {
                this.defaultAction = "perform";
            }
            String handler = this.element.getAttributeNS(NamespaceConstants.XMLEVENTS_NS, "handler");
            if (handler != null && !(handler.equals(""))) {
                LOGGER.warn("Attribute 'handler' for " + DOMUtil.getCanonicalPath(this.element)
                        + " ignored (not implemented yet)");
            }

            EventTarget targetElement = null;
            if (eventTargetId != null && !(eventTargetId.equals(""))) {
                final XFormsElement xformsElement = this.container.lookup(this.eventTargetId);
                if (xformsElement != null) {
                    targetElement = xformsElement.getTarget();
                } else {
                    // In some cases the xforms element isn't registered yet (e.g.: action refers to an Instance, or a forward reference)
                    targetElement = (EventTarget) de.betterform.xml.xpath.impl.saxon.XPathUtil.getAsNode(
                            XPathUtil.evaluate(container.getDocument(), "//*[@id = '" + eventTargetId + "']"), 1);
                    if (targetElement == null) {
                        throw new XFormsException(
                                "targetElement with id '" + eventTargetId + "' is not present in this form");
                    }
                }
                addListener(targetElement);
            }

            if (observer != null && !(observer.equals(""))) {
                final XFormsElement xformsElement = this.container.lookup(observer);
                if (xformsElement != null) {
                    targetElement = xformsElement.getTarget();
                } else {
                    // In some cases the xforms element isn't registered yet (e.g.: action refers to an Instance, or a forward reference)
                    targetElement = (EventTarget) de.betterform.xml.xpath.impl.saxon.XPathUtil.getAsNode(
                            XPathUtil.evaluate(container.getDocument(), "//*[@id = '" + observer + "']"), 1);
                    if (targetElement == null) {
                        throw new XFormsException(
                                "targetElement with id '" + observer + "' is not present in this form");
                    }
                }
                addListener(targetElement);
            } else {
                XFormsElement parent = getParentObject();
                if (parent != null) {
                    targetElement = parent.getTarget();
                    addListener(targetElement);
                } else {
                    getLogger().warn("parent is null");
                }
            }
            if (getLogger().isTraceEnabled()) {
                getLogger().trace(this + " init: added handler for event '" + this.eventType + "' to "
                        + DOMUtil.getCanonicalPath(this.element));
            }
        }
    }

    private void addListener(EventTarget targetElement) {
        addEventToUsedEvents();

        if (this.phase.equals("capture")) {
            targetElement.addEventListener(this.eventType, this, true);
        } else {
            targetElement.addEventListener(this.eventType, this, false);
        }
    }

    private void addEventToUsedEvents() {
        //add eventType to list of used events in XFormsProcessor
        List usedEvents = this.container.getProcessor().getEventList();
        if (!usedEvents.contains(this.eventType)) {
            usedEvents.add(this.eventType);
        }
    }

    /**
     * Disposes this action.
     */
    protected final void disposeAction() {
        if (!(getParentObject() instanceof XFormsAction)) {
            XFormsElement parent = getParentObject();
            parent.getTarget().removeEventListener(this.eventType, this, false);
        }
    }

    // implementation of org.w3c.dom.event.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 final void handleEvent(Event event) {
        try {
            if (event.getType().equals(this.eventType)) {
                boolean doPerform = false;

                if (this.propagate.equals("stop")) {
                    event.stopPropagation();
                }
                if (this.defaultAction.equals("cancel")) {
                    event.preventDefault();
                }

                if (getLogger().isTraceEnabled()) {
                    String currentEventId = (((Element) event.getCurrentTarget()).getAttribute("id"));
                    String targetEventId = (((Element) event.getTarget()).getAttribute("id"));
                    LOGGER.trace(
                            "currentEventTargetId: '" + currentEventId + "' eventTargetId:'" + targetEventId + "'");

                    getLogger().trace("handling event '" + this.eventType + "' target: "
                            + DOMUtil.getCanonicalPath(this.element) + "' Event Phase: (" + event.getEventPhase()
                            + ")");
                }

                if (getLogger().isDebugEnabled()) {
                    switch (event.getEventPhase()) {
                    case Event.CAPTURING_PHASE:
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("CAPTURING PHASE");
                        }

                        break;
                    case Event.AT_TARGET:
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("AT_TARGET");
                        }

                        break;
                    case Event.BUBBLING_PHASE:
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("BUBBLING");
                        }
                        break;
                    }
                }

                if (event.getEventPhase() == Event.CAPTURING_PHASE && this.phase.equals("capture")) {
                    doPerform = true;
                } else if (event.getEventPhase() == Event.AT_TARGET) {
                    doPerform = true;
                } else if (event.getEventPhase() == Event.BUBBLING_PHASE) {
                    doPerform = true;
                }

                String targetEventId = (((Element) event.getTarget()).getAttribute("id"));
                if (!(this.eventTargetId.equals(""))) {
                    if (event.getType().equals(this.eventType) && targetEventId.equals(this.eventTargetId)) {
                        doPerform = true;
                    } else {
                        doPerform = false;
                    }
                }

                if (doPerform) {
                    performConditional(this.element);
                }
            }
        } catch (Exception e) {
            // handle exception, prevent default action and stop event propagation
            this.container.handleEventException(e);
            event.preventDefault();
            event.stopPropagation();
        }
    }

    /**
     * checks @while and/or @if Attributes before actually executing the Action.
     * @param actionEl the Action Element to trigger
     * @throws XFormsException
     */
    protected void performConditional(Element actionEl) throws XFormsException {
        final XFormsAction action = (XFormsAction) actionEl.getUserData("");
        final String whileCondition = XFormsElement.getXFormsAttribute(actionEl, XFormsConstants.WHILE_ATTRIBUTE);

        if (whileCondition == null) {
            if (execute((AbstractAction) action)) {
                action.perform();
            }
        } else {
            while (evalCondition(whileCondition)) {
                if (execute((AbstractAction) action)) {
                    action.perform();
                } else {
                    break;
                }
            }
        }
    }

    /**
     * checks for existence of XForms 1.1 'if' attribute and if present evaluates it.
     *
     * @param action the DOM Element representing the action
     * @return true if no 'if' attribute is present or the if condition evaluates to 'true'. False otherwise
     */
    //    protected boolean execute (Element action) throws XFormsException{
    protected boolean execute(AbstractAction action) throws XFormsException {
        String ifCondition = action.getXFormsAttribute(action.getElement(), XFormsConstants.IF_CONDITION);

        if (ifCondition == null) {
            return true;
        } else {
            if (action.evalCondition(ifCondition))
                return true;
        }
        return false;
    }

    protected boolean evalCondition(String condition) throws XFormsException {

        List resultNodeset = evalInScopeContext();
        final String relativeExpr = condition;

        boolean b;
        try {
            String result = XPathCache.getInstance().evaluateAsString(resultNodeset, position, condition,
                    getPrefixMapping(), xpathFunctionContext);
            if (result.equalsIgnoreCase("true")) {
                b = true;
            } else {
                b = false;
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("evaluating " + relativeExpr + " in context '"
                        + ((NodeInfo) resultNodeset.get(0)).getDisplayName() + "' to " + b);
            }
            return b;
        } catch (XFormsException e) {
            throw new XFormsException(e);
        }
    }
    // convenience

    /**
     * Tells the action wether to perform a rebuild or not.
     * <p/>
     * If an update handler is set on the context model, the rebuild will be
     * deferred by the handler. If there is no update handler and the rebuild
     * flag is set to true, the rebuild takes place immediately.
     *
     * @param rebuild specifies wether to perform a rebuild or not.
     */
    protected final void doRebuild(boolean rebuild) throws XFormsException {
        UpdateHandler updateHandler = this.model.getUpdateHandler();
        if (updateHandler != null) {
            updateHandler.doRebuild(rebuild);
            return;
        }

        if (rebuild) {
            this.container.dispatch(this.model.getTarget(), XFormsEventNames.REBUILD, null);
        }
    }

    /**
     * Tells the action wether to perform a recalculate or not.
     * <p/>
     * If an update handler is set on the context model, the recalculate will be
     * deferred by the handler. If there is no update handler and the recalculate
     * flag is set to true, the recalculate takes place immediately.
     *
     * @param recalculate specifies wether to perform a recalculate or not.
     */
    protected final void doRecalculate(boolean recalculate) throws XFormsException {
        UpdateHandler updateHandler = this.model.getUpdateHandler();
        if (updateHandler != null) {
            updateHandler.doRecalculate(recalculate);
            return;
        }

        if (recalculate) {
            this.container.dispatch(this.model.getTarget(), XFormsEventNames.RECALCULATE, null);
        }
    }

    /**
     * Tells the action wether to perform a revalidate or not.
     * <p/>
     * If an update handler is set on the context model, the revalidate will be
     * deferred by the handler. If there is no update handler and the revalidate
     * flag is set to true, the revalidate takes place immediately.
     *
     * @param revalidate specifies wether to perform a revalidate or not.
     */
    protected final void doRevalidate(boolean revalidate) throws XFormsException {
        UpdateHandler updateHandler = this.model.getUpdateHandler();
        if (updateHandler != null) {
            updateHandler.doRevalidate(revalidate);
            return;
        }

        if (revalidate) {
            this.container.dispatch(this.model.getTarget(), XFormsEventNames.REVALIDATE, null);
        }
    }

    /**
     * Tells the action wether to perform a refresh or not.
     * <p/>
     * If an update handler is set on the context model, the refresh will be
     * deferred by the handler. If there is no update handler and the refresh
     * flag is set to true, the refresh takes place immediately.
     *
     * @param refresh specifies wether to perform a refresh or not.
     */
    protected final void doRefresh(boolean refresh) throws XFormsException {
        UpdateHandler updateHandler = this.model.getUpdateHandler();
        if (updateHandler != null) {
            updateHandler.doRefresh(refresh);
            return;
        }

        if (refresh) {
            this.container.dispatch(this.model.getTarget(), XFormsEventNames.REFRESH, null);
        }
    }

    @Override
    public Binding getEnclosingUIBinding(Element elementImpl, Object binding, String modelId) {
        if (!(binding instanceof Binding)) {
            return null;
        }

        Binding bindingElem = (Binding) binding;
        if (BindingResolver.hasUIBinding(elementImpl)
                || (bindingElem instanceof Group && bindingElem.hasBindingExpression())
                        && !(bindingElem instanceof RepeatItem)
                || (bindingElem instanceof Item) && (((Item) bindingElem).getItemset() != null)) {
            if (bindingElem.getModelId().equals(modelId)) {
                return bindingElem;
            }
        }
        return null;
    }
}

// end of class