2 * Copyright (c) 2002-2004 Widespace, OU and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://solareclipse.sourceforge.net/legal/cpl-v10.html
9 * Igor Malinin - initial contribution
11 * $Id: MultiViewPartitioner.java,v 1.10 2006-10-21 23:13:53 pombredanne Exp $
14 package net.sourceforge.phpeclipse.ui.text.rules;
16 import java.util.ArrayList;
17 import java.util.List;
20 //import org.eclipse.jface.text.Assert;
21 import org.eclipse.core.runtime.Assert;
22 import org.eclipse.jface.text.BadLocationException;
23 import org.eclipse.jface.text.DocumentEvent;
24 import org.eclipse.jface.text.IDocument;
25 import org.eclipse.jface.text.IDocumentPartitioner;
26 import org.eclipse.jface.text.IDocumentPartitioningListener;
27 import org.eclipse.jface.text.IDocumentPartitioningListenerExtension;
28 import org.eclipse.jface.text.IRegion;
29 import org.eclipse.jface.text.ITypedRegion;
30 import org.eclipse.jface.text.TypedRegion;
31 import org.eclipse.jface.text.rules.IPartitionTokenScanner;
34 * Advanced partitioner which maintains partitions as views to connected
35 * document. Views have own partitioners themselves. This class is designed as a
36 * base for complex partitioners such as for JSP, PHP, ASP, etc. languages.
38 * @author Igor Malinin
40 public abstract class MultiViewPartitioner extends AbstractPartitioner {
42 class ViewListener implements IDocumentPartitioningListener,
43 IDocumentPartitioningListenerExtension {
46 * @see org.eclipse.jface.text.IDocumentPartitioningListener#documentPartitioningChanged(IDocument)
48 public void documentPartitioningChanged(IDocument document) {
49 IDocumentView view = (IDocumentView) document;
51 int start = view.getParentOffset(0);
52 int end = view.getParentOffset(view.getLength());
54 rememberRegion(start, end - start);
58 * @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument,
61 public void documentPartitioningChanged(IDocument document,
63 IDocumentView view = (IDocumentView) document;
65 int offset = region.getOffset();
67 int start = view.getParentOffset(offset);
68 int end = view.getParentOffset(offset + region.getLength());
70 rememberRegion(start, end - start);
74 private ViewListener viewListener = new ViewListener();
76 private OuterDocumentView outerDocument;
78 private DocumentEvent outerDocumentEvent;
80 public MultiViewPartitioner(IPartitionTokenScanner scanner) {
84 public void setOuterPartitioner(IDocumentPartitioner partitioner) {
85 if (outerDocument == null) {
86 if (partitioner == null) {
90 outerDocument = new OuterDocumentView(document, nodes);
91 outerDocument.addDocumentPartitioningListener(viewListener);
94 IDocumentPartitioner old = outerDocument.getDocumentPartitioner();
96 outerDocument.setDocumentPartitioner(null);
100 if (partitioner != null) {
101 partitioner.connect(outerDocument);
104 outerDocument.setDocumentPartitioner(partitioner);
106 if (partitioner == null) {
107 outerDocument.removeDocumentPartitioningListener(viewListener);
108 outerDocument = null;
113 * Create subpartitioner.
116 * name of inner partition or <code>null</code> for outer
119 protected abstract IDocumentPartitioner createPartitioner(String contentType);
121 protected void addInnerRegion(FlatNode position) {
122 if (outerDocument != null) {
124 Assert.isTrue(position.offset >= 0, Integer
125 .toString(position.offset));
127 int outerOffset = outerDocument.getLocalOffset(position.offset);
129 DocumentEvent event = null;
130 if (outerOffset >= 0) {
132 event = new DocumentEvent(outerDocument, outerOffset,
133 position.length, null);
135 outerDocument.fireDocumentAboutToBeChanged(event);
137 super.addInnerRegion(position);
141 outerDocument.fireDocumentChanged(event);
144 super.addInnerRegion(position);
147 if (position instanceof ViewNode) {
148 // TODO: revisit condition
149 IDocumentPartitioner partitioner = createPartitioner(position.type);
150 if (partitioner != null) {
151 InnerDocumentView innerDocument = new InnerDocumentView(
152 document, (ViewNode) position);
154 ((ViewNode) position).view = innerDocument;
156 partitioner.connect(innerDocument);
157 innerDocument.setDocumentPartitioner(partitioner);
158 innerDocument.addDocumentPartitioningListener(viewListener);
163 protected void removeInnerRegion(FlatNode position) {
165 if (outerDocument != null) {
166 DocumentEvent event = null;
167 if (position.offset >= 0 && position.length >= 0) {
168 int outerOffset = outerDocument
169 .getLocalOffset(position.offset);
170 if (outerOffset > 0) {
171 event = new DocumentEvent(outerDocument, outerOffset,
172 0, document.get(position.offset,
175 outerDocument.fireDocumentAboutToBeChanged(event);
178 super.removeInnerRegion(position);
179 if (position.offset >= 0) {
181 outerDocument.fireDocumentChanged(event);
185 super.removeInnerRegion(position);
188 if (position instanceof ViewNode) {
189 // TODO: revisit condition
190 InnerDocumentView innerDocument = ((ViewNode) position).view;
191 if (innerDocument != null) {
192 IDocumentPartitioner partitioner = innerDocument
193 .getDocumentPartitioner();
196 .removeDocumentPartitioningListener(viewListener);
197 innerDocument.setDocumentPartitioner(null);
198 partitioner.disconnect();
201 } catch (BadLocationException e) {
205 protected void deleteInnerRegion(FlatNode position) {
206 super.deleteInnerRegion(position);
208 if (position instanceof ViewNode) {
209 // TODO: revisit condition
210 InnerDocumentView innerDocument = ((ViewNode) position).view;
211 if (innerDocument != null) {
212 IDocumentPartitioner partitioner = innerDocument
213 .getDocumentPartitioner();
215 innerDocument.removeDocumentPartitioningListener(viewListener);
216 innerDocument.setDocumentPartitioner(null);
217 partitioner.disconnect();
222 public void connect(IDocument document) {
223 // outerDocument = new OuterDocumentView(document, innerPositions);
225 super.connect(document);
227 setOuterPartitioner(createPartitioner(null));
228 // IDocumentPartitioner partitioner =
229 // partitioner.connect(outerDocument);
230 // outerDocument.setDocumentPartitioner(partitioner);
231 // outerDocument.addDocumentPartitioningListener(viewListener);
234 public void disconnect() {
236 if (outerDocument != null) {
237 outerDocument.removeDocumentPartitioningListener(viewListener);
239 IDocumentPartitioner partitioner = outerDocument
240 .getDocumentPartitioner();
242 outerDocument.setDocumentPartitioner(null);
243 partitioner.disconnect();
246 // TODO: cleanup listeners
247 outerDocument = null;
252 * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
254 public void documentAboutToBeChanged(DocumentEvent event) {
255 super.documentAboutToBeChanged(event);
257 outerDocumentEvent = null;
259 int offset = event.getOffset();
260 int length = event.getLength();
261 int end = offset + length;
263 // find left partition
264 int first = computeFlatNodeIndex(offset);
266 FlatNode p = (FlatNode) nodes.get(first - 1);
268 int right = p.offset + p.length;
269 if (offset < right) {
270 // change overlaps with partition
271 InnerDocumentView innerDocument = null;
272 if (p instanceof ViewNode) {
273 // TODO: revisit condition
274 innerDocument = ((ViewNode) p).view;
278 if (innerDocument != null) {
279 // cahnge completely inside partition
280 int start = innerDocument.getLocalOffset(offset);
282 .fireDocumentAboutToBeChanged(new DocumentEvent(
283 innerDocument, start, length, event
290 if (innerDocument != null) {
291 // cut partition at right
292 int start = innerDocument.getLocalOffset(offset);
294 .fireDocumentAboutToBeChanged(new DocumentEvent(
295 innerDocument, start, innerDocument
302 // find right partition
303 int last = computeFlatNodeIndex(end);
305 FlatNode p = (FlatNode) nodes.get(last - 1);
307 if (p instanceof ViewNode) {
308 // TODO: revisit condition
309 InnerDocumentView innerDocument = ((ViewNode) p).view;
310 if (innerDocument != null) {
311 int right = p.offset + p.length;
313 // cut partition at left
314 int cut = innerDocument.getLocalOffset(end);
316 .fireDocumentAboutToBeChanged(new DocumentEvent(
317 innerDocument, 0, cut, null));
323 if (outerDocument != null) {
324 int left = outerDocument.getLocalOffset(offset);
325 int right = outerDocument.getLocalOffset(end);
327 String text = event.getText();
329 if (left >= 0 && (right - left >= 0)) {
330 if (left != right || text != null && text.length() > 0) {
331 outerDocumentEvent = new DocumentEvent(outerDocument, left,
335 .fireDocumentAboutToBeChanged(outerDocumentEvent);
341 protected int fixupPartitions(DocumentEvent event) {
342 int offset = event.getOffset();
343 int length = event.getLength();
344 int end = offset + length;
346 // fixup/notify inner views laying on change boundaries
348 int first = computeFlatNodeIndex(offset);
350 FlatNode p = (FlatNode) nodes.get(first - 1);
352 int right = p.offset + p.length;
353 if (offset < right) {
354 // change overlaps with partition
356 // cahnge completely inside partition
357 String text = event.getText();
360 p.length += text.length();
363 if (p instanceof ViewNode) {
364 // TODO: revisit condition
365 InnerDocumentView innerDocument = ((ViewNode) p).view;
366 if (innerDocument != null) {
367 int start = innerDocument.getLocalOffset(offset);
369 .fireDocumentChanged(new DocumentEvent(
370 innerDocument, start, length, text));
374 // cut partition at right
375 int cut = p.offset + p.length - offset;
378 if (p instanceof ViewNode) {
379 // TODO: revisit condition
380 InnerDocumentView innerDocument = ((ViewNode) p).view;
381 if (innerDocument != null) {
382 int start = innerDocument.getLocalOffset(offset);
383 // TODO: ???fireDocumentAboutToBeChanged???
385 .fireDocumentChanged(new DocumentEvent(
386 innerDocument, start, cut, null));
393 int last = computeFlatNodeIndex(end);
394 if (last > 0 && first != last) {
395 FlatNode p = (FlatNode) nodes.get(last - 1);
397 int right = p.offset + p.length;
399 // cut partition at left
400 int cut = end - p.offset;
404 String text = event.getText();
406 p.offset += text.length();
409 if (p instanceof ViewNode) {
410 // TODO: revisit condition
411 InnerDocumentView innerDocument = ((ViewNode) p).view;
412 if (innerDocument != null) {
413 // TODO: ???fireDocumentAboutToBeChanged???
414 innerDocument.fireDocumentChanged(new DocumentEvent(
415 innerDocument, 0, cut, null));
423 // fixup inner views laying afrer change
425 String text = event.getText();
427 length -= text.length();
430 for (int i = last, size = nodes.size(); i < size; i++) {
431 ((FlatNode) nodes.get(i)).offset -= length;
434 // delete inner views laying completely inside change boundaries
438 deleteInnerRegion((FlatNode) nodes.get(--last));
439 } while (first < last);
441 rememberRegion(offset, 0);
446 if (outerDocumentEvent != null) {
447 outerDocument.fireDocumentChanged(outerDocumentEvent);
454 * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int,
457 protected String getContentType(String parent, String view) {
462 if (parent != null) {
466 return IDocument.DEFAULT_CONTENT_TYPE;
470 * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int,
473 public ITypedRegion[] computePartitioning(int offset, int length) {
474 List list = new ArrayList();
476 // if (length>=9400) {
479 // System.out.print("MultiViewPartitioner::computePartitioning - Offset:
481 // System.out.print(offset);
482 // System.out.print(", Length: ");
483 // System.out.print(length);
484 // System.out.println("");
486 int end = offset + length;
488 int index = computeFlatNodeIndex(offset);
490 FlatNode prev = (index > 0) ? (FlatNode) nodes.get(index - 1)
494 if (prev.overlapsWith(offset, length)) {
495 addInnerPartitions(list, offset, length, prev);
498 if (end <= prev.offset + prev.length) {
503 FlatNode next = (index < nodes.size()) ? (FlatNode) nodes
506 if (next == null || offset < next.offset) {
507 addOuterPartitions(list, offset, length, prev, next);
520 return (TypedRegion[]) list.toArray(new TypedRegion[list.size()]);
523 private void showList(List list) {
525 throw new NullPointerException();
526 } catch (Exception e) {
529 System.out.println(">>>>>List start");
531 for (int i = 0; i < list.size(); i++) {
532 temp = (TypedRegion) list.get(i);
533 System.out.print("Offset: ");
534 System.out.print(temp.getOffset());
535 System.out.print(", Length: ");
536 System.out.print(temp.getLength());
537 System.out.print(", Type: ");
538 System.out.print(temp.getType());
539 System.out.println("");
541 System.out.println("<<<<<List end");
544 private void addOuterPartitions(List list, int offset, int length,
545 FlatNode prev, FlatNode next) {
548 int end = offset + length;
550 if (prev != null && start < prev.offset + prev.length) {
551 start = prev.offset + prev.length;
554 if (next != null && next.offset < end) {
562 if (outerDocument == null) {
564 // if (end - start<0) {
565 // throw new IndexOutOfBoundsException();
568 list.add(new TypedRegion(start, end - start, getContentType(null,
569 IDocument.DEFAULT_CONTENT_TYPE)));
574 // convert to outer offsets
575 start = outerDocument.getLocalOffset(start);
576 end = outerDocument.getLocalOffset(end);
577 int len = end - start;
579 ITypedRegion[] regions = null;
581 regions = outerDocument.computePartitioning(start, len);
582 } catch (Exception e) {
583 // nasty workaround, which prevents cursor from moveing
584 // backwards in the editor
585 // but doesn't solve the partitioning problem
586 regions = new ITypedRegion[0];
588 .println("MultiViewerPartitioner#addOuterPartitions failure\n"
593 + "\n" + outerDocument.get(start, len));
595 for (int i = 0; i < regions.length; i++) {
596 ITypedRegion region = regions[i];
598 // convert back to parent offsets
599 start = outerDocument.getParentOffset(region.getOffset());
600 end = start + region.getLength();
603 offset = prev.offset + prev.length;
604 if (start < offset) {
610 offset = next.offset;
616 // if (end - start<0) {
618 // System.out.print("MultiViewPartitioner::addOuterPartitions
620 // System.out.print(offset);
621 // System.out.print(", Start: ");
622 // System.out.print(start);
623 // System.out.print(", End: ");
624 // System.out.print(end);
625 // System.out.print(", Type: ");
626 // System.out.print(region.getType());
627 // System.out.println("");
628 // throw new IndexOutOfBoundsException();
631 list.add(new TypedRegion(start, end - start,
632 getContentType(null, region.getType())));
635 } catch (BadLocationException x) {
639 private void addInnerPartitions(List list, int offset, int length,
641 InnerDocumentView innerDocument = null;
642 if (position instanceof ViewNode) {
643 // TODO: revisit condition
644 innerDocument = ((ViewNode) position).view;
647 if (innerDocument == null) {
650 // if (position.length<0) {
651 // throw new IndexOutOfBoundsException();
654 list.add(new TypedRegion(position.offset, position.length,
655 getContentType(position.type, null)));
659 // multiplexing to inner view
662 int start = Math.max(offset, position.offset);
663 int end = Math.min(offset + length, position.offset
666 // convert to document offsets
667 length = end - start;
668 offset = innerDocument.getLocalOffset(start);
670 ITypedRegion[] regions = innerDocument.computePartitioning(offset,
673 for (int i = 0; i < regions.length; i++) {
674 ITypedRegion region = regions[i];
676 // convert back to parent offsets
677 offset = innerDocument.getParentOffset(region.getOffset());
678 length = region.getLength();
681 // throw new IndexOutOfBoundsException();
684 list.add(new TypedRegion(offset, length, getContentType(
685 position.type, region.getType())));
687 } catch (BadLocationException x) {
692 * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
694 public ITypedRegion getPartition(int offset) {
695 if (nodes.size() == 0) {
696 return getOuterPartition(offset, null, null);
699 int index = computeFlatNodeIndex(offset);
700 if (index < nodes.size()) {
701 FlatNode next = (FlatNode) nodes.get(index);
703 if (offset == next.offset) {
704 return getInnerPartition(offset, next);
708 return getOuterPartition(offset, null, next);
711 FlatNode prev = (FlatNode) nodes.get(index - 1);
713 if (prev.includes(offset)) {
714 return getInnerPartition(offset, prev);
717 return getOuterPartition(offset, prev, next);
720 FlatNode prev = (FlatNode) nodes.get(nodes.size() - 1);
722 if (prev.includes(offset)) {
723 return getInnerPartition(offset, prev);
726 return getOuterPartition(offset, prev, null);
729 protected ITypedRegion getOuterPartition(int offset, FlatNode prev,
735 if (outerDocument == null) {
737 end = document.getLength();
738 type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
740 int outerOffset = outerDocument.getLocalOffset(offset);
742 if (outerOffset < 0) {
744 end = document.getLength();
745 type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
748 ITypedRegion region = outerDocument
749 .getPartition(outerOffset);
751 start = region.getOffset();
752 end = start + region.getLength();
754 // convert to parent offset
755 start = outerDocument.getParentOffset(start);
756 end = outerDocument.getParentOffset(end);
758 type = getContentType(null, region.getType());
763 offset = prev.offset + prev.length;
764 if (start < offset) {
770 offset = next.offset;
776 return new TypedRegion(start, end - start, type);
777 } catch (BadLocationException x) {
779 throw new IllegalArgumentException();
783 protected ITypedRegion getInnerPartition(int offset, FlatNode position) {
784 if (position instanceof ViewNode) {
785 // TODO: revisit condition
786 InnerDocumentView innerDocument = ((ViewNode) position).view;
788 if (innerDocument != null) {
789 // multiplexing to inner view
791 // convert to inner offset
792 ITypedRegion region = innerDocument
793 .getPartition(innerDocument.getLocalOffset(offset));
795 // convert to parent offset
796 offset = innerDocument.getParentOffset(region.getOffset());
798 return new TypedRegion(offset, region.getLength(),
799 getContentType(position.type, region.getType()));
800 } catch (BadLocationException x) {
806 return new TypedRegion(position.offset, position.length, position.type);