/*
* (C) Copyright 2005 Arnaud Bailly (arnaud.oqube@gmail.com),
* Yves Roos (yroos@lifl.fr) and others.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rationals.ioautomata;
import rationals.State;
import rationals.Transition;
import rationals.ioautomata.IOTransition.IOLetter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* A basic implementation of an IOStateMachine that wraps an IOAutomaton. This
* implementation of IOStateMachine has the following semantics:
* <ul>
* <li>A call to {@link #input(Object)} is synchronously executed in the
* underlying automaton: It advances the state if at least one input matches and
* and otherwise throws an {@link IllegalStateException},</li>
* <li>All internal transitions available in current state are always fired. If
* they are labelled by {@see Function} instances, the function is called with a
* parameter that is the latest input available</li>
* <li>A call to output returns the available output data if any,</li>
* <li>If several output are available from the same state, one is fired at
* random from the set of fireable output transitions.</li>
* <li>A call to {@link #output()} is succesful if an output transition is
* currently enabled. If not, it returns null.</li>
* </ul>
*
* @author nono
*
*/
public class IOAutomatonSMAdapter implements IOStateMachine {
protected IOAutomaton<IOTransition,IOTransitionBuilder> automaton;
protected Set<State> state;
private boolean inputEnabled;
private TransitionSelector selectOutput;
private TransitionSelector selectInternal;
protected Object savedInput;
public final Function<IOAutomaton<IOTransition,IOTransitionBuilder>, Object> lastInput = new Function<IOAutomaton<IOTransition,IOTransitionBuilder>, Object>() {
public Object apply(IOAutomaton<IOTransition,IOTransitionBuilder> message)
throws Exception {
assert message == automaton;
return savedInput;
}
public String toString() {
return "<last input>";
}
};
public IOAutomatonSMAdapter(IOAutomaton<IOTransition,IOTransitionBuilder> a) {
this.automaton = a;
this.state = a.initials();
this.selectOutput = new TransitionSelector(IOAlphabetType.OUTPUT);
this.selectInternal = new TransitionSelector(IOAlphabetType.INTERNAL);
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#availableOutput()
*/
public int availableOutput() {
return 0;
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#input(java.lang.Object)
*/
public void input(Object o) {
boolean ok = doInput(o);
/* check input is OK, else error */
if (!ok) {
if (!inputEnabled)
throw new IllegalStateException("No input possible in state :"
+ state);
else
return;
}
savedInput = o;
doInternal(o);
}
private void doInternal(Object o) {
Set<IOTransition.IOLetter> lts = null;
List<Object> collectedResults = new ArrayList<Object>();
do {
lts = selectInternal.selectAllFrom(automaton, state);
Set<State> ns = automaton.getStateFactory().stateSet();
for (IOLetter lt : lts)
ns.addAll(fire(lt, o, collectedResults));
// update state
if (!lts.isEmpty())
state = ns;
} while (!lts.isEmpty());
// inject inputs
for (Object res : collectedResults)
input(res);
}
private Collection<? extends State> fire(IOLetter lt, Object o,
List<Object> res) {
// first invoke functions
Object ret = call(lt, o);
if (ret != null)
res.add(ret);
// compute states
return automaton.step(state, lt);
}
/**
* @param lt
* @param o
* @return
*/
private Object call(IOLetter lt, Object o) {
Object ret = null;
if (lt.label instanceof Function) {
try {
ret = ((Function) lt.label).apply(o);
} catch (Exception e) {
ret = e;
}
}
return ret;
}
/**
* @param o
* @return
*/
private boolean doInput(Object o) {
boolean ok = false;
for (Transition tr : automaton.delta(state)) {
IOTransition.IOLetter lt = (IOLetter) ((IOTransition) tr).label();
if (lt.type == IOAlphabetType.INPUT
&& IOSynchronization.inputMatchesLetter(o, lt)) {
doTransition(tr.label());
ok = true;
}
}
return ok;
}
/**
* @param object
*/
private void doTransition(Object object) {
state = automaton.step(state, object);
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#isInputEnabled()
*/
public boolean isInputEnabled() {
return inputEnabled;
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#output()
*/
public Object output() {
IOTransition.IOLetter lt = selectOutput.selectFrom(automaton, state);
if (lt == null)
if (!inputEnabled)
throw new IllegalStateException("No available output in state:"
+ state);
else
return null;
Object ret = call(lt, automaton);
doTransition(lt);
doInternal(lt.label);
return ret == null ? lt.label : ret;
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#output(java.lang.Object[], int,
* int)
*/
public int output(Object[] o, int start, int len) {
if (len <= 0)
return 0;
Object out = output();
o[start] = out;
return out == null ? 0 : 1;
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#reset()
*/
public void reset() {
state = automaton.initials();
}
/*
* (non-Javadoc)
*
* @see
* rationals.ioautomata.IOStateMachine#setInternalHandler(rationals.ioautomata
* .Function)
*/
public void setInternalHandler(Function hdl) {
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.IOStateMachine#stop()
*/
public void stop() {
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
public void run() {
}
public Set<State> getState() {
return state;
}
public void setInputEnabled(boolean b) {
this.inputEnabled = b;
}
}