/*
* 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);
}