}
RandomizerOperationComponent generateWithLookahead(final DocOpAutomaton a, boolean valid,
final Stage stage) {
{
ValidationResult r = a.checkAnnotationBoundary(
AnnotationBoundaryMapImpl.builder().updateValues("a", null, "1").build(), null);
assert r.isIllFormed() || r.isValid();
if (r.isIllFormed()) {
return null;
}
}
Set<String> keySet = new TreeSet<String>(new StringNullComparator());
for (AnnotationOption o : p.getAnnotationOptions()) {
keySet.add(o.key);
}
keySet.addAll(a.currentAnnotations().keySet());
keySet.addAll(a.inheritedAnnotations().keySet());
final ArrayList<String> keys = new ArrayList<String>(keySet);
Collections.sort(keys);
// For every key, either pick it, or don't (choice point, recursively
// explore both options).
// For each key, one option is to end that key if it currently is in
// openAnnotations().
// Another option is not to end that key: In that case, given the key,
// the valid old values are those from annotationOptions and
// those from currentAnnotations() (for deletions) and
// those from inheritedAnnotations() (for insertions);
// the valid new values are those from annotationOptions and
// those from inheritedAnnotations() (for deletion).
//
// Given the full map, we need to check if the component is valid, then
// temporarily apply it to find out if there is any valid component
// to follow up with.
final RunnableWithException<Result> chooseKeys = new RunnableWithException<Result>() {
ArrayList<String> keysToEnd = new ArrayList<String>();
ArrayList<String> changeKeys = new ArrayList<String>();
ArrayList<String> changeOldValues = new ArrayList<String>();
ArrayList<String> changeNewValues = new ArrayList<String>();
void tryThisOption() throws Result {
AnnotationBoundaryMapImpl map = AnnotationBoundaryMapImpl.builder()
.initializationEnd(toArray(keysToEnd))
.updateValues(toArray(changeKeys), toArray(changeOldValues),
toArray(changeNewValues)).build();
final RandomizerOperationComponent component = generate(map);
DocOpAutomaton temp = new DocOpAutomaton(a);
ViolationCollector v = new ViolationCollector();
component.check(temp, v);
assert !component.check(temp, null).isIllFormed();
component.apply(temp);
// System.err.println("begin lookahead for " + map);
RandomizerOperationComponent followup = pickComponent(temp, stage);
if (followup != null) {
// System.err.println("end lookahead, success");
throw new Result(component);
}
// System.err.println("end lookahead, failed");
}
void removeLastMaybe(ArrayList<String> l, int lastItemIndex) {
assert lastItemIndex == l.size() || lastItemIndex == l.size() - 1;
if (lastItemIndex == l.size() - 1) {
l.remove(lastItemIndex);
}
}
void take(int nextKeyIndex, String key) throws Result {
assert key != null;
if (a.openAnnotations().contains(key)) {
int oldSize = keysToEnd.size();
try {
keysToEnd.add(key);
nextKey(nextKeyIndex);
} finally {
removeLastMaybe(keysToEnd, oldSize);
}
}
Set<String> valueSet = new TreeSet<String>(new StringNullComparator());
for (AnnotationOption o : p.getAnnotationOptions()) {
if (key.equals(o.key)) {
valueSet.addAll(o.valueAlternatives);
}
}
AnnotationMap inheritedAnnotations = a.inheritedAnnotations();
if (inheritedAnnotations.containsKey(key)) {
valueSet.add(inheritedAnnotations.get(key));
} else {
valueSet.add(null);
}
ArrayList<String> newValues = new ArrayList<String>(valueSet);
AnnotationMap currentAnnotations = a.currentAnnotations();
if (currentAnnotations.containsKey(key)) {
valueSet.add(currentAnnotations.get(key));
} else {
valueSet.add(null);
}
ArrayList<String> oldValues = new ArrayList<String>(valueSet);
shuffle(r, oldValues);
shuffle(r, newValues);
for (String oldValue : oldValues) {
for (String newValue : newValues) {
assert changeKeys.size() == changeOldValues.size();
assert changeKeys.size() == changeNewValues.size();
int oldSize = changeKeys.size();
try {
changeKeys.add(key);
changeOldValues.add(oldValue);
changeNewValues.add(newValue);
nextKey(nextKeyIndex);
} finally {
removeLastMaybe(changeNewValues, oldSize);
removeLastMaybe(changeOldValues, oldSize);
removeLastMaybe(changeKeys, oldSize);
assert changeKeys.size() == changeOldValues.size();
assert changeKeys.size() == changeNewValues.size();
}
}
}
}
void nextKey(int nextKeyIndex) throws Result {
if (nextKeyIndex >= keys.size()) {
tryThisOption();
return;
}
String key = keys.get(nextKeyIndex);
boolean take = r.nextBoolean();
if (take) {
take(nextKeyIndex + 1, key);
nextKey(nextKeyIndex + 1);
} else {
nextKey(nextKeyIndex + 1);