/*
* (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.transformations;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import rationals.Automaton;
import rationals.Builder;
import rationals.NoSuchStateException;
import rationals.State;
import rationals.Transition;
/**
* A (rational) substitution is a morphism that maps
* letters to languages.
* A rational substitution is constructed like a {@see rationals.transformations.Morphism}
* with an instance of {@see java.util.Map} that has Object as
* keys and either Object or Automaton instances as values.
* If the value of a key is an object, then it is considered a letter
* and this class acts like a morphism. If the value of the key is an
* Automaton, then the complete transition is replaced by the language
* denoted by the automaton.
*
* @author nono
* @see J.Berstel, "Transductions and context-free languages"
*/
public class Substitution<L, Tr extends Transition<L>, T extends Builder<L, Tr, T>> implements UnaryTransformation<L, Tr, T> {
private Map<?, ?> morph;
public Substitution(Map<?, ?> m) {
this.morph = m;
}
/* (non-Javadoc)
* @see rationals.transformations.UnaryTransformation#transform(rationals.Automaton)
*/
public Automaton<L, Tr, T> transform(Automaton<L, Tr, T> a) {
Automaton<L, Tr, T> b = new Automaton<>();
/* state map */
Map<State, State> stm = new HashMap<>();
for (Iterator<Transition<L>> i = a.delta().iterator(); i.hasNext();) {
Transition<L> tr = i.next();
State ns = tr.start();
State nss = stm.get(ns);
if (nss == null) {
nss = b.addState(ns.isInitial(), ns.isTerminal());
stm.put(ns, nss);
}
State ne = tr.end();
State nse = stm.get(ne);
if (nse == null) {
nse = b.addState(ne.isInitial(), ne.isTerminal());
stm.put(ne, nse);
}
L lbl = tr.label();
if (!morph.containsKey(lbl))
try {
b.addTransition(new Transition<L>(nss, lbl, nse));
} catch (NoSuchStateException e) {
}
else
try {
/* is value an automaton ? */
Object o = morph.get(lbl);
if (o instanceof Automaton)
insert(nss, nse, b, (Automaton<L, Tr, T>) o);
else
b.addTransition(new Transition<L>(nss, (L) morph.get(lbl), nse));
} catch (NoSuchStateException e1) {
}
}
return b;
}
/**
* Insert <code>automaton</code> between states <code>nss</code> and
* <code>nse</code> in automaton <code>b</code>.
* This method add epsilon transitions from <code>nss</code> to each starting
* state of automaton and from each ending state to <code>nse</code>.
*
* @param nss
* @param nse
* @param b
* @param automaton
*/
private void insert(State nss, State nse, Automaton<L, Tr, T> b, Automaton<L, Tr, T> automaton) {
/* map states */
Map<State, State> map = new HashMap<State, State>();
for (Iterator<State> i = automaton.states().iterator(); i.hasNext();) {
State e = i.next();
State n = b.addState(false, false);
map.put(e, n);
if (e.isInitial())
try {
b.addTransition(new Transition<L>(nss, null, n));
} catch (NoSuchStateException e1) {
}
if (e.isTerminal())
try {
b.addTransition(new Transition<L>(n, null, nse));
} catch (NoSuchStateException e1) {
}
}
for (Iterator<Transition<L>> i = automaton.delta().iterator(); i.hasNext();) {
Transition<L> t = i.next();
try {
b.addTransition(new Transition<L>(map.get(t.start()), t.label(), map.get(t.end())));
} catch (NoSuchStateException x) {
}
}
}
}