3 import java
.util
.HashMap
;
4 import java
.util
.LinkedList
;
8 import net
.automatalib
.words
.Word
;
11 * @author Ramon Janssen
13 * @param <I> the input type of the observations
14 * @param <O> the output type of the observations
16 public class ObservationTree
<I
,O
> {
17 private final ObservationTree
<I
,O
> parent
;
18 private final I parentInput
;
19 private final O parentOutput
;
20 private final Map
<I
, ObservationTree
<I
,O
>> children
;
21 private final Map
<I
, O
> outputs
;
23 public ObservationTree() {
24 this(null, null, null);
27 private ObservationTree(ObservationTree
<I
,O
> parent
, I parentInput
, O parentOutput
) {
28 this.children
= new HashMap
<>();
29 this.outputs
= new HashMap
<>();
31 this.parentInput
= parentInput
;
32 this.parentOutput
= parentOutput
;
36 * @return The outputs observed from the root of the tree until this node
38 private List
<O
> getOutputChain() {
39 if (this.parent
== null) {
40 return new LinkedList
<O
>();
42 List
<O
> parentChain
= this.parent
.getOutputChain();
43 parentChain
.add(parentOutput
);
48 private List
<I
> getInputChain() {
49 if (this.parent
== null) {
50 return new LinkedList
<I
>();
52 List
<I
> parentChain
= this.parent
.getInputChain();
53 parentChain
.add(this.parentInput
);
59 * Add one input and output symbol and traverse the tree to the next node
62 * @return the next node
63 * @throws InconsistencyException
65 public ObservationTree
<I
,O
> addObservation(I input
, O output
) throws CacheInconsistencyException
{
66 O previousOutput
= this.outputs
.get(input
);
67 boolean createNewBranch
= previousOutput
== null;
68 if (createNewBranch
) {
69 // input hasn't been queried before, make a new branch for it and traverse
70 this.outputs
.put(input
, output
);
71 ObservationTree
<I
,O
> child
= new ObservationTree
<I
,O
>(this, input
, output
);
72 this.children
.put(input
, child
);
74 } else if (!previousOutput
.equals(output
)) {
75 // input is inconsistent with previous observations, throw exception
76 List
<O
> oldOutputChain
= this.children
.get(input
).getOutputChain();
77 List
<O
> newOutputChain
= this.getOutputChain();
78 List
<I
> inputChain
= this.getInputChain();
79 newOutputChain
.add(output
);
80 throw new CacheInconsistencyException(toWord(inputChain
), toWord(oldOutputChain
), toWord(newOutputChain
));
82 // input is consistent with previous observations, just traverse
83 return this.children
.get(input
);
88 * Add Observation to the tree
91 * @throws CacheInconsistencyException Inconsistency between new and stored observations
93 public void addObservation(Word
<I
> inputs
, Word
<O
> outputs
) throws CacheInconsistencyException
{
94 addObservation(inputs
.asList(), outputs
.asList());
98 public void addObservation(List
<I
> inputs
, List
<O
> outputs
) throws CacheInconsistencyException
{
99 if (inputs
.isEmpty() && outputs
.isEmpty()) {
101 } else if (inputs
.isEmpty() || outputs
.isEmpty()) {
102 throw new RuntimeException("Input and output words should have the same length:\n" + inputs
+ "\n" + outputs
);
104 I firstInput
= inputs
.get(0);
105 O firstOutput
= outputs
.get(0);
107 this.addObservation(firstInput
, firstOutput
)
108 .addObservation(inputs
.subList(1, inputs
.size()), outputs
.subList(1, outputs
.size()));
109 } catch (CacheInconsistencyException e
) {
110 throw new CacheInconsistencyException(toWord(inputs
), e
.getOldOutput(), toWord(outputs
));
115 public static<T
> Word
<T
> toWord(List
<T
> symbolList
) {
116 return Word
.fromList(symbolList
);