code: /*--------------------------------------------------------------*
    Copyright (C) 2006-2015 OpenSim Ltd.
  
    This file is distributed WITHOUT ANY WARRANTY. See the file
    'License' for details on this and other legal matters.
  *--------------------------------------------------------------*/
  
  package org.omnetpp.common.editor.text;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.eclipse.jface.text.Position;
  import org.eclipse.jface.text.source.IAnnotationModel;
  import org.eclipse.jface.text.source.IAnnotationModelExtension;
  import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
  import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
  import org.eclipse.ui.texteditor.ITextEditor;
  
  /**
   * This class synchronizes folding regions on a text editor.
   *
   * @author andras
   */
  public class FoldingRegionSynchronizer {
      private ITextEditor editor;
  
      // holds existing folding regions, because we need to keep synchronizing it with the model
      private Map<String,ProjectionAnnotation> oldAnnotations = new HashMap<String,ProjectionAnnotation>();
  
      /**
       * Constructor accepts associated text editor.
       */
      public FoldingRegionSynchronizer(ITextEditor editor) {
          this.editor = editor;
      }
  
      /**
       * Adds/removes folding regions (ProjectionAnnotation) on the text editor, so
       * that it's in sync with the positions in the given map. Folding region identity
       * is based on the string keys in the map (this is important so as not to lose
       * the expanded/collapsed state of the regions).
       *
       * Just plain deleting existing annotations and re-adding them would result
       * in their getting expanded all the time.
       */
      public void updateFoldingRegions(Map<String,Position> newAnnotationPositions) {
  
          IAnnotationModel annotationModel = getAnnotationModel(editor);
          IAnnotationModelExtension annotationModel2 = ((IAnnotationModelExtension)annotationModel);
  
          // first: remove extra annotations
          List<String> trash = new ArrayList<String>(); // to avoid ConcurrentModificationException
          for (String key : oldAnnotations.keySet())
              if (!newAnnotationPositions.containsKey(key))
                  trash.add(key);
          for (String key : trash) {
              annotationModel.removeAnnotation(oldAnnotations.get(key));
              oldAnnotations.remove(key);
          }
  
          // second: add missing annotations and update positions
          for (String key : newAnnotationPositions.keySet()) {
              if (!oldAnnotations.containsKey(key)) {
                  // missing: add it
                  ProjectionAnnotation annotation = new ProjectionAnnotation();
                  annotationModel.addAnnotation(annotation, newAnnotationPositions.get(key));
                  oldAnnotations.put(key, annotation);
              }
              else {
                  // update position
                  ProjectionAnnotation annotation = oldAnnotations.get(key);
                  Position position = annotationModel.getPosition(annotation);
                  if (position == null)
                      oldAnnotations.remove(key); // disappeared from the editor, forget it
                  else if (!position.equals(newAnnotationPositions.get(key)))
                      annotationModel2.modifyAnnotationPosition(annotation, newAnnotationPositions.get(key));
              }
          }
      }
  
      private static IAnnotationModel getAnnotationModel(ITextEditor editor) {
          return (IAnnotationModel) editor.getAdapter(ProjectionAnnotationModel.class);
      }
  }
  