@Override
 public String toString() {
   List<ITextSegment> list = toTokenAndGapList();
   if (list.isEmpty()) return "(empty)";
   Multimap<IHiddenRegion, IEObjectRegion> hiddens = LinkedListMultimap.create();
   List<String> errors = Lists.newArrayList();
   ITextRegionAccess access = list.get(0).getTextRegionAccess();
   TreeIterator<EObject> all = EcoreUtil2.eAll(access.regionForRootEObject().getSemanticElement());
   while (all.hasNext()) {
     EObject element = all.next();
     IEObjectRegion obj = access.regionForEObject(element);
     if (obj == null) continue;
     IHiddenRegion previous = obj.getPreviousHiddenRegion();
     IHiddenRegion next = obj.getNextHiddenRegion();
     if (previous == null)
       errors.add("ERROR: " + EmfFormatter.objPath(element) + " has no leading HiddenRegion.");
     else hiddens.put(previous, obj);
     if (previous != next) {
       if (next == null)
         errors.add("ERROR: " + EmfFormatter.objPath(element) + " has no trailing HiddenRegion.");
       else hiddens.put(next, obj);
     }
   }
   TextRegionListToString result = new TextRegionListToString();
   if (!hideColumnExplanation) {
     result.add("Columns: 1:offset 2:length 3:kind 4: text 5:grammarElement", false);
     result.add("Kind: H=IHiddenRegion S=ISemanticRegion B/E=IEObjectRegion", false);
     result.add("", false);
   }
   for (String error : errors) result.add(error, false);
   int indentation = 0;
   for (ITextSegment region : list) {
     List<IEObjectRegion> previous = Lists.newArrayList();
     List<IEObjectRegion> next = Lists.newArrayList();
     List<String> middle = Lists.newArrayList(toString(region));
     if (region instanceof IHiddenRegion) {
       Collection<IEObjectRegion> found = hiddens.get((IHiddenRegion) region);
       for (IEObjectRegion obj : found) {
         boolean p = obj.getNextHiddenRegion().equals(region);
         boolean n = obj.getPreviousHiddenRegion().equals(region);
         if (p && n) middle.add(EMPTY_TITLE + "Semantic " + toString(obj));
         else if (p) previous.add(obj);
         else if (n) next.add(obj);
       }
       Collections.sort(previous, AstRegionComparator.CHILDREN_FIRST);
       Collections.sort(next, AstRegionComparator.CONTAINER_FIRST);
     }
     for (IEObjectRegion obj : previous) {
       indentation--;
       result.add(indent(indentation) + EOBJECT_END_PADDED + toString(obj));
     }
     String indent = indent(indentation);
     result.add(region, indent + Joiner.on("\n").join(middle).replace("\n", "\n" + indent));
     for (IEObjectRegion obj : next) {
       result.add(indent(indentation) + EOBJECT_BEGIN_PADDED + toString(obj));
       indentation++;
     }
   }
   return result.toString();
 }
 @Override
 public void accept(ISerializationDiagnostic diagnostic) {
   if (diagnostic == null || diagnostic.getMessage() == null)
     throw new RuntimeException("Something went wrong during serialization");
   else if (diagnostic.getException() != null)
     throw new RuntimeException(diagnostic.getException());
   else {
     String msg = diagnostic.getMessage();
     if (diagnostic.getSemanticObject() != null)
       msg += "\nSemantic Object: " + EmfFormatter.objPath(diagnostic.getSemanticObject());
     if (diagnostic.getContext() != null)
       msg += "\nContext: " + new Context2NameFunction().getContextName(diagnostic.getContext());
     throw new RuntimeException(msg);
   }
 }
 @Override
 public void acceptAssignedCrossRefTerminal(
     RuleCall rc, String token, EObject value, int index, ILeafNode node) {
   add(titles.doSwitch(rc), token, EmfFormatter.objPath(value), index, node);
   super.acceptAssignedCrossRefTerminal(rc, token, value, index, node);
 }
 /** @since 2.3 */
 @Override
 public void acceptAssignedCrossRefKeyword(
     Keyword kw, String token, EObject value, int index, ILeafNode node) {
   add(titles.doSwitch(kw), token, EmfFormatter.objPath(value), index, node);
   super.acceptAssignedCrossRefKeyword(kw, token, value, index, node);
 }
 @Override
 public void acceptAssignedCrossRefEnum(
     RuleCall enumRC, String token, EObject value, int index, ICompositeNode node) {
   add(titles.doSwitch(enumRC), token, EmfFormatter.objPath(value), index, node);
   super.acceptAssignedCrossRefEnum(enumRC, token, value, index, node);
 }