Fixed: 1777185 - Remote Debug option 'Open with ...' should work
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpeclipse / ui / text / rules / MultiViewPartitioner.java
1 /*
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
7  * 
8  * Contributors:
9  *     Igor Malinin - initial contribution
10  * 
11  * $Id: MultiViewPartitioner.java,v 1.10 2006-10-21 23:13:53 pombredanne Exp $
12  */
13
14 package net.sourceforge.phpeclipse.ui.text.rules;
15
16 import java.util.ArrayList;
17 import java.util.List;
18
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;
30
31 /**
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.
35  * 
36  * @author Igor Malinin
37  */
38 public abstract class MultiViewPartitioner extends AbstractPartitioner {
39
40         class ViewListener implements IDocumentPartitioningListener,
41                         IDocumentPartitioningListenerExtension {
42
43                 /*
44                  * @see org.eclipse.jface.text.IDocumentPartitioningListener#documentPartitioningChanged(IDocument)
45                  */
46                 public void documentPartitioningChanged(IDocument document) {
47                         IDocumentView view = (IDocumentView) document;
48
49                         int start = view.getParentOffset(0);
50                         int end = view.getParentOffset(view.getLength());
51
52                         rememberRegion(start, end - start);
53                 }
54
55                 /*
56                  * @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument,
57                  *      IRegion)
58                  */
59                 public void documentPartitioningChanged(IDocument document,
60                                 IRegion region) {
61                         IDocumentView view = (IDocumentView) document;
62
63                         int offset = region.getOffset();
64
65                         int start = view.getParentOffset(offset);
66                         int end = view.getParentOffset(offset + region.getLength());
67
68                         rememberRegion(start, end - start);
69                 }
70         }
71
72         private ViewListener viewListener = new ViewListener();
73
74         private OuterDocumentView outerDocument;
75
76         private DocumentEvent outerDocumentEvent;
77
78         public MultiViewPartitioner(IPartitionTokenScanner scanner) {
79                 super(scanner);
80         }
81
82         public void setOuterPartitioner(IDocumentPartitioner partitioner) {
83                 if (outerDocument == null) {
84                         if (partitioner == null) {
85                                 return;
86                         }
87
88                         outerDocument = new OuterDocumentView(document, nodes);
89                         outerDocument.addDocumentPartitioningListener(viewListener);
90                 }
91
92                 IDocumentPartitioner old = outerDocument.getDocumentPartitioner();
93                 if (old != null) {
94                         outerDocument.setDocumentPartitioner(null);
95                         old.disconnect();
96                 }
97
98                 if (partitioner != null) {
99                         partitioner.connect(outerDocument);
100                 }
101
102                 outerDocument.setDocumentPartitioner(partitioner);
103
104                 if (partitioner == null) {
105                         outerDocument.removeDocumentPartitioningListener(viewListener);
106                         outerDocument = null;
107                 }
108         }
109
110         /**
111          * Create subpartitioner.
112          * 
113          * @param contentType
114          *            name of inner partition or <code>null</code> for outer
115          *            partition
116          */
117         protected abstract IDocumentPartitioner createPartitioner(String contentType);
118
119         protected void addInnerRegion(FlatNode position) {
120                 if (outerDocument != null) {
121                         if (DEBUG) {
122                                 Assert.isTrue(position.offset >= 0, Integer
123                                                 .toString(position.offset));
124                         }
125                         int outerOffset = outerDocument.getLocalOffset(position.offset);
126                         // axelcl start
127                         DocumentEvent event = null;
128                         if (outerOffset >= 0) {
129                                 // axelcl end
130                                 event = new DocumentEvent(outerDocument, outerOffset,
131                                                 position.length, null);
132
133                                 outerDocument.fireDocumentAboutToBeChanged(event);
134                         }
135                         super.addInnerRegion(position);
136                         // axelcl start
137                         if (event != null) {
138                                 // axelcl end
139                                 outerDocument.fireDocumentChanged(event);
140                         }
141                 } else {
142                         super.addInnerRegion(position);
143                 }
144
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);
151
152                                 ((ViewNode) position).view = innerDocument;
153
154                                 partitioner.connect(innerDocument);
155                                 innerDocument.setDocumentPartitioner(partitioner);
156                                 innerDocument.addDocumentPartitioningListener(viewListener);
157                         }
158                 }
159         }
160
161         protected void removeInnerRegion(FlatNode position) {
162                 try {
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,
171                                                                                 position.length));
172
173                                                 outerDocument.fireDocumentAboutToBeChanged(event);
174                                         }
175                                 }
176                                 super.removeInnerRegion(position);
177                                 if (position.offset >= 0) {
178                                         if (event != null) {
179                                                 outerDocument.fireDocumentChanged(event);
180                                         }
181                                 }
182                         } else {
183                                 super.removeInnerRegion(position);
184                         }
185
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();
192
193                                         innerDocument
194                                                         .removeDocumentPartitioningListener(viewListener);
195                                         innerDocument.setDocumentPartitioner(null);
196                                         partitioner.disconnect();
197                                 }
198                         }
199                 } catch (BadLocationException e) {
200                 }
201         }
202
203         protected void deleteInnerRegion(FlatNode position) {
204                 super.deleteInnerRegion(position);
205
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();
212
213                                 innerDocument.removeDocumentPartitioningListener(viewListener);
214                                 innerDocument.setDocumentPartitioner(null);
215                                 partitioner.disconnect();
216                         }
217                 }
218         }
219
220         public void connect(IDocument document) {
221                 // outerDocument = new OuterDocumentView(document, innerPositions);
222
223                 super.connect(document);
224
225                 setOuterPartitioner(createPartitioner(null));
226                 // IDocumentPartitioner partitioner =
227                 // partitioner.connect(outerDocument);
228                 // outerDocument.setDocumentPartitioner(partitioner);
229                 // outerDocument.addDocumentPartitioningListener(viewListener);
230         }
231
232         public void disconnect() {
233                 try {
234                         if (outerDocument != null) {
235                                 outerDocument.removeDocumentPartitioningListener(viewListener);
236
237                                 IDocumentPartitioner partitioner = outerDocument
238                                                 .getDocumentPartitioner();
239
240                                 outerDocument.setDocumentPartitioner(null);
241                                 partitioner.disconnect();
242                         }
243                 } finally {
244                         // TODO: cleanup listeners
245                         outerDocument = null;
246                 }
247         }
248
249         /*
250          * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
251          */
252         public void documentAboutToBeChanged(DocumentEvent event) {
253                 super.documentAboutToBeChanged(event);
254
255                 outerDocumentEvent = null;
256
257                 int offset = event.getOffset();
258                 int length = event.getLength();
259                 int end = offset + length;
260
261                 // find left partition
262                 int first = computeFlatNodeIndex(offset);
263                 if (first > 0) {
264                         FlatNode p = (FlatNode) nodes.get(first - 1);
265
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;
273                                 }
274
275                                 if (end < right) {
276                                         if (innerDocument != null) {
277                                                 // cahnge completely inside partition
278                                                 int start = innerDocument.getLocalOffset(offset);
279                                                 innerDocument
280                                                                 .fireDocumentAboutToBeChanged(new DocumentEvent(
281                                                                                 innerDocument, start, length, event
282                                                                                                 .getText()));
283                                         }
284
285                                         return;
286                                 }
287
288                                 if (innerDocument != null) {
289                                         // cut partition at right
290                                         int start = innerDocument.getLocalOffset(offset);
291                                         innerDocument
292                                                         .fireDocumentAboutToBeChanged(new DocumentEvent(
293                                                                         innerDocument, start, innerDocument
294                                                                                         .getLength()
295                                                                                         - start, null));
296                                 }
297                         }
298                 }
299
300                 // find right partition
301                 int last = computeFlatNodeIndex(end);
302                 if (last > 0) {
303                         FlatNode p = (FlatNode) nodes.get(last - 1);
304
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;
310                                         if (end < right) {
311                                                 // cut partition at left
312                                                 int cut = innerDocument.getLocalOffset(end);
313                                                 innerDocument
314                                                                 .fireDocumentAboutToBeChanged(new DocumentEvent(
315                                                                                 innerDocument, 0, cut, null));
316                                         }
317                                 }
318                         }
319                 }
320
321                 if (outerDocument != null) {
322                         int left = outerDocument.getLocalOffset(offset);
323                         int right = outerDocument.getLocalOffset(end);
324
325                         String text = event.getText();
326
327                         if (left >= 0 && (right - left >= 0)) {
328                                 if (left != right || text != null && text.length() > 0) {
329                                         outerDocumentEvent = new DocumentEvent(outerDocument, left,
330                                                         right - left, text);
331
332                                         outerDocument
333                                                         .fireDocumentAboutToBeChanged(outerDocumentEvent);
334                                 }
335                         }
336                 }
337         }
338
339         protected int fixupPartitions(DocumentEvent event) {
340                 int offset = event.getOffset();
341                 int length = event.getLength();
342                 int end = offset + length;
343
344                 // fixup/notify inner views laying on change boundaries
345
346                 int first = computeFlatNodeIndex(offset);
347                 if (first > 0) {
348                         FlatNode p = (FlatNode) nodes.get(first - 1);
349
350                         int right = p.offset + p.length;
351                         if (offset < right) {
352                                 // change overlaps with partition
353                                 if (end < right) {
354                                         // cahnge completely inside partition
355                                         String text = event.getText();
356                                         p.length -= length;
357                                         if (text != null) {
358                                                 p.length += text.length();
359                                         }
360
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);
366                                                         innerDocument
367                                                                         .fireDocumentChanged(new DocumentEvent(
368                                                                                         innerDocument, start, length, text));
369                                                 }
370                                         }
371                                 } else {
372                                         // cut partition at right
373                                         int cut = p.offset + p.length - offset;
374                                         p.length -= cut;
375
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???
382                                                         innerDocument
383                                                                         .fireDocumentChanged(new DocumentEvent(
384                                                                                         innerDocument, start, cut, null));
385                                                 }
386                                         }
387                                 }
388                         }
389                 }
390
391                 int last = computeFlatNodeIndex(end);
392                 if (last > 0 && first != last) {
393                         FlatNode p = (FlatNode) nodes.get(last - 1);
394
395                         int right = p.offset + p.length;
396                         if (end < right) {
397                                 // cut partition at left
398                                 int cut = end - p.offset;
399                                 p.length -= cut;
400                                 p.offset = offset;
401
402                                 String text = event.getText();
403                                 if (text != null) {
404                                         p.offset += text.length();
405                                 }
406
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));
414                                         }
415                                 }
416
417                                 --last;
418                         }
419                 }
420
421                 // fixup inner views laying afrer change
422
423                 String text = event.getText();
424                 if (text != null) {
425                         length -= text.length();
426                 }
427
428                 for (int i = last, size = nodes.size(); i < size; i++) {
429                         ((FlatNode) nodes.get(i)).offset -= length;
430                 }
431
432                 // delete inner views laying completely inside change boundaries
433
434                 if (first < last) {
435                         do {
436                                 deleteInnerRegion((FlatNode) nodes.get(--last));
437                         } while (first < last);
438
439                         rememberRegion(offset, 0);
440                 }
441
442                 // notify outer view
443
444                 if (outerDocumentEvent != null) {
445                         outerDocument.fireDocumentChanged(outerDocumentEvent);
446                 }
447
448                 return first;
449         }
450
451         /*
452          * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int,
453          *      int)
454          */
455         protected String getContentType(String parent, String view) {
456                 if (view != null) {
457                         return view;
458                 }
459
460                 if (parent != null) {
461                         return parent;
462                 }
463
464                 return IDocument.DEFAULT_CONTENT_TYPE;
465         }
466
467         /*
468          * @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int,
469          *      int)
470          */
471         public ITypedRegion[] computePartitioning(int offset, int length) {
472                 List list = new ArrayList();
473                 // if (DEBUG) {
474                 // if (length>=9400) {
475                 // length--;
476                 // }
477                 // System.out.print("MultiViewPartitioner::computePartitioning - Offset:
478                 // ");
479                 // System.out.print(offset);
480                 // System.out.print(", Length: ");
481                 // System.out.print(length);
482                 // System.out.println("");
483                 // }
484                 int end = offset + length;
485
486                 int index = computeFlatNodeIndex(offset);
487                 while (true) {
488                         FlatNode prev = (index > 0) ? (FlatNode) nodes.get(index - 1)
489                                         : null;
490
491                         if (prev != null) {
492                                 if (prev.overlapsWith(offset, length)) {
493                                         addInnerPartitions(list, offset, length, prev);
494                                 }
495
496                                 if (end <= prev.offset + prev.length) {
497                                         break;
498                                 }
499                         }
500
501                         FlatNode next = (index < nodes.size()) ? (FlatNode) nodes
502                                         .get(index) : null;
503
504                         if (next == null || offset < next.offset) {
505                                 addOuterPartitions(list, offset, length, prev, next);
506                         }
507
508                         if (next == null) {
509                                 break;
510                         }
511
512                         ++index;
513                 }
514                 // if (DEBUG) {
515                 // showList(list);
516                 // }
517
518                 return (TypedRegion[]) list.toArray(new TypedRegion[list.size()]);
519         }
520
521         private void showList(List list) {
522                 try {
523                         throw new NullPointerException();
524                 } catch (Exception e) {
525                         e.printStackTrace();
526                 }
527                 System.out.println(">>>>>List start");
528                 TypedRegion temp;
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("");
538                 }
539                 System.out.println("<<<<<List end");
540         }
541
542         private void addOuterPartitions(List list, int offset, int length,
543                         FlatNode prev, FlatNode next) {
544                 // limit region
545                 int start = offset;
546                 int end = offset + length;
547
548                 if (prev != null && start < prev.offset + prev.length) {
549                         start = prev.offset + prev.length;
550                 }
551
552                 if (next != null && next.offset < end) {
553                         end = next.offset;
554                 }
555
556                 if (start == end) {
557                         return;
558                 }
559
560                 if (outerDocument == null) {
561                         // if (DEBUG) {
562                         // if (end - start<0) {
563                         // throw new IndexOutOfBoundsException();
564                         // }
565                         // }
566                         list.add(new TypedRegion(start, end - start, getContentType(null,
567                                         IDocument.DEFAULT_CONTENT_TYPE)));
568                         return;
569                 }
570
571                 try {
572                         // convert to outer offsets
573                         start = outerDocument.getLocalOffset(start);
574                         end = outerDocument.getLocalOffset(end);
575                         int len = end - start;
576                         if (len >= 0) {
577                                 ITypedRegion[] regions = null;
578                                 try {
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];
585                                         System.out
586                                                         .println("MultiViewerPartitioner#addOuterPartitions failure\n"
587                                                                         + "start:"
588                                                                         + start
589                                                                         + " length:"
590                                                                         + len
591                                                                         + "\n" + outerDocument.get(start, len));
592                                 }
593                                 for (int i = 0; i < regions.length; i++) {
594                                         ITypedRegion region = regions[i];
595
596                                         // convert back to parent offsets
597                                         start = outerDocument.getParentOffset(region.getOffset());
598                                         end = start + region.getLength();
599
600                                         if (prev != null) {
601                                                 offset = prev.offset + prev.length;
602                                                 if (start < offset) {
603                                                         start = offset;
604                                                 }
605                                         }
606
607                                         if (next != null) {
608                                                 offset = next.offset;
609                                                 if (offset < end) {
610                                                         end = offset;
611                                                 }
612                                         }
613                                         // if (DEBUG) {
614                                         // if (end - start<0) {
615                                         // showList(list);
616                                         // System.out.print("MultiViewPartitioner::addOuterPartitions
617                                         // - Offset: ");
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();
627                                         // }
628                                         // }
629                                         list.add(new TypedRegion(start, end - start,
630                                                         getContentType(null, region.getType())));
631                                 }
632                         }
633                 } catch (BadLocationException x) {
634                 }
635         }
636
637         private void addInnerPartitions(List list, int offset, int length,
638                         FlatNode position) {
639                 InnerDocumentView innerDocument = null;
640                 if (position instanceof ViewNode) {
641                         // TODO: revisit condition
642                         innerDocument = ((ViewNode) position).view;
643                 }
644
645                 if (innerDocument == null) {
646                         // simple partition
647                         // if (DEBUG) {
648                         // if (position.length<0) {
649                         // throw new IndexOutOfBoundsException();
650                         // }
651                         // }
652                         list.add(new TypedRegion(position.offset, position.length,
653                                         getContentType(position.type, null)));
654                         return;
655                 }
656
657                 // multiplexing to inner view
658                 try {
659                         // limit region
660                         int start = Math.max(offset, position.offset);
661                         int end = Math.min(offset + length, position.offset
662                                         + position.length);
663
664                         // convert to document offsets
665                         length = end - start;
666                         offset = innerDocument.getLocalOffset(start);
667
668                         ITypedRegion[] regions = innerDocument.computePartitioning(offset,
669                                         length);
670
671                         for (int i = 0; i < regions.length; i++) {
672                                 ITypedRegion region = regions[i];
673
674                                 // convert back to parent offsets
675                                 offset = innerDocument.getParentOffset(region.getOffset());
676                                 length = region.getLength();
677                                 // if (DEBUG) {
678                                 // if (length<0) {
679                                 // throw new IndexOutOfBoundsException();
680                                 // }
681                                 // }
682                                 list.add(new TypedRegion(offset, length, getContentType(
683                                                 position.type, region.getType())));
684                         }
685                 } catch (BadLocationException x) {
686                 }
687         }
688
689         /*
690          * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
691          */
692         public ITypedRegion getPartition(int offset) {
693                 if (nodes.size() == 0) {
694                         return getOuterPartition(offset, null, null);
695                 }
696
697                 int index = computeFlatNodeIndex(offset);
698                 if (index < nodes.size()) {
699                         FlatNode next = (FlatNode) nodes.get(index);
700
701                         if (offset == next.offset) {
702                                 return getInnerPartition(offset, next);
703                         }
704
705                         if (index == 0) {
706                                 return getOuterPartition(offset, null, next);
707                         }
708
709                         FlatNode prev = (FlatNode) nodes.get(index - 1);
710
711                         if (prev.includes(offset)) {
712                                 return getInnerPartition(offset, prev);
713                         }
714
715                         return getOuterPartition(offset, prev, next);
716                 }
717
718                 FlatNode prev = (FlatNode) nodes.get(nodes.size() - 1);
719
720                 if (prev.includes(offset)) {
721                         return getInnerPartition(offset, prev);
722                 }
723
724                 return getOuterPartition(offset, prev, null);
725         }
726
727         protected ITypedRegion getOuterPartition(int offset, FlatNode prev,
728                         FlatNode next) {
729                 try {
730                         int start, end;
731                         String type;
732
733                         if (outerDocument == null) {
734                                 start = 0;
735                                 end = document.getLength();
736                                 type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
737                         } else {
738                                 int outerOffset = outerDocument.getLocalOffset(offset);
739                                 // axelcl start
740                                 if (outerOffset < 0) {
741                                         start = 0;
742                                         end = document.getLength();
743                                         type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
744                                 } else {
745                                         // axelcl end
746                                         ITypedRegion region = outerDocument
747                                                         .getPartition(outerOffset);
748
749                                         start = region.getOffset();
750                                         end = start + region.getLength();
751
752                                         // convert to parent offset
753                                         start = outerDocument.getParentOffset(start);
754                                         end = outerDocument.getParentOffset(end);
755
756                                         type = getContentType(null, region.getType());
757                                 }
758                         }
759
760                         if (prev != null) {
761                                 offset = prev.offset + prev.length;
762                                 if (start < offset) {
763                                         start = offset;
764                                 }
765                         }
766
767                         if (next != null) {
768                                 offset = next.offset;
769                                 if (offset < end) {
770                                         end = offset;
771                                 }
772                         }
773
774                         return new TypedRegion(start, end - start, type);
775                 } catch (BadLocationException x) {
776                         x.printStackTrace();
777                         throw new IllegalArgumentException();
778                 }
779         }
780
781         protected ITypedRegion getInnerPartition(int offset, FlatNode position) {
782                 if (position instanceof ViewNode) {
783                         // TODO: revisit condition
784                         InnerDocumentView innerDocument = ((ViewNode) position).view;
785
786                         if (innerDocument != null) {
787                                 // multiplexing to inner view
788                                 try {
789                                         // convert to inner offset
790                                         ITypedRegion region = innerDocument
791                                                         .getPartition(innerDocument.getLocalOffset(offset));
792
793                                         // convert to parent offset
794                                         offset = innerDocument.getParentOffset(region.getOffset());
795
796                                         return new TypedRegion(offset, region.getLength(),
797                                                         getContentType(position.type, region.getType()));
798                                 } catch (BadLocationException x) {
799                                 }
800                         }
801                 }
802
803                 // simple partition
804                 return new TypedRegion(position.offset, position.length, position.type);
805         }
806 }