Source Code Cross Referenced for ProjectionDocument.java in  » IDE-Eclipse » text » org » eclipse » jface » text » projection » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Eclipse » text » org.eclipse.jface.text.projection 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*******************************************************************************
002:         * Copyright (c) 2000, 2007 IBM Corporation and others.
003:         * All rights reserved. This program and the accompanying materials
004:         * are made available under the terms of the Eclipse Public License v1.0
005:         * which accompanies this distribution, and is available at
006:         * http://www.eclipse.org/legal/epl-v10.html
007:         *
008:         * Contributors:
009:         *     IBM Corporation - initial API and implementation
010:         *******************************************************************************/package org.eclipse.jface.text.projection;
011:
012:        import java.util.ArrayList;
013:        import java.util.List;
014:
015:        import org.eclipse.jface.text.AbstractDocument;
016:        import org.eclipse.jface.text.BadLocationException;
017:        import org.eclipse.jface.text.BadPositionCategoryException;
018:        import org.eclipse.jface.text.DefaultLineTracker;
019:        import org.eclipse.jface.text.DocumentEvent;
020:        import org.eclipse.jface.text.IDocument;
021:        import org.eclipse.jface.text.IDocumentExtension;
022:        import org.eclipse.jface.text.IDocumentInformationMapping;
023:        import org.eclipse.jface.text.IDocumentListener;
024:        import org.eclipse.jface.text.ILineTracker;
025:        import org.eclipse.jface.text.IRegion;
026:        import org.eclipse.jface.text.ITextStore;
027:        import org.eclipse.jface.text.Position;
028:        import org.eclipse.jface.text.Region;
029:        import org.eclipse.jface.text.TextUtilities;
030:
031:        /**
032:         * A <code>ProjectionDocument</code> represents a projection of its master
033:         * document. The contents of a projection document is a sequence of fragments of
034:         * the master document, i.e. the projection document can be thought as being
035:         * constructed from the master document by not copying the whole master document
036:         * but omitting several ranges of the master document.
037:         * <p>
038:         * The projection document indirectly utilizes its master document as
039:         * <code>ITextStore</code> by means of a <code>ProjectionTextStore</code>.
040:         * <p>
041:         * The content of a projection document can be changed in two ways. Either by a
042:         * text replace applied to the master document or the projection document. Or by
043:         * changing the projection between the master document and the projection
044:         * document. For the latter the two methods <code>addMasterDocumentRange</code>
045:         * and <code>removeMasterDocumentRange</code> are provided. For any
046:         * manipulation, the projection document sends out a
047:         * {@link org.eclipse.jface.text.projection.ProjectionDocumentEvent} describing
048:         * the change.
049:         * <p>
050:         * Clients are not supposed to directly instantiate this class. In order to
051:         * obtain a projection document, a
052:         * {@link org.eclipse.jface.text.projection.ProjectionDocumentManager}should be
053:         * used. This class is not intended to be subclassed outside of its origin
054:         * package.</p>
055:         *
056:         * @since 3.0
057:         */
058:        public class ProjectionDocument extends AbstractDocument {
059:
060:            /**
061:             * Prefix of the name of the position category used to keep track of the master
062:             * document's fragments that correspond to the segments of the projection
063:             * document.
064:             */
065:            private final static String FRAGMENTS_CATEGORY_PREFIX = "__fragmentsCategory"; //$NON-NLS-1$
066:
067:            /**
068:             * Name of the position category used to keep track of the project
069:             * document's segments that correspond to the fragments of the master
070:             * document.
071:             */
072:            private final static String SEGMENTS_CATEGORY = "__segmentsCategory"; //$NON-NLS-1$
073:
074:            /** The master document */
075:            private IDocument fMasterDocument;
076:            /** The master document as document extension */
077:            private IDocumentExtension fMasterDocumentExtension;
078:            /** The fragments' position category */
079:            private String fFragmentsCategory;
080:            /** The segment's position category */
081:            private String fSegmentsCategory;
082:            /** The document event issued by the master document */
083:            private DocumentEvent fMasterEvent;
084:            /** The document event to be issued by the projection document */
085:            private ProjectionDocumentEvent fSlaveEvent;
086:            /** The original document event generated by a direct manipulation of this projection document */
087:            private DocumentEvent fOriginalEvent;
088:            /** Indicates whether the projection document initiated a master document update or not */
089:            private boolean fIsUpdating = false;
090:            /** Indicated whether the projection document is in auto expand mode nor not */
091:            private boolean fIsAutoExpanding = false;
092:            /** The position updater for the segments */
093:            private SegmentUpdater fSegmentUpdater;
094:            /** The position updater for the fragments */
095:            private FragmentUpdater fFragmentsUpdater;
096:            /** The projection mapping */
097:            private ProjectionMapping fMapping;
098:
099:            /**
100:             * Creates a projection document for the given master document.
101:             *
102:             * @param masterDocument the master document
103:             */
104:            public ProjectionDocument(IDocument masterDocument) {
105:                super ();
106:
107:                fMasterDocument = masterDocument;
108:                if (fMasterDocument instanceof  IDocumentExtension)
109:                    fMasterDocumentExtension = (IDocumentExtension) fMasterDocument;
110:
111:                fSegmentsCategory = SEGMENTS_CATEGORY;
112:                fFragmentsCategory = FRAGMENTS_CATEGORY_PREFIX + hashCode();
113:                fMasterDocument.addPositionCategory(fFragmentsCategory);
114:                fFragmentsUpdater = new FragmentUpdater(fFragmentsCategory);
115:                fMasterDocument.addPositionUpdater(fFragmentsUpdater);
116:
117:                fMapping = new ProjectionMapping(masterDocument,
118:                        fFragmentsCategory, this , fSegmentsCategory);
119:
120:                ITextStore s = new ProjectionTextStore(masterDocument, fMapping);
121:                ILineTracker tracker = new DefaultLineTracker();
122:
123:                setTextStore(s);
124:                setLineTracker(tracker);
125:
126:                completeInitialization();
127:
128:                initializeProjection();
129:                tracker.set(s.get(0, s.getLength()));
130:            }
131:
132:            /**
133:             * Disposes this projection document.
134:             */
135:            public void dispose() {
136:                fMasterDocument.removePositionUpdater(fFragmentsUpdater);
137:                try {
138:                    fMasterDocument.removePositionCategory(fFragmentsCategory);
139:                } catch (BadPositionCategoryException x) {
140:                    // allow multiple dispose calls
141:                }
142:            }
143:
144:            private void internalError() {
145:                throw new IllegalStateException();
146:            }
147:
148:            /**
149:             * Returns the fragments of the master documents.
150:             *
151:             * @return the fragment of the master document
152:             */
153:            protected final Position[] getFragments() {
154:                try {
155:                    return fMasterDocument.getPositions(fFragmentsCategory);
156:                } catch (BadPositionCategoryException e) {
157:                    internalError();
158:                }
159:                // unreachable
160:                return null;
161:            }
162:
163:            /**
164:             * Returns the segments of this projection document.
165:             *
166:             * @return the segments of this projection document
167:             */
168:            protected final Position[] getSegments() {
169:                try {
170:                    return getPositions(fSegmentsCategory);
171:                } catch (BadPositionCategoryException e) {
172:                    internalError();
173:                }
174:                // unreachable
175:                return null;
176:            }
177:
178:            /**
179:             * Returns the projection mapping used by this document.
180:             *
181:             * @return the projection mapping used by this document
182:             */
183:            public IDocumentInformationMapping getProjectionMapping() {
184:                return fMapping;
185:            }
186:
187:            /**
188:             * Returns the master document of this projection document.
189:             *
190:             * @return the master document of this projection document
191:             */
192:            public IDocument getMasterDocument() {
193:                return fMasterDocument;
194:            }
195:
196:            /*
197:             * @see org.eclipse.jface.text.IDocumentExtension4#getDefaultLineDelimiter()
198:             * @since 3.1
199:             */
200:            public String getDefaultLineDelimiter() {
201:                return TextUtilities.getDefaultLineDelimiter(fMasterDocument);
202:            }
203:
204:            /**
205:             * Initializes the projection document from the master document based on
206:             * the master's fragments.
207:             */
208:            private void initializeProjection() {
209:
210:                try {
211:
212:                    addPositionCategory(fSegmentsCategory);
213:                    fSegmentUpdater = new SegmentUpdater(fSegmentsCategory);
214:                    addPositionUpdater(fSegmentUpdater);
215:
216:                    int offset = 0;
217:                    Position[] fragments = getFragments();
218:                    for (int i = 0; i < fragments.length; i++) {
219:                        Fragment fragment = (Fragment) fragments[i];
220:                        Segment segment = new Segment(offset, fragment
221:                                .getLength());
222:                        segment.fragment = fragment;
223:                        addPosition(fSegmentsCategory, segment);
224:                        offset += fragment.length;
225:                    }
226:
227:                } catch (BadPositionCategoryException x) {
228:                    internalError();
229:                } catch (BadLocationException x) {
230:                    internalError();
231:                }
232:            }
233:
234:            /**
235:             * Creates a segment for the given fragment at the given position inside the list of segments.
236:             *
237:             * @param fragment the corresponding fragment
238:             * @param index the index in the list of segments
239:             * @return the created segment
240:             * @throws BadLocationException in case the fragment is invalid
241:             * @throws BadPositionCategoryException in case the segment category is invalid
242:             */
243:            private Segment createSegmentFor(Fragment fragment, int index)
244:                    throws BadLocationException, BadPositionCategoryException {
245:
246:                int offset = 0;
247:                if (index > 0) {
248:                    Position[] segments = getSegments();
249:                    Segment segment = (Segment) segments[index - 1];
250:                    offset = segment.getOffset() + segment.getLength();
251:                }
252:
253:                Segment segment = new Segment(offset, 0);
254:                segment.fragment = fragment;
255:                fragment.segment = segment;
256:                addPosition(fSegmentsCategory, segment);
257:                return segment;
258:            }
259:
260:            /**
261:             * Adds the given range of the master document to this projection document.
262:             *
263:             * @param offsetInMaster offset of the master document range
264:             * @param lengthInMaster length of the master document range
265:             * @param masterDocumentEvent the master document event that causes this
266:             *            projection change or <code>null</code> if none
267:             * @throws BadLocationException if the given range is invalid in the master
268:             *             document
269:             */
270:            private void internalAddMasterDocumentRange(int offsetInMaster,
271:                    int lengthInMaster, DocumentEvent masterDocumentEvent)
272:                    throws BadLocationException {
273:                if (lengthInMaster == 0)
274:                    return;
275:
276:                try {
277:
278:                    Position[] fragments = getFragments();
279:                    int index = fMasterDocument.computeIndexInCategory(
280:                            fFragmentsCategory, offsetInMaster);
281:
282:                    Fragment left = null;
283:                    Fragment right = null;
284:
285:                    if (index < fragments.length) {
286:                        Fragment fragment = (Fragment) fragments[index];
287:                        if (offsetInMaster == fragment.offset)
288:                            if (fragment.length == 0) // the fragment does not overlap - it is a zero-length fragment at the same offset
289:                                left = fragment;
290:                            else
291:                                throw new IllegalArgumentException(
292:                                        "overlaps with existing fragment"); //$NON-NLS-1$
293:                        if (offsetInMaster + lengthInMaster == fragment.offset)
294:                            right = fragment;
295:                    }
296:
297:                    if (0 < index && index <= fragments.length) {
298:                        Fragment fragment = (Fragment) fragments[index - 1];
299:                        if (fragment.includes(offsetInMaster))
300:                            throw new IllegalArgumentException(
301:                                    "overlaps with existing fragment"); //$NON-NLS-1$
302:                        if (fragment.getOffset() + fragment.getLength() == offsetInMaster)
303:                            left = fragment;
304:                    }
305:
306:                    int offsetInSlave = 0;
307:                    if (index > 0) {
308:                        Fragment fragment = (Fragment) fragments[index - 1];
309:                        Segment segment = fragment.segment;
310:                        offsetInSlave = segment.getOffset()
311:                                + segment.getLength();
312:                    }
313:
314:                    ProjectionDocumentEvent event = new ProjectionDocumentEvent(
315:                            this , offsetInSlave, 0, fMasterDocument.get(
316:                                    offsetInMaster, lengthInMaster),
317:                            offsetInMaster, lengthInMaster, masterDocumentEvent);
318:                    super .fireDocumentAboutToBeChanged(event);
319:
320:                    // check for neighboring fragment
321:                    if (left != null && right != null) {
322:
323:                        int endOffset = right.getOffset() + right.getLength();
324:                        left.setLength(endOffset - left.getOffset());
325:                        left.segment.setLength(left.segment.getLength()
326:                                + right.segment.getLength());
327:
328:                        removePosition(fSegmentsCategory, right.segment);
329:                        fMasterDocument.removePosition(fFragmentsCategory,
330:                                right);
331:
332:                    } else if (left != null) {
333:                        int endOffset = offsetInMaster + lengthInMaster;
334:                        left.setLength(endOffset - left.getOffset());
335:                        left.segment.markForStretch();
336:
337:                    } else if (right != null) {
338:                        right.setOffset(right.getOffset() - lengthInMaster);
339:                        right.setLength(right.getLength() + lengthInMaster);
340:                        right.segment.markForStretch();
341:
342:                    } else {
343:                        // create a new segment
344:                        Fragment fragment = new Fragment(offsetInMaster,
345:                                lengthInMaster);
346:                        fMasterDocument.addPosition(fFragmentsCategory,
347:                                fragment);
348:                        Segment segment = createSegmentFor(fragment, index);
349:                        segment.markForStretch();
350:                    }
351:
352:                    getTracker().replace(event.getOffset(), event.getLength(),
353:                            event.getText());
354:                    super .fireDocumentChanged(event);
355:
356:                } catch (BadPositionCategoryException x) {
357:                    internalError();
358:                }
359:            }
360:
361:            /**
362:             * Finds the fragment of the master document that represents the given range.
363:             *
364:             * @param offsetInMaster the offset of the range in the master document
365:             * @param lengthInMaster the length of the range in the master document
366:             * @return the fragment representing the given master document range
367:             */
368:            private Fragment findFragment(int offsetInMaster, int lengthInMaster) {
369:                Position[] fragments = getFragments();
370:                for (int i = 0; i < fragments.length; i++) {
371:                    Fragment f = (Fragment) fragments[i];
372:                    if (f.getOffset() <= offsetInMaster
373:                            && offsetInMaster + lengthInMaster <= f.getOffset()
374:                                    + f.getLength())
375:                        return f;
376:                }
377:                return null;
378:            }
379:
380:            /**
381:             * Removes the given range of the master document from this projection
382:             * document.
383:             *
384:             * @param offsetInMaster the offset of the range in the master document
385:             * @param lengthInMaster the length of the range in the master document
386:             *
387:             * @throws BadLocationException if the given range is not valid in the
388:             *             master document
389:             * @throws IllegalArgumentException if the given range is not projected in
390:             *             this projection document or is not completely comprised by
391:             *             an existing fragment
392:             */
393:            private void internalRemoveMasterDocumentRange(int offsetInMaster,
394:                    int lengthInMaster) throws BadLocationException {
395:                try {
396:
397:                    IRegion imageRegion = fMapping
398:                            .toExactImageRegion(new Region(offsetInMaster,
399:                                    lengthInMaster));
400:                    if (imageRegion == null)
401:                        throw new IllegalArgumentException();
402:
403:                    Fragment fragment = findFragment(offsetInMaster,
404:                            lengthInMaster);
405:                    if (fragment == null)
406:                        throw new IllegalArgumentException();
407:
408:                    ProjectionDocumentEvent event = new ProjectionDocumentEvent(
409:                            this , imageRegion.getOffset(), imageRegion
410:                                    .getLength(),
411:                            "", offsetInMaster, lengthInMaster); //$NON-NLS-1$
412:                    super .fireDocumentAboutToBeChanged(event);
413:
414:                    if (fragment.getOffset() == offsetInMaster) {
415:                        fragment.setOffset(offsetInMaster + lengthInMaster);
416:                        fragment.setLength(fragment.getLength()
417:                                - lengthInMaster);
418:                    } else if (fragment.getOffset() + fragment.getLength() == offsetInMaster
419:                            + lengthInMaster) {
420:                        fragment.setLength(fragment.getLength()
421:                                - lengthInMaster);
422:                    } else {
423:                        // split fragment into three fragments, let position updater remove it
424:
425:                        // add fragment for the region to be removed
426:                        Fragment newFragment = new Fragment(offsetInMaster,
427:                                lengthInMaster);
428:                        Segment segment = new Segment(imageRegion.getOffset(),
429:                                imageRegion.getLength());
430:                        newFragment.segment = segment;
431:                        segment.fragment = newFragment;
432:                        fMasterDocument.addPosition(fFragmentsCategory,
433:                                newFragment);
434:                        addPosition(fSegmentsCategory, segment);
435:
436:                        // add fragment for the remainder right of the deleted range in the original fragment
437:                        int offset = offsetInMaster + lengthInMaster;
438:                        newFragment = new Fragment(offset, fragment.getOffset()
439:                                + fragment.getLength() - offset);
440:                        offset = imageRegion.getOffset()
441:                                + imageRegion.getLength();
442:                        segment = new Segment(offset, fragment.segment
443:                                .getOffset()
444:                                + fragment.segment.getLength() - offset);
445:                        newFragment.segment = segment;
446:                        segment.fragment = newFragment;
447:                        fMasterDocument.addPosition(fFragmentsCategory,
448:                                newFragment);
449:                        addPosition(fSegmentsCategory, segment);
450:
451:                        // adjust length of initial fragment (the left one)
452:                        fragment.setLength(offsetInMaster
453:                                - fragment.getOffset());
454:                        fragment.segment.setLength(imageRegion.getOffset()
455:                                - fragment.segment.getOffset());
456:                    }
457:
458:                    getTracker().replace(event.getOffset(), event.getLength(),
459:                            event.getText());
460:                    super .fireDocumentChanged(event);
461:
462:                } catch (BadPositionCategoryException x) {
463:                    internalError();
464:                }
465:            }
466:
467:            /**
468:             * Returns the sequence of all master document regions which are contained
469:             * in the given master document range and which are not yet part of this
470:             * projection document.
471:             *
472:             * @param offsetInMaster the range offset in the master document
473:             * @param lengthInMaster the range length in the master document
474:             * @return the sequence of regions which are not yet part of the projection
475:             *         document
476:             * @throws BadLocationException in case the given range is invalid in the
477:             *         master document
478:             */
479:            public final IRegion[] computeUnprojectedMasterRegions(
480:                    int offsetInMaster, int lengthInMaster)
481:                    throws BadLocationException {
482:
483:                IRegion[] fragments = null;
484:                IRegion imageRegion = fMapping.toImageRegion(new Region(
485:                        offsetInMaster, lengthInMaster));
486:                if (imageRegion != null)
487:                    fragments = fMapping.toExactOriginRegions(imageRegion);
488:
489:                if (fragments == null || fragments.length == 0)
490:                    return new IRegion[] { new Region(offsetInMaster,
491:                            lengthInMaster) };
492:
493:                List gaps = new ArrayList();
494:
495:                IRegion region = fragments[0];
496:                if (offsetInMaster < region.getOffset())
497:                    gaps.add(new Region(offsetInMaster, region.getOffset()
498:                            - offsetInMaster));
499:
500:                for (int i = 0; i < fragments.length - 1; i++) {
501:                    IRegion left = fragments[i];
502:                    IRegion right = fragments[i + 1];
503:                    int leftEnd = left.getOffset() + left.getLength();
504:                    if (leftEnd < right.getOffset())
505:                        gaps.add(new Region(leftEnd, right.getOffset()
506:                                - leftEnd));
507:                }
508:
509:                region = fragments[fragments.length - 1];
510:                int leftEnd = region.getOffset() + region.getLength();
511:                int rightEnd = offsetInMaster + lengthInMaster;
512:                if (leftEnd < rightEnd)
513:                    gaps.add(new Region(leftEnd, rightEnd - leftEnd));
514:
515:                IRegion[] result = new IRegion[gaps.size()];
516:                gaps.toArray(result);
517:                return result;
518:            }
519:
520:            /**
521:             * Returns the first master document region which is contained in the given
522:             * master document range and which is not yet part of this projection
523:             * document.
524:             *
525:             * @param offsetInMaster the range offset in the master document
526:             * @param lengthInMaster the range length in the master document
527:             * @return the first region that is not yet part of the projection document
528:             * @throws BadLocationException in case the given range is invalid in the
529:             *         master document
530:             * @since 3.1
531:             */
532:            private IRegion computeFirstUnprojectedMasterRegion(
533:                    int offsetInMaster, int lengthInMaster)
534:                    throws BadLocationException {
535:
536:                IRegion[] fragments = null;
537:                IRegion imageRegion = fMapping.toImageRegion(new Region(
538:                        offsetInMaster, lengthInMaster));
539:                if (imageRegion != null)
540:                    fragments = fMapping.toExactOriginRegions(imageRegion);
541:
542:                if (fragments == null || fragments.length == 0)
543:                    return new Region(offsetInMaster, lengthInMaster);
544:
545:                IRegion region = fragments[0];
546:                if (offsetInMaster < region.getOffset())
547:                    return new Region(offsetInMaster, region.getOffset()
548:                            - offsetInMaster);
549:
550:                for (int i = 0; i < fragments.length - 1; i++) {
551:                    IRegion left = fragments[i];
552:                    IRegion right = fragments[i + 1];
553:                    int leftEnd = left.getOffset() + left.getLength();
554:                    if (leftEnd < right.getOffset())
555:                        return new Region(leftEnd, right.getOffset() - leftEnd);
556:                }
557:
558:                region = fragments[fragments.length - 1];
559:                int leftEnd = region.getOffset() + region.getLength();
560:                int rightEnd = offsetInMaster + lengthInMaster;
561:                if (leftEnd < rightEnd)
562:                    return new Region(leftEnd, rightEnd - leftEnd);
563:
564:                return null;
565:            }
566:
567:            /**
568:             * Ensures that the given range of the master document is part of this
569:             * projection document.
570:             *
571:             * @param offsetInMaster the offset of the master document range
572:             * @param lengthInMaster the length of the master document range
573:             * @throws BadLocationException in case the master event is not valid
574:             */
575:            public void addMasterDocumentRange(int offsetInMaster,
576:                    int lengthInMaster) throws BadLocationException {
577:                addMasterDocumentRange(offsetInMaster, lengthInMaster, null);
578:            }
579:
580:            /**
581:             * Ensures that the given range of the master document is part of this
582:             * projection document.
583:             *
584:             * @param offsetInMaster the offset of the master document range
585:             * @param lengthInMaster the length of the master document range
586:             * @param masterDocumentEvent the master document event which causes this
587:             *            projection change, or <code>null</code> if none
588:             * @throws BadLocationException in case the master event is not valid
589:             */
590:            private void addMasterDocumentRange(int offsetInMaster,
591:                    int lengthInMaster, DocumentEvent masterDocumentEvent)
592:                    throws BadLocationException {
593:                /*
594:                 * Calling internalAddMasterDocumentRange may cause other master ranges
595:                 * to become unfolded, resulting in re-entrant calls to this method. In
596:                 * order to not add a region twice, we have to compute the next region
597:                 * to add in every iteration.
598:                 *
599:                 * To place an upper bound on the number of iterations, we use the number
600:                 * of fragments * 2 as the limit.
601:                 */
602:                int limit = Math.max(getFragments().length * 2, 20);
603:                while (true) {
604:                    if (limit-- < 0)
605:                        throw new IllegalArgumentException(
606:                                "safety loop termination"); //$NON-NLS-1$
607:
608:                    IRegion gap = computeFirstUnprojectedMasterRegion(
609:                            offsetInMaster, lengthInMaster);
610:                    if (gap == null)
611:                        return;
612:
613:                    internalAddMasterDocumentRange(gap.getOffset(), gap
614:                            .getLength(), masterDocumentEvent);
615:                }
616:            }
617:
618:            /**
619:             * Ensures that the given range of the master document is not part of this
620:             * projection document.
621:             *
622:             * @param offsetInMaster the offset of the master document range
623:             * @param lengthInMaster the length of the master document range
624:             * @throws BadLocationException in case the master event is not valid
625:             */
626:            public void removeMasterDocumentRange(int offsetInMaster,
627:                    int lengthInMaster) throws BadLocationException {
628:                IRegion[] fragments = computeProjectedMasterRegions(
629:                        offsetInMaster, lengthInMaster);
630:                if (fragments == null || fragments.length == 0)
631:                    return;
632:
633:                for (int i = 0; i < fragments.length; i++) {
634:                    IRegion fragment = fragments[i];
635:                    internalRemoveMasterDocumentRange(fragment.getOffset(),
636:                            fragment.getLength());
637:                }
638:            }
639:
640:            /**
641:             * Returns the sequence of all master document regions with are contained in the given master document
642:             * range and which are part of this projection document. May return <code>null</code> if no such
643:             * regions exist.
644:             *
645:             * @param offsetInMaster the range offset in the master document
646:             * @param lengthInMaster the range length in the master document
647:             * @return the sequence of regions which are part of the projection document or <code>null</code>
648:             * @throws BadLocationException in case the given range is invalid in the master document
649:             */
650:            final public IRegion[] computeProjectedMasterRegions(
651:                    int offsetInMaster, int lengthInMaster)
652:                    throws BadLocationException {
653:                IRegion imageRegion = fMapping.toImageRegion(new Region(
654:                        offsetInMaster, lengthInMaster));
655:                return imageRegion != null ? fMapping
656:                        .toExactOriginRegions(imageRegion) : null;
657:            }
658:
659:            /**
660:             * Returns whether this projection is being updated.
661:             *
662:             * @return <code>true</code> if the document is updating
663:             */
664:            protected boolean isUpdating() {
665:                return fIsUpdating;
666:            }
667:
668:            /*
669:             * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
670:             */
671:            public void replace(int offset, int length, String text)
672:                    throws BadLocationException {
673:                try {
674:                    fIsUpdating = true;
675:                    if (fMasterDocumentExtension != null)
676:                        fMasterDocumentExtension
677:                                .stopPostNotificationProcessing();
678:
679:                    super .replace(offset, length, text);
680:
681:                } finally {
682:                    fIsUpdating = false;
683:                    if (fMasterDocumentExtension != null)
684:                        fMasterDocumentExtension
685:                                .resumePostNotificationProcessing();
686:                }
687:            }
688:
689:            /*
690:             * @see org.eclipse.jface.text.IDocument#set(java.lang.String)
691:             */
692:            public void set(String text) {
693:                try {
694:                    fIsUpdating = true;
695:                    if (fMasterDocumentExtension != null)
696:                        fMasterDocumentExtension
697:                                .stopPostNotificationProcessing();
698:
699:                    super .set(text);
700:
701:                } finally {
702:                    fIsUpdating = false;
703:                    if (fMasterDocumentExtension != null)
704:                        fMasterDocumentExtension
705:                                .resumePostNotificationProcessing();
706:                }
707:            }
708:
709:            /**
710:             * Transforms a document event of the master document into a projection
711:             * document based document event.
712:             *
713:             * @param masterEvent the master document event
714:             * @return the slave document event
715:             * @throws BadLocationException in case the master event is not valid
716:             */
717:            private ProjectionDocumentEvent normalize(DocumentEvent masterEvent)
718:                    throws BadLocationException {
719:                if (!isUpdating()) {
720:                    IRegion imageRegion = fMapping
721:                            .toExactImageRegion(new Region(masterEvent
722:                                    .getOffset(), masterEvent.getLength()));
723:                    if (imageRegion != null)
724:                        return new ProjectionDocumentEvent(this , imageRegion
725:                                .getOffset(), imageRegion.getLength(),
726:                                masterEvent.getText(), masterEvent);
727:                    return null;
728:                }
729:
730:                ProjectionDocumentEvent event = new ProjectionDocumentEvent(
731:                        this , fOriginalEvent.getOffset(), fOriginalEvent
732:                                .getLength(), fOriginalEvent.getText(),
733:                        masterEvent);
734:                fOriginalEvent = null;
735:                return event;
736:            }
737:
738:            /**
739:             * Ensures that when the master event affects this projection document, that the whole region described by the
740:             * event is part of this projection document.
741:             *
742:             * @param masterEvent the master document event
743:             * @return <code>true</code> if masterEvent affects this projection document
744:             * @throws BadLocationException in case the master event is not valid
745:             */
746:            protected final boolean adaptProjectionToMasterChange(
747:                    DocumentEvent masterEvent) throws BadLocationException {
748:                if (!isUpdating()
749:                        && fFragmentsUpdater.affectsPositions(masterEvent)
750:                        || fIsAutoExpanding && masterEvent.getLength() > 0) {
751:
752:                    addMasterDocumentRange(masterEvent.getOffset(), masterEvent
753:                            .getLength(), masterEvent);
754:                    return true;
755:
756:                } else if (fMapping.getImageLength() == 0
757:                        && masterEvent.getLength() == 0) {
758:
759:                    Position[] fragments = getFragments();
760:                    if (fragments.length == 0) {
761:                        // there is no segment in this projection document, thus one must be created
762:                        // need to bypass the usual infrastructure as the new segment/fragment would be of length 0 and thus the segmentation be not well formed
763:                        try {
764:                            Fragment fragment = new Fragment(0, 0);
765:                            fMasterDocument.addPosition(fFragmentsCategory,
766:                                    fragment);
767:                            createSegmentFor(fragment, 0);
768:                        } catch (BadPositionCategoryException x) {
769:                            internalError();
770:                        }
771:                    }
772:                }
773:
774:                return isUpdating();
775:            }
776:
777:            /**
778:             * When called, this projection document is informed about a forthcoming
779:             * change of its master document. This projection document checks whether
780:             * the master document change affects it and if so informs all document
781:             * listeners.
782:             *
783:             * @param masterEvent the master document event
784:             */
785:            public void masterDocumentAboutToBeChanged(DocumentEvent masterEvent) {
786:                try {
787:
788:                    boolean assertNotNull = adaptProjectionToMasterChange(masterEvent);
789:                    fSlaveEvent = normalize(masterEvent);
790:                    if (assertNotNull && fSlaveEvent == null)
791:                        internalError();
792:
793:                    fMasterEvent = masterEvent;
794:                    if (fSlaveEvent != null)
795:                        delayedFireDocumentAboutToBeChanged();
796:
797:                } catch (BadLocationException e) {
798:                    internalError();
799:                }
800:            }
801:
802:            /**
803:             * When called, this projection document is informed about a change of its
804:             * master document. If this projection document is affected it informs all
805:             * of its document listeners.
806:             *
807:             * @param masterEvent the master document event
808:             */
809:            public void masterDocumentChanged(DocumentEvent masterEvent) {
810:                if (!isUpdating() && masterEvent == fMasterEvent) {
811:                    if (fSlaveEvent != null) {
812:                        try {
813:                            getTracker().replace(fSlaveEvent.getOffset(),
814:                                    fSlaveEvent.getLength(),
815:                                    fSlaveEvent.getText());
816:                            fireDocumentChanged(fSlaveEvent);
817:                        } catch (BadLocationException e) {
818:                            internalError();
819:                        }
820:                    } else if (ensureWellFormedSegmentation(masterEvent
821:                            .getOffset()))
822:                        fMapping.projectionChanged();
823:                }
824:            }
825:
826:            /*
827:             * @see org.eclipse.jface.text.AbstractDocument#fireDocumentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
828:             */
829:            protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
830:                fOriginalEvent = event;
831:                // delay it until there is a notification from the master document
832:                // at this point, it is expensive to construct the master document information
833:            }
834:
835:            /**
836:             * Fires the slave document event as about-to-be-changed event to all registered listeners.
837:             */
838:            private void delayedFireDocumentAboutToBeChanged() {
839:                super .fireDocumentAboutToBeChanged(fSlaveEvent);
840:            }
841:
842:            /**
843:             * Ignores the given event and sends the semantically equal slave document event instead.
844:             *
845:             * @param event the event to be ignored
846:             */
847:            protected void fireDocumentChanged(DocumentEvent event) {
848:                super .fireDocumentChanged(fSlaveEvent);
849:            }
850:
851:            /*
852:             * @see org.eclipse.jface.text.AbstractDocument#updateDocumentStructures(org.eclipse.jface.text.DocumentEvent)
853:             */
854:            protected void updateDocumentStructures(DocumentEvent event) {
855:                super .updateDocumentStructures(event);
856:                ensureWellFormedSegmentation(computeAnchor(event));
857:                fMapping.projectionChanged();
858:            }
859:
860:            private int computeAnchor(DocumentEvent event) {
861:                if (event instanceof  ProjectionDocumentEvent) {
862:                    ProjectionDocumentEvent slave = (ProjectionDocumentEvent) event;
863:                    Object changeType = slave.getChangeType();
864:                    if (ProjectionDocumentEvent.CONTENT_CHANGE == changeType) {
865:                        DocumentEvent master = slave.getMasterEvent();
866:                        if (master != null)
867:                            return master.getOffset();
868:                    } else if (ProjectionDocumentEvent.PROJECTION_CHANGE == changeType) {
869:                        return slave.getMasterOffset();
870:                    }
871:                }
872:                return -1;
873:            }
874:
875:            private boolean ensureWellFormedSegmentation(int anchorOffset) {
876:                boolean changed = false;
877:                Position[] segments = getSegments();
878:                for (int i = 0; i < segments.length; i++) {
879:                    Segment segment = (Segment) segments[i];
880:                    if (segment.isDeleted() || segment.getLength() == 0) {
881:                        try {
882:                            removePosition(fSegmentsCategory, segment);
883:                            fMasterDocument.removePosition(fFragmentsCategory,
884:                                    segment.fragment);
885:                            changed = true;
886:                        } catch (BadPositionCategoryException e) {
887:                            internalError();
888:                        }
889:                    } else if (i < segments.length - 1) {
890:                        Segment next = (Segment) segments[i + 1];
891:                        if (next.isDeleted() || next.getLength() == 0)
892:                            continue;
893:                        Fragment fragment = segment.fragment;
894:                        if (fragment.getOffset() + fragment.getLength() == next.fragment
895:                                .getOffset()) {
896:                            // join fragments and their corresponding segments
897:                            segment.setLength(segment.getLength()
898:                                    + next.getLength());
899:                            fragment.setLength(fragment.getLength()
900:                                    + next.fragment.getLength());
901:                            next.delete();
902:                        }
903:                    }
904:                }
905:
906:                if (changed && anchorOffset != -1) {
907:                    Position[] changedSegments = getSegments();
908:                    if (changedSegments == null || changedSegments.length == 0) {
909:                        Fragment fragment = new Fragment(anchorOffset, 0);
910:                        try {
911:                            fMasterDocument.addPosition(fFragmentsCategory,
912:                                    fragment);
913:                            createSegmentFor(fragment, 0);
914:                        } catch (BadLocationException e) {
915:                            internalError();
916:                        } catch (BadPositionCategoryException e) {
917:                            internalError();
918:                        }
919:                    }
920:                }
921:
922:                return changed;
923:            }
924:
925:            /*
926:             * @see IDocumentExtension#registerPostNotificationReplace(IDocumentListener, IDocumentExtension.IReplace)
927:             */
928:            public void registerPostNotificationReplace(
929:                    IDocumentListener owner, IDocumentExtension.IReplace replace) {
930:                if (!isUpdating())
931:                    throw new UnsupportedOperationException();
932:                super .registerPostNotificationReplace(owner, replace);
933:            }
934:
935:            /**
936:             * Sets the auto expand mode for this document.
937:             *
938:             * @param autoExpandMode <code>true</code> if auto-expanding
939:             */
940:            public void setAutoExpandMode(boolean autoExpandMode) {
941:                fIsAutoExpanding = autoExpandMode;
942:            }
943:
944:            /**
945:             * Replaces all master document ranges with the given master document range.
946:             *
947:             * @param offsetInMaster the offset in the master document
948:             * @param lengthInMaster the length in the master document
949:             * @throws BadLocationException if the given range of the master document is not valid
950:             */
951:            public void replaceMasterDocumentRanges(int offsetInMaster,
952:                    int lengthInMaster) throws BadLocationException {
953:                try {
954:
955:                    ProjectionDocumentEvent event = new ProjectionDocumentEvent(
956:                            this , 0, fMapping.getImageLength(), fMasterDocument
957:                                    .get(offsetInMaster, lengthInMaster),
958:                            offsetInMaster, lengthInMaster);
959:                    super .fireDocumentAboutToBeChanged(event);
960:
961:                    Position[] fragments = getFragments();
962:                    for (int i = 0; i < fragments.length; i++) {
963:                        Fragment fragment = (Fragment) fragments[i];
964:                        fMasterDocument.removePosition(fFragmentsCategory,
965:                                fragment);
966:                        removePosition(fSegmentsCategory, fragment.segment);
967:                    }
968:
969:                    Fragment fragment = new Fragment(offsetInMaster,
970:                            lengthInMaster);
971:                    Segment segment = new Segment(0, 0);
972:                    segment.fragment = fragment;
973:                    fragment.segment = segment;
974:                    fMasterDocument.addPosition(fFragmentsCategory, fragment);
975:                    addPosition(fSegmentsCategory, segment);
976:
977:                    getTracker()
978:                            .set(
979:                                    fMasterDocument.get(offsetInMaster,
980:                                            lengthInMaster));
981:                    super .fireDocumentChanged(event);
982:
983:                } catch (BadPositionCategoryException x) {
984:                    internalError();
985:                }
986:            }
987:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.