Java tutorial
/******************************************************************************* * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.xtext.util.formallang; import java.util.Collections; import java.util.List; import java.util.Set; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Sets; /** * @author Moritz Eysholdt - Initial contribution and API */ public class FollowerFunctionImpl<E, T> implements FollowerFunction<E> { public enum Direction { L2R, R2L } public enum UnorderedStrategy { MULIT_ALTERNATIVE, SEQUENCE } protected Direction direction = Direction.L2R; protected Predicate<E> filter; protected Production<E, T> production; protected UnorderedStrategy unorderedStrategy = UnorderedStrategy.MULIT_ALTERNATIVE; public FollowerFunctionImpl(Production<E, T> production) { super(); this.production = production; } protected void collectByParent(E element, Set<E> result, Set<E> visited) { E container = production.getParent(element); Iterable<E> children; if (container == null) result.add(null); else if ((children = production.getSequentialChildren(container)) != null) collectByParentSequence(element, container, children, result, visited); else if ((children = production.getUnorderedChildren(container)) != null) switch (unorderedStrategy) { case SEQUENCE: collectByParentSequence(element, container, children, result, visited); break; case MULIT_ALTERNATIVE: collectElement(container, result, visited); collectByParent(container, result, visited); break; } else { if (production.isMany(container)) collectElement(container, result, visited); collectByParent(container, result, visited); } } protected void collectByParentSequence(E element, E container, Iterable<E> children, Set<E> result, Set<E> visited) { List<E> sequentialChildren = orderedList(children); int i = sequentialChildren.indexOf(element) + 1; while (i < sequentialChildren.size()) { E next = sequentialChildren.get(i); collectElement(next, result, visited); if (production.isOptional(next)) i++; else break; } if (i >= sequentialChildren.size()) { if (production.isMany(container)) collectElement(container, result, visited); collectByParent(container, result, visited); } } protected void collectChildren(E element, Set<E> result, Set<E> visited) { Iterable<E> children; if ((children = production.getSequentialChildren(element)) != null) collectChildrenSequence(element, children, result, visited); else if ((children = production.getAlternativeChildren(element)) != null) collectChildrenAlternative(element, children, result, visited); else if ((children = production.getUnorderedChildren(element)) != null) switch (unorderedStrategy) { case SEQUENCE: collectChildrenSequence(element, children, result, visited); break; case MULIT_ALTERNATIVE: collectChildrenUnorderedAlt(element, children, result, visited); break; } else { if (production.isMany(element) /* && filter(element) */) collectElement(element, result, visited); collectByParent(element, result, visited); } } protected void collectChildrenAlternative(E element, Iterable<E> alternativeChildren, Set<E> result, Set<E> visited) { boolean optional = production.isOptional(element); for (E child : orderedIterable(alternativeChildren)) { optional |= production.isOptional(child); collectElement(child, result, visited); } if (optional) collectByParent(element, result, visited); } protected void collectChildrenSequence(E element, Iterable<E> sequentialChildren, Set<E> result, Set<E> visited) { boolean reachedEnd = true; for (E child : orderedIterable(sequentialChildren)) { collectElement(child, result, visited); if (!production.isOptional(child)) { reachedEnd = false; break; } } if (reachedEnd || production.isOptional(element)) collectByParent(element, result, visited); } protected void collectChildrenUnorderedAlt(E element, Iterable<E> alternativeChildren, Set<E> result, Set<E> visited) { boolean hasMandatory = false; for (E child : orderedIterable(alternativeChildren)) { hasMandatory |= !production.isOptional(child); collectElement(child, result, visited); } if (!hasMandatory || production.isOptional(element)) collectByParent(element, result, visited); } protected void collectElement(E ele, Set<E> result, Set<E> visited) { if (!visited.add(ele)) return; if (filter(ele)) result.add(ele); else collectChildren(ele, result, visited); } protected boolean filter(E ele) { if (filter != null) return filter.apply(ele); return production.getSequentialChildren(ele) == null && production.getAlternativeChildren(ele) == null && production.getUnorderedChildren(ele) == null; } public Direction getDirection() { return direction; } public Predicate<E> getFilter() { return filter; } @Override public Iterable<E> getFollowers(E element) { if (filter(element)) { if (element == null) throw new NullPointerException(); Set<E> outgoing = Sets.newLinkedHashSet(); collectChildren(element, outgoing, Sets.<E>newHashSet()); return outgoing; } else return Collections.emptyList(); } public Production<E, ?> getProduction() { return production; } @Override public Iterable<E> getStarts(E root) { if (root == null) throw new NullPointerException(); Set<E> outgoing = Sets.newLinkedHashSet(); if (filter(root)) { outgoing.add(root); if (production.isOptional(root)) outgoing.add(null); } else collectChildren(root, outgoing, Sets.<E>newHashSet()); return outgoing; } public UnorderedStrategy getUnorderedStrategy() { return unorderedStrategy; } protected Iterable<E> orderedIterable(Iterable<E> elements) { return direction == Direction.L2R ? elements : Lists.reverse(toList(elements)); } protected List<E> orderedList(Iterable<E> elements) { if (direction == Direction.L2R) return toList(elements); List<E> result = Lists.newArrayList(elements); Collections.reverse(result); return result; } public FollowerFunctionImpl<E, T> setDirection(Direction direction) { this.direction = direction; return this; } public FollowerFunctionImpl<E, T> setFilter(Predicate<E> filter) { this.filter = filter; return this; } public FollowerFunctionImpl<E, T> setUnorderedStrategy(UnorderedStrategy unorderedStrategy) { this.unorderedStrategy = unorderedStrategy; return this; } protected List<E> toList(Iterable<E> elements) { return elements instanceof List<?> ? (List<E>) elements : Lists.newArrayList(elements); } }