DefaultBindingControl.java :  » Media » praxis » net » neilcsmith » praxis » gui » impl » Java Open Source

Java Open Source » Media » praxis 
praxis » net » neilcsmith » praxis » gui » impl » DefaultBindingControl.java
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2010 Neil C Smith.
 * 
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3 only, as
 * published by the Free Software Foundation.
 * 
 * This code 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
 * version 3 for more details.
 * 
 * You should have received a copy of the GNU General Public License version 3
 * along with this work; if not, see http://www.gnu.org/licenses/
 * 
 * 
 * Please visit http://neilcsmith.net if you need additional information or
 * have any questions.
 */
package net.neilcsmith.praxis.gui.impl;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Timer;
import net.neilcsmith.praxis.core.Call;
import net.neilcsmith.praxis.core.CallArguments;
import net.neilcsmith.praxis.core.PacketRouter;
import net.neilcsmith.praxis.core.ControlAddress;
import net.neilcsmith.praxis.core.ArgumentFormatException;
import net.neilcsmith.praxis.core.info.ComponentInfo;
import net.neilcsmith.praxis.core.info.ControlInfo;
import net.neilcsmith.praxis.core.interfaces.ComponentInterface;
import net.neilcsmith.praxis.gui.ControlBinding;
import net.neilcsmith.praxis.impl.AbstractControl;

/**
 * 
 * @author Neil C Smith
 */
// @TODO sync on error?
// @TODO take router rather than host in constructor
public class DefaultBindingControl extends AbstractControl {

    private final static Logger LOG =
            Logger.getLogger(DefaultBindingControl.class.getName());
    private final static int LOW_SYNC_DELAY = 1000;
    private final static int MED_SYNC_DELAY = 200;
    private final static int HIGH_SYNC_DELAY = 50;
    private ControlAddress boundAddress;
    private Binding binding;
    private PacketRouter router;

    public DefaultBindingControl(ControlAddress boundAddress) {
        if (boundAddress == null) {
            throw new NullPointerException();
        }
        this.boundAddress = boundAddress;
        binding = new Binding();
    }

    public ControlInfo getInfo() {
        return null;
    }

    public void bind(ControlBinding.Adaptor adaptor) {
        binding.addAdaptor(adaptor);
    }

    public void unbind(ControlBinding.Adaptor adaptor) {
        binding.removeAdaptor(adaptor);
    }

    public void call(Call call, PacketRouter router) throws Exception {
        switch (call.getType()) {
            case RETURN:
                processReturn(call);
                break;
            case ERROR:
                processError(call);
                break;
            default:
                throw new UnsupportedOperationException();
        }
    }

    private void processReturn(Call call) {
        if (boundAddress.equals(call.getFromAddress())) {
            binding.processResponse(call);
        } else {
            binding.processInfo(call);
        }
    }

    private void processError(Call call) {
        if (boundAddress.equals(call.getFromAddress())) {
            binding.processError(call);
        } else {
            binding.processInfoError(call);
        }
    }

    private ControlAddress getReturnAddress() {
        return getAddress();
    }

    @Override
    public void hierarchyChanged() {
        super.hierarchyChanged();
        router = null;
    }

    private PacketRouter getRouter() {
        if (router == null) {
            router = getLookup().get(PacketRouter.class);
        }
        return router;
    }

    private class Binding extends ControlBinding {

        private long invokeTimeOut = TimeUnit.MILLISECONDS.toNanos(5000);
        private long quietTimeOut = TimeUnit.MILLISECONDS.toNanos(200);
        private List<ControlBinding.Adaptor> adaptors;
        private ControlInfo bindingInfo;
        private Timer syncTimer;
//        private int lastCallID;
        private int infoMatchID;
        private boolean isProperty;
        private Call activeCall;
        private Adaptor activeAdaptor;
        private CallArguments arguments;

        private Binding() {
            adaptors = new ArrayList<ControlBinding.Adaptor>();
            syncTimer = new Timer(LOW_SYNC_DELAY, new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    processSync();
                }
            });
            arguments = CallArguments.EMPTY;
        }

        private void addAdaptor(Adaptor adaptor) {
            if (adaptor == null) {
                throw new NullPointerException();
            }
            if (adaptors.contains(adaptor)) {
                return;
            }
            adaptors.add(adaptor);
            bind(adaptor);
            updateAdaptorConfiguration(adaptor); // duplicate functionality
            if (bindingInfo == null) {
                sendInfoRequest();
            }
        }

        private void removeAdaptor(Adaptor adaptor) {
            if (adaptors.remove(adaptor)) {
                unbind(adaptor);
            }
        }

        @Override
        protected void send(Adaptor adaptor, CallArguments args) {
            PacketRouter router = getRouter();
            ControlAddress returnAddress = getReturnAddress();
            Call call;
            if (adaptor.getValueIsAdjusting()) {
                call = Call.createQuietCall(boundAddress, returnAddress,
                        System.nanoTime(), args);
            } else {
                call = Call.createCall(boundAddress, returnAddress,
                        System.nanoTime(), args);
            }
            router.route(call);
            activeCall = call;
            activeAdaptor = adaptor;
            arguments = args;
            for (Adaptor ad : adaptors) {
                if (ad != adaptor) {
                    ad.update();
                }
            }
        }

        @Override
        protected void updateAdaptorConfiguration(Adaptor adaptor) {
            updateSyncConfiguration();
        }

        private void updateSyncConfiguration() {
            if (isProperty) {
                boolean active = false;
                SyncRate highRate = SyncRate.None;
                for (Adaptor a : adaptors) {
                    if (a.isActive()) {
                        active = true;
                        SyncRate aRate = a.getSyncRate();
                        if (aRate.compareTo(highRate) > 0) {
                            highRate = aRate;
                        }
                    }
                }
                if (!active || highRate == SyncRate.None) {
                    if (syncTimer.isRunning()) {
                        syncTimer.stop();
                    }
                } else {
                    syncTimer.setDelay(delayForRate(highRate));
                    if (!syncTimer.isRunning()) {
                        syncTimer.start();
                    }
                    processSync();
                }
            } else {
                if (syncTimer.isRunning()) {
                    syncTimer.stop();
                }
            }

        }

        private int delayForRate(SyncRate rate) {
            switch (rate) {
                case Low:
                    return LOW_SYNC_DELAY;
                case Medium:
                    return MED_SYNC_DELAY;
                case High:
                    return HIGH_SYNC_DELAY;
            }
            throw new IllegalArgumentException();
        }

        private void sendInfoRequest() {
            PacketRouter router = getRouter();
            ControlAddress returnAddress = getReturnAddress();
            ControlAddress toAddress = ControlAddress.create(
                    boundAddress.getComponentAddress(), ComponentInterface.INFO);
            Call call = Call.createCall(toAddress, returnAddress,
                    System.nanoTime(), CallArguments.EMPTY);

            infoMatchID = call.getMatchID();
            router.route(call);
        }

        private void processInfo(Call call) {
            if (call.getMatchID() == infoMatchID) {
                CallArguments args = call.getArgs();
                if (args.getSize() > 0) {
                    ComponentInfo compInfo = null;
                    try {
                        compInfo = ComponentInfo.coerce(args.get(0));
                        // @TODO on null?
                        bindingInfo = compInfo.getControlInfo(boundAddress.getID());
                        ControlInfo.Type type = bindingInfo.getType();
                        isProperty = (type == ControlInfo.Type.RW_Property)
                                || (type == ControlInfo.Type.RO_Property);

                    } catch (ArgumentFormatException ex) {
                        isProperty = false;
                        bindingInfo = null;
                        LOG.log(Level.WARNING, "" + call + "\n" + compInfo, ex);
                    }
                    for (Adaptor a : adaptors) {
                        a.updateBindingConfiguration();
                    }
                    updateSyncConfiguration();
                }
            }
        }

        private void processInfoError(Call call) {
            isProperty = false;
            bindingInfo = null;
            LOG.log(Level.WARNING, "Couldn't get info for {0}", boundAddress);
            for (Adaptor a : adaptors) {
                a.updateBindingConfiguration();
            }
            updateSyncConfiguration();
        }

        private void processResponse(Call call) {
            if (activeCall != null && call.getMatchID() == activeCall.getMatchID()) {
                if (activeAdaptor != null) {
                    activeAdaptor.onResponse(call.getArgs());
                    activeAdaptor = null;
                }
                if (isProperty) {
                    arguments = call.getArgs();
                    for (Adaptor a : adaptors) {
                        a.update();
                    }
                }
                activeCall = null;
            }
        }

        private void processError(Call call) {
            if (activeCall != null && call.getMatchID() == activeCall.getMatchID()) {
                if (activeAdaptor != null) {
                    activeAdaptor.onError(call.getArgs());
                    activeAdaptor = null;
                } else {
                    LOG.warning("Error on sync call");
                }
                activeCall = null;
            }
        }

        private void processSync() {
            long now = System.nanoTime();
            if (activeCall != null) {
                if (activeCall.getType() == Call.Type.INVOKE) {
                    if ((now - activeCall.getTimecode()) < invokeTimeOut) {
                        return;
                    }
                } else {
                    if ((now - activeCall.getTimecode()) < quietTimeOut) {
                        return;
                    }
                }
            }
            if (isProperty) {
                PacketRouter router = getRouter();
                Call call = Call.createCall(boundAddress, getReturnAddress(),
                        now, CallArguments.EMPTY);
                router.route(call);
                activeCall = call;
                activeAdaptor = null;
            }

//            PacketRouter router = getRouter();
//            Call call = Call.createCall(boundAddress, getReturnAddress(),
//                    System.nanoTime(), CallArguments.EMPTY);
//            router.route(call);
//            lastCallID = call.getMatchID();
        }

        @Override
        public ControlInfo getBindingInfo() {
            return bindingInfo;
        }

        @Override
        public CallArguments getArguments() {
            return arguments;
        }

        @Override
        public ControlAddress getAddress() {
            return boundAddress;
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.