org.pentaho.ui.xul.binding.DefaultBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.ui.xul.binding.DefaultBinding.java

Source

/*!
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 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 Lesser General Public License for more details.
 *
 * Copyright (c) 2002-2013 Pentaho Corporation..  All rights reserved.
 */

package org.pentaho.ui.xul.binding;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.ui.xul.XulComponent;
import org.pentaho.ui.xul.XulEventSource;
import org.pentaho.ui.xul.XulException;
import org.pentaho.ui.xul.binding.BindingConvertor.Direction;
import org.pentaho.ui.xul.dom.Document;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class DefaultBinding implements Binding {

    protected Reference source;

    protected Reference target;

    protected String sourceAttr;

    protected String targetAttr;

    protected boolean negateBooleanAssignment = false;

    protected boolean reverseConversion = false;

    protected BindingConvertor conversion;

    protected PropertyChangeListener forwardListener, reverseListener;

    protected Stack<Method> getterMethods = new Stack<Method>();

    protected Method sourceGetterMethod, targetGetterMethod;

    protected BindingContext context;

    protected static final Log logger = LogFactory.getLog(DefaultBinding.class);

    protected boolean destroyed = false;

    protected Type bindingStrategy = Type.BI_DIRECTIONAL;

    private BindingExceptionHandler exceptionHandler;

    public DefaultBinding() {

    }

    @Deprecated
    public DefaultBinding(Document document, String sourceId, String sourceAttr, String targetId,
            String targetAttr) {
        this.source = new WeakReference<XulComponent>(document.getElementById(sourceId));
        setSourceAttr(sourceAttr);
        this.target = new WeakReference<XulComponent>(document.getElementById(targetId));
        setTargetAttr(targetAttr);
    }

    @Deprecated
    public DefaultBinding(Document document, Object source, String sourceAttr, String targetId, String targetAttr) {
        this.source = new WeakReference<Object>(source);
        setSourceAttr(sourceAttr);
        this.target = new WeakReference<XulComponent>(document.getElementById(targetId));
        setTargetAttr(targetAttr);
    }

    @Deprecated
    public DefaultBinding(Document document, String sourceId, String sourceAttr, Object target, String targetAttr) {
        this.source = new WeakReference<XulComponent>(document.getElementById(sourceId));
        setSourceAttr(sourceAttr);
        this.target = new WeakReference<Object>(target);
        setTargetAttr(targetAttr);
    }

    public DefaultBinding(Object source, String sourceAttr, Object target, String targetAttr) {
        this.source = new WeakReference<Object>(source);
        setSourceAttr(sourceAttr);
        this.target = new WeakReference<Object>(target);
        setTargetAttr(targetAttr);
    }

    public void setBindingType(Type t) {
        this.bindingStrategy = t;
    }

    public Type getBindingType() {
        return bindingStrategy;
    }

    public void initialize() {
        bindForward();

        if (getBindingType() == Binding.Type.BI_DIRECTIONAL) {
            bindReverse();
        }
    }

    public Reference getSource() {
        return source;
    }

    public void setSource(Object source) {

        this.source = new WeakReference(source);
    }

    public Reference getTarget() {

        return target;
    }

    public void setTarget(Object target) {

        this.target = new WeakReference(target);
    }

    public String getSourceAttr() {

        return sourceAttr;
    }

    public void setSourceAttr(String sourceAttr) {

        if (sourceAttr.charAt(0) == '!') {
            // Negation of Boolean
            negateBooleanAssignment = !negateBooleanAssignment; // two negations will cancel
            sourceAttr = sourceAttr.substring(1);
        }
        this.sourceAttr = sourceAttr;

    }

    public String getTargetAttr() {

        return targetAttr;
    }

    public void setTargetAttr(String targetAttr) {

        if (targetAttr.charAt(0) == '!') {
            // Negation of Boolean
            negateBooleanAssignment = !negateBooleanAssignment; // two negations will cancel
            targetAttr = targetAttr.substring(1);
        }
        this.targetAttr = targetAttr;
    }

    public Object evaluateExpressions(Object val) {
        if (negateBooleanAssignment && val instanceof Boolean) {
            return !((Boolean) val);
        }
        return val;
    }

    public Object doConversions(Object val, Direction dir) {
        if (conversion != null) {
            return (dir == Direction.FORWARD) ? conversion.sourceToTarget(val) : conversion.targetToSource(val);
        }
        return val;
    }

    public BindingConvertor getConversion() {
        return conversion;
    }

    public void setConversion(BindingConvertor conversion) {
        this.conversion = conversion;
    }

    public boolean isReverseConversion() {

        return reverseConversion;
    }

    public void setReverseConversion(boolean reverseConversion) {

        this.reverseConversion = reverseConversion;
    }

    public void fireSourceChanged() throws IllegalArgumentException, XulException, InvocationTargetException {
        try {
            Object getRetVal = sourceGetterMethod.invoke(getSource().get());
            forwardListener.propertyChange(new PropertyChangeEvent(getSource(), getSourceAttr(), null, getRetVal));
        } catch (IllegalAccessException e) {
            // TODO: re-implement IllegalAccessException.
            // cannot be in interface due to GWT incompatibility.
            handleException(new BindingException(e));
        }
    }

    public void bindForward() {
        setForwardListener(
                setupBinding(getSource(), getSourceAttr(), getTarget(), getTargetAttr(), Direction.FORWARD));
        sourceGetterMethod = getterMethods.pop();
        logger.debug("Forward binding established: " + source.get() + "." + sourceAttr + " ==> " + target.get()
                + "." + targetAttr);
    }

    public void bindReverse() {
        setReverseListener(
                setupBinding(getTarget(), getTargetAttr(), getSource(), getSourceAttr(), Direction.BACK));
        targetGetterMethod = getterMethods.pop();
        logger.debug("Reverse binding established: " + source.get() + "." + sourceAttr + " <== " + target.get()
                + "." + targetAttr);
    }

    protected PropertyChangeListener setupBinding(final Reference a, final String va, final Reference b,
            final String vb, final Direction dir) {
        if (a.get() == null || va == null) {
            handleException(new BindingException("source bean or property is null"));
        }
        if (!(a.get() instanceof XulEventSource)) {
            handleException(new BindingException(
                    "Binding error, source object " + a.get() + " not a XulEventSource instance"));
        }
        if (b.get() == null || vb == null) {
            handleException(new BindingException("target bean or property is null"));
        }
        Method sourceGetMethod = BindingUtil.findGetMethod(a.get(), va);

        Class getterClazz = BindingUtil.getMethodReturnType(sourceGetMethod, a.get());
        getterMethods.push(sourceGetMethod);

        // find set method
        final Method targetSetMethod = BindingUtil.findSetMethod(b.get(), vb, getterClazz);

        // setup prop change listener to handle binding
        PropertyChangeListener listener = new PropertyChangeListener() {
            public void propertyChange(final PropertyChangeEvent evt) {
                final PropertyChangeListener cThis = this;
                if (evt.getPropertyName().equalsIgnoreCase(va)) {
                    try {
                        Object targetObject = b.get();
                        if (targetObject == null) {
                            logger.debug("Binding target was Garbage Collected, removing propListener");
                            DefaultBinding.this.destroyBindings();
                            return;
                        }

                        Object value = doConversions(evt.getNewValue(), dir);
                        final Object finalVal = evaluateExpressions(value);

                        logger.debug("Setting val: " + finalVal + " on: " + targetObject);
                        targetSetMethod.invoke(targetObject, finalVal);

                    } catch (Exception e) {
                        logger.debug(e);
                        handleException(new BindingException("Error invoking setter method ["
                                + targetSetMethod.getName() + "] on target: " + target.get(), e));
                    }
                }
            }
        };
        ((XulEventSource) a.get()).addPropertyChangeListener(listener);

        return listener;
    }

    protected void handleException(BindingException exception) {
        if (exceptionHandler != null) {
            exceptionHandler.handleException(exception);
        } else {
            throw exception;
        }
    }

    public void destroyBindings() {
        if (destroyed) {
            // circular catch from context.remove()
            return;
        }
        Object sourceObj = getSource().get();
        Object targetObj = getTarget().get();

        if (forwardListener != null && sourceObj != null && sourceObj instanceof XulEventSource) {
            ((XulEventSource) sourceObj).removePropertyChangeListener(forwardListener);
            logger.debug("Removing forward binding on " + sourceObj);
        }

        if (reverseListener != null && targetObj != null && targetObj instanceof XulEventSource) {
            ((XulEventSource) targetObj).removePropertyChangeListener(reverseListener);
            logger.debug("Removing reverse binding on " + targetObj);
        }

        setDestroyed(true);
        if (context != null) {
            context.remove(this);
        }
    }

    protected void setDestroyed(boolean flag) {
        this.destroyed = flag;
    }

    public PropertyChangeListener getForwardListener() {
        return forwardListener;
    }

    public void setForwardListener(PropertyChangeListener forwardListener) {
        this.forwardListener = forwardListener;
    }

    public PropertyChangeListener getReverseListener() {
        return reverseListener;
    }

    public void setReverseListener(PropertyChangeListener reverseListener) {
        this.reverseListener = reverseListener;
    }

    public List<PropertyChangeListener> getListeneners() {
        List<PropertyChangeListener> l = new ArrayList<PropertyChangeListener>();
        if (forwardListener != null) {
            l.add(forwardListener);
        }
        if (reverseListener != null) {
            l.add(reverseListener);
        }
        return l;
    }

    public BindingContext getContext() {
        return context;
    }

    public void setContext(BindingContext context) {
        this.context = context;
    }

    public void setExceptionHandler(BindingExceptionHandler handler) {
        this.exceptionHandler = handler;
    }

}