Source Code Cross Referenced for LinkedModeModel.java in  » IDE-Eclipse » text » org » eclipse » jface » text » link » 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.link 
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.link;
011:
012:        import java.util.ArrayList;
013:        import java.util.Arrays;
014:        import java.util.HashSet;
015:        import java.util.Iterator;
016:        import java.util.List;
017:        import java.util.Map;
018:        import java.util.Set;
019:
020:        import org.eclipse.core.runtime.Assert;
021:
022:        import org.eclipse.text.edits.MalformedTreeException;
023:        import org.eclipse.text.edits.TextEdit;
024:
025:        import org.eclipse.jface.text.BadLocationException;
026:        import org.eclipse.jface.text.BadPositionCategoryException;
027:        import org.eclipse.jface.text.DocumentEvent;
028:        import org.eclipse.jface.text.IDocument;
029:        import org.eclipse.jface.text.IDocumentExtension;
030:        import org.eclipse.jface.text.IDocumentListener;
031:        import org.eclipse.jface.text.IPositionUpdater;
032:        import org.eclipse.jface.text.Position;
033:        import org.eclipse.jface.text.IDocumentExtension.IReplace;
034:
035:        /**
036:         * The model for linked mode, umbrellas several
037:         * {@link LinkedPositionGroup}s. Once installed, the model
038:         * propagates any changes to a position to all its siblings in the same position
039:         * group.
040:         * <p>
041:         * Setting up a model consists of first adding
042:         * <code>LinkedPositionGroup</code>s to it, and then installing the
043:         * model by either calling {@link #forceInstall()} or
044:         * {@link #tryInstall()}. After installing the model, it becomes
045:         * <em>sealed</em> and no more groups may be added.
046:         * </p>
047:         * <p>
048:         * If a document change occurs that would modify more than one position
049:         * group or that would invalidate the disjointness requirement of the positions,
050:         * the model is torn down and all positions are deleted. The same happens
051:         * upon calling {@link #exit(int)}.
052:         * </p>
053:         * <h4>Nesting</h4>
054:         * <p>
055:         * A <code>LinkedModeModel</code> may be nested into another model. This
056:         * happens when installing a model the positions of which all fit into a
057:         * single position in a parent model that has previously been installed on
058:         * the same document(s).
059:         * </p>
060:         * <p>
061:         * Clients may instantiate instances of this class.
062:         * </p>
063:         *
064:         * @since 3.0
065:         */
066:        public class LinkedModeModel {
067:
068:            /**
069:             * Checks whether there is already a model installed on <code>document</code>.
070:             *
071:             * @param document the <code>IDocument</code> of interest
072:             * @return <code>true</code> if there is an existing model, <code>false</code>
073:             *         otherwise
074:             */
075:            public static boolean hasInstalledModel(IDocument document) {
076:                // if there is a manager, there also is a model
077:                return LinkedModeManager.hasManager(document);
078:            }
079:
080:            /**
081:             * Checks whether there is already a linked mode model installed on any of
082:             * the <code>documents</code>.
083:             *
084:             * @param documents the <code>IDocument</code>s of interest
085:             * @return <code>true</code> if there is an existing model, <code>false</code>
086:             *         otherwise
087:             */
088:            public static boolean hasInstalledModel(IDocument[] documents) {
089:                // if there is a manager, there also is a model
090:                return LinkedModeManager.hasManager(documents);
091:            }
092:
093:            /**
094:             * Cancels any linked mode model on the specified document. If there is no
095:             * model, nothing happens.
096:             *
097:             * @param document the document whose <code>LinkedModeModel</code> should
098:             * 		  be canceled
099:             */
100:            public static void closeAllModels(IDocument document) {
101:                LinkedModeManager.cancelManager(document);
102:            }
103:
104:            /**
105:             * Returns the model currently active on <code>document</code> at
106:             * <code>offset</code>, or <code>null</code> if there is none.
107:             *
108:             * @param document the document for which the caller asks for a
109:             *        model
110:             * @param offset the offset into <code>document</code>, as there may be
111:             *        several models on a document
112:             * @return the model currently active on <code>document</code>, or
113:             *         <code>null</code>
114:             */
115:            public static LinkedModeModel getModel(IDocument document,
116:                    int offset) {
117:                if (!hasInstalledModel(document))
118:                    return null;
119:
120:                LinkedModeManager mgr = LinkedModeManager.getLinkedManager(
121:                        new IDocument[] { document }, false);
122:                if (mgr != null)
123:                    return mgr.getTopEnvironment();
124:                return null;
125:            }
126:
127:            /**
128:             * Encapsulates the edition triggered by a change to a linking position. Can
129:             * be applied to a document as a whole.
130:             */
131:            private class Replace implements  IReplace {
132:
133:                /** The edition to apply on a document. */
134:                private TextEdit fEdit;
135:
136:                /**
137:                 * Creates a new instance.
138:                 *
139:                 * @param edit the edition to apply to a document.
140:                 */
141:                public Replace(TextEdit edit) {
142:                    fEdit = edit;
143:                }
144:
145:                /*
146:                 * @see org.eclipse.jface.text.IDocumentExtension.IReplace#perform(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocumentListener)
147:                 */
148:                public void perform(IDocument document, IDocumentListener owner)
149:                        throws RuntimeException, MalformedTreeException {
150:                    document.removeDocumentListener(owner);
151:                    fIsChanging = true;
152:                    try {
153:                        fEdit.apply(document, TextEdit.UPDATE_REGIONS
154:                                | TextEdit.CREATE_UNDO);
155:                    } catch (BadLocationException e) {
156:                        /* XXX: perform should really throw a BadLocationException
157:                         *		see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52950
158:                         */
159:                        throw new RuntimeException(e);
160:                    } finally {
161:                        document.addDocumentListener(owner);
162:                        fIsChanging = false;
163:                    }
164:                }
165:
166:            }
167:
168:            /**
169:             * The document listener triggering the linked updating of positions
170:             * managed by this model.
171:             */
172:            private class DocumentListener implements  IDocumentListener {
173:
174:                private boolean fExit = false;
175:
176:                /**
177:                 * Checks whether <code>event</code> occurs within any of the positions
178:                 * managed by this model. If not, the linked mode is left.
179:                 *
180:                 * @param event {@inheritDoc}
181:                 */
182:                public void documentAboutToBeChanged(DocumentEvent event) {
183:                    // don't react on changes executed by the parent model
184:                    if (fParentEnvironment != null
185:                            && fParentEnvironment.isChanging())
186:                        return;
187:
188:                    for (Iterator it = fGroups.iterator(); it.hasNext();) {
189:                        LinkedPositionGroup group = (LinkedPositionGroup) it
190:                                .next();
191:                        if (!group.isLegalEvent(event)) {
192:                            fExit = true;
193:                            return;
194:                        }
195:                    }
196:                }
197:
198:                /**
199:                 * Propagates a change to a linked position to all its sibling positions.
200:                 *
201:                 * @param event {@inheritDoc}
202:                 */
203:                public void documentChanged(DocumentEvent event) {
204:                    if (fExit) {
205:                        LinkedModeModel.this 
206:                                .exit(ILinkedModeListener.EXTERNAL_MODIFICATION);
207:                        return;
208:                    }
209:                    fExit = false;
210:
211:                    // don't react on changes executed by the parent model
212:                    if (fParentEnvironment != null
213:                            && fParentEnvironment.isChanging())
214:                        return;
215:
216:                    // collect all results
217:                    Map result = null;
218:                    for (Iterator it = fGroups.iterator(); it.hasNext();) {
219:                        LinkedPositionGroup group = (LinkedPositionGroup) it
220:                                .next();
221:
222:                        Map map = group.handleEvent(event);
223:                        if (result != null && map != null) {
224:                            // exit if more than one position was changed
225:                            LinkedModeModel.this 
226:                                    .exit(ILinkedModeListener.EXTERNAL_MODIFICATION);
227:                            return;
228:                        }
229:                        if (map != null)
230:                            result = map;
231:                    }
232:
233:                    if (result != null) {
234:                        // edit all documents
235:                        for (Iterator it2 = result.keySet().iterator(); it2
236:                                .hasNext();) {
237:                            IDocument doc = (IDocument) it2.next();
238:                            TextEdit edit = (TextEdit) result.get(doc);
239:                            Replace replace = new Replace(edit);
240:
241:                            // apply the edition, either as post notification replace
242:                            // on the calling document or directly on any other
243:                            // document
244:                            if (doc == event.getDocument()) {
245:                                if (doc instanceof  IDocumentExtension) {
246:                                    ((IDocumentExtension) doc)
247:                                            .registerPostNotificationReplace(
248:                                                    this , replace);
249:                                } else {
250:                                    // ignore - there is no way we can log from JFace text...
251:                                }
252:                            } else {
253:                                replace.perform(doc, this );
254:                            }
255:                        }
256:                    }
257:                }
258:
259:            }
260:
261:            /** The set of linked position groups. */
262:            private final List fGroups = new ArrayList();
263:            /** The set of documents spanned by this group. */
264:            private final Set fDocuments = new HashSet();
265:            /** The position updater for linked positions. */
266:            private final IPositionUpdater fUpdater = new InclusivePositionUpdater(
267:                    getCategory());
268:            /** The document listener on the documents affected by this model. */
269:            private final DocumentListener fDocumentListener = new DocumentListener();
270:            /** The parent model for a hierarchical set up, or <code>null</code>. */
271:            private LinkedModeModel fParentEnvironment;
272:            /**
273:             * The position in <code>fParentEnvironment</code> that includes all
274:             * positions in this object, or <code>null</code> if there is no parent
275:             * model.
276:             */
277:            private LinkedPosition fParentPosition = null;
278:            /**
279:             * A model is sealed once it has children - no more positions can be
280:             * added.
281:             */
282:            private boolean fIsSealed = false;
283:            /** <code>true</code> when this model is changing documents. */
284:            private boolean fIsChanging = false;
285:            /** The linked listeners. */
286:            private final List fListeners = new ArrayList();
287:            /** Flag telling whether we have exited: */
288:            private boolean fIsActive = true;
289:            /**
290:             * The sequence of document positions as we are going to iterate through
291:             * them.
292:             */
293:            private List fPositionSequence = new ArrayList();
294:
295:            /**
296:             * Whether we are in the process of editing documents (set by <code>Replace</code>,
297:             * read by <code>DocumentListener</code>.
298:             *
299:             * @return <code>true</code> if we are in the process of editing a
300:             *         document, <code>false</code> otherwise
301:             */
302:            private boolean isChanging() {
303:                return fIsChanging || fParentEnvironment != null
304:                        && fParentEnvironment.isChanging();
305:            }
306:
307:            /**
308:             * Throws a <code>BadLocationException</code> if <code>group</code>
309:             * conflicts with this model's groups.
310:             *
311:             * @param group the group being checked
312:             * @throws BadLocationException if <code>group</code> conflicts with this
313:             *         model's groups
314:             */
315:            private void enforceDisjoint(LinkedPositionGroup group)
316:                    throws BadLocationException {
317:                for (Iterator it = fGroups.iterator(); it.hasNext();) {
318:                    LinkedPositionGroup g = (LinkedPositionGroup) it.next();
319:                    g.enforceDisjoint(group);
320:                }
321:            }
322:
323:            /**
324:             * Causes this model to exit. Called either if an illegal document change
325:             * is detected, or by the UI.
326:             *
327:             * @param flags the exit flags as defined in {@link ILinkedModeListener}
328:             */
329:            public void exit(int flags) {
330:                if (!fIsActive)
331:                    return;
332:
333:                fIsActive = false;
334:
335:                for (Iterator it = fDocuments.iterator(); it.hasNext();) {
336:                    IDocument doc = (IDocument) it.next();
337:                    try {
338:                        doc.removePositionCategory(getCategory());
339:                    } catch (BadPositionCategoryException e) {
340:                        // won't happen
341:                        Assert.isTrue(false);
342:                    }
343:                    doc.removePositionUpdater(fUpdater);
344:                    doc.removeDocumentListener(fDocumentListener);
345:                }
346:
347:                fDocuments.clear();
348:                fGroups.clear();
349:
350:                List listeners = new ArrayList(fListeners);
351:                fListeners.clear();
352:                for (Iterator it = listeners.iterator(); it.hasNext();) {
353:                    ILinkedModeListener listener = (ILinkedModeListener) it
354:                            .next();
355:                    listener.left(this , flags);
356:                }
357:
358:                if (fParentEnvironment != null)
359:                    fParentEnvironment.resume(flags);
360:            }
361:
362:            /**
363:             * Causes this model to stop forwarding updates. The positions are not
364:             * unregistered however, which will only happen when <code>exit</code>
365:             * is called, or after the next document change.
366:             *
367:             * @param flags the exit flags as defined in {@link ILinkedModeListener}
368:             * @since 3.1
369:             */
370:            public void stopForwarding(int flags) {
371:                fDocumentListener.fExit = true;
372:            }
373:
374:            /**
375:             * Puts <code>document</code> into the set of managed documents. This
376:             * involves registering the document listener and adding our position
377:             * category.
378:             *
379:             * @param document the new document
380:             */
381:            private void manageDocument(IDocument document) {
382:                if (!fDocuments.contains(document)) {
383:                    fDocuments.add(document);
384:                    document.addPositionCategory(getCategory());
385:                    document.addPositionUpdater(fUpdater);
386:                    document.addDocumentListener(fDocumentListener);
387:                }
388:
389:            }
390:
391:            /**
392:             * Returns the position category used by this model.
393:             *
394:             * @return the position category used by this model
395:             */
396:            private String getCategory() {
397:                return toString();
398:            }
399:
400:            /**
401:             * Adds a position group to this <code>LinkedModeModel</code>. This
402:             * method may not be called if the model has been installed. Also, if
403:             * a UI has been set up for this model, it may not pick up groups
404:             * added afterwards.
405:             * <p>
406:             * If the positions in <code>group</code> conflict with any other group in
407:             * this model, a <code>BadLocationException</code> is thrown. Also,
408:             * if this model is nested inside another one, all positions in all
409:             * groups of the child model have to reside within a single position in the
410:             * parent model, otherwise a <code>BadLocationException</code> is thrown.
411:             * </p>
412:             * <p>
413:             * If <code>group</code> already exists, nothing happens.
414:             * </p>
415:             *
416:             * @param group the group to be added to this model
417:             * @throws BadLocationException if the group conflicts with the other groups
418:             *         in this model or violates the nesting requirements.
419:             * @throws IllegalStateException if the method is called when the
420:             *         model is already sealed
421:             */
422:            public void addGroup(LinkedPositionGroup group)
423:                    throws BadLocationException {
424:                if (group == null)
425:                    throw new IllegalArgumentException("group may not be null"); //$NON-NLS-1$
426:                if (fIsSealed)
427:                    throw new IllegalStateException(
428:                            "model is already installed"); //$NON-NLS-1$
429:                if (fGroups.contains(group))
430:                    // nothing happens
431:                    return;
432:
433:                enforceDisjoint(group);
434:                group.seal();
435:                fGroups.add(group);
436:            }
437:
438:            /**
439:             * Creates a new model.
440:             * @since 3.1
441:             */
442:            public LinkedModeModel() {
443:            }
444:
445:            /**
446:             * Installs this model, which includes registering as document
447:             * listener on all involved documents and storing global information about
448:             * this model. Any conflicting model already present will be
449:             * closed.
450:             * <p>
451:             * If an exception is thrown, the installation failed and
452:             * the model is unusable.
453:             * </p>
454:             *
455:             * @throws BadLocationException if some of the positions of this model
456:             *         were not valid positions on their respective documents
457:             */
458:            public void forceInstall() throws BadLocationException {
459:                if (!install(true))
460:                    Assert.isTrue(false);
461:            }
462:
463:            /**
464:             * Installs this model, which includes registering as document
465:             * listener on all involved documents and storing global information about
466:             * this model. If there is another model installed on the
467:             * document(s) targeted by the receiver that conflicts with it, installation
468:             * may fail.
469:             * <p>
470:             * The return value states whether installation was
471:             * successful; if not, the model is not installed and will not work.
472:             * </p>
473:             *
474:             * @return <code>true</code> if installation was successful,
475:             *         <code>false</code> otherwise
476:             * @throws BadLocationException if some of the positions of this model
477:             *         were not valid positions on their respective documents
478:             */
479:            public boolean tryInstall() throws BadLocationException {
480:                return install(false);
481:            }
482:
483:            /**
484:             * Installs this model, which includes registering as document
485:             * listener on all involved documents and storing global information about
486:             * this model. The return value states whether installation was
487:             * successful; if not, the model is not installed and will not work.
488:             * The return value can only then become <code>false</code> if
489:             * <code>force</code> was set to <code>false</code> as well.
490:             *
491:             * @param force if <code>true</code>, any other model that cannot
492:             *        coexist with this one is canceled; if <code>false</code>,
493:             *        install will fail when conflicts occur and return false
494:             * @return <code>true</code> if installation was successful,
495:             *         <code>false</code> otherwise
496:             * @throws BadLocationException if some of the positions of this model
497:             *         were not valid positions on their respective documents
498:             */
499:            private boolean install(boolean force) throws BadLocationException {
500:                if (fIsSealed)
501:                    throw new IllegalStateException(
502:                            "model is already installed"); //$NON-NLS-1$
503:                enforceNotEmpty();
504:
505:                IDocument[] documents = getDocuments();
506:                LinkedModeManager manager = LinkedModeManager.getLinkedManager(
507:                        documents, force);
508:                // if we force creation, we require a valid manager
509:                Assert.isTrue(!(force && manager == null));
510:                if (manager == null)
511:                    return false;
512:
513:                if (!manager.nestEnvironment(this , force))
514:                    if (force)
515:                        Assert.isTrue(false);
516:                    else
517:                        return false;
518:
519:                // we set up successfully. After this point, exit has to be called to
520:                // remove registered listeners...
521:                fIsSealed = true;
522:                if (fParentEnvironment != null)
523:                    fParentEnvironment.suspend();
524:
525:                // register positions
526:                try {
527:                    for (Iterator it = fGroups.iterator(); it.hasNext();) {
528:                        LinkedPositionGroup group = (LinkedPositionGroup) it
529:                                .next();
530:                        group.register(this );
531:                    }
532:                    return true;
533:                } catch (BadLocationException e) {
534:                    // if we fail to add, make sure to release all listeners again
535:                    exit(ILinkedModeListener.NONE);
536:                    throw e;
537:                }
538:            }
539:
540:            /**
541:             * Asserts that there is at least one linked position in this linked mode
542:             * model, throws an IllegalStateException otherwise.
543:             */
544:            private void enforceNotEmpty() {
545:                boolean hasPosition = false;
546:                for (Iterator it = fGroups.iterator(); it.hasNext();)
547:                    if (!((LinkedPositionGroup) it.next()).isEmpty()) {
548:                        hasPosition = true;
549:                        break;
550:                    }
551:                if (!hasPosition)
552:                    throw new IllegalStateException(
553:                            "must specify at least one linked position"); //$NON-NLS-1$
554:
555:            }
556:
557:            /**
558:             * Collects all the documents that contained positions are set upon.
559:             * @return the set of documents affected by this model
560:             */
561:            private IDocument[] getDocuments() {
562:                Set docs = new HashSet();
563:                for (Iterator it = fGroups.iterator(); it.hasNext();) {
564:                    LinkedPositionGroup group = (LinkedPositionGroup) it.next();
565:                    docs.addAll(Arrays.asList(group.getDocuments()));
566:                }
567:                return (IDocument[]) docs.toArray(new IDocument[docs.size()]);
568:            }
569:
570:            /**
571:             * Returns whether the receiver can be nested into the given <code>parent</code>
572:             * model. If yes, the parent model and its position that the receiver
573:             * fits in are remembered.
574:             *
575:             * @param parent the parent model candidate
576:             * @return <code>true</code> if the receiver can be nested into <code>parent</code>, <code>false</code> otherwise
577:             */
578:            boolean canNestInto(LinkedModeModel parent) {
579:                for (Iterator it = fGroups.iterator(); it.hasNext();) {
580:                    LinkedPositionGroup group = (LinkedPositionGroup) it.next();
581:                    if (!enforceNestability(group, parent)) {
582:                        fParentPosition = null;
583:                        return false;
584:                    }
585:                }
586:
587:                Assert.isNotNull(fParentPosition);
588:                fParentEnvironment = parent;
589:                return true;
590:            }
591:
592:            /**
593:             * Called by nested models when a group is added to them. All
594:             * positions in all groups of a nested model have to fit inside a
595:             * single position in the parent model.
596:             *
597:             * @param group the group of the nested model to be adopted.
598:             * @param model the model to check against
599:             * @return <code>false</code> if it failed to enforce nestability
600:             */
601:            private boolean enforceNestability(LinkedPositionGroup group,
602:                    LinkedModeModel model) {
603:                Assert.isNotNull(model);
604:                Assert.isNotNull(group);
605:
606:                try {
607:                    for (Iterator it = model.fGroups.iterator(); it.hasNext();) {
608:                        LinkedPositionGroup pg = (LinkedPositionGroup) it
609:                                .next();
610:                        LinkedPosition pos;
611:                        pos = pg.adopt(group);
612:                        if (pos != null && fParentPosition != null
613:                                && fParentPosition != pos)
614:                            return false; // group does not fit into one parent position, which is illegal
615:                        else if (fParentPosition == null && pos != null)
616:                            fParentPosition = pos;
617:                    }
618:                } catch (BadLocationException e) {
619:                    return false;
620:                }
621:
622:                // group must fit into exactly one of the parent's positions
623:                return fParentPosition != null;
624:            }
625:
626:            /**
627:             * Returns whether this model is nested.
628:             *
629:             * <p>
630:             * This method is part of the private protocol between
631:             * <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
632:             * </p>
633:             *
634:             * @return <code>true</code> if this model is nested,
635:             *         <code>false</code> otherwise
636:             */
637:            public boolean isNested() {
638:                return fParentEnvironment != null;
639:            }
640:
641:            /**
642:             * Returns the positions in this model that have a tab stop, in the
643:             * order they were added.
644:             *
645:             * <p>
646:             * This method is part of the private protocol between
647:             * <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
648:             * </p>
649:             *
650:             * @return the positions in this model that have a tab stop, in the
651:             *         order they were added
652:             */
653:            public List getTabStopSequence() {
654:                return fPositionSequence;
655:            }
656:
657:            /**
658:             * Adds <code>listener</code> to the set of listeners that are informed
659:             * upon state changes.
660:             *
661:             * @param listener the new listener
662:             */
663:            public void addLinkingListener(ILinkedModeListener listener) {
664:                Assert.isNotNull(listener);
665:                if (!fListeners.contains(listener))
666:                    fListeners.add(listener);
667:            }
668:
669:            /**
670:             * Removes <code>listener</code> from the set of listeners that are
671:             * informed upon state changes.
672:             *
673:             * @param listener the new listener
674:             */
675:            public void removeLinkingListener(ILinkedModeListener listener) {
676:                fListeners.remove(listener);
677:            }
678:
679:            /**
680:             * Finds the position in this model that is closest after
681:             * <code>toFind</code>. <code>toFind</code> needs not be a position in
682:             * this model and serves merely as an offset.
683:             *
684:             * <p>
685:             * This method part of the private protocol between
686:             * <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
687:             * </p>
688:             *
689:             * @param toFind the position to search from
690:             * @return the closest position in the same document as <code>toFind</code>
691:             *         after the offset of <code>toFind</code>, or <code>null</code>
692:             */
693:            public LinkedPosition findPosition(LinkedPosition toFind) {
694:                LinkedPosition position = null;
695:                for (Iterator it = fGroups.iterator(); it.hasNext();) {
696:                    LinkedPositionGroup group = (LinkedPositionGroup) it.next();
697:                    position = group.getPosition(toFind);
698:                    if (position != null)
699:                        break;
700:                }
701:                return position;
702:            }
703:
704:            /**
705:             * Registers a <code>LinkedPosition</code> with this model. Called
706:             * by <code>PositionGroup</code>.
707:             *
708:             * @param position the position to register
709:             * @throws BadLocationException if the position cannot be added to its
710:             *         document
711:             */
712:            void register(LinkedPosition position) throws BadLocationException {
713:                Assert.isNotNull(position);
714:
715:                IDocument document = position.getDocument();
716:                manageDocument(document);
717:                try {
718:                    document.addPosition(getCategory(), position);
719:                } catch (BadPositionCategoryException e) {
720:                    // won't happen as the category has been added by manageDocument()
721:                    Assert.isTrue(false);
722:                }
723:                int seqNr = position.getSequenceNumber();
724:                if (seqNr != LinkedPositionGroup.NO_STOP) {
725:                    fPositionSequence.add(position);
726:                }
727:            }
728:
729:            /**
730:             * Suspends this model.
731:             */
732:            private void suspend() {
733:                List l = new ArrayList(fListeners);
734:                for (Iterator it = l.iterator(); it.hasNext();) {
735:                    ILinkedModeListener listener = (ILinkedModeListener) it
736:                            .next();
737:                    listener.suspend(this );
738:                }
739:            }
740:
741:            /**
742:             * Resumes this model. <code>flags</code> can be <code>NONE</code>
743:             * or <code>SELECT</code>.
744:             *
745:             * @param flags <code>NONE</code> or <code>SELECT</code>
746:             */
747:            private void resume(int flags) {
748:                List l = new ArrayList(fListeners);
749:                for (Iterator it = l.iterator(); it.hasNext();) {
750:                    ILinkedModeListener listener = (ILinkedModeListener) it
751:                            .next();
752:                    listener.resume(this , flags);
753:                }
754:            }
755:
756:            /**
757:             * Returns whether an offset is contained by any position in this
758:             * model.
759:             *
760:             * @param offset the offset to check
761:             * @return <code>true</code> if <code>offset</code> is included by any
762:             *         position (see {@link LinkedPosition#includes(int)}) in this
763:             *         model, <code>false</code> otherwise
764:             */
765:            public boolean anyPositionContains(int offset) {
766:                for (Iterator it = fGroups.iterator(); it.hasNext();) {
767:                    LinkedPositionGroup group = (LinkedPositionGroup) it.next();
768:                    if (group.contains(offset))
769:                        // take the first hit - exclusion is guaranteed by enforcing
770:                        // disjointness when adding positions
771:                        return true;
772:                }
773:                return false;
774:            }
775:
776:            /**
777:             * Returns the linked position group that contains <code>position</code>,
778:             * or <code>null</code> if <code>position</code> is not contained in any
779:             * group within this model. Group containment is tested by calling
780:             * <code>group.contains(position)</code> for every <code>group</code> in
781:             * this model.
782:             *
783:             * <p>
784:             * This method part of the private protocol between
785:             * <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
786:             * </p>
787:             *
788:             * @param position the position the group of which is requested
789:             * @return the first group in this model for which
790:             *         <code>group.contains(position)</code> returns <code>true</code>,
791:             *         or <code>null</code> if no group contains <code>position</code>
792:             */
793:            public LinkedPositionGroup getGroupForPosition(Position position) {
794:                for (Iterator it = fGroups.iterator(); it.hasNext();) {
795:                    LinkedPositionGroup group = (LinkedPositionGroup) it.next();
796:                    if (group.contains(position))
797:                        return group;
798:                }
799:                return null;
800:            }
801:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.