/* * Copyright 2003-2015 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 jetbrains.mps.smodel; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.language.SContainmentLink; import org.jetbrains.mps.openapi.language.SProperty; import org.jetbrains.mps.openapi.language.SReferenceLink; /** * <code>SNodeOwner</code> captures what node or a tree of nodes knows of / demands from its environment. * As node's environment includes not only model, but also used-to-be-in-model (aka myModelForUndo), repository, UndoHelper, * UnregisteredNodes and likely something else. * To manage all these from within a node became a tedious task, therefore the state node's surroundings could be in was extracted * as <code>SNodeOwner</code>. At the moment, three states were identified, {@link jetbrains.mps.smodel.FreeFloatNodeOwner 'free-float'}, * {@link jetbrains.mps.smodel.AttachedNodeOwner 'attached'} and {@link jetbrains.mps.smodel.DetachedNodeOwner 'detached'}. * * <em>Free-float node</em> is a fresh, newly created node, which has not been in any model yet. * <em>Attached</em> is a node that belongs to a model. * <em>Detached</em> is a node that once has been part of a model, but no longer is. * * <p>It might turn out we don't really need 'detached' nodes, as its primary purpose is to keep model-for-undo, and is coupled * with present approach UndoHelper and UnregisteredNodes advocates. * * <p>Abstract methods of this class are deemed mandatory for thoughtful implementation, as they constitute the essence of the state. * * <p>NOTE: This class and its subclasses are deliberately made package-local and final. We might add more states, if needed, but there's * no evident reason to augment existing states, nor there's reason to re-use <code>SNodeOwner</code> in any possible future alternative SNode * implementations. Even if such need arises, one may extract reasonable parts (e.g. <code>fire*</code> methods) into distinct facility. * * @see jetbrains.mps.smodel.AttachedNodeOwner * @see jetbrains.mps.smodel.DetachedNodeOwner * @see jetbrains.mps.smodel.FreeFloatNodeOwner * @author Artem Tikhomirov */ abstract class SNodeOwner { /** * Access model node attached to, or <code>null</code> if node doesn't belong to any model right now. * Note, model this node used to be doesn't qualify as 'right now'. */ @Nullable abstract SModel getModel(); /** * Fail with exception if there's no read access to the model, and the state requires access tracking. */ void assertLegalRead() {} /** * Fail with exception if there's no write access to the model, and the state requires access tracking. */ void assertLegalChange() {} // CRUD notifications void fireNodeRead(SNode node, boolean needUnclassified) {} void firePropertyRead(SNode node, SProperty p, String value, boolean hasProperty) {} void fireReferenceRead(SNode node, SReferenceLink link, SNode target) {} void firePropertyChange(SNode node, SProperty property, String oldValue, String newValue) {} void fireReferenceChange(SNode node, SReferenceLink l, org.jetbrains.mps.openapi.model.SReference oldRef, org.jetbrains.mps.openapi.model.SReference newRef) {} void fireNodeAdd(SNode node, SContainmentLink role, SNode child, SNode anchor) {} void fireBeforeNodeRemove(SNode node, SContainmentLink role, SNode child, SNode anchor) {} void fireNodeRemove(SNode node, SContainmentLink role, SNode child, SNode anchor) {} // establish SNode->SModel connection /** * Nodes being attached (add/insert node to a model) to the owner announce themselves using this method. * @param node non-null */ void registerNode(SNode node) { } /** * Nodes being detached from the owner announce themselves using this method. * @param node non-null */ void unregisterNode(SNode node) { } // /** * FIXME the method truly needs justification for existence. now it's merely a hack * FIXME If it indeed required, perhaps symmetric call needed in removeChild? If not, explain. * @param parent non-null, node being modified * @param child non-null, node being added */ void startUndoTracking(SNode parent, SNode child) { } /** * Notify environment there's a change in the model that could be undone. It's up to owner's discretion whether * the action shall end up in undo queue. * @param node non-null * @param action non-null */ abstract void performUndoableAction(org.jetbrains.mps.openapi.model.SNode node, SNodeUndoableAction action); }