gt: 		if (keywords.length == 1)
context: /*******************************************************************************
   * Copyright (c) 2015 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.formatting2.regionaccess.internal;
  
  import java.util.Collection;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Set;
  
  import org.eclipse.emf.ecore.EAttribute;
  import org.eclipse.emf.ecore.EClass;
  import org.eclipse.emf.ecore.EObject;
  import org.eclipse.emf.ecore.EReference;
  import org.eclipse.emf.ecore.EStructuralFeature;
  import org.eclipse.xtext.AbstractElement;
  import org.eclipse.xtext.AbstractRule;
  import org.eclipse.xtext.Assignment;
  import org.eclipse.xtext.CompoundElement;
  import org.eclipse.xtext.CrossReference;
  import org.eclipse.xtext.GrammarUtil;
  import org.eclipse.xtext.Keyword;
  import org.eclipse.xtext.RuleCall;
  import org.eclipse.xtext.XtextPackage;
  import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
  import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
  import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
  import org.eclipse.xtext.xbase.lib.Pair;
  
  import com.google.common.base.Joiner;
  import com.google.common.base.Preconditions;
  import com.google.common.base.Predicate;
  import com.google.common.base.Predicates;
  import com.google.common.collect.ImmutableList;
  import com.google.common.collect.ImmutableList.Builder;
  import com.google.common.collect.Lists;
  import com.google.common.collect.Sets;
  
  /**
   * @author Moritz Eysholdt - Initial contribution and API
   */
  public abstract class AbstractSemanticRegionsFinder implements ISemanticRegionsFinder {
  
  	protected static class FeaturePredicate implements Predicate<ISemanticRegion> {
  
  		private final String name;
  		private final EClass type;
  
  		public FeaturePredicate(EStructuralFeature feature) {
  			this.name = feature.getName();
  			this.type = feature.getEContainingClass();
  		}
  
  		@Override
  		public boolean apply(ISemanticRegion input) {
  			if (input == null)
  				return false;
  			EObject element = input.getGrammarElement();
  			Assignment assignment = GrammarUtil.containingAssignment(element);
  			if (assignment == null || !name.equals(assignment.getFeature()))
  				return false;
  			EObject semanticElement = input.getSemanticElement();
  			return type.isInstance(semanticElement);
  		}
  
  	}
  
  	protected static class GrammarElementPredicate implements Predicate<ISemanticRegion> {
  		private final EObject grammarElement;
  
  		public GrammarElementPredicate(EObject grammarElement) {
  			super();
  			this.grammarElement = grammarElement;
  		}
  
  		@Override
  		public boolean apply(ISemanticRegion input) {
  			if (input == null)
  				return false;
  			return input.getGrammarElement() == grammarElement;
  		}
  
  		@Override
  		public String toString() {
  			String string = new GrammarElementTitleSwitch().showRule().showQualified().doSwitch(grammarElement);
  			return "Predicate[" + string + "]";
  		}
  
  	}
  
  	protected static class GrammarElementsPredicate implements Predicate<ISemanticRegion> {
  		private final Set<? extends EObject> grammarElements;
  
  		public GrammarElementsPredicate(Set<? extends EObject> grammarElements) {
  			super();
  			this.grammarElements = grammarElements;
  		}
  
  		@Override
  		public boolean apply(ISemanticRegion input) {
  			if (input == null)
  				return false;
  			return grammarElements.contains(input.getGrammarElement());
  		}
  
  		@Override
  		public String toString() {
  			List<String> strings = Lists.newArrayList();
  			GrammarElementTitleSwitch toString = new GrammarElementTitleSwitch().showRule().showQualified();
  			for (EObject ele : grammarElements)
  				strings.add(toString.doSwitch(ele));
  			return "Predicate[" + Joiner.on(", ").join(strings) + "]";
  		}
  	}
  
  	protected static class KeywordPredicate implements Predicate<ISemanticRegion> {
  		private final String keyword;
  
  		public KeywordPredicate(String keyword) {
  			super();
  			this.keyword = keyword;
  		}
  
  		@Override
  		public boolean apply(ISemanticRegion input) {
  			if (input == null)
  				return false;
  			EObject element = input.getGrammarElement();
  			return element instanceof Keyword && keyword.equals(((Keyword) element).getValue());
  		}
  
  		@Override
  		public String toString() {
  			return "Predicate[" + keyword + "]";
  		}
  	}
  
  	protected static class KeywordsPredicate implements Predicate<ISemanticRegion> {
  		private final Set<String> keywords;
  
  		public KeywordsPredicate(Set<String> keywords) {
  			super();
  			this.keywords = keywords;
  		}
  
  		@Override
  		public boolean apply(ISemanticRegion input) {
  			if (input == null)
  				return false;
  			EObject element = input.getGrammarElement();
  			return element instanceof Keyword && keywords.contains(((Keyword) element).getValue());
  		}
  
  		@Override
  		public String toString() {
  			return "Predicate[" + Joiner.on(", ").join(keywords) + "]";
  		}
  	}
  
  	protected static class RulePredicate implements Predicate<ISemanticRegion> {
  		private final AbstractRule rule;
  
  		public RulePredicate(AbstractRule rule) {
  			super();
  			this.rule = rule;
  		}
  
  		@Override
  		public boolean apply(ISemanticRegion input) {
  			if (input == null)
  				return false;
  			EObject element = input.getGrammarElement();
  			return element instanceof RuleCall && ((RuleCall) element).getRule() == rule;
  		}
  
  		@Override
  		public String toString() {
  			return "Predicate[" + rule.getName() + "]";
  		}
  	}
  
  	protected static class RulesPredicate implements Predicate<ISemanticRegion> {
  		private final Set<AbstractRule> rules;
  
  		public RulesPredicate(Set<AbstractRule> rules) {
  			super();
  			this.rules = rules;
  		}
  
  		@Override
  		public boolean apply(ISemanticRegion input) {
  			if (input == null)
  				return false;
  			EObject element = input.getGrammarElement();
  			return element instanceof RuleCall && rules.contains(((RuleCall) element).getRule());
  		}
  
  		@Override
  		public String toString() {
  			List<String> strings = Lists.newArrayList();
  			for (AbstractRule rule : rules)
  				strings.add(rule.getName());
  			return "Predicate[" + Joiner.on(", ").join(strings) + "]";
  		}
  	}
  
  	protected void assertNoContainment(EStructuralFeature feat) {
  		if (!(feat instanceof EAttribute) && !(feat instanceof EReference && !((EReference) feat).isContainment()))
  			throw new IllegalStateException("Only EAttributes and CrossReferences allowed.");
  	}
  
  	protected void assertNoEObjectRule(AbstractRule rule) {
  		if (GrammarUtil.isEObjectRule(rule))
  			throw new IllegalStateException("Only Enum, Datatype and Terminal Rule Calls allowed.");
  	}
  
  	protected void assertNoEObjectRuleCall(RuleCall ruleCall) {
  		assertNoEObjectRule(ruleCall.getRule());
  	}
  
  	@Override
  	public ISemanticRegion assignment(Assignment assignment) {
  		return findFirst(createPredicate(assignment));
  	}
  
  	@Override
  	public List<ISemanticRegion> assignments(Assignment... assignments) {
  		return findAll(createPredicate(assignments));
  	}
  
  	protected void collectMatchableElements(AbstractElement ele, Collection<AbstractElement> result) {
  		switch (ele.eClass().getClassifierID()) {
  			case XtextPackage.RULE_CALL:
  				assertNoEObjectRuleCall((RuleCall) ele);
  			case XtextPackage.KEYWORD:
  				result.add(ele);
  				break;
  			case XtextPackage.CROSS_REFERENCE:
  				collectMatchableElements(((CrossReference) ele).getTerminal(), result);
  				break;
  			case XtextPackage.ASSIGNMENT:
  				collectMatchableElements(((Assignment) ele).getTerminal(), result);
  				break;
  			case XtextPackage.ALTERNATIVES:
  			case XtextPackage.GROUP:
  			case XtextPackage.UNORDERED_GROUP:
  				for (AbstractElement child : ((CompoundElement) ele).getElements())
  					collectMatchableElements(child, result);
  				break;
  		}
  	}
  
  	protected Predicate<ISemanticRegion> createPredicate(AbstractElement ele) {
  		switch (ele.eClass().getClassifierID()) {
  			case XtextPackage.RULE_CALL:
  				assertNoEObjectRuleCall((RuleCall) ele);
  			case XtextPackage.KEYWORD:
  				return new GrammarElementPredicate(ele);
  		}
  		return createPredicate(new AbstractElement[] { ele });
  	}
  
  	protected Predicate<ISemanticRegion> createPredicate(AbstractElement... ele) {
  		Set<AbstractElement> result = Sets.newHashSet();
  		for (AbstractElement e : ele)
  			collectMatchableElements(e, result);
  		switch (result.size()) {
  			case 0:
  				return Predicates.alwaysFalse();
  			case 1:
  				return new GrammarElementPredicate(result.iterator().next());
  			default:
  				return new GrammarElementsPredicate(result);
  		}
  	}
  
  	@Override
  	public ISemanticRegion crossRef(CrossReference crossReference) {
  		return findFirst(createPredicate(crossReference));
  	}
  
  	@Override
  	public List<ISemanticRegion> crossRefs(CrossReference... crossReferences) {
  		return findAll(createPredicate(crossReferences));
  	}
  
  	@Override
  	public ISemanticRegion element(AbstractElement element) {
  		return findFirst(createPredicate(element));
  	}
  
  	@Override
  	public List<ISemanticRegion> elements(AbstractElement... elements) {
  		return findAll(createPredicate(elements));
  	}
  
  	@Override
  	public ISemanticRegion feature(EStructuralFeature feature) {
  		assertNoContainment(feature);
  		return findFirst(new FeaturePredicate(feature));
  	}
  
  	@Override
  	public List<ISemanticRegion> features(EStructuralFeature... features) {
  		Set<Predicate<ISemanticRegion>> predicates = Sets.newHashSet();
  		for (int i = 0; i < features.length; i++) {
  			EStructuralFeature feat = features[i];
  			assertNoContainment(feat);
  			predicates.add(new FeaturePredicate(feat));
  		}
  		return findAll(Predicates.or(predicates));
  	}
  
  	protected abstract ImmutableList<ISemanticRegion> findAll(Predicate<ISemanticRegion> predicate);
  
  	protected abstract ISemanticRegion findFirst(Predicate<ISemanticRegion> predicate);
  
  	@Override
  	public ISemanticRegion keyword(Keyword keyword) {
  		return findFirst(createPredicate(keyword));
  	}
  
  	@Override
  	public ISemanticRegion keyword(String keyword) {
  		return findFirst(new KeywordPredicate(keyword));
  	}
  
  	@Override
  	public List<Pair<ISemanticRegion, ISemanticRegion>> keywordPairs(Keyword kw1, Keyword kw2) {
  		Preconditions.checkNotNull(kw1);
  		Preconditions.checkNotNull(kw2);
  		Preconditions.checkArgument(kw1 != kw2);
  		Predicate<ISemanticRegion> p1 = createPredicate(kw1);
  		Predicate<ISemanticRegion> p2 = createPredicate(kw2);
  		List<ISemanticRegion> all = findAll(Predicates.or(p1, p2));
  		Builder<Pair<ISemanticRegion, ISemanticRegion>> result = ImmutableList.builder();
  		LinkedList<ISemanticRegion> stack = new LinkedList<ISemanticRegion>();
  		for (ISemanticRegion region : all) {
  			if (p1.apply(region))
  				stack.push(region);
  			else if (!stack.isEmpty())
  				result.add(Pair.of(stack.pop(), region));
  		}
  		return result.build();
  	}
  
  	@Override
  	public List<Pair<ISemanticRegion, ISemanticRegion>> keywordPairs(String kw1, String kw2) {
  		Preconditions.checkNotNull(kw1);
  		Preconditions.checkNotNull(kw2);
  		Preconditions.checkArgument(!kw1.equals(kw2));
  		Predicate<ISemanticRegion> p1 = new KeywordPredicate(kw1);
  		Predicate<ISemanticRegion> p2 = new KeywordPredicate(kw2);
  		List<ISemanticRegion> all = findAll(Predicates.or(p1, p2));
  		Builder<Pair<ISemanticRegion, ISemanticRegion>> result = ImmutableList.builder();
  		LinkedList<ISemanticRegion> stack = new LinkedList<ISemanticRegion>();
  		for (ISemanticRegion region : all) {
  			if (p1.apply(region))
  				stack.push(region);
  			else {
  				AbstractRule regionRule = GrammarUtil.containingRule(region.getGrammarElement());
  				while (!stack.isEmpty()) {
  					ISemanticRegion candidate = stack.pop();
  					if (region.getSemanticElement() == candidate.getSemanticElement()) {
  						AbstractRule candidateRule = GrammarUtil.containingRule(candidate.getGrammarElement());
  						if (regionRule == candidateRule) {
  							result.add(Pair.of(candidate, region));
  							break;
  						}
  					}
  				}
  			}
  		}
  		return result.build();
  	}
  
  	@Override
  	public List<ISemanticRegion> keywords(Keyword... keywords) {
  		return findAll(createPredicate(keywords));
  	}
  
  	@Override
  	public List<ISemanticRegion> keywords(String... keywords) {
  		Predicate<ISemanticRegion> predicate;
  