com.rcpcompany.uibindings.financial.FinancialUpDownExtender.java Source code

Java tutorial

Introduction

Here is the source code for com.rcpcompany.uibindings.financial.FinancialUpDownExtender.java

Source

/*******************************************************************************
 * Copyright (c) 2006-2013 The RCP Company and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     The RCP Company - initial API and implementation
 *******************************************************************************/
package com.rcpcompany.uibindings.financial;

import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.plugin.AbstractUIPlugin;

import com.rcpcompany.uibindings.IDisposable;
import com.rcpcompany.uibindings.IUIBindingDecoratorExtenderContext;
import com.rcpcompany.uibindings.IValueBinding;
import com.rcpcompany.uibindings.decorators.extenders.AbstractUIBindingDecoratorExtender;
import com.rcpcompany.uibindings.financial.internal.Activator;
import com.rcpcompany.utils.logging.LogUtils;

/**
 * Extender that will format a financial number as follows:
 * <ul>
 * <li>when the number increases it should be green with an up-arrow</li>
 * <li>when the number decreases it should be red with a down-arrow</li>
 * <li>after a short while - a preference - it should go back to black</li>
 * </ul>
 * 
 * @author Tonny Madsen, The RCP Company
 */
public class FinancialUpDownExtender extends AbstractUIBindingDecoratorExtender {
    /**
     * Argument name to enable the financial up/down arrow for numbers.
     * <p>
     * The argument value is a {@link Boolean}.Defaults to <code>false</code>.
     */
    public static final String ARG_FINANCIAL_UP_DOWN = "financialUpDown";

    /**
     * {@link JFaceResources} identity of image used for "up".
     */
    public static final String UP_ARROW = FinancialUpDownExtender.class.getName() + "$up";

    /**
     * {@link JFaceResources} identity of image used for "down".
     */
    public static final String DOWN_ARROW = FinancialUpDownExtender.class.getName() + "$down";

    /**
     * The timeout for the up or down trends.
     * 
     * TODO: change to preference
     */
    public static final int TIMEOUT = 2000;

    public FinancialUpDownExtender() {
        final ImageRegistry colorRegistry = JFaceResources.getImageRegistry();
        colorRegistry.put(UP_ARROW,
                AbstractUIPlugin.imageDescriptorFromPlugin(Activator.ID, "/images/up-arrow.png"));
        colorRegistry.put(DOWN_ARROW,
                AbstractUIPlugin.imageDescriptorFromPlugin(Activator.ID, "/images/down-arrow.png"));
    }

    @Override
    public boolean isEnabled(IValueBinding binding) {
        if (!binding.getArgument(ARG_FINANCIAL_UP_DOWN, Boolean.class, false))
            return false;

        if (binding.getModelObservableValue() == null)
            return false;

        return true;
    }

    @Override
    public void extend(IUIBindingDecoratorExtenderContext context) {
        final IValueBinding binding = context.getBinding();
        BindingData data = binding.getService(BindingData.class);
        if (data == null) {
            data = new BindingData(binding);
        }

        data.extend(context);
    }

    /**
     * Per binding specific data.
     */
    public class BindingData implements IDisposable {
        /**
         * The binding
         */
        private final IValueBinding myBinding;

        /**
         * The observable value for the binding
         */
        private final IObservableValue myObservableValue;

        /**
         * The currently reported trend for this binding
         */
        protected int myCurrentTrend = 0;

        /**
         * The time-out for the current trend. 0 if no timeout is needed.
         */
        protected long myCurrentTimeout = 0;

        /**
         * The last known value for the binding
         */
        private Object myValue;

        private DisplayTimeout myDisplayTimeout;

        /**
         * Constructs and returns a new data record for the specified binding.
         * 
         * @param binding the binding
         */
        public BindingData(IValueBinding binding) {
            myBinding = binding;
            /*
             * Tested in isEnabled(...)
             */
            myObservableValue = binding.getModelObservableValue();
            myValue = myObservableValue.getValue();
            myBinding.registerService(this);
        }

        @Override
        public void dispose() {
            myBinding.unregisterService(this);
        }

        /**
         * Extends the specified binding context.
         * 
         * @param context the context to extend
         */
        public void extend(IUIBindingDecoratorExtenderContext context) {
            int trend = getTrend();
            /*
             * Within the timeout, we keep the same trend.
             */
            if (trend == 0) {
                if (System.currentTimeMillis() < myCurrentTimeout) {
                    // Old trend
                    trend = myCurrentTrend;
                } else
                    // No old or new trends
                    return;
            } else {
                // New trend
                if (myDisplayTimeout != null) {
                    myDisplayTimeout.cancelled = true;
                    myDisplayTimeout = null;
                }
                myDisplayTimeout = new DisplayTimeout();
                Display.getDefault().timerExec(TIMEOUT, myDisplayTimeout);
            }

            switch (trend) {
            case -1:
                context.setForegound(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GREEN));
                context.setImage(JFaceResources.getImage(DOWN_ARROW));
                break;
            case 1:
                context.setForegound(Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED));
                context.setImage(JFaceResources.getImage(UP_ARROW));
                break;
            case 0:
                break;
            default:
                LogUtils.error(this, "Trend value illegal: " + trend);
                break;
            }
        }

        protected class DisplayTimeout implements Runnable {
            public boolean cancelled = false;

            @Override
            public void run() {
                if (cancelled)
                    return;
                /*
                 * Repaint the field!
                 */
                myBinding.updateBinding();
            }
        }

        /**
         * Returns the current trend for this data item.
         * <p>
         * The rules are:
         * <ul>
         * <li>-1 if it is decreasing</li>
         * <li>+1 if it is increasing</li>
         * <li>0 if it is the same</li>
         * <li>0 if either the old or the new value is <code>null</code></li>
         * </ul>
         * <p>
         * If the old value implements the {@link Comparable} interface this is used. Otherwise TODO
         * 
         * @return the trend
         */
        private int getTrend() {
            final Object newValue = myObservableValue.getValue();

            try {
                if (newValue == null)
                    return 0;
                if (myValue == null)
                    return 0;
                if (myValue == newValue)
                    return 0;
                if (newValue.equals(myValue))
                    return 0;

                if (myValue instanceof Comparable)
                    return ((Comparable) myValue).compareTo(newValue);

                final Object valueType = myObservableValue.getValueType();
                LogUtils.debug(valueType, "Cannot compare type " + valueType);

                return 0;
            } finally {
                myValue = newValue;
            }
        }
    }

}