Package org.cfeclipse.cfml.editors.codefolding

Source Code of org.cfeclipse.cfml.editors.codefolding.CodeFoldingSetter

/*
* Created on Oct 19, 2004
*
* The MIT License
* Copyright (c) 2004 Stephen Milligan
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.cfeclipse.cfml.editors.codefolding;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.cfeclipse.cfml.editors.ICFDocument;
import org.cfeclipse.cfml.editors.partitioner.scanners.CFPartitionScanner;
import org.cfeclipse.cfml.parser.CFNodeList;
import org.cfeclipse.cfml.parser.docitems.CfmlTagItem;
import org.cfeclipse.cfml.parser.docitems.DocItem;
import org.cfeclipse.cfml.preferences.CFMLPreferenceManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.ui.texteditor.ITextEditor;

/**
* @author Stephen Milligan
*
*         This class is used to set the code folding markers.
*/
public class CodeFoldingSetter {
  private QualifiedName foldStateQN = new QualifiedName("foldedCodeState", "foldState");

  private ITextEditor editor;

  private List<StoredFold> storedFolds = null;

  private ProjectionAnnotationModel model = null;

  private ICFDocument doc = null;

  private CFMLPreferenceManager preferenceManager = null;

  private IFile resource;

  private Boolean restoredFoldState;

  public CodeFoldingSetter(ITextEditor editor) {
    this.editor = editor;
    model = (ProjectionAnnotationModel) editor.getAdapter(ProjectionAnnotationModel.class);
    doc = (ICFDocument) editor.getDocumentProvider().getDocument(editor.getEditorInput());
    resource = ((IFile) editor.getEditorInput().getAdapter(IFile.class));
    preferenceManager = new CFMLPreferenceManager();
    restoredFoldState = false;
  }

  public synchronized void docChanged(boolean autoCollapse) {
    // storeMemento();
    // restoreMemento();
    if (preferenceManager.enableFolding()) {
      addMarksToModel(autoCollapse);
      if (preferenceManager.persistFoldState()) {
        if (!restoredFoldState) {
          restoredFoldState = true;
          restoreFoldState();
        } else {
          persistFoldState();
        }
      }
    }
  }

  /**
   * @param root
   * @param model
   */
  private void addMarksToModel(boolean autoCollapse) {
    try {
      if (model != null) {

        // We need this to keep track of what should be collapsed once
        // we've added all the markers.
        HashMap markerMap = new HashMap();

        scrubAnnotations();

        if (preferenceManager.foldCFMLComments()) {
          foldPartitions(markerMap, CFPartitionScanner.CF_COMMENT, preferenceManager.collapseCFMLComments()
              && autoCollapse, preferenceManager.minimumFoldingLines() - 1);
          foldPartitions(markerMap, CFPartitionScanner.CF_SCRIPT_COMMENT_BLOCK, preferenceManager.collapseCFMLComments()
              && autoCollapse, preferenceManager.minimumFoldingLines() - 1);
          foldPartitions(markerMap, CFPartitionScanner.JAVADOC_COMMENT, preferenceManager.collapseCFMLComments() && autoCollapse,
              preferenceManager.minimumFoldingLines() - 1);
        }
        if (preferenceManager.foldHTMLComments()) {
          foldPartitions(markerMap, CFPartitionScanner.HTM_COMMENT, preferenceManager.collapseHTMLComments()
              && autoCollapse, preferenceManager.minimumFoldingLines() - 1);
        }

        for (int i = 1; i < 9; i++) {
          // System.out.println("Checking " +
          // preferenceManager.foldingTagName(i).trim());
          if (preferenceManager.foldTag(i) && preferenceManager.foldingTagName(i).trim().length() > 0) {
            foldTags(markerMap, preferenceManager.foldingTagName(i).trim(), preferenceManager
                .collapseTag(i)
                && autoCollapse, preferenceManager.minimumFoldingLines() - 1);
          }
        }

        // Now collapse anything that should be collapsed
        Iterator x = markerMap.keySet().iterator();

        while (x.hasNext()) {
          ProjectionAnnotation p = (ProjectionAnnotation) x.next();
          boolean collapsed = ((Boolean) markerMap.get(p)).booleanValue();
          if (collapsed) {
            model.collapse(p);
          }
        }

      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private void foldTags(HashMap markerMap, String tagName, boolean autoCollapse, int minLines) {
    DocItem rootItem = null;
    try {
      rootItem = doc.getCFDocument().getDocumentRoot();
    } catch (NullPointerException e) {
      // e.printStackTrace();
      System.out.println("CodeFoldingSetter::foldTags got a null from doc.getCFDocument().");
      return;
    }
    // nodes =
    // rootItem.selectNodes("//function[#startpos>=0 and #endpos < 200]");
    CFNodeList nodes = rootItem.selectNodes("//" + tagName.toLowerCase());

    Iterator it = nodes.iterator();
    while (it.hasNext()) {
      Object o = it.next();
      if (o instanceof CfmlTagItem && ((CfmlTagItem) o).matchingItem != null) {
        CfmlTagItem tag = (CfmlTagItem) o;

        int start = tag.getStartPosition();
        int length = tag.matchingItem.getEndPosition() - start;
        try {
          int startLine = doc.getLineOfOffset(start);
          int endLine = doc.getLineOfOffset(start + length);
          start = doc.getLineOffset(startLine);
          length = doc.getLineOffset(endLine) + doc.getLineLength(endLine) - start;
          if (endLine - startLine > minLines) {

            addFoldingMark(markerMap, start, length, new TagProjectionAnnotation(tagName), autoCollapse);
          }
        } catch (BadLocationException blx) {

        }

      }
    }
  }

  private void foldPartitions(HashMap markerMap, String partitionType, boolean autoCollapse, int minLines) {
    // This will hold the regions that should have folding markers.
    ArrayList regions = new ArrayList();

    IDocumentPartitioner partitioner = doc.getDocumentPartitioner();
    ITypedRegion[] regionArray = partitioner.computePartitioning(0, doc.getLength());

    for (int i = 0; i < regionArray.length; i++) {
      ITypedRegion region = regionArray[i];
      if (region.getType() == partitionType) {
        // Position position= new Position(region.getOffset(),
        // region.getLength());
        regions.add(region);
      }
    }

    foldRegions(markerMap, regions, autoCollapse, minLines, partitionType);
  }

    private void scrubAnnotations() {
        Iterator iter = model.getAnnotationIterator();
       
        while (iter.hasNext()) {
            Object o = iter.next();
            ProjectionAnnotation annotation = (ProjectionAnnotation)o;
            if (!(annotation instanceof TagProjectionAnnotation)
                    && !annotation.isCollapsed()) {
        /* Changed the requirement here as there were problems relating
         * to ticket #143
         *
         * We should remove all annotations as they are going to be
         * re-created as part of the addMarksToModel function call that
         * calls this function in the first place. If we don't, we get
         * strange conflicts that cause IndexOutOfBounds errors.
         *
         * This is a partial fix to the situation and it is only half
         * the story. My thoughts are that this issue lies somewhere in
         * the CFEPartitioner. This issue doesn't just relate to
         * comments.
         *
         * Paul V. Ticket #143
        */
                 
                 model.removeAnnotation(annotation);
            }
        }
    }

  public void expandAll() {
    initModel();
    model.expandAll(0, doc.getLength());
  }

  public void collapseAll() {
    initModel();
    Iterator i = model.getAnnotationIterator();
    while (i.hasNext()) {
      model.collapse((ProjectionAnnotation) i.next());
    }
  }

  /**
   *
   * @param regions
   * @param model
   * @param doc
   */
  private void foldRegions(HashMap markerMap, ArrayList regions, boolean autoCollapse, int minLines, String regionType) {
    int i = 0;

    try {

      for (Iterator iter = regions.iterator(); iter.hasNext(); ++i) {

        ITypedRegion element = (ITypedRegion) iter.next();
        // Find the start and end lines of the region

        int start = element.getOffset();
        int length = element.getLength();
        int startLine = doc.getLineOfOffset(start);
        int endLine = doc.getLineOfOffset(start + length);
        int end = doc.getLineLength(endLine);
        int stop = doc.getLineOffset(endLine) + end;
        // Make sure our position starts at the start of the line
        start = doc.getLineOffset(startLine);
        length = length + element.getOffset() - doc.getLineOffset(startLine);
        if (endLine - startLine > minLines) {
          try {
            CommentProjectionAnnotation annotation = new CommentProjectionAnnotation(regionType);
            addFoldingMark(markerMap, start, stop - start, annotation, autoCollapse);
          } catch (BadLocationException e) {
            e.printStackTrace();
          }
        }

      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * @param start
   * @param length
   * @param model
   * @param annotation
   * @throws BadLocationException
   */
  public void addFoldingMark(HashMap markerMap, int start, int length, ProjectionAnnotation annotation,
      boolean autoCollapse) throws BadLocationException {

    if (!preferenceManager.enableFolding()) {
      return;
    }

    Position position = new Position(start, length);

    Iterator i = model.getAnnotationIterator();

    while (i.hasNext()) {
      Object o = i.next();
      Position thisPosition = model.getPosition((ProjectionAnnotation) o);

      if (thisPosition.offset == position.offset) {

        return;
      }
    }
    markerMap.put(annotation, new Boolean(autoCollapse));
    model.addAnnotation(annotation, position);

  }

  public void addFoldToSelection(boolean collapse) {
    if (!preferenceManager.enableFolding()) {
      return;
    }
    initModel();

    HashMap markerMap = new HashMap();

    ITextSelection textSelection = getSelection();

    if (!textSelection.isEmpty()) {
      int start = textSelection.getStartLine();
      int end = textSelection.getEndLine();
      if (start < end)
        try {

          int offset = doc.getLineOffset(start);
          int endOffset = doc.getLineOffset(end + 1);

          addFoldingMark(markerMap, offset, endOffset - offset, new CustomProjectionAnnotation(collapse),
              collapse);

        } catch (BadLocationException x) {
        }
    }
    persistFoldState();
  }

  public void removeFoldFromSelection() {
    if (!preferenceManager.enableFolding()) {
      return;
    }
    initModel();

    ArrayList annotations = findAnnotations(getSelection());
    Iterator i = annotations.iterator();
    while (i.hasNext()) {
      model.removeAnnotation((ProjectionAnnotation) i.next());
    }
    persistFoldState();
  }

  public void toggleSelection() {

    if (!preferenceManager.enableFolding()) {
      return;
    }

    initModel();

    Boolean collapsing = null;

    ArrayList annotations = findAnnotations(getSelection());

    Iterator i = annotations.iterator();

    // int cursorOffset =
    // ((ITextSelection)editor.getSelectionProvider().getSelection()).getOffset();
    // int selectionLength =
    // ((ITextSelection)editor.getSelectionProvider().getSelection()).getLength();

    while (i.hasNext()) {
      ProjectionAnnotation annotation = (ProjectionAnnotation) i.next();

      if (collapsing == null) {
        if (annotation.isCollapsed()) {
          collapsing = new Boolean(false);
        } else {
          collapsing = new Boolean(true);
        }

      }
      if (collapsing.booleanValue()) {
        model.collapse(annotation);
      } else {
        model.expand(annotation);
      }

    }

  }

  public void expandMatchingMarkers() {

    if (!preferenceManager.enableFolding()) {
      return;
    }

    initModel();
    ArrayList annotations = findAnnotations(getSelection());

    Iterator i = annotations.iterator();
    while (i.hasNext()) {
      CFEProjectionAnnotation p = (CFEProjectionAnnotation) i.next();
      String type = p.getRegionType();
      Iterator j = model.getAnnotationIterator();
      while (j.hasNext()) {
        CFEProjectionAnnotation p1 = (CFEProjectionAnnotation) j.next();
        if (p1.getRegionType().equalsIgnoreCase(type)) {
          model.expand(p1);
        }
      }
    }

  }

  public void collapseMatchingMarkers() {

    if (!preferenceManager.enableFolding()) {
      return;
    }

    initModel();
    ArrayList annotations = findAnnotations(getSelection());

    Iterator i = annotations.iterator();
    while (i.hasNext()) {
      CFEProjectionAnnotation p = (CFEProjectionAnnotation) i.next();
      String type = p.getRegionType();
      Iterator j = model.getAnnotationIterator();
      while (j.hasNext()) {
        CFEProjectionAnnotation p1 = (CFEProjectionAnnotation) j.next();
        if (p1.getRegionType().equalsIgnoreCase(type)) {
          model.collapse(p1);
        }
      }
    }

  }

  public void collapseTags(String regionType) {

    if (!preferenceManager.enableFolding()) {
      return;
    }

    initModel();

    Iterator i = model.getAnnotationIterator();
    while (i.hasNext()) {
      Object o = i.next();
      if (o instanceof TagProjectionAnnotation
          && ((TagProjectionAnnotation) o).getRegionType().equalsIgnoreCase(regionType)) {
        model.collapse((ProjectionAnnotation) o);
      }
    }

  }

  public void expandSelection() {

    if (!preferenceManager.enableFolding()) {
      return;
    }

    initModel();

    ArrayList annotations = findAnnotations(getSelection());
    Iterator i = annotations.iterator();
    while (i.hasNext()) {
      model.expand((ProjectionAnnotation) i.next());
    }

  }

  public void collapseSelection() {

    if (!preferenceManager.enableFolding()) {
      return;
    }

    initModel();

    ArrayList annotations = findAnnotations(getSelection());
    Iterator i = annotations.iterator();
    while (i.hasNext()) {
      model.collapse((ProjectionAnnotation) i.next());
    }

  }

  /**
   * Returns a list of annotations that are either in or surrounding the
   * selected text.
   *
   * @param selection
   * @return
   */
  private ArrayList findAnnotations(ITextSelection selection) {
    ArrayList annotations = new ArrayList();

    if (!selection.isEmpty()) {
      try {
        int start = doc.getLineOffset(selection.getStartLine());
        int end = start + selection.getLength();

        // Check to see if there's anything selected. If not we want to
        // find the fold that contains the cursor
        boolean findContainer = selection.getLength() > 0 ? false : true;

        ProjectionAnnotation containingAnnotation = null;
        Position oldPosition = null;

        Iterator i = model.getAnnotationIterator();
        while (i.hasNext()) {
          ProjectionAnnotation annotation = (ProjectionAnnotation) i.next();

          Position position = model.getPosition(annotation);
          if (position.offset >= start && position.offset <= end) {
            annotations.add(annotation);
            // If there's nothing selected we can exit once we find
            // the first match
            if (findContainer) {
              containingAnnotation = null;
              break;
            }
          } else if (findContainer && position.offset < start && position.length + position.offset > end) {
            if (containingAnnotation == null) {
              containingAnnotation = annotation;
              oldPosition = position;
            } else if (position.offset > oldPosition.offset) {
              containingAnnotation = annotation;
              oldPosition = position;
            }
          }

        }
        if (containingAnnotation != null) {
          annotations.add(containingAnnotation);
          // int endOfLine = oldPosition.offset +
          // doc.getLineLength(doc.getLineOfOffset(oldPosition.offset))-2;
          TextSelection newSelection = new TextSelection(doc, oldPosition.offset, 0);
          editor.getSelectionProvider().setSelection(newSelection);
        }
      } catch (BadLocationException bex) {
        bex.printStackTrace();
      }

    }

    return annotations;
  }

  private ITextSelection getSelection() {
    ITextSelection selection = (ITextSelection) editor.getSelectionProvider().getSelection();
    ITextSelection textSelection = (ITextSelection) selection;
    return textSelection;
  }

  /*
   * private void setSelection(int offset,int length) { TextSelection
   * selection = new TextSelection(offset,length);
   * editor.getSelectionProvider().setSelection(selection);
   *
   * }
   */

  /*
   * public void storeMemento() { initModel();
   * if(!preferenceManager.enableFolding()) { return; }
   *
   * IMemento memento = ((CFMLEditor) editor).getMemento(); if(memento ==
   * null) { ((CFMLEditor) editor).saveState(memento);
   * saveStateAction.run(null); memento = ((CFMLEditor) editor).getMemento();
   * }
   *
   * snapshot = new HashMap(); ProjectionAnnotationModel model =
   * (ProjectionAnnotationModel) editor
   * .getAdapter(ProjectionAnnotationModel.class); Iterator iter =
   * model.getAnnotationIterator(); while (iter.hasNext()) {
   * ProjectionAnnotation p = (ProjectionAnnotation)iter.next();
   * snapshot.put(p,new Boolean(p.isCollapsed())); }
   * memento.createChild("CollapsedAnnotations");
   * memento.getChild("CollapsedAnnotations"
   * ).putTextData(snapshot.toString()); ((CFMLEditor)
   * editor).saveState(memento); saveStateAction.run(null);
   *
   * }
   *
   *
   * public void restoreMemento() { IMemento memento = ((CFMLEditor)
   * editor).getMemento(); if(preferenceManager.enableFolding()) { String data
   * = memento.getChild("CollapsedAnnotations").getTextData(); return; }
   *
   * }
   */

  public void persistFoldState() {
    if (preferenceManager.persistFoldState() && resource != null && !resource.isPhantom()) {     
      StringBuffer sb = new StringBuffer();
      Iterator iter = model.getAnnotationIterator();
      int positions = 0;
      while (iter.hasNext()) {
        positions++;
        ProjectionAnnotation annotation = (ProjectionAnnotation) iter.next();
        Position position = model.getPosition(annotation);
        sb.append(position.offset + "," + position.length + ",");
        sb.append((annotation.isCollapsed())?1:0);
        sb.append("\n");
      }
      if (positions < 300) {
        try {
          if (resource.isAccessible()) {
            resource.setPersistentProperty(foldStateQN, sb.toString());
          }
        } catch (CoreException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }
  }

  public void loadSnapshotFromProperty() {
    String foldStates = "";
    storedFolds = new ArrayList();
    try {
      foldStates = resource.getPersistentProperty(foldStateQN);
    } catch (CoreException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    if (!preferenceManager.enableFolding() || foldStates == null) {
      return;
    }
    String[] values = foldStates.split("\n");
    for (int i = 0; i < values.length; i++) {
        String[] split = values[i].split(",");
      if(split.length == 3) {
        storedFolds.add(new StoredFold(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])));
      }
    }
  }

  public void restoreFoldState() {
    if(resource != null) {
      loadSnapshotFromProperty();
      if (this.storedFolds != null) {
        Iterator iter;
        HashMap aHashMap = new HashMap();
        for (StoredFold fold : storedFolds) {
          iter = model.getAnnotationIterator(fold.offset, fold.length, false, false);
          if (iter.hasNext()) {
            ProjectionAnnotation p = (ProjectionAnnotation) iter.next();
            if (fold.collapsed!=0) {
              model.collapse(p);
            } else {
              model.expand(p);
            }
          } else {
            try {
              addFoldingMark(aHashMap, fold.offset, fold.length, new CustomProjectionAnnotation(fold.collapsed!=0),
                  fold.collapsed!=0);
            } catch (BadLocationException e) {
              e.printStackTrace();
            }
          }
        }
      }
    }
  }

  private void initModel() {
    if (model == null) {
      model = (ProjectionAnnotationModel) editor.getAdapter(ProjectionAnnotationModel.class);
    }
  }

  /*
   * cheese class for iterating;
   */
  private class StoredFold {
    public StoredFold(int offset, int length, int i) {
      super();
      this.offset = offset;
      this.length = length;
      this.collapsed = i;
    }

    public int offset;
    public int length;
    public int collapsed;
  }

}
TOP

Related Classes of org.cfeclipse.cfml.editors.codefolding.CodeFoldingSetter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.