/* * Copyright (c) 2009 The Regents of the University of California. * All rights reserved. * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and its documentation for any purpose, provided that the above * copyright notice and the following two paragraphs appear in all copies * of this software. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, * ENHANCEMENTS, OR MODIFICATIONS. */ package org.clothocore.api.actor.workflow; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.clothocore.api.actor.Actor; import org.clothocore.api.actor.data.ListToken; import org.clothocore.api.actor.data.Token; import org.clothocore.api.actor.io.InputPort; import org.clothocore.api.actor.io.ListInputPort; import org.clothocore.api.actor.io.ListOutputPort; import org.clothocore.api.actor.io.OutputPort; import org.clothocore.api.actor.RunStatus; import org.jgrapht.alg.CycleDetector; /** * * @author Bing Xia <bxia@bxia.net> */ public class LoopActor extends CompositeActor { public LoopActor() { _lastInputs = new HashMap<ListInputPort, Token>(); _multToSingleInputs = new HashMap<ListInputPort, InputPort>(); _singleToMultInputs = new HashMap<InputPort, ListInputPort>(); _multToSingleOutputs = new HashMap<ListOutputPort, OutputPort>(); _singleToMultOutputs = new HashMap<OutputPort, ListOutputPort>(); } public LoopActor( CompositeActor alg ) { } @Override public boolean isReady() { if ( _inputData == null ) { return false; } for ( ListInputPort inp : _multToSingleInputs.keySet() ) { if ( !_inputData.containsKey( inp ) ) { return false; } else { List data = _inputData.get( inp ); if ( data.size() != 1 && data.size() != _iterations ) { return false; } } } return true; } /** * inputData should be a map from InputPort to a list of Tokens * which should be used as sequential input to the given inputPorts. * The length of each list should either be the number of desired iterations * or one. For example, we could send in 3 lists of length 3, 3, and 1, but * we can't send in 3 lists of length 3, 2, and 1. * @param inputData */ public void setInputs( Map<ListInputPort, ListToken> inputData ) { _inputData = inputData; for ( ListInputPort inp : _inputData.keySet() ) { if ( _inputData.get( inp ).size() != 1 ) { _iterations = _inputData.get( inp ).size(); return; } } } @Override @SuppressWarnings (value="unchecked") public boolean addAlgorithm( Actor alg ) { if ( _workflow.addVertex( alg ) ) { for ( InputPort inp : alg.getInputs() ) { ListInputPort multInp; if ( inp instanceof ListInputPort ) { multInp = new ListInputPort( this, ListToken.class ); } else { multInp = new ListInputPort( this, inp.getType() ); } _multToSingleInputs.put( multInp, inp ); _singleToMultInputs.put( inp, multInp ); } for ( OutputPort out : alg.getOutputs() ) { ListOutputPort multOut; if ( out instanceof ListOutputPort ) { multOut = new ListOutputPort( this, ListToken.class ); } else { multOut = new ListOutputPort( this, out.getType() ); } _multToSingleOutputs.put( multOut, out ); _singleToMultOutputs.put( out, multOut ); } return true; } else { return false; } } @Override @SuppressWarnings (value="unchecked") public boolean removeAlgorithm( Actor alg, boolean breakLinks ) { if ( !_workflow.containsVertex( alg ) ) { return false; } Set<IOLink> edgesOf = _workflow.edgesOf( alg ); if ( !breakLinks && !edgesOf.isEmpty() ) { return false; } if ( breakLinks ) { for ( IOLink inLink : _workflow.incomingEdgesOf( alg ) ) { ListOutputPort multOut; if ( inLink.out instanceof ListOutputPort ) { multOut = new ListOutputPort( this, ListToken.class ); } else { multOut = new ListOutputPort( this, inLink.out.getType() ); } _multToSingleOutputs.put( multOut, inLink.out ); _singleToMultOutputs.put( inLink.out, multOut ); } for ( IOLink outLink : _workflow.outgoingEdgesOf( alg ) ) { ListInputPort multIn; if ( outLink.in instanceof ListInputPort ) { multIn = new ListInputPort( this, ListToken.class ); } else { multIn = new ListInputPort( this, outLink.out.getType() ); } _multToSingleInputs.put( multIn, outLink.in ); _singleToMultInputs.put( outLink.in, multIn ); } _workflow.removeAllEdges( edgesOf ); } return true; } @Override public boolean link( OutputPort out, InputPort in ) { Actor start = out.getAlgorithm(); Actor end = in.getAlgorithm(); if ( !_workflow.containsVertex( start ) || !_workflow.containsVertex( end ) ) { return false; } try { IOLink link = new IOLink( in, out ); if ( !_workflow.addEdge( start, end, link ) ) { return false; } else { CycleDetector<Actor, IOLink> cd = new CycleDetector<Actor, IOLink>( _workflow ); if ( cd.detectCycles() ) { _workflow.removeEdge( link ); return false; } else { ListInputPort multInp = _singleToMultInputs.get( in ); ListOutputPort multOut = _singleToMultOutputs.get( out ); _inputs.remove( multInp ); _outputs.remove( multOut ); _singleToMultInputs.remove( in ); _singleToMultOutputs.remove( out ); _multToSingleInputs.remove( multInp ); _multToSingleOutputs.remove( multOut ); return true; } } } catch ( IllegalArgumentException iae ) { return false; } } @Override public boolean unlink( OutputPort out, InputPort in ) { return false; } @Override @SuppressWarnings (value="unchecked") public RunStatus run() { if ( !isReady() ) { return RunStatus.NOT_READY_ERROR; } _lastInputs.clear(); for ( ListOutputPort multOut : _multToSingleOutputs.keySet() ) { multOut.put( new ListToken( multOut.getType() ) ); } for ( int i = 0; i < _iterations; i++ ) { insertNextInputs(); if ( !runTopological() ) { return RunStatus.RUN_ERROR; } for ( ListOutputPort multOut : _multToSingleOutputs.keySet() ) { OutputPort out = _multToSingleOutputs.get( multOut ); Token data = out.get(); multOut.get().add( data ); } } return RunStatus.COMPLETE; } @SuppressWarnings (value="unchecked") private void insertNextInputs() { for ( ListInputPort multInp : _inputData.keySet() ) { List<Token> data = _inputData.get( multInp ); Token tok; if ( data.isEmpty() ) { tok = _lastInputs.get( multInp ); } else { tok = data.remove( 0 ); _lastInputs.put( multInp, tok ); } InputPort inp = _multToSingleInputs.get( multInp ); inp.put( tok ); } } /******* Protected Variables *******/ protected Map<ListInputPort, ListToken> _inputData; protected int _iterations; protected Map<ListInputPort, Token> _lastInputs; protected Map<ListInputPort, InputPort> _multToSingleInputs; protected Map<InputPort, ListInputPort> _singleToMultInputs; protected Map<ListOutputPort, OutputPort> _multToSingleOutputs; protected Map<OutputPort, ListOutputPort> _singleToMultOutputs; }