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;
19 import org.eclipse.jface.text.Assert;
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.DocumentEvent;
22 import org.eclipse.jface.text.IDocument;
23 import org.eclipse.jface.text.IDocumentPartitioner;
24 import org.eclipse.jface.text.IDocumentPartitioningListener;
25 import org.eclipse.jface.text.IDocumentPartitioningListenerExtension;
26 import org.eclipse.jface.text.IRegion;
27 import org.eclipse.jface.text.ITypedRegion;
28 import org.eclipse.jface.text.TypedRegion;
29 import org.eclipse.jface.text.rules.IPartitionTokenScanner;
32 * Advanced partitioner which maintains partitions as views to connected
33 * document. Views have own partitioners themselves. This class is designed as a
34 * base for complex partitioners such as for JSP, PHP, ASP, etc. languages.
36 * @author Igor Malinin
38 public abstract class MultiViewPartitioner extends AbstractPartitioner {
40 class ViewListener implements IDocumentPartitioningListener,
41 IDocumentPartitioningListenerExtension {
44 * @see org.eclipse.jface.text.IDocumentPartitioningListener#documentPartitioningChanged(IDocument)
46 public void documentPartitioningChanged(IDocument document) {
47 IDocumentView view = (IDocumentView) document;
49 int start = view.getParentOffset(0);
50 int end = view.getParentOffset(view.getLength());
52 rememberRegion(start, end - start);
56 * @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument,
59 public void documentPartitioningChanged(IDocument document,
61 IDocumentView view = (IDocumentView) document;
63 int offset = region.getOffset();
65 int start = view.getParentOffset(offset);
66 int end = view.getParentOffset(offset + region.getLength());
68 rememberRegion(start, end - start);
72 private ViewListener viewListener = new ViewListener();
74 private OuterDocumentView outerDocument;
76 private DocumentEvent outerDocumentEvent;
78 public MultiViewPartitioner(IPartitionTokenScanner scanner) {
82 public void setOuterPartitioner(IDocumentPartitioner partitioner) {
83 if (outerDocument == null) {
84 if (partitioner == null) {
88 outerDocument = new OuterDocumentView(document, nodes);
89 outerDocument.addDocumentPartitioningListener(viewListener);
92 IDocumentPartitioner old = outerDocument.getDocumentPartitioner();
94 outerDocument.setDocumentPartitioner(null);
98 if (partitioner != null) {
99 partitioner.connect(outerDocument);
102 outerDocument.setDocumentPartitioner(partitioner);
104 if (partitioner == null) {
105 outerDocument.removeDocumentPartitioningListener(viewListener);
106 outerDocument = null;
111 * Create subpartitioner.
114 * name of inner partition or <code>null</code> for outer
117 protected abstract IDocumentPartitioner createPartitioner(String contentType);
119 protected void addInnerRegion(FlatNode position) {
120 if (outerDocument != null) {
122 Assert.isTrue(position.offset >= 0, Integer
123 .toString(position.offset));
125 int outerOffset = outerDocument.getLocalOffset(position.offset);
127 DocumentEvent event = null;
128 if (outerOffset >= 0) {
130 event = new DocumentEvent(outerDocument, outerOffset,
131 position.length, null);
133 outerDocument.fireDocumentAboutToBeChanged(event);
135 super.addInnerRegion(position);
139 outerDocument.fireDocumentChanged(event);
142 super.addInnerRegion(position);
145 if (position instanceof ViewNode) {
146 // TODO: revisit condition
147 IDocumentPartitioner partitioner = createPartitioner(position.type);
148 if (partitioner != null) {
149 InnerDocumentView innerDocument = new InnerDocumentView(
150 document, (ViewNode) position);
152 ((ViewNode) position).view = innerDocument;
154 partitioner.connect(innerDocument);
155 innerDocument.setDocumentPartitioner(partitioner);
156 innerDocument.addDocumentPartitioningListener(viewListener);
161 protected void removeInnerRegion(FlatNode position) {
163 if (outerDocument != null) {
164 DocumentEvent event = null;
165 if (position.offset >= 0 && position.length >= 0) {
166 int outerOffset = outerDocument
167 .getLocalOffset(position.offset);
168 if (outerOffset > 0) {
169 event = new DocumentEvent(outerDocument, outerOffset,
170 0, document.get(position.offset,
173 outerDocument.fireDocumentAboutToBeChanged(event);
176 super.removeInnerRegion(position);
177 if (position.offset >= 0) {
179 outerDocument.fireDocumentChanged(event);
183 super.removeInnerRegion(position);
186 if (position instanceof ViewNode) {
187 // TODO: revisit condition
188 InnerDocumentView innerDocument = ((ViewNode) position).view;
189 if (innerDocument != null) {
190 IDocumentPartitioner partitioner = innerDocument
191 .getDocumentPartitioner();
194 .removeDocumentPartitioningListener(viewListener);
195 innerDocument.setDocumentPartitioner(null);
196 partitioner.disconnect();
199 } catch (BadLocationException e) {
203 protected void deleteInnerRegion(FlatNode position) {
204 super.deleteInnerRegion(position);
206 if (position instanceof ViewNode) {
207 // TODO: revisit condition
208 InnerDocumentView innerDocument = ((ViewNode) position).view;
209 if (innerDocument != null) {
210 IDocumentPartitioner partitioner = innerDocument
211 .getDocumentPartitioner();
213 innerDocument.removeDocumentPartitioningListener(viewListener);
214 innerDocument.setDocumentPartitioner(null);
215 partitioner.disconnect();
220 public void connect(IDocument document) {
221 // outerDocument = new OuterDocumentView(document, innerPositions);
223 super.connect(document);
225 setOuterPartitioner(createPartitioner(null));
226 // IDocumentPartitioner partitioner =
227 // partitioner.connect(outerDocument);
228 // outerDocument.setDocumentPartitioner(partitioner);
229 // outerDocument.addDocumentPartitioningListener(viewListener);
232 public void disconnect() {
234 if (outerDocument != null) {
235 outerDocument.removeDocumentPartitioningListener(viewListener);
237 IDocumentPartitioner partitioner = outerDocument
238 .getDocumentPartitioner();
240 outerDocument.setDocumentPartitioner(null);
241 partitioner.disconnect();
244 // TODO: cleanup listeners
245 outerDocument = null;
250 * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
252 public void documentAboutToBeChanged(DocumentEvent event) {
253 super.documentAboutToBeChanged(event);
255 outerDocumentEvent = null;
257 int offset = event.getOffset();
258 int length = event.getLength();
259 int end = offset + length;
261 // find left partition
262 int first = computeFlatNodeIndex(offset);
264 FlatNode p = (FlatNode) nodes.get(first - 1);
266 int right = p.offset + p.length;
267 if (offset < right) {
268 // change overlaps with partition
269 InnerDocumentView innerDocument = null;
270 if (p instanceof ViewNode) {
271 // TODO: revisit condition
272 innerDocument = ((ViewNode) p).view;
276 if (innerDocument != null) {
277 // cahnge completely inside partition
278 int start = innerDocument.getLocalOffset(offset);
280 .fireDocumentAboutToBeChanged(new DocumentEvent(
281 innerDocument, start, length, event
288 if (innerDocument != null) {
289 // cut partition at right
290 int start = innerDocument.getLocalOffset(offset);
292 .fireDocumentAboutToBeChanged(new DocumentEvent(
293 innerDocument, start, innerDocument
300 // find right partition
301 int last = computeFlatNodeIndex(end);
303 FlatNode p = (FlatNode) nodes.get(last - 1);
305 if (p instanceof ViewNode) {
306 // TODO: revisit condition
307 InnerDocumentView innerDocument = ((ViewNode) p).view;
308 if (innerDocument != null) {
309 int right = p.offset + p.length;
311 // cut partition at left
312 int cut = innerDocument.getLocalOffset(end);
314 .fireDocumentAboutToBeChanged(new DocumentEvent(
315 innerDocument, 0, cut, null));
321 if (outerDocument != null) {
322 int left = outerDocument.getLocalOffset(offset);
323 int right = outerDocument.getLocalOffset(end);
325 String text = event.getText();
327 if (left >= 0 && (right - left >= 0)) {
328 if (left != right || text != null && text.length() > 0) {
329 outerDocumentEvent = new DocumentEvent(outerDocument, left,
333 .fireDocumentAboutToBeChanged(outerDocumentEvent);
339 protected int fixupPartitions(DocumentEvent event) {
340 int offset = event.getOffset();
341 int length = event.getLength();
342 int end = offset + length;
344 // fixup/notify inner views laying on change boundaries
346 int first = computeFlatNodeIndex(offset);
348 FlatNode p = (FlatNode) nodes.get(first - 1);
350 int right = p.offset + p.length;
351 if (offset < right) {
352 // change overlaps with partition
354 // cahnge completely inside partition
355 String text = event.getText();
358 p.length += text.length();
361 if (p instanceof ViewNode) {
362 // TODO: revisit condition
363 InnerDocumentView innerDocument = ((ViewNode) p).view;
364 if (innerDocument != null) {
365 int start = innerDocument.getLocalOffset(offset);
367 .fireDocumentChanged(new DocumentEvent(
368 innerDocument, start, length, text));
372 // cut partition at right
373 int cut = p.offset + p.length - offset;
376 if (p instanceof ViewNode) {
377 // TODO: revisit condition
378 InnerDocumentView innerDocument = ((ViewNode) p).view;
379 if (innerDocument != null) {
380 int start = innerDocument.getLocalOffset(offset);
381 // TODO: ???fireDocumentAboutToBeChanged???
383 .fireDocumentChanged(new DocumentEvent(
384 innerDocument, start, cut, null));
391 int last = computeFlatNodeIndex(end);
392 if (last > 0 && first != last) {
393 FlatNode p = (FlatNode) nodes.get(last - 1);
395 int right = p.offset + p.length;
397 // cut partition at left
398 int cut = end - p.offset;
402 String text = event.getText();
404 p.offset += text.length();
407 if (p instanceof ViewNode) {
408 // TODO: revisit condition
409 InnerDocumentView innerDocument = ((ViewNode) p).view;
410 if (innerDocument != null) {
411 // TODO: ???fireDocumentAboutToBeChanged???
412 innerDocument.fireDocumentChanged(new DocumentEvent(
413 innerDocument, 0, cut, null));
421 // fixup inner views laying afrer change
423 String text = event.getText();
425 length -= text.length();
428 for (int i = last, size = nodes.size(); i < size; i++) {
429 ((FlatNode) nodes.get(i)).offset -= length;
432 // delete inner views laying completely inside change boundaries
436 deleteInnerRegion((FlatNode) nodes.get(--last));
437 } while (first < last);
439 rememberRegion(offset, 0);
444 if (outerDocumentEvent != null) {
445 outerDocument.fireDocumentChanged(outerDocumentEvent);
452 * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int,
455 protected String getContentType(String parent, String view) {
460 if (parent != null) {
464 return IDocument.DEFAULT_CONTENT_TYPE;
468 * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int,
471 public ITypedRegion[] computePartitioning(int offset, int length) {
472 List list = new ArrayList();
474 // if (length>=9400) {
477 // System.out.print("MultiViewPartitioner::computePartitioning - Offset:
479 // System.out.print(offset);
480 // System.out.print(", Length: ");
481 // System.out.print(length);
482 // System.out.println("");
484 int end = offset + length;
486 int index = computeFlatNodeIndex(offset);
488 FlatNode prev = (index > 0) ? (FlatNode) nodes.get(index - 1)
492 if (prev.overlapsWith(offset, length)) {
493 addInnerPartitions(list, offset, length, prev);
496 if (end <= prev.offset + prev.length) {
501 FlatNode next = (index < nodes.size()) ? (FlatNode) nodes
504 if (next == null || offset < next.offset) {
505 addOuterPartitions(list, offset, length, prev, next);
518 return (TypedRegion[]) list.toArray(new TypedRegion[list.size()]);
521 private void showList(List list) {
523 throw new NullPointerException();
524 } catch (Exception e) {
527 System.out.println(">>>>>List start");
529 for (int i = 0; i < list.size(); i++) {
530 temp = (TypedRegion) list.get(i);
531 System.out.print("Offset: ");
532 System.out.print(temp.getOffset());
533 System.out.print(", Length: ");
534 System.out.print(temp.getLength());
535 System.out.print(", Type: ");
536 System.out.print(temp.getType());
537 System.out.println("");
539 System.out.println("<<<<<List end");
542 private void addOuterPartitions(List list, int offset, int length,
543 FlatNode prev, FlatNode next) {
546 int end = offset + length;
548 if (prev != null && start < prev.offset + prev.length) {
549 start = prev.offset + prev.length;
552 if (next != null && next.offset < end) {
560 if (outerDocument == null) {
562 // if (end - start<0) {
563 // throw new IndexOutOfBoundsException();
566 list.add(new TypedRegion(start, end - start, getContentType(null,
567 IDocument.DEFAULT_CONTENT_TYPE)));
572 // convert to outer offsets
573 start = outerDocument.getLocalOffset(start);
574 end = outerDocument.getLocalOffset(end);
575 int len = end - start;
577 ITypedRegion[] regions = null;
579 regions = outerDocument.computePartitioning(start, len);
580 } catch (Exception e) {
581 // nasty workaround, which prevents cursor from moveing
582 // backwards in the editor
583 // but doesn't solve the partitioning problem
584 regions = new ITypedRegion[0];
586 .println("MultiViewerPartitioner#addOuterPartitions failure\n"
591 + "\n" + outerDocument.get(start, len));
593 for (int i = 0; i < regions.length; i++) {
594 ITypedRegion region = regions[i];
596 // convert back to parent offsets
597 start = outerDocument.getParentOffset(region.getOffset());
598 end = start + region.getLength();
601 offset = prev.offset + prev.length;
602 if (start < offset) {
608 offset = next.offset;
614 // if (end - start<0) {
616 // System.out.print("MultiViewPartitioner::addOuterPartitions
618 // System.out.print(offset);
619 // System.out.print(", Start: ");
620 // System.out.print(start);
621 // System.out.print(", End: ");
622 // System.out.print(end);
623 // System.out.print(", Type: ");
624 // System.out.print(region.getType());
625 // System.out.println("");
626 // throw new IndexOutOfBoundsException();
629 list.add(new TypedRegion(start, end - start,
630 getContentType(null, region.getType())));
633 } catch (BadLocationException x) {
637 private void addInnerPartitions(List list, int offset, int length,
639 InnerDocumentView innerDocument = null;
640 if (position instanceof ViewNode) {
641 // TODO: revisit condition
642 innerDocument = ((ViewNode) position).view;
645 if (innerDocument == null) {
648 // if (position.length<0) {
649 // throw new IndexOutOfBoundsException();
652 list.add(new TypedRegion(position.offset, position.length,
653 getContentType(position.type, null)));
657 // multiplexing to inner view
660 int start = Math.max(offset, position.offset);
661 int end = Math.min(offset + length, position.offset
664 // convert to document offsets
665 length = end - start;
666 offset = innerDocument.getLocalOffset(start);
668 ITypedRegion[] regions = innerDocument.computePartitioning(offset,
671 for (int i = 0; i < regions.length; i++) {
672 ITypedRegion region = regions[i];
674 // convert back to parent offsets
675 offset = innerDocument.getParentOffset(region.getOffset());
676 length = region.getLength();
679 // throw new IndexOutOfBoundsException();
682 list.add(new TypedRegion(offset, length, getContentType(
683 position.type, region.getType())));
685 } catch (BadLocationException x) {
690 * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
692 public ITypedRegion getPartition(int offset) {
693 if (nodes.size() == 0) {
694 return getOuterPartition(offset, null, null);
697 int index = computeFlatNodeIndex(offset);
698 if (index < nodes.size()) {
699 FlatNode next = (FlatNode) nodes.get(index);
701 if (offset == next.offset) {
702 return getInnerPartition(offset, next);
706 return getOuterPartition(offset, null, next);
709 FlatNode prev = (FlatNode) nodes.get(index - 1);
711 if (prev.includes(offset)) {
712 return getInnerPartition(offset, prev);
715 return getOuterPartition(offset, prev, next);
718 FlatNode prev = (FlatNode) nodes.get(nodes.size() - 1);
720 if (prev.includes(offset)) {
721 return getInnerPartition(offset, prev);
724 return getOuterPartition(offset, prev, null);
727 protected ITypedRegion getOuterPartition(int offset, FlatNode prev,
733 if (outerDocument == null) {
735 end = document.getLength();
736 type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
738 int outerOffset = outerDocument.getLocalOffset(offset);
740 if (outerOffset < 0) {
742 end = document.getLength();
743 type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
746 ITypedRegion region = outerDocument
747 .getPartition(outerOffset);
749 start = region.getOffset();
750 end = start + region.getLength();
752 // convert to parent offset
753 start = outerDocument.getParentOffset(start);
754 end = outerDocument.getParentOffset(end);
756 type = getContentType(null, region.getType());
761 offset = prev.offset + prev.length;
762 if (start < offset) {
768 offset = next.offset;
774 return new TypedRegion(start, end - start, type);
775 } catch (BadLocationException x) {
777 throw new IllegalArgumentException();
781 protected ITypedRegion getInnerPartition(int offset, FlatNode position) {
782 if (position instanceof ViewNode) {
783 // TODO: revisit condition
784 InnerDocumentView innerDocument = ((ViewNode) position).view;
786 if (innerDocument != null) {
787 // multiplexing to inner view
789 // convert to inner offset
790 ITypedRegion region = innerDocument
791 .getPartition(innerDocument.getLocalOffset(offset));
793 // convert to parent offset
794 offset = innerDocument.getParentOffset(region.getOffset());
796 return new TypedRegion(offset, region.getLength(),
797 getContentType(position.type, region.getType()));
798 } catch (BadLocationException x) {
804 return new TypedRegion(position.offset, position.length, position.type);