Copyright (c) 2000, 2015 IBM Corporation and others.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
IBM Corporation - initial API and implementation
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.text.link;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension;
import org.eclipse.jface.text.IDocumentExtension.IReplace;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.Position;
The model for linked mode, umbrellas several LinkedPositionGroup
s. Once installed, the model propagates any changes to a position to all its siblings in the same position group.
Setting up a model consists of first adding
LinkedPositionGroup
s to it, and then installing the model by either calling forceInstall()
or tryInstall()
. After installing the model, it becomes sealed and no more groups may be added.
If a document change occurs that would modify more than one position group or that would invalidate the disjointness requirement of the positions, the model is torn down and all positions are deleted. The same happens upon calling exit(int)
.
Nesting
A LinkedModeModel
may be nested into another model. This
happens when installing a model the positions of which all fit into a
single position in a parent model that has previously been installed on
the same document(s).
Clients may instantiate instances of this class.
Since: 3.0 @noextend This class is not intended to be subclassed by clients.
/**
* The model for linked mode, umbrellas several
* {@link LinkedPositionGroup}s. Once installed, the model
* propagates any changes to a position to all its siblings in the same position
* group.
* <p>
* Setting up a model consists of first adding
* <code>LinkedPositionGroup</code>s to it, and then installing the
* model by either calling {@link #forceInstall()} or
* {@link #tryInstall()}. After installing the model, it becomes
* <em>sealed</em> and no more groups may be added.
* </p>
* <p>
* If a document change occurs that would modify more than one position
* group or that would invalidate the disjointness requirement of the positions,
* the model is torn down and all positions are deleted. The same happens
* upon calling {@link #exit(int)}.
* </p>
* <h4>Nesting</h4>
* <p>
* A <code>LinkedModeModel</code> may be nested into another model. This
* happens when installing a model the positions of which all fit into a
* single position in a parent model that has previously been installed on
* the same document(s).
* </p>
* <p>
* Clients may instantiate instances of this class.
* </p>
*
* @since 3.0
* @noextend This class is not intended to be subclassed by clients.
*/
public class LinkedModeModel {
Checks whether there is already a model installed on document
.
Params: - document – the
IDocument
of interest
Returns: true
if there is an existing model, false
otherwise
/**
* Checks whether there is already a model installed on <code>document</code>.
*
* @param document the <code>IDocument</code> of interest
* @return <code>true</code> if there is an existing model, <code>false</code>
* otherwise
*/
public static boolean hasInstalledModel(IDocument document) {
// if there is a manager, there also is a model
return LinkedModeManager.hasManager(document);
}
Checks whether there is already a linked mode model installed on any of
the documents
.
Params: - documents – the
IDocument
s of interest
Returns: true
if there is an existing model, false
otherwise
/**
* Checks whether there is already a linked mode model installed on any of
* the <code>documents</code>.
*
* @param documents the <code>IDocument</code>s of interest
* @return <code>true</code> if there is an existing model, <code>false</code>
* otherwise
*/
public static boolean hasInstalledModel(IDocument[] documents) {
// if there is a manager, there also is a model
return LinkedModeManager.hasManager(documents);
}
Cancels any linked mode model on the specified document. If there is no
model, nothing happens.
Params: - document – the document whose
LinkedModeModel
should
be canceled
/**
* Cancels any linked mode model on the specified document. If there is no
* model, nothing happens.
*
* @param document the document whose <code>LinkedModeModel</code> should
* be canceled
*/
public static void closeAllModels(IDocument document) {
LinkedModeManager.cancelManager(document);
}
Returns the model currently active on document
at
offset
, or null
if there is none.
Params: - document – the document for which the caller asks for a
model
- offset – the offset into
document
, as there may be
several models on a document
Returns: the model currently active on document
, or
null
/**
* Returns the model currently active on <code>document</code> at
* <code>offset</code>, or <code>null</code> if there is none.
*
* @param document the document for which the caller asks for a
* model
* @param offset the offset into <code>document</code>, as there may be
* several models on a document
* @return the model currently active on <code>document</code>, or
* <code>null</code>
*/
public static LinkedModeModel getModel(IDocument document, int offset) {
if (!hasInstalledModel(document))
return null;
LinkedModeManager mgr= LinkedModeManager.getLinkedManager(new IDocument[] {document}, false);
if (mgr != null)
return mgr.getTopEnvironment();
return null;
}
Encapsulates the edition triggered by a change to a linking position. Can
be applied to a document as a whole.
/**
* Encapsulates the edition triggered by a change to a linking position. Can
* be applied to a document as a whole.
*/
private class Replace implements IReplace {
The edition to apply on a document. /** The edition to apply on a document. */
private TextEdit fEdit;
Creates a new instance.
Params: - edit – the edition to apply to a document.
/**
* Creates a new instance.
*
* @param edit the edition to apply to a document.
*/
public Replace(TextEdit edit) {
fEdit= edit;
}
@Override
public void perform(IDocument document, IDocumentListener owner) throws RuntimeException, MalformedTreeException {
document.removeDocumentListener(owner);
fIsChanging= true;
try {
fEdit.apply(document, TextEdit.UPDATE_REGIONS | TextEdit.CREATE_UNDO);
} catch (BadLocationException e) {
/* XXX: perform should really throw a BadLocationException
* see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52950
*/
throw new RuntimeException(e);
} finally {
document.addDocumentListener(owner);
fIsChanging= false;
}
}
}
The document listener triggering the linked updating of positions
managed by this model.
/**
* The document listener triggering the linked updating of positions
* managed by this model.
*/
private class DocumentListener implements IDocumentListener {
private boolean fExit= false;
Checks whether event
occurs within any of the positions
managed by this model. If not, the linked mode is left.
Params: - event – {@inheritDoc}
/**
* Checks whether <code>event</code> occurs within any of the positions
* managed by this model. If not, the linked mode is left.
*
* @param event {@inheritDoc}
*/
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
// don't react on changes executed by the parent model
if (fParentEnvironment != null && fParentEnvironment.isChanging())
return;
for (LinkedPositionGroup group : fGroups) {
if (!group.isLegalEvent(event)) {
fExit= true;
return;
}
}
}
Propagates a change to a linked position to all its sibling positions.
Params: - event – {@inheritDoc}
/**
* Propagates a change to a linked position to all its sibling positions.
*
* @param event {@inheritDoc}
*/
@Override
public void documentChanged(DocumentEvent event) {
if (fExit) {
LinkedModeModel.this.exit(ILinkedModeListener.EXTERNAL_MODIFICATION);
return;
}
fExit= false;
// don't react on changes executed by the parent model
if (fParentEnvironment != null && fParentEnvironment.isChanging())
return;
// collect all results
Map<IDocument, TextEdit> result= null;
for (LinkedPositionGroup group : fGroups) {
Map<IDocument, TextEdit> map= group.handleEvent(event);
if (result != null && map != null) {
// exit if more than one position was changed
LinkedModeModel.this.exit(ILinkedModeListener.EXTERNAL_MODIFICATION);
return;
}
if (map != null)
result= map;
}
if (result != null) {
// edit all documents
for (Entry<IDocument, TextEdit> entry : result.entrySet()) {
IDocument doc = entry.getKey();
TextEdit edit= entry.getValue();
Replace replace= new Replace(edit);
// apply the edition, either as post notification replace
// on the calling document or directly on any other
// document
if (doc == event.getDocument()) {
if (doc instanceof IDocumentExtension) {
((IDocumentExtension) doc).registerPostNotificationReplace(this, replace);
} else {
// ignore - there is no way we can log from JFace text...
}
} else {
replace.perform(doc, this);
}
}
}
}
}
The set of linked position groups. /** The set of linked position groups. */
private final List<LinkedPositionGroup> fGroups= new ArrayList<>();
The set of documents spanned by this group. /** The set of documents spanned by this group. */
private final Set<IDocument> fDocuments= new HashSet<>();
The position updater for linked positions. /** The position updater for linked positions. */
private final IPositionUpdater fUpdater= new InclusivePositionUpdater(getCategory());
The document listener on the documents affected by this model. /** The document listener on the documents affected by this model. */
private final DocumentListener fDocumentListener= new DocumentListener();
The parent model for a hierarchical set up, or null
. /** The parent model for a hierarchical set up, or <code>null</code>. */
private LinkedModeModel fParentEnvironment;
The position in fParentEnvironment
that includes all
positions in this object, or null
if there is no parent
model.
/**
* The position in <code>fParentEnvironment</code> that includes all
* positions in this object, or <code>null</code> if there is no parent
* model.
*/
private LinkedPosition fParentPosition= null;
A model is sealed once it has children - no more positions can be
added.
/**
* A model is sealed once it has children - no more positions can be
* added.
*/
private boolean fIsSealed= false;
true
when this model is changing documents. /** <code>true</code> when this model is changing documents. */
private boolean fIsChanging= false;
The linked listeners. /** The linked listeners. */
private final List<ILinkedModeListener> fListeners= new ArrayList<>();
Flag telling whether we have exited: /** Flag telling whether we have exited: */
private boolean fIsActive= true;
The sequence of document positions as we are going to iterate through
them.
/**
* The sequence of document positions as we are going to iterate through
* them.
*/
private List<LinkedPosition> fPositionSequence= new ArrayList<>();
Whether we are in the process of editing documents (set by Replace
,
read by DocumentListener
.
Returns: true
if we are in the process of editing a
document, false
otherwise
/**
* Whether we are in the process of editing documents (set by <code>Replace</code>,
* read by <code>DocumentListener</code>.
*
* @return <code>true</code> if we are in the process of editing a
* document, <code>false</code> otherwise
*/
private boolean isChanging() {
return fIsChanging || fParentEnvironment != null && fParentEnvironment.isChanging();
}
Throws a BadLocationException
if group
conflicts with this model's groups.
Params: - group – the group being checked
Throws: - BadLocationException – if
group
conflicts with this
model's groups
/**
* Throws a <code>BadLocationException</code> if <code>group</code>
* conflicts with this model's groups.
*
* @param group the group being checked
* @throws BadLocationException if <code>group</code> conflicts with this
* model's groups
*/
private void enforceDisjoint(LinkedPositionGroup group) throws BadLocationException {
for (LinkedPositionGroup g : fGroups) {
g.enforceDisjoint(group);
}
}
Causes this model to exit. Called either if an illegal document change
is detected, or by the UI.
Params: - flags – the exit flags as defined in
ILinkedModeListener
/**
* Causes this model to exit. Called either if an illegal document change
* is detected, or by the UI.
*
* @param flags the exit flags as defined in {@link ILinkedModeListener}
*/
public void exit(int flags) {
if (!fIsActive)
return;
fIsActive= false;
for (IDocument doc : fDocuments) {
try {
doc.removePositionCategory(getCategory());
} catch (BadPositionCategoryException e) {
// won't happen
Assert.isTrue(false);
}
doc.removePositionUpdater(fUpdater);
doc.removeDocumentListener(fDocumentListener);
}
fDocuments.clear();
fGroups.clear();
List<ILinkedModeListener> listeners= new ArrayList<>(fListeners);
fListeners.clear();
for (ILinkedModeListener listener : listeners) {
listener.left(this, flags);
}
if (fParentEnvironment != null)
fParentEnvironment.resume(flags);
}
Causes this model to stop forwarding updates. The positions are not
unregistered however, which will only happen when exit
is called, or after the next document change.
Params: - flags – the exit flags as defined in
ILinkedModeListener
Since: 3.1
/**
* Causes this model to stop forwarding updates. The positions are not
* unregistered however, which will only happen when <code>exit</code>
* is called, or after the next document change.
*
* @param flags the exit flags as defined in {@link ILinkedModeListener}
* @since 3.1
*/
public void stopForwarding(int flags) {
fDocumentListener.fExit= true;
}
Puts document
into the set of managed documents. This
involves registering the document listener and adding our position
category.
Params: - document – the new document
/**
* Puts <code>document</code> into the set of managed documents. This
* involves registering the document listener and adding our position
* category.
*
* @param document the new document
*/
private void manageDocument(IDocument document) {
if (!fDocuments.contains(document)) {
fDocuments.add(document);
document.addPositionCategory(getCategory());
document.addPositionUpdater(fUpdater);
document.addDocumentListener(fDocumentListener);
}
}
Returns the position category used by this model.
Returns: the position category used by this model
/**
* Returns the position category used by this model.
*
* @return the position category used by this model
*/
private String getCategory() {
return toString();
}
Adds a position group to this LinkedModeModel
. This
method may not be called if the model has been installed. Also, if
a UI has been set up for this model, it may not pick up groups
added afterwards.
If the positions in group
conflict with any other group in
this model, a BadLocationException
is thrown. Also,
if this model is nested inside another one, all positions in all
groups of the child model have to reside within a single position in the
parent model, otherwise a BadLocationException
is thrown.
If group
already exists, nothing happens.
Params: - group – the group to be added to this model
Throws: - BadLocationException – if the group conflicts with the other groups
in this model or violates the nesting requirements.
- IllegalStateException – if the method is called when the
model is already sealed
/**
* Adds a position group to this <code>LinkedModeModel</code>. This
* method may not be called if the model has been installed. Also, if
* a UI has been set up for this model, it may not pick up groups
* added afterwards.
* <p>
* If the positions in <code>group</code> conflict with any other group in
* this model, a <code>BadLocationException</code> is thrown. Also,
* if this model is nested inside another one, all positions in all
* groups of the child model have to reside within a single position in the
* parent model, otherwise a <code>BadLocationException</code> is thrown.
* </p>
* <p>
* If <code>group</code> already exists, nothing happens.
* </p>
*
* @param group the group to be added to this model
* @throws BadLocationException if the group conflicts with the other groups
* in this model or violates the nesting requirements.
* @throws IllegalStateException if the method is called when the
* model is already sealed
*/
public void addGroup(LinkedPositionGroup group) throws BadLocationException {
if (group == null)
throw new IllegalArgumentException("group may not be null"); //$NON-NLS-1$
if (fIsSealed)
throw new IllegalStateException("model is already installed"); //$NON-NLS-1$
if (fGroups.contains(group))
// nothing happens
return;
enforceDisjoint(group);
group.seal();
fGroups.add(group);
}
Creates a new model.
Since: 3.1
/**
* Creates a new model.
* @since 3.1
*/
public LinkedModeModel() {
}
Installs this model, which includes registering as document
listener on all involved documents and storing global information about
this model. Any conflicting model already present will be
closed.
If an exception is thrown, the installation failed and
the model is unusable.
Throws: - BadLocationException – if some of the positions of this model
were not valid positions on their respective documents
/**
* Installs this model, which includes registering as document
* listener on all involved documents and storing global information about
* this model. Any conflicting model already present will be
* closed.
* <p>
* If an exception is thrown, the installation failed and
* the model is unusable.
* </p>
*
* @throws BadLocationException if some of the positions of this model
* were not valid positions on their respective documents
*/
public void forceInstall() throws BadLocationException {
if (!install(true))
Assert.isTrue(false);
}
Installs this model, which includes registering as document
listener on all involved documents and storing global information about
this model. If there is another model installed on the
document(s) targeted by the receiver that conflicts with it, installation
may fail.
The return value states whether installation was
successful; if not, the model is not installed and will not work.
Throws: - BadLocationException – if some of the positions of this model
were not valid positions on their respective documents
Returns: true
if installation was successful,
false
otherwise
/**
* Installs this model, which includes registering as document
* listener on all involved documents and storing global information about
* this model. If there is another model installed on the
* document(s) targeted by the receiver that conflicts with it, installation
* may fail.
* <p>
* The return value states whether installation was
* successful; if not, the model is not installed and will not work.
* </p>
*
* @return <code>true</code> if installation was successful,
* <code>false</code> otherwise
* @throws BadLocationException if some of the positions of this model
* were not valid positions on their respective documents
*/
public boolean tryInstall() throws BadLocationException {
return install(false);
}
Installs this model, which includes registering as document
listener on all involved documents and storing global information about
this model. The return value states whether installation was
successful; if not, the model is not installed and will not work.
The return value can only then become false
if
force
was set to false
as well.
Params: - force – if
true
, any other model that cannot
coexist with this one is canceled; if false
,
install will fail when conflicts occur and return false
Throws: - BadLocationException – if some of the positions of this model
were not valid positions on their respective documents
Returns: true
if installation was successful,
false
otherwise
/**
* Installs this model, which includes registering as document
* listener on all involved documents and storing global information about
* this model. The return value states whether installation was
* successful; if not, the model is not installed and will not work.
* The return value can only then become <code>false</code> if
* <code>force</code> was set to <code>false</code> as well.
*
* @param force if <code>true</code>, any other model that cannot
* coexist with this one is canceled; if <code>false</code>,
* install will fail when conflicts occur and return false
* @return <code>true</code> if installation was successful,
* <code>false</code> otherwise
* @throws BadLocationException if some of the positions of this model
* were not valid positions on their respective documents
*/
private boolean install(boolean force) throws BadLocationException {
if (fIsSealed)
throw new IllegalStateException("model is already installed"); //$NON-NLS-1$
enforceNotEmpty();
IDocument[] documents= getDocuments();
LinkedModeManager manager= LinkedModeManager.getLinkedManager(documents, force);
// if we force creation, we require a valid manager
Assert.isTrue(!(force && manager == null));
if (manager == null)
return false;
if (!manager.nestEnvironment(this, force))
if (force)
Assert.isTrue(false);
else
return false;
// we set up successfully. After this point, exit has to be called to
// remove registered listeners...
fIsSealed= true;
if (fParentEnvironment != null)
fParentEnvironment.suspend();
// register positions
try {
for (LinkedPositionGroup group : fGroups) {
group.register(this);
}
return true;
} catch (BadLocationException e){
// if we fail to add, make sure to release all listeners again
exit(ILinkedModeListener.NONE);
throw e;
}
}
Asserts that there is at least one linked position in this linked mode
model, throws an IllegalStateException otherwise.
/**
* Asserts that there is at least one linked position in this linked mode
* model, throws an IllegalStateException otherwise.
*/
private void enforceNotEmpty() {
boolean hasPosition= false;
for (LinkedPositionGroup linkedPositionGroup : fGroups)
if (!linkedPositionGroup.isEmpty()) {
hasPosition= true;
break;
}
if (!hasPosition)
throw new IllegalStateException("must specify at least one linked position"); //$NON-NLS-1$
}
Collects all the documents that contained positions are set upon.
Returns: the set of documents affected by this model
/**
* Collects all the documents that contained positions are set upon.
* @return the set of documents affected by this model
*/
private IDocument[] getDocuments() {
Set<IDocument> docs= new HashSet<>();
for (LinkedPositionGroup group : fGroups) {
docs.addAll(Arrays.asList(group.getDocuments()));
}
return docs.toArray(new IDocument[docs.size()]);
}
Returns whether the receiver can be nested into the given parent
model. If yes, the parent model and its position that the receiver
fits in are remembered.
Params: - parent – the parent model candidate
Returns: true
if the receiver can be nested into parent
, false
otherwise
/**
* Returns whether the receiver can be nested into the given <code>parent</code>
* model. If yes, the parent model and its position that the receiver
* fits in are remembered.
*
* @param parent the parent model candidate
* @return <code>true</code> if the receiver can be nested into <code>parent</code>, <code>false</code> otherwise
*/
boolean canNestInto(LinkedModeModel parent) {
for (LinkedPositionGroup group : fGroups) {
if (!enforceNestability(group, parent)) {
fParentPosition= null;
return false;
}
}
Assert.isNotNull(fParentPosition);
fParentEnvironment= parent;
return true;
}
Called by nested models when a group is added to them. All
positions in all groups of a nested model have to fit inside a
single position in the parent model.
Params: - group – the group of the nested model to be adopted.
- model – the model to check against
Returns: false
if it failed to enforce nestability
/**
* Called by nested models when a group is added to them. All
* positions in all groups of a nested model have to fit inside a
* single position in the parent model.
*
* @param group the group of the nested model to be adopted.
* @param model the model to check against
* @return <code>false</code> if it failed to enforce nestability
*/
private boolean enforceNestability(LinkedPositionGroup group, LinkedModeModel model) {
Assert.isNotNull(model);
Assert.isNotNull(group);
try {
for (LinkedPositionGroup pg : model.fGroups) {
LinkedPosition pos;
pos= pg.adopt(group);
if (pos != null && fParentPosition != null && fParentPosition != pos)
return false; // group does not fit into one parent position, which is illegal
else if (fParentPosition == null && pos != null)
fParentPosition= pos;
}
} catch (BadLocationException e) {
return false;
}
// group must fit into exactly one of the parent's positions
return fParentPosition != null;
}
Returns whether this model is nested.
This method is part of the private protocol between
LinkedModeUI
and LinkedModeModel
.
Returns: true
if this model is nested,
false
otherwise
/**
* Returns whether this model is nested.
*
* <p>
* This method is part of the private protocol between
* <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
* </p>
*
* @return <code>true</code> if this model is nested,
* <code>false</code> otherwise
*/
public boolean isNested() {
return fParentEnvironment != null;
}
Returns the positions in this model that have a tab stop, in the
order they were added.
This method is part of the private protocol between
LinkedModeUI
and LinkedModeModel
.
Returns: the positions in this model that have a tab stop, in the
order they were added
/**
* Returns the positions in this model that have a tab stop, in the
* order they were added.
*
* <p>
* This method is part of the private protocol between
* <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
* </p>
*
* @return the positions in this model that have a tab stop, in the
* order they were added
*/
public List<LinkedPosition> getTabStopSequence() {
return fPositionSequence;
}
Adds listener
to the set of listeners that are informed
upon state changes.
Params: - listener – the new listener
/**
* Adds <code>listener</code> to the set of listeners that are informed
* upon state changes.
*
* @param listener the new listener
*/
public void addLinkingListener(ILinkedModeListener listener) {
Assert.isNotNull(listener);
if (!fListeners.contains(listener))
fListeners.add(listener);
}
Removes listener
from the set of listeners that are
informed upon state changes.
Params: - listener – the new listener
/**
* Removes <code>listener</code> from the set of listeners that are
* informed upon state changes.
*
* @param listener the new listener
*/
public void removeLinkingListener(ILinkedModeListener listener) {
fListeners.remove(listener);
}
Finds the position in this model that is closest after
toFind
. toFind
needs not be a position in
this model and serves merely as an offset.
This method part of the private protocol between
LinkedModeUI
and LinkedModeModel
.
Params: - toFind – the position to search from
Returns: the closest position in the same document as toFind
after the offset of toFind
, or null
/**
* Finds the position in this model that is closest after
* <code>toFind</code>. <code>toFind</code> needs not be a position in
* this model and serves merely as an offset.
*
* <p>
* This method part of the private protocol between
* <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
* </p>
*
* @param toFind the position to search from
* @return the closest position in the same document as <code>toFind</code>
* after the offset of <code>toFind</code>, or <code>null</code>
*/
public LinkedPosition findPosition(LinkedPosition toFind) {
LinkedPosition position= null;
for (LinkedPositionGroup group : fGroups) {
position= group.getPosition(toFind);
if (position != null)
break;
}
return position;
}
Registers a LinkedPosition
with this model. Called
by PositionGroup
.
Params: - position – the position to register
Throws: - BadLocationException – if the position cannot be added to its
document
/**
* Registers a <code>LinkedPosition</code> with this model. Called
* by <code>PositionGroup</code>.
*
* @param position the position to register
* @throws BadLocationException if the position cannot be added to its
* document
*/
void register(LinkedPosition position) throws BadLocationException {
Assert.isNotNull(position);
IDocument document= position.getDocument();
manageDocument(document);
try {
document.addPosition(getCategory(), position);
} catch (BadPositionCategoryException e) {
// won't happen as the category has been added by manageDocument()
Assert.isTrue(false);
}
int seqNr= position.getSequenceNumber();
if (seqNr != LinkedPositionGroup.NO_STOP) {
fPositionSequence.add(position);
}
}
Suspends this model.
/**
* Suspends this model.
*/
private void suspend() {
List<ILinkedModeListener> l= new ArrayList<>(fListeners);
for (ILinkedModeListener listener : l) {
listener.suspend(this);
}
}
Resumes this model. flags
can be NONE
or SELECT
.
Params: - flags –
NONE
or SELECT
/**
* Resumes this model. <code>flags</code> can be <code>NONE</code>
* or <code>SELECT</code>.
*
* @param flags <code>NONE</code> or <code>SELECT</code>
*/
private void resume(int flags) {
List<ILinkedModeListener> l= new ArrayList<>(fListeners);
for (ILinkedModeListener listener : l) {
listener.resume(this, flags);
}
}
Returns whether an offset is contained by any position in this
model.
Params: - offset – the offset to check
Returns: true
if offset
is included by any position (see LinkedPosition.includes(int)
) in this model, false
otherwise
/**
* Returns whether an offset is contained by any position in this
* model.
*
* @param offset the offset to check
* @return <code>true</code> if <code>offset</code> is included by any
* position (see {@link LinkedPosition#includes(int)}) in this
* model, <code>false</code> otherwise
*/
public boolean anyPositionContains(int offset) {
for (LinkedPositionGroup group : fGroups) {
if (group.contains(offset))
// take the first hit - exclusion is guaranteed by enforcing
// disjointness when adding positions
return true;
}
return false;
}
Returns the linked position group that contains position
,
or null
if position
is not contained in any
group within this model. Group containment is tested by calling
group.contains(position)
for every group
in
this model.
This method part of the private protocol between
LinkedModeUI
and LinkedModeModel
.
Params: - position – the position the group of which is requested
Returns: the first group in this model for which
group.contains(position)
returns true
,
or null
if no group contains position
/**
* Returns the linked position group that contains <code>position</code>,
* or <code>null</code> if <code>position</code> is not contained in any
* group within this model. Group containment is tested by calling
* <code>group.contains(position)</code> for every <code>group</code> in
* this model.
*
* <p>
* This method part of the private protocol between
* <code>LinkedModeUI</code> and <code>LinkedModeModel</code>.
* </p>
*
* @param position the position the group of which is requested
* @return the first group in this model for which
* <code>group.contains(position)</code> returns <code>true</code>,
* or <code>null</code> if no group contains <code>position</code>
*/
public LinkedPositionGroup getGroupForPosition(Position position) {
for (LinkedPositionGroup group : fGroups) {
if (group.contains(position))
return group;
}
return null;
}
}