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.4 2004-10-25 17:03:28 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 DocumentEvent event = new DocumentEvent(outerDocument, outerDocument.getLocalOffset(position.offset), position.length, null);
121 outerDocument.fireDocumentAboutToBeChanged(event);
122 super.addInnerRegion(position);
123 outerDocument.fireDocumentChanged(event);
125 super.addInnerRegion(position);
128 if (position instanceof ViewNode) {
129 // TODO: revisit condition
130 IDocumentPartitioner partitioner = createPartitioner(position.type);
131 if (partitioner != null) {
132 InnerDocumentView innerDocument = new InnerDocumentView(document, (ViewNode) position);
134 ((ViewNode) position).view = innerDocument;
136 partitioner.connect(innerDocument);
137 innerDocument.setDocumentPartitioner(partitioner);
138 innerDocument.addDocumentPartitioningListener(viewListener);
143 protected void removeInnerRegion(FlatNode position) {
145 if (outerDocument != null) {
146 DocumentEvent event = null;
147 if (position.offset >= 0 && position.length >= 0) {
148 int outerOffset = outerDocument.getLocalOffset(position.offset);
149 if (outerOffset > 0) {
150 event = new DocumentEvent(outerDocument, outerOffset, 0, document.get(
151 position.offset, position.length));
153 outerDocument.fireDocumentAboutToBeChanged(event);
156 super.removeInnerRegion(position);
157 if (position.offset >= 0) {
159 outerDocument.fireDocumentChanged(event);
163 super.removeInnerRegion(position);
166 if (position instanceof ViewNode) {
167 // TODO: revisit condition
168 InnerDocumentView innerDocument = ((ViewNode) position).view;
169 if (innerDocument != null) {
170 IDocumentPartitioner partitioner = innerDocument.getDocumentPartitioner();
172 innerDocument.removeDocumentPartitioningListener(viewListener);
173 innerDocument.setDocumentPartitioner(null);
174 partitioner.disconnect();
177 } catch (BadLocationException e) {
181 protected void deleteInnerRegion(FlatNode position) {
182 super.deleteInnerRegion(position);
184 if (position instanceof ViewNode) {
185 // TODO: revisit condition
186 InnerDocumentView innerDocument = ((ViewNode) position).view;
187 if (innerDocument != null) {
188 IDocumentPartitioner partitioner = innerDocument.getDocumentPartitioner();
190 innerDocument.removeDocumentPartitioningListener(viewListener);
191 innerDocument.setDocumentPartitioner(null);
192 partitioner.disconnect();
197 public void connect(IDocument document) {
198 // outerDocument = new OuterDocumentView(document, innerPositions);
200 super.connect(document);
202 setOuterPartitioner(createPartitioner(null));
203 // IDocumentPartitioner partitioner =
204 // partitioner.connect(outerDocument);
205 // outerDocument.setDocumentPartitioner(partitioner);
206 // outerDocument.addDocumentPartitioningListener(viewListener);
209 public void disconnect() {
211 if (outerDocument != null) {
212 outerDocument.removeDocumentPartitioningListener(viewListener);
214 IDocumentPartitioner partitioner = outerDocument.getDocumentPartitioner();
216 outerDocument.setDocumentPartitioner(null);
217 partitioner.disconnect();
220 // TODO: cleanup listeners
221 outerDocument = null;
226 * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
228 public void documentAboutToBeChanged(DocumentEvent event) {
229 super.documentAboutToBeChanged(event);
231 outerDocumentEvent = null;
233 int offset = event.getOffset();
234 int length = event.getLength();
235 int end = offset + length;
237 // find left partition
238 int first = computeFlatNodeIndex(offset);
240 FlatNode p = (FlatNode) nodes.get(first - 1);
242 int right = p.offset + p.length;
243 if (offset < right) {
244 // change overlaps with partition
245 InnerDocumentView innerDocument = null;
246 if (p instanceof ViewNode) {
247 // TODO: revisit condition
248 innerDocument = ((ViewNode) p).view;
252 if (innerDocument != null) {
253 // cahnge completely inside partition
254 int start = innerDocument.getLocalOffset(offset);
255 innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, start, length, event.getText()));
261 if (innerDocument != null) {
262 // cut partition at right
263 int start = innerDocument.getLocalOffset(offset);
264 innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, start, innerDocument.getLength() - start,
270 // find right partition
271 int last = computeFlatNodeIndex(end);
273 FlatNode p = (FlatNode) nodes.get(last - 1);
275 if (p instanceof ViewNode) {
276 // TODO: revisit condition
277 InnerDocumentView innerDocument = ((ViewNode) p).view;
278 if (innerDocument != null) {
279 int right = p.offset + p.length;
281 // cut partition at left
282 int cut = innerDocument.getLocalOffset(end);
283 innerDocument.fireDocumentAboutToBeChanged(new DocumentEvent(innerDocument, 0, cut, null));
289 if (outerDocument != null) {
290 int left = outerDocument.getLocalOffset(offset);
291 int right = outerDocument.getLocalOffset(end);
293 String text = event.getText();
295 if (left != right || text != null && text.length() > 0) {
296 outerDocumentEvent = new DocumentEvent(outerDocument, left, right - left, text);
298 outerDocument.fireDocumentAboutToBeChanged(outerDocumentEvent);
303 protected int fixupPartitions(DocumentEvent event) {
304 int offset = event.getOffset();
305 int length = event.getLength();
306 int end = offset + length;
308 // fixup/notify inner views laying on change boundaries
310 int first = computeFlatNodeIndex(offset);
312 FlatNode p = (FlatNode) nodes.get(first - 1);
314 int right = p.offset + p.length;
315 if (offset < right) {
316 // change overlaps with partition
318 // cahnge completely inside partition
319 String text = event.getText();
322 p.length += text.length();
325 if (p instanceof ViewNode) {
326 // TODO: revisit condition
327 InnerDocumentView innerDocument = ((ViewNode) p).view;
328 if (innerDocument != null) {
329 int start = innerDocument.getLocalOffset(offset);
330 innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, start, length, text));
334 // cut partition at right
335 int cut = p.offset + p.length - offset;
338 if (p instanceof ViewNode) {
339 // TODO: revisit condition
340 InnerDocumentView innerDocument = ((ViewNode) p).view;
341 if (innerDocument != null) {
342 int start = innerDocument.getLocalOffset(offset);
343 // TODO: ???fireDocumentAboutToBeChanged???
344 innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, start, cut, null));
351 int last = computeFlatNodeIndex(end);
352 if (last > 0 && first != last) {
353 FlatNode p = (FlatNode) nodes.get(last - 1);
355 int right = p.offset + p.length;
357 // cut partition at left
358 int cut = end - p.offset;
362 String text = event.getText();
364 p.offset += text.length();
367 if (p instanceof ViewNode) {
368 // TODO: revisit condition
369 InnerDocumentView innerDocument = ((ViewNode) p).view;
370 if (innerDocument != null) {
371 // TODO: ???fireDocumentAboutToBeChanged???
372 innerDocument.fireDocumentChanged(new DocumentEvent(innerDocument, 0, cut, null));
380 // fixup inner views laying afrer change
382 String text = event.getText();
384 length -= text.length();
387 for (int i = last, size = nodes.size(); i < size; i++) {
388 ((FlatNode) nodes.get(i)).offset -= length;
391 // delete inner views laying completely inside change boundaries
395 deleteInnerRegion((FlatNode) nodes.get(--last));
396 } while (first < last);
398 rememberRegion(offset, 0);
403 if (outerDocumentEvent != null) {
404 outerDocument.fireDocumentChanged(outerDocumentEvent);
411 * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
413 protected String getContentType(String parent, String view) {
418 if (parent != null) {
422 return IDocument.DEFAULT_CONTENT_TYPE;
426 * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int, int)
428 public ITypedRegion[] computePartitioning(int offset, int length) {
429 List list = new ArrayList();
431 int end = offset + length;
433 int index = computeFlatNodeIndex(offset);
435 FlatNode prev = (index > 0) ? (FlatNode) nodes.get(index - 1) : null;
438 if (prev.overlapsWith(offset, length)) {
439 addInnerPartitions(list, offset, length, prev);
442 if (end <= prev.offset + prev.length) {
447 FlatNode next = (index < nodes.size()) ? (FlatNode) nodes.get(index) : null;
449 if (next == null || offset < next.offset) {
450 addOuterPartitions(list, offset, length, prev, next);
460 return (TypedRegion[]) list.toArray(new TypedRegion[list.size()]);
463 private void addOuterPartitions(List list, int offset, int length, FlatNode prev, FlatNode next) {
466 int end = offset + length;
468 if (prev != null && start < prev.offset + prev.length) {
469 start = prev.offset + prev.length;
472 if (next != null && next.offset < end) {
480 if (outerDocument == null) {
481 list.add(new TypedRegion(start, end - start, getContentType(null, IDocument.DEFAULT_CONTENT_TYPE)));
486 // convert to outer offsets
487 start = outerDocument.getLocalOffset(start);
488 end = outerDocument.getLocalOffset(end);
489 if (end - start >= 0) {//jsurfer insert line
490 ITypedRegion[] regions = outerDocument.computePartitioning(start, end - start);
492 for (int i = 0; i < regions.length; i++) {
493 ITypedRegion region = regions[i];
495 // convert back to parent offsets
496 start = outerDocument.getParentOffset(region.getOffset());
497 end = start + region.getLength();
500 offset = prev.offset + prev.length;
501 if (start < offset) {
507 offset = next.offset;
513 list.add(new TypedRegion(start, end - start, getContentType(null, region.getType())));
516 } catch (BadLocationException x) {
520 private void addInnerPartitions(List list, int offset, int length, FlatNode position) {
521 InnerDocumentView innerDocument = null;
522 if (position instanceof ViewNode) {
523 // TODO: revisit condition
524 innerDocument = ((ViewNode) position).view;
527 if (innerDocument == null) {
529 list.add(new TypedRegion(position.offset, position.length, getContentType(position.type, null)));
533 // multiplexing to inner view
536 int start = Math.max(offset, position.offset);
537 int end = Math.min(offset + length, position.offset + position.length);
539 // convert to document offsets
540 length = end - start;
541 offset = innerDocument.getLocalOffset(start);
543 ITypedRegion[] regions = innerDocument.computePartitioning(offset, length);
545 for (int i = 0; i < regions.length; i++) {
546 ITypedRegion region = regions[i];
548 // convert back to parent offsets
549 offset = innerDocument.getParentOffset(region.getOffset());
550 length = region.getLength();
552 list.add(new TypedRegion(offset, length, getContentType(position.type, region.getType())));
554 } catch (BadLocationException x) {
559 * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
561 public ITypedRegion getPartition(int offset) {
562 if (nodes.size() == 0) {
563 return getOuterPartition(offset, null, null);
566 int index = computeFlatNodeIndex(offset);
567 if (index < nodes.size()) {
568 FlatNode next = (FlatNode) nodes.get(index);
570 if (offset == next.offset) {
571 return getInnerPartition(offset, next);
575 return getOuterPartition(offset, null, next);
578 FlatNode prev = (FlatNode) nodes.get(index - 1);
580 if (prev.includes(offset)) {
581 return getInnerPartition(offset, prev);
584 return getOuterPartition(offset, prev, next);
587 FlatNode prev = (FlatNode) nodes.get(nodes.size() - 1);
589 if (prev.includes(offset)) {
590 return getInnerPartition(offset, prev);
593 return getOuterPartition(offset, prev, null);
596 protected ITypedRegion getOuterPartition(int offset, FlatNode prev, FlatNode next) {
601 if (outerDocument == null) {
603 end = document.getLength();
604 type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
606 ITypedRegion region = outerDocument.getPartition(outerDocument.getLocalOffset(offset));
608 start = region.getOffset();
609 end = start + region.getLength();
611 // convert to parent offset
612 start = outerDocument.getParentOffset(start);
613 end = outerDocument.getParentOffset(end);
615 type = getContentType(null, region.getType());
619 offset = prev.offset + prev.length;
620 if (start < offset) {
626 offset = next.offset;
632 return new TypedRegion(start, end - start, type);
633 } catch (BadLocationException x) {
634 throw new IllegalArgumentException();
638 protected ITypedRegion getInnerPartition(int offset, FlatNode position) {
639 if (position instanceof ViewNode) {
640 // TODO: revisit condition
641 InnerDocumentView innerDocument = ((ViewNode) position).view;
643 if (innerDocument != null) {
644 // multiplexing to inner view
646 // convert to inner offset
647 ITypedRegion region = innerDocument.getPartition(innerDocument.getLocalOffset(offset));
649 // convert to parent offset
650 offset = innerDocument.getParentOffset(region.getOffset());
652 return new TypedRegion(offset, region.getLength(), getContentType(position.type, region.getType()));
653 } catch (BadLocationException x) {
659 return new TypedRegion(position.offset, position.length, position.type);