intial source from http://www.sf.net/projects/wdte
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpeclipse / ui / text / rules / MultiViewPartitioner.java
diff --git a/net.sourceforge.phpeclipse.ui/src/net/sourceforge/phpeclipse/ui/text/rules/MultiViewPartitioner.java b/net.sourceforge.phpeclipse.ui/src/net/sourceforge/phpeclipse/ui/text/rules/MultiViewPartitioner.java
new file mode 100644 (file)
index 0000000..c5aa8ec
--- /dev/null
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 2002-2004 Widespace, OU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://solareclipse.sourceforge.net/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     Igor Malinin - initial contribution
+ * 
+ * $Id: MultiViewPartitioner.java,v 1.1 2004-09-02 18:26:29 jsurfer Exp $
+ */
+
+package net.sourceforge.phpeclipse.ui.text.rules;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.IDocumentPartitioningListener;
+import org.eclipse.jface.text.IDocumentPartitioningListenerExtension;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.TypedRegion;
+import org.eclipse.jface.text.rules.IPartitionTokenScanner;
+
+/**
+ * Advanced partitioner which maintains partitions as views to connected document. Views have own partitioners themselves. This
+ * class is designed as a base for complex partitioners such as for JSP, PHP, ASP, etc. languages.
+ * 
+ * @author Igor Malinin
+ */
+public abstract class MultiViewPartitioner extends AbstractPartitioner {
+
+  class ViewListener implements IDocumentPartitioningListener, IDocumentPartitioningListenerExtension {
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitioningListener#documentPartitioningChanged(IDocument)
+     */
+    public void documentPartitioningChanged(IDocument document) {
+      IDocumentView view = (IDocumentView) document;
+
+      int start = view.getParentOffset(0);
+      int end = view.getParentOffset(view.getLength());
+
+      rememberRegion(start, end - start);
+    }
+
+    /*
+     * @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument, IRegion)
+     */
+    public void documentPartitioningChanged(IDocument document, IRegion region) {
+      IDocumentView view = (IDocumentView) document;
+
+      int offset = region.getOffset();
+
+      int start = view.getParentOffset(offset);
+      int end = view.getParentOffset(offset + region.getLength());
+
+      rememberRegion(start, end - start);
+    }
+  }
+
+  private ViewListener viewListener = new ViewListener();
+
+  private OuterDocumentView outerDocument;
+
+  private DocumentEvent outerDocumentEvent;
+
+  public MultiViewPartitioner(IPartitionTokenScanner scanner) {
+    super(scanner);
+  }
+
+  public void setOuterPartitioner(IDocumentPartitioner partitioner) {
+    if (outerDocument == null) {
+      if (partitioner == null) {
+        return;
+      }
+
+      outerDocument = new OuterDocumentView(document, nodes);
+      outerDocument.addDocumentPartitioningListener(viewListener);
+    }
+
+    IDocumentPartitioner old = outerDocument.getDocumentPartitioner();
+    if (old != null) {
+      outerDocument.setDocumentPartitioner(null);
+      old.disconnect();
+    }
+
+    if (partitioner != null) {
+      partitioner.connect(outerDocument);
+    }
+
+    outerDocument.setDocumentPartitioner(partitioner);
+
+    if (partitioner == null) {
+      outerDocument.removeDocumentPartitioningListener(viewListener);
+      outerDocument = null;
+    }
+  }
+
+  /**
+   * Create subpartitioner.
+   * 
+   * @param contentType
+   *          name of inner partition or <code>null</code> for outer partition
+   */
+  protected abstract IDocumentPartitioner createPartitioner(String contentType);
+
+  protected void addInnerRegion(FlatNode position) {
+    if (outerDocument != null) {
+      if (DEBUG) {
+        Assert.isTrue(position.offset >= 0, Integer.toString(position.offset));
+      }
+      DocumentEvent event = new DocumentEvent(outerDocument, outerDocument.getLocalOffset(position.offset), position.length, null);
+
+      outerDocument.fireDocumentAboutToBeChanged(event);
+      super.addInnerRegion(position);
+      outerDocument.fireDocumentChanged(event);
+    } else {
+      super.addInnerRegion(position);
+    }
+
+    if (position instanceof ViewNode) {
+      // TODO: revisit condition
+      IDocumentPartitioner partitioner = createPartitioner(position.type);
+      if (partitioner != null) {
+        InnerDocumentView innerDocument = new InnerDocumentView(document, (ViewNode) position);
+
+        ((ViewNode) position).view = innerDocument;
+
+        partitioner.connect(innerDocument);
+        innerDocument.setDocumentPartitioner(partitioner);
+        innerDocument.addDocumentPartitioningListener(viewListener);
+      }
+    }
+  }
+
+  protected void removeInnerRegion(FlatNode position) {
+    try {
+      if (outerDocument != null) {
+        DocumentEvent event = null;
+        if (position.offset >= 0) {
+          event = new DocumentEvent(outerDocument, outerDocument.getLocalOffset(position.offset), 0, document.get(position.offset,
+              position.length));
+
+          outerDocument.fireDocumentAboutToBeChanged(event);
+        }
+        super.removeInnerRegion(position);
+        if (position.offset >= 0) {
+          outerDocument.fireDocumentChanged(event);
+        }
+      } else {
+        super.removeInnerRegion(position);
+      }
+
+      if (position instanceof ViewNode) {
+        // TODO: revisit condition
+        InnerDocumentView innerDocument = ((ViewNode) position).view;
+        if (innerDocument != null) {
+          IDocumentPartitioner partitioner = innerDocument.getDocumentPartitioner();
+
+          innerDocument.removeDocumentPartitioningListener(viewListener);
+          innerDocument.setDocumentPartitioner(null);
+          partitioner.disconnect();
+        }
+      }
+    } catch (BadLocationException e) {
+    }
+  }
+
+  protected void deleteInnerRegion(FlatNode position) {
+    super.deleteInnerRegion(position);
+
+    if (position instanceof ViewNode) {
+      // TODO: revisit condition
+      InnerDocumentView innerDocument = ((ViewNode) position).view;
+      if (innerDocument != null) {
+        IDocumentPartitioner partitioner = innerDocument.getDocumentPartitioner();
+
+        innerDocument.removeDocumentPartitioningListener(viewListener);
+        innerDocument.setDocumentPartitioner(null);
+        partitioner.disconnect();
+      }
+    }
+  }
+
+  public void connect(IDocument document) {
+    //         outerDocument = new OuterDocumentView(document, innerPositions);
+
+    super.connect(document);
+
+    setOuterPartitioner(createPartitioner(null));
+    //         IDocumentPartitioner partitioner =
+    //                 partitioner.connect(outerDocument);
+    //         outerDocument.setDocumentPartitioner(partitioner);
+    //         outerDocument.addDocumentPartitioningListener(viewListener);
+  }
+
+  public void disconnect() {
+    try {
+      if (outerDocument != null) {
+        outerDocument.removeDocumentPartitioningListener(viewListener);
+
+        IDocumentPartitioner partitioner = outerDocument.getDocumentPartitioner();
+
+        outerDocument.setDocumentPartitioner(null);
+        partitioner.disconnect();
+      }
+    } finally {
+      // TODO: cleanup listeners
+      outerDocument = null;
+    }
+  }
+
+  /*
+   * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
+   */
+  public void documentAboutToBeChanged(DocumentEvent event) {
+    super.documentAboutToBeChanged(event);
+
+    outerDocumentEvent = null;
+
+    int offset = event.getOffset();
+    int length = event.getLength();
+    int end = offset + length;
+
+    // find left partition
+    int first = computeFlatNodeIndex(offset);
+    if (first > 0) {
+      FlatNode p = (FlatNode) nodes.get(first - 1);
+
+      int right = p.offset + p.length;
+      if (offset < right) {
+        // change overlaps with partition
+        InnerDocumentView innerDocument = null;
+        if (p instanceof ViewNode) {
+          // TODO: revisit condition
+          innerDocument = ((ViewNode) p).view;
+        }
+
+        if (end < right) {
+          if (innerDocument != null) {
+            // cahnge completely inside partition
+            int start = innerDocument.getLocalOffset(offset);
+            innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, start, length, event.getText()));
+          }
+
+          return;
+        }
+
+        if (innerDocument != null) {
+          // cut partition at right
+          int start = innerDocument.getLocalOffset(offset);
+          innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, start, innerDocument.getLength() - start,
+              null));
+        }
+      }
+    }
+
+    // find right partition
+    int last = computeFlatNodeIndex(end);
+    if (last > 0) {
+      FlatNode p = (FlatNode) nodes.get(last - 1);
+
+      if (p instanceof ViewNode) {
+        // TODO: revisit condition
+        InnerDocumentView innerDocument = ((ViewNode) p).view;
+        if (innerDocument != null) {
+          int right = p.offset + p.length;
+          if (end < right) {
+            // cut partition at left
+            int cut = innerDocument.getLocalOffset(end);
+            innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, 0, cut, null));
+          }
+        }
+      }
+    }
+
+    if (outerDocument != null) {
+      int left = outerDocument.getLocalOffset(offset);
+      int right = outerDocument.getLocalOffset(end);
+
+      String text = event.getText();
+
+      if (left != right || text != null && text.length() > 0) {
+        outerDocumentEvent = new DocumentEvent(outerDocument, left, right - left, text);
+
+        outerDocument.fireDocumentAboutToBeChanged(outerDocumentEvent);
+      }
+    }
+  }
+
+  protected int fixupPartitions(DocumentEvent event) {
+    int offset = event.getOffset();
+    int length = event.getLength();
+    int end = offset + length;
+
+    // fixup/notify inner views laying on change boundaries
+
+    int first = computeFlatNodeIndex(offset);
+    if (first > 0) {
+      FlatNode p = (FlatNode) nodes.get(first - 1);
+
+      int right = p.offset + p.length;
+      if (offset < right) {
+        // change overlaps with partition
+        if (end < right) {
+          // cahnge completely inside partition
+          String text = event.getText();
+          p.length -= length;
+          if (text != null) {
+            p.length += text.length();
+          }
+
+          if (p instanceof ViewNode) {
+            // TODO: revisit condition
+            InnerDocumentView innerDocument = ((ViewNode) p).view;
+            if (innerDocument != null) {
+              int start = innerDocument.getLocalOffset(offset);
+              innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, start, length, text));
+            }
+          }
+        } else {
+          // cut partition at right
+          int cut = p.offset + p.length - offset;
+          p.length -= cut;
+
+          if (p instanceof ViewNode) {
+            // TODO: revisit condition
+            InnerDocumentView innerDocument = ((ViewNode) p).view;
+            if (innerDocument != null) {
+              int start = innerDocument.getLocalOffset(offset);
+              // TODO: ???fireDocumentAboutToBeChanged???
+              innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, start, cut, null));
+            }
+          }
+        }
+      }
+    }
+
+    int last = computeFlatNodeIndex(end);
+    if (last > 0 && first != last) {
+      FlatNode p = (FlatNode) nodes.get(last - 1);
+
+      int right = p.offset + p.length;
+      if (end < right) {
+        // cut partition at left
+        int cut = end - p.offset;
+        p.length -= cut;
+        p.offset = offset;
+
+        String text = event.getText();
+        if (text != null) {
+          p.offset += text.length();
+        }
+
+        if (p instanceof ViewNode) {
+          // TODO: revisit condition
+          InnerDocumentView innerDocument = ((ViewNode) p).view;
+          if (innerDocument != null) {
+            // TODO: ???fireDocumentAboutToBeChanged???
+            innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, 0, cut, null));
+          }
+        }
+
+        --last;
+      }
+    }
+
+    // fixup inner views laying afrer change
+
+    String text = event.getText();
+    if (text != null) {
+      length -= text.length();
+    }
+
+    for (int i = last, size = nodes.size(); i < size; i++) {
+      ((FlatNode) nodes.get(i)).offset -= length;
+    }
+
+    // delete inner views laying completely inside change boundaries
+
+    if (first < last) {
+      do {
+        deleteInnerRegion((FlatNode) nodes.get(--last));
+      } while (first < last);
+
+      rememberRegion(offset, 0);
+    }
+
+    // notify outer view
+
+    if (outerDocumentEvent != null) {
+      outerDocument.fireDocumentChanged(outerDocumentEvent);
+    }
+
+    return first;
+  }
+
+  /*
+   * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
+   */
+  protected String getContentType(String parent, String view) {
+    if (view != null) {
+      return view;
+    }
+
+    if (parent != null) {
+      return parent;
+    }
+
+    return IDocument.DEFAULT_CONTENT_TYPE;
+  }
+
+  /*
+   * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
+   */
+  public ITypedRegion[] computePartitioning(int offset, int length) {
+    List list = new ArrayList();
+
+    int end = offset + length;
+
+    int index = computeFlatNodeIndex(offset);
+    while (true) {
+      FlatNode prev = (index > 0) ? (FlatNode) nodes.get(index - 1) : null;
+
+      if (prev != null) {
+        if (prev.overlapsWith(offset, length)) {
+          addInnerPartitions(list, offset, length, prev);
+        }
+
+        if (end <= prev.offset + prev.length) {
+          break;
+        }
+      }
+
+      FlatNode next = (index < nodes.size()) ? (FlatNode) nodes.get(index) : null;
+
+      if (next == null || offset < next.offset) {
+        addOuterPartitions(list, offset, length, prev, next);
+      }
+
+      if (next == null) {
+        break;
+      }
+
+      ++index;
+    }
+
+    return (TypedRegion[]) list.toArray(new TypedRegion[list.size()]);
+  }
+
+  private void addOuterPartitions(List list, int offset, int length, FlatNode prev, FlatNode next) {
+    // limit region
+    int start = offset;
+    int end = offset + length;
+
+    if (prev != null && start < prev.offset + prev.length) {
+      start = prev.offset + prev.length;
+    }
+
+    if (next != null && next.offset < end) {
+      end = next.offset;
+    }
+
+    if (start == end) {
+      return;
+    }
+
+    if (outerDocument == null) {
+      list.add(new TypedRegion(start, end - start, getContentType(null, IDocument.DEFAULT_CONTENT_TYPE)));
+      return;
+    }
+
+    try {
+      // convert to outer offsets
+      start = outerDocument.getLocalOffset(start);
+      end = outerDocument.getLocalOffset(end);
+      if (end - start >= 0) {//jsurfer insert line
+        ITypedRegion[] regions = outerDocument.computePartitioning(start, end - start);
+
+        for (int i = 0; i < regions.length; i++) {
+          ITypedRegion region = regions[i];
+
+          // convert back to parent offsets
+          start = outerDocument.getParentOffset(region.getOffset());
+          end = start + region.getLength();
+
+          if (prev != null) {
+            offset = prev.offset + prev.length;
+            if (start < offset) {
+              start = offset;
+            }
+          }
+
+          if (next != null) {
+            offset = next.offset;
+            if (offset < end) {
+              end = offset;
+            }
+          }
+
+          list.add(new TypedRegion(start, end - start, getContentType(null, region.getType())));
+        }
+      }
+    } catch (BadLocationException x) {
+    }
+  }
+
+  private void addInnerPartitions(List list, int offset, int length, FlatNode position) {
+    InnerDocumentView innerDocument = null;
+    if (position instanceof ViewNode) {
+      // TODO: revisit condition
+      innerDocument = ((ViewNode) position).view;
+    }
+
+    if (innerDocument == null) {
+      // simple partition
+      list.add(new TypedRegion(position.offset, position.length, getContentType(position.type, null)));
+      return;
+    }
+
+    // multiplexing to inner view
+    try {
+      // limit region
+      int start = Math.max(offset, position.offset);
+      int end = Math.min(offset + length, position.offset + position.length);
+
+      // convert to document offsets
+      length = end - start;
+      offset = innerDocument.getLocalOffset(start);
+
+      ITypedRegion[] regions = innerDocument.computePartitioning(offset, length);
+
+      for (int i = 0; i < regions.length; i++) {
+        ITypedRegion region = regions[i];
+
+        // convert back to parent offsets
+        offset = innerDocument.getParentOffset(region.getOffset());
+        length = region.getLength();
+
+        list.add(new TypedRegion(offset, length, getContentType(position.type, region.getType())));
+      }
+    } catch (BadLocationException x) {
+    }
+  }
+
+  /*
+   * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
+   */
+  public ITypedRegion getPartition(int offset) {
+    if (nodes.size() == 0) {
+      return getOuterPartition(offset, null, null);
+    }
+
+    int index = computeFlatNodeIndex(offset);
+    if (index < nodes.size()) {
+      FlatNode next = (FlatNode) nodes.get(index);
+
+      if (offset == next.offset) {
+        return getInnerPartition(offset, next);
+      }
+
+      if (index == 0) {
+        return getOuterPartition(offset, null, next);
+      }
+
+      FlatNode prev = (FlatNode) nodes.get(index - 1);
+
+      if (prev.includes(offset)) {
+        return getInnerPartition(offset, prev);
+      }
+
+      return getOuterPartition(offset, prev, next);
+    }
+
+    FlatNode prev = (FlatNode) nodes.get(nodes.size() - 1);
+
+    if (prev.includes(offset)) {
+      return getInnerPartition(offset, prev);
+    }
+
+    return getOuterPartition(offset, prev, null);
+  }
+
+  protected ITypedRegion getOuterPartition(int offset, FlatNode prev, FlatNode next) {
+    try {
+      int start, end;
+      String type;
+
+      if (outerDocument == null) {
+        start = 0;
+        end = document.getLength();
+        type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
+      } else {
+        ITypedRegion region = outerDocument.getPartition(outerDocument.getLocalOffset(offset));
+
+        start = region.getOffset();
+        end = start + region.getLength();
+
+        // convert to parent offset
+        start = outerDocument.getParentOffset(start);
+        end = outerDocument.getParentOffset(end);
+
+        type = getContentType(null, region.getType());
+      }
+
+      if (prev != null) {
+        offset = prev.offset + prev.length;
+        if (start < offset) {
+          start = offset;
+        }
+      }
+
+      if (next != null) {
+        offset = next.offset;
+        if (offset < end) {
+          end = offset;
+        }
+      }
+
+      return new TypedRegion(start, end - start, type);
+    } catch (BadLocationException x) {
+      throw new IllegalArgumentException();
+    }
+  }
+
+  protected ITypedRegion getInnerPartition(int offset, FlatNode position) {
+    if (position instanceof ViewNode) {
+      // TODO: revisit condition
+      InnerDocumentView innerDocument = ((ViewNode) position).view;
+
+      if (innerDocument != null) {
+        // multiplexing to inner view
+        try {
+          // convert to inner offset
+          ITypedRegion region = innerDocument.getPartition(innerDocument.getLocalOffset(offset));
+
+          // convert to parent offset
+          offset = innerDocument.getParentOffset(region.getOffset());
+
+          return new TypedRegion(offset, region.getLength(), getContentType(position.type, region.getType()));
+        } catch (BadLocationException x) {
+        }
+      }
+    }
+
+    // simple partition
+    return new TypedRegion(position.offset, position.length, position.type);
+  }
+}
\ No newline at end of file