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.9 2005-05-13 20:17:31 axelcl 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 document. Views have own partitioners themselves. This
33 * class is designed as a base for complex partitioners such as for JSP, PHP, ASP, etc. languages.
35 * @author Igor Malinin
37 public abstract class MultiViewPartitioner extends AbstractPartitioner {
39 class ViewListener implements IDocumentPartitioningListener, IDocumentPartitioningListenerExtension {
42 * @see org.eclipse.jface.text.IDocumentPartitioningListener#documentPartitioningChanged(IDocument)
44 public void documentPartitioningChanged(IDocument document) {
45 IDocumentView view = (IDocumentView) document;
47 int start = view.getParentOffset(0);
48 int end = view.getParentOffset(view.getLength());
50 rememberRegion(start, end - start);
54 * @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument, IRegion)
56 public void documentPartitioningChanged(IDocument document, IRegion region) {
57 IDocumentView view = (IDocumentView) document;
59 int offset = region.getOffset();
61 int start = view.getParentOffset(offset);
62 int end = view.getParentOffset(offset + region.getLength());
64 rememberRegion(start, end - start);
68 private ViewListener viewListener = new ViewListener();
70 private OuterDocumentView outerDocument;
72 private DocumentEvent outerDocumentEvent;
74 public MultiViewPartitioner(IPartitionTokenScanner scanner) {
78 public void setOuterPartitioner(IDocumentPartitioner partitioner) {
79 if (outerDocument == null) {
80 if (partitioner == null) {
84 outerDocument = new OuterDocumentView(document, nodes);
85 outerDocument.addDocumentPartitioningListener(viewListener);
88 IDocumentPartitioner old = outerDocument.getDocumentPartitioner();
90 outerDocument.setDocumentPartitioner(null);
94 if (partitioner != null) {
95 partitioner.connect(outerDocument);
98 outerDocument.setDocumentPartitioner(partitioner);
100 if (partitioner == null) {
101 outerDocument.removeDocumentPartitioningListener(viewListener);
102 outerDocument = null;
107 * Create subpartitioner.
110 * name of inner partition or <code>null</code> for outer partition
112 protected abstract IDocumentPartitioner createPartitioner(String contentType);
114 protected void addInnerRegion(FlatNode position) {
115 if (outerDocument != null) {
117 Assert.isTrue(position.offset >= 0, Integer.toString(position.offset));
119 int outerOffset = outerDocument.getLocalOffset(position.offset);
121 DocumentEvent event = null;
122 if (outerOffset >= 0) {
124 event = new DocumentEvent(outerDocument, outerOffset, position.length, null);
126 outerDocument.fireDocumentAboutToBeChanged(event);
128 super.addInnerRegion(position);
132 outerDocument.fireDocumentChanged(event);
135 super.addInnerRegion(position);
138 if (position instanceof ViewNode) {
139 // TODO: revisit condition
140 IDocumentPartitioner partitioner = createPartitioner(position.type);
141 if (partitioner != null) {
142 InnerDocumentView innerDocument = new InnerDocumentView(document, (ViewNode) position);
144 ((ViewNode) position).view = innerDocument;
146 partitioner.connect(innerDocument);
147 innerDocument.setDocumentPartitioner(partitioner);
148 innerDocument.addDocumentPartitioningListener(viewListener);
153 protected void removeInnerRegion(FlatNode position) {
155 if (outerDocument != null) {
156 DocumentEvent event = null;
157 if (position.offset >= 0 && position.length >= 0) {
158 int outerOffset = outerDocument.getLocalOffset(position.offset);
159 if (outerOffset > 0) {
160 event = new DocumentEvent(outerDocument, outerOffset, 0, document.get(position.offset, position.length));
162 outerDocument.fireDocumentAboutToBeChanged(event);
165 super.removeInnerRegion(position);
166 if (position.offset >= 0) {
168 outerDocument.fireDocumentChanged(event);
172 super.removeInnerRegion(position);
175 if (position instanceof ViewNode) {
176 // TODO: revisit condition
177 InnerDocumentView innerDocument = ((ViewNode) position).view;
178 if (innerDocument != null) {
179 IDocumentPartitioner partitioner = innerDocument.getDocumentPartitioner();
181 innerDocument.removeDocumentPartitioningListener(viewListener);
182 innerDocument.setDocumentPartitioner(null);
183 partitioner.disconnect();
186 } catch (BadLocationException e) {
190 protected void deleteInnerRegion(FlatNode position) {
191 super.deleteInnerRegion(position);
193 if (position instanceof ViewNode) {
194 // TODO: revisit condition
195 InnerDocumentView innerDocument = ((ViewNode) position).view;
196 if (innerDocument != null) {
197 IDocumentPartitioner partitioner = innerDocument.getDocumentPartitioner();
199 innerDocument.removeDocumentPartitioningListener(viewListener);
200 innerDocument.setDocumentPartitioner(null);
201 partitioner.disconnect();
206 public void connect(IDocument document) {
207 // outerDocument = new OuterDocumentView(document, innerPositions);
209 super.connect(document);
211 setOuterPartitioner(createPartitioner(null));
212 // IDocumentPartitioner partitioner =
213 // partitioner.connect(outerDocument);
214 // outerDocument.setDocumentPartitioner(partitioner);
215 // outerDocument.addDocumentPartitioningListener(viewListener);
218 public void disconnect() {
220 if (outerDocument != null) {
221 outerDocument.removeDocumentPartitioningListener(viewListener);
223 IDocumentPartitioner partitioner = outerDocument.getDocumentPartitioner();
225 outerDocument.setDocumentPartitioner(null);
226 partitioner.disconnect();
229 // TODO: cleanup listeners
230 outerDocument = null;
235 * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
237 public void documentAboutToBeChanged(DocumentEvent event) {
238 super.documentAboutToBeChanged(event);
240 outerDocumentEvent = null;
242 int offset = event.getOffset();
243 int length = event.getLength();
244 int end = offset + length;
246 // find left partition
247 int first = computeFlatNodeIndex(offset);
249 FlatNode p = (FlatNode) nodes.get(first - 1);
251 int right = p.offset + p.length;
252 if (offset < right) {
253 // change overlaps with partition
254 InnerDocumentView innerDocument = null;
255 if (p instanceof ViewNode) {
256 // TODO: revisit condition
257 innerDocument = ((ViewNode) p).view;
261 if (innerDocument != null) {
262 // cahnge completely inside partition
263 int start = innerDocument.getLocalOffset(offset);
264 innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, start, length, event.getText()));
270 if (innerDocument != null) {
271 // cut partition at right
272 int start = innerDocument.getLocalOffset(offset);
273 innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, start, innerDocument.getLength() - start,
279 // find right partition
280 int last = computeFlatNodeIndex(end);
282 FlatNode p = (FlatNode) nodes.get(last - 1);
284 if (p instanceof ViewNode) {
285 // TODO: revisit condition
286 InnerDocumentView innerDocument = ((ViewNode) p).view;
287 if (innerDocument != null) {
288 int right = p.offset + p.length;
290 // cut partition at left
291 int cut = innerDocument.getLocalOffset(end);
292 innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, 0, cut, null));
298 if (outerDocument != null) {
299 int left = outerDocument.getLocalOffset(offset);
300 int right = outerDocument.getLocalOffset(end);
302 String text = event.getText();
304 if (left >= 0 && (right - left >= 0)) {
305 if (left != right || text != null && text.length() > 0) {
306 outerDocumentEvent = new DocumentEvent(outerDocument, left, right - left, text);
308 outerDocument.fireDocumentAboutToBeChanged(outerDocumentEvent);
314 protected int fixupPartitions(DocumentEvent event) {
315 int offset = event.getOffset();
316 int length = event.getLength();
317 int end = offset + length;
319 // fixup/notify inner views laying on change boundaries
321 int first = computeFlatNodeIndex(offset);
323 FlatNode p = (FlatNode) nodes.get(first - 1);
325 int right = p.offset + p.length;
326 if (offset < right) {
327 // change overlaps with partition
329 // cahnge completely inside partition
330 String text = event.getText();
333 p.length += text.length();
336 if (p instanceof ViewNode) {
337 // TODO: revisit condition
338 InnerDocumentView innerDocument = ((ViewNode) p).view;
339 if (innerDocument != null) {
340 int start = innerDocument.getLocalOffset(offset);
341 innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, start, length, text));
345 // cut partition at right
346 int cut = p.offset + p.length - offset;
349 if (p instanceof ViewNode) {
350 // TODO: revisit condition
351 InnerDocumentView innerDocument = ((ViewNode) p).view;
352 if (innerDocument != null) {
353 int start = innerDocument.getLocalOffset(offset);
354 // TODO: ???fireDocumentAboutToBeChanged???
355 innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, start, cut, null));
362 int last = computeFlatNodeIndex(end);
363 if (last > 0 && first != last) {
364 FlatNode p = (FlatNode) nodes.get(last - 1);
366 int right = p.offset + p.length;
368 // cut partition at left
369 int cut = end - p.offset;
373 String text = event.getText();
375 p.offset += text.length();
378 if (p instanceof ViewNode) {
379 // TODO: revisit condition
380 InnerDocumentView innerDocument = ((ViewNode) p).view;
381 if (innerDocument != null) {
382 // TODO: ???fireDocumentAboutToBeChanged???
383 innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, 0, cut, null));
391 // fixup inner views laying afrer change
393 String text = event.getText();
395 length -= text.length();
398 for (int i = last, size = nodes.size(); i < size; i++) {
399 ((FlatNode) nodes.get(i)).offset -= length;
402 // delete inner views laying completely inside change boundaries
406 deleteInnerRegion((FlatNode) nodes.get(--last));
407 } while (first < last);
409 rememberRegion(offset, 0);
414 if (outerDocumentEvent != null) {
415 outerDocument.fireDocumentChanged(outerDocumentEvent);
422 * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
424 protected String getContentType(String parent, String view) {
429 if (parent != null) {
433 return IDocument.DEFAULT_CONTENT_TYPE;
437 * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
439 public ITypedRegion[] computePartitioning(int offset, int length) {
440 List list = new ArrayList();
442 // if (length>=9400) {
445 // System.out.print("MultiViewPartitioner::computePartitioning - Offset: ");
446 // System.out.print(offset);
447 // System.out.print(", Length: ");
448 // System.out.print(length);
449 // System.out.println("");
451 int end = offset + length;
453 int index = computeFlatNodeIndex(offset);
455 FlatNode prev = (index > 0) ? (FlatNode) nodes.get(index - 1) : null;
458 if (prev.overlapsWith(offset, length)) {
459 addInnerPartitions(list, offset, length, prev);
462 if (end <= prev.offset + prev.length) {
467 FlatNode next = (index < nodes.size()) ? (FlatNode) nodes.get(index) : null;
469 if (next == null || offset < next.offset) {
470 addOuterPartitions(list, offset, length, prev, next);
483 return (TypedRegion[]) list.toArray(new TypedRegion[list.size()]);
486 private void showList(List list) {
488 throw new NullPointerException();
489 } catch (Exception e) {
492 System.out.println(">>>>>List start");
494 for (int i = 0; i < list.size(); i++) {
495 temp = (TypedRegion) list.get(i);
496 System.out.print("Offset: ");
497 System.out.print(temp.getOffset());
498 System.out.print(", Length: ");
499 System.out.print(temp.getLength());
500 System.out.print(", Type: ");
501 System.out.print(temp.getType());
502 System.out.println("");
504 System.out.println("<<<<<List end");
507 private void addOuterPartitions(List list, int offset, int length, FlatNode prev, FlatNode next) {
510 int end = offset + length;
512 if (prev != null && start < prev.offset + prev.length) {
513 start = prev.offset + prev.length;
516 if (next != null && next.offset < end) {
524 if (outerDocument == null) {
526 // if (end - start<0) {
527 // throw new IndexOutOfBoundsException();
530 list.add(new TypedRegion(start, end - start, getContentType(null, IDocument.DEFAULT_CONTENT_TYPE)));
535 // convert to outer offsets
536 start = outerDocument.getLocalOffset(start);
537 end = outerDocument.getLocalOffset(end);
538 int len = end - start;
540 ITypedRegion[] regions = null;
542 regions = outerDocument.computePartitioning(start, len);
543 } catch (Exception e) {
544 // nasty workaround, which prevents cursor from moveing backwards in the editor
545 // but doesn't solve the partitioning problem
546 regions = new ITypedRegion[0];
547 System.out.println("MultiViewerPartitioner#addOuterPartitions failure\n"+"start:"+start +" length:" +len+"\n"+outerDocument.get(start,len));
549 for (int i = 0; i < regions.length; i++) {
550 ITypedRegion region = regions[i];
552 // convert back to parent offsets
553 start = outerDocument.getParentOffset(region.getOffset());
554 end = start + region.getLength();
557 offset = prev.offset + prev.length;
558 if (start < offset) {
564 offset = next.offset;
570 // if (end - start<0) {
572 // System.out.print("MultiViewPartitioner::addOuterPartitions - Offset: ");
573 // System.out.print(offset);
574 // System.out.print(", Start: ");
575 // System.out.print(start);
576 // System.out.print(", End: ");
577 // System.out.print(end);
578 // System.out.print(", Type: ");
579 // System.out.print(region.getType());
580 // System.out.println("");
581 // throw new IndexOutOfBoundsException();
584 list.add(new TypedRegion(start, end - start, getContentType(null, region.getType())));
587 } catch (BadLocationException x) {
591 private void addInnerPartitions(List list, int offset, int length, FlatNode position) {
592 InnerDocumentView innerDocument = null;
593 if (position instanceof ViewNode) {
594 // TODO: revisit condition
595 innerDocument = ((ViewNode) position).view;
598 if (innerDocument == null) {
601 // if (position.length<0) {
602 // throw new IndexOutOfBoundsException();
605 list.add(new TypedRegion(position.offset, position.length, getContentType(position.type, null)));
609 // multiplexing to inner view
612 int start = Math.max(offset, position.offset);
613 int end = Math.min(offset + length, position.offset + position.length);
615 // convert to document offsets
616 length = end - start;
617 offset = innerDocument.getLocalOffset(start);
619 ITypedRegion[] regions = innerDocument.computePartitioning(offset, length);
621 for (int i = 0; i < regions.length; i++) {
622 ITypedRegion region = regions[i];
624 // convert back to parent offsets
625 offset = innerDocument.getParentOffset(region.getOffset());
626 length = region.getLength();
629 // throw new IndexOutOfBoundsException();
632 list.add(new TypedRegion(offset, length, getContentType(position.type, region.getType())));
634 } catch (BadLocationException x) {
639 * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
641 public ITypedRegion getPartition(int offset) {
642 if (nodes.size() == 0) {
643 return getOuterPartition(offset, null, null);
646 int index = computeFlatNodeIndex(offset);
647 if (index < nodes.size()) {
648 FlatNode next = (FlatNode) nodes.get(index);
650 if (offset == next.offset) {
651 return getInnerPartition(offset, next);
655 return getOuterPartition(offset, null, next);
658 FlatNode prev = (FlatNode) nodes.get(index - 1);
660 if (prev.includes(offset)) {
661 return getInnerPartition(offset, prev);
664 return getOuterPartition(offset, prev, next);
667 FlatNode prev = (FlatNode) nodes.get(nodes.size() - 1);
669 if (prev.includes(offset)) {
670 return getInnerPartition(offset, prev);
673 return getOuterPartition(offset, prev, null);
676 protected ITypedRegion getOuterPartition(int offset, FlatNode prev, FlatNode next) {
681 if (outerDocument == null) {
683 end = document.getLength();
684 type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
686 int outerOffset = outerDocument.getLocalOffset(offset);
688 if (outerOffset < 0) {
690 end = document.getLength();
691 type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
694 ITypedRegion region = outerDocument.getPartition(outerOffset);
696 start = region.getOffset();
697 end = start + region.getLength();
699 // convert to parent offset
700 start = outerDocument.getParentOffset(start);
701 end = outerDocument.getParentOffset(end);
703 type = getContentType(null, region.getType());
708 offset = prev.offset + prev.length;
709 if (start < offset) {
715 offset = next.offset;
721 return new TypedRegion(start, end - start, type);
722 } catch (BadLocationException x) {
724 throw new IllegalArgumentException();
728 protected ITypedRegion getInnerPartition(int offset, FlatNode position) {
729 if (position instanceof ViewNode) {
730 // TODO: revisit condition
731 InnerDocumentView innerDocument = ((ViewNode) position).view;
733 if (innerDocument != null) {
734 // multiplexing to inner view
736 // convert to inner offset
737 ITypedRegion region = innerDocument.getPartition(innerDocument.getLocalOffset(offset));
739 // convert to parent offset
740 offset = innerDocument.getParentOffset(region.getOffset());
742 return new TypedRegion(offset, region.getLength(), getContentType(position.type, region.getType()));
743 } catch (BadLocationException x) {
749 return new TypedRegion(position.offset, position.length, position.type);