/* * Copyright 2003-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.mps.openapi.model; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.language.SConcept; import org.jetbrains.mps.openapi.language.SContainmentLink; import org.jetbrains.mps.openapi.language.SProperty; import org.jetbrains.mps.openapi.language.SReferenceLink; /** * NODE STATES * A node can have 2 states: it can be either attached to some repository or not. * In case it's not attached, it behaves just like any simple Java object. * After getting attached to a repository, the node starts checking correct read/write access permissions (locks) through repository.getModelAccess() * and also starts sending notifications about node reads. * <p/> * CHILDREN * Child nodes must have a global ordering meaning that the order in which child nodes are returned by getNextSibling, getPrevSibling, getChildren * and other methods, must be consistent with order determined by child add operations. The ordering should be consistent not only for children in * any particular role, but also for nodes with different roles. * <p/> * NODE MANIPULATION * After detaching from a model, the node is held in a special place in the repository until the end of the current Command * (UnregisteredNodes/ImmatureReferences) so that all references in the node and from the outside still work. * E.g. we have A,B and C nodes in model M, where A and B references C. After * C.delete or M.removeRoot(C) or C.getParent().removeChild(C), A and B will still have C as a target of reference until the end the current command. * If a node has been detached from its parent, it becomes detached from the whole tree. SModel.getRootNodes will not return it as a root in this case. * <p/> * STORING NODES * Keeping references to nodes between subsequent read actions will cause errors and possible memory leaks. See getReference() * <p/> * EXTERNAL CONSTRAINTS * SNode represents the raw node in the AST. SNode does not know about constraints, behavior, getters and setters * for props/refs. * <p/> * READ NOTIFICATIONS * Accessing node triggers read notifications ({@link org.jetbrains.mps.openapi.model.SModelAccessListener} and legacy 'event casters'). * Notifications for a node are dispatched the moment node is made available to outer world, not the moment its property or reference is read, i.e. * if we read 3 properties and 2 references of a node A, we get single nodeRead(A) followed by 3 propertyRead() and 2 referenceRead() notifications. * <p/> * SEE ALSO SNodeUtil, SNodeAccessUtil */ public interface SNode { /** * Containing model or null if the node is not contained in any model * Does not produce node read event as the function depending on model is not a pure node function. * * @see SModelAccessListener */ @Nullable SModel getModel(); /** * Uniquely identifies the node within its containing model. May also be null. * Does not produce node read event as the result value can't be changed. */ SNodeId getNodeId(); /** * Uniquely identifies the node in a repository. Never changes between subsequent read and write actions and behaves as a "weak reference" for a node * Represents the only correct way to pass or store nodes between read/write actions. * Does not produce node read event as the node is already obtained, and the read event has already happened. * If obtained for a node that is not in repository, can return invalid reference */ @NotNull SNodeReference getReference(); /** * The concept that this node represents. Concepts can be checked for equality with equals(). */ @NotNull SConcept getConcept(); boolean isInstanceOfConcept(@NotNull SAbstractConcept c); /** * A string representing the node used to show the node in UI */ String getPresentation(); /** * For instances of INamedConcept concepts retrieves "name" property * * @return null if node is not instance of INamedConcept */ @Nullable String getName(); // tree operation /** * Adds a new child as a last child in a given role. */ void addChild(@NotNull SContainmentLink role, @NotNull SNode child); /** * Inserts the given node as a child of the current node of the specified role right in front of the anchor node.<br/> * * @param role a role to insert new child into * @param child a node to insert * @param anchor a new child node will be inserted just before this node. If anchor is not specified, * a new child is inserted as a last child. If anchor is the first child element, newly added * child becomes head of collection */ void insertChildBefore(@NotNull SContainmentLink role, @NotNull SNode child, @Nullable SNode anchor); /** * Inserts the given node as a child of the current node of the specified role right after the anchor node.<br/> * * @param role a role to insert new child into * @param child a node to insert * @param anchor a new child node will be inserted just before this node. If anchor is not specified, * a new child is inserted as a first child. If anchor is the last child element, newly added * child becomes tail of collection */ void insertChildAfter(@NotNull SContainmentLink role, @NotNull SNode child, @Nullable SNode anchor); /** * Removes the child of this node. See "node manipulation" section in class doc */ void removeChild(@NotNull SNode child); /** * If the node is a root, removes it from a model, else removes the node from its parent. * See "node manipulation" section in class doc */ void delete(); //base tree queries /** * Returns the parent of this node * Does not produce read on current as current is already obtained, does notify read for the parent. * * @return parent of this node */ @Nullable SNode getParent(); //complex queries /** * Returns the ancestor of current node, which parent is null * Does not produce read on current as current is already obtained * * @return root containing this node */ @NotNull SNode getContainingRoot(); /** * Returns role of this node in parent node * Returns null if a node has no parent */ @Nullable SContainmentLink getContainmentLink(); /** * Works together with getLastChild(). Allows to iterate through a collection of all children. * * @return first element in a collection of children */ @Nullable SNode getFirstChild(); /** * Works together with getFirstChild(). Allows to iterate through a collection of all children. * * @return last element in a collection of children */ @Nullable SNode getLastChild(); /** * no parent -> no sibling. Root has no siblings * Does not produce read on current as current is already obtained * Notifies read for the parent node and sibling node, if any. */ @Nullable SNode getPrevSibling(); /** * no parent -> no sibling. Root has no siblings * Does not produce read on current as current is already obtained. * Notifies read for the parent node and sibling node, if any. */ @Nullable SNode getNextSibling(); /** * Returns an immutable collection of children in the specified role. * Does not produce read on current as current is already obtained, produces read accesses to child nodes lazily (when really accessed), * does not produce read accesses for skipped children */ @NotNull Iterable<? extends SNode> getChildren(SContainmentLink role); /** * Returns an immutable collection of all children. * Read access policy is same to getChildren(role) */ @NotNull Iterable<? extends SNode> getChildren(); // refs /** * Sets a reference of the given role to a particular node */ void setReferenceTarget(@NotNull SReferenceLink role, @Nullable SNode target); /** * Null means the reference has not been set or was set to null. It's impossible to the distinguish the two cases. */ @Nullable SNode getReferenceTarget(@NotNull SReferenceLink role); // SReferences /** * Retrieves an SReference of the given role to a node. * Since SReference can refer to nodes by name and resolve them dynamically, this method may be able to help you resolve * the target node even when working with invalid code. */ @Nullable SReference getReference(@NotNull SReferenceLink role); /** * Sets a reference of the given role to a node that is resolved from the SReference. * Since SReference can refer to nodes by name and resolve them dynamically, this method may be able to resolve * the target node even when working with invalid code. */ void setReference(@NotNull SReferenceLink role, @Nullable SReference reference); /** * Retrieves all SReferences from the node. * Since SReference can refer to nodes by name and resolve them dynamically, this method may be able to help you resolve * the target nodes even when working with invalid code. * <p/> * The returned collection is immutable. * Produces read access on the node. */ @NotNull public Iterable<? extends SReference> getReferences(); // props /** * Retrieves keys of all properties. The returned collection is immutable. */ @NotNull Iterable<SProperty> getProperties(); //todo remove this method as it has strange semantics boolean hasProperty(@NotNull SProperty property); /** * Returns the value of this property * * @return value of a property or null if the property was not set */ @Nullable String getProperty(@NotNull SProperty property); /** * Sets the value of the raw property. Constraints are not checked. */ void setProperty(@NotNull SProperty property, @Nullable String propertyValue); // user objects Object getUserObject(Object key); void putUserObject(Object key, @Nullable Object value); Iterable<Object> getUserObjectKeys(); //------------deprecated, remove after 3.2----------- /** * @deprecated use getContainmentLink() */ @Deprecated String getRoleInParent(); /** * @deprecated use hasProperty(SProperty) */ @Deprecated boolean hasProperty(String propertyName); /** * @deprecated use getProperty(SProperty) */ @Deprecated String getProperty(String propertyName); /** * @deprecated use setProperty(SProperty) */ @Deprecated void setProperty(String propertyName, String propertyValue); /** * @deprecated use getProperties() */ @Deprecated Iterable<String> getPropertyNames(); /** * @deprecated use setReferenceTarget(SReferenceLink, SNode) */ @Deprecated void setReferenceTarget(String role, @Nullable SNode target); /** * @deprecated use getReferenceTarget(SReferenceLink) */ @Deprecated SNode getReferenceTarget(String role); // SReferences /** * @deprecated use getReference(SReferenceLink) */ @Deprecated SReference getReference(String role); /** * @deprecated use setReference(SReferenceLink, SReference) */ @Deprecated void setReference(String role, SReference reference); /** * @deprecated use insertChildBefore(SContainmentLink, SNode, SNode) */ @Deprecated void insertChildBefore(String role, SNode child, @Nullable SNode anchor); /** * @deprecated use addChild(SContainmentLink, SNode) */ @Deprecated void addChild(String role, SNode child); /** * @deprecated use getChildren(SContainmentLink) */ @Deprecated Iterable<? extends SNode> getChildren(String role); }