| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.commons.scxml2.model; |
| |
| import java.io.Serializable; |
| |
| /** |
| * An abstract base class for elements in SCXML that can serve as a |
| * <target> for a <transition>, such as State or Parallel. |
| * |
| */ |
| public abstract class TransitionTarget implements Serializable, Observable { |
| |
| private static final EnterableState[] ZERO_ANCESTORS = new EnterableState[0]; |
| |
| /** |
| * The id for this {@link Observable} which is unique within the SCXML state machine |
| */ |
| private Integer observableId; |
| |
| /** |
| * Identifier for this transition target. Other parts of the SCXML |
| * document may refer to this <state> using this ID. |
| */ |
| private String id; |
| |
| /** |
| * The parent of this transition target (may be null, if the parent |
| * is the SCXML document root). |
| */ |
| private EnterableState parent; |
| |
| private EnterableState[] ancestors = ZERO_ANCESTORS; |
| |
| /** |
| * Constructor. |
| */ |
| public TransitionTarget() { |
| parent = null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public final Integer getObservableId() { |
| return observableId; |
| } |
| |
| /** |
| * Sets the observableId for this Observable, which must be unique within the SCXML state machine |
| * @param observableId the observableId |
| */ |
| public final void setObservableId(Integer observableId) { |
| this.observableId = observableId; |
| } |
| |
| /** |
| * Get the identifier for this transition target (may be null). |
| * |
| * @return Returns the id. |
| */ |
| public final String getId() { |
| return id; |
| } |
| |
| /** |
| * Set the identifier for this transition target. |
| * |
| * @param id The id to set. |
| */ |
| public final void setId(final String id) { |
| this.id = id; |
| } |
| |
| /** |
| * @return the number of TransitionTarget ancestors |
| */ |
| public int getNumberOfAncestors() { |
| return ancestors.length; |
| } |
| |
| /** |
| * Get the ancestor of this TransitionTarget at specified level |
| * @param level the level of the ancestor to return, zero being top |
| * @return the ancestor at specified level |
| */ |
| public EnterableState getAncestor(int level) { |
| return ancestors[level]; |
| } |
| |
| /** |
| * Get the parent TransitionTarget. |
| * |
| * @return Returns the parent state |
| * (null if parent is <scxml> element) |
| */ |
| public EnterableState getParent() { |
| return parent; |
| } |
| |
| /** |
| * Set the parent EnterableState. |
| * <p> |
| * The parent of a TransitionTarget must be of type EnterableState as a History (as only non-EnterableState) |
| * TransitionTarget cannot have children. |
| * </p> |
| * |
| * @param parent The parent state to set |
| */ |
| protected void setParent(final EnterableState parent) { |
| if (parent == null) { |
| throw new IllegalArgumentException("Parent parameter cannot be null"); |
| } |
| if (parent == this) { |
| throw new IllegalArgumentException("Cannot set self as parent"); |
| } |
| if (this.parent != parent) { |
| this.parent = parent; |
| updateDescendantsAncestors(); |
| } |
| } |
| |
| /** |
| * Update TransitionTarget descendants their ancestors |
| */ |
| protected void updateDescendantsAncestors() { |
| TransitionTarget ttParent = parent; |
| ancestors = new EnterableState[ttParent.ancestors.length+1]; |
| System.arraycopy(ttParent.ancestors, 0, ancestors, 0, ttParent.ancestors.length); |
| ancestors[ttParent.ancestors.length] = parent; |
| } |
| |
| /** |
| * Checks whether this transition target (State or Parallel) is a |
| * descendant of the transition target context. |
| * |
| * @param context |
| * TransitionTarget context - a potential ancestor |
| * @return true if this is a descendant of context, false otherwise |
| */ |
| public final boolean isDescendantOf(TransitionTarget context) { |
| return getNumberOfAncestors() > context.getNumberOfAncestors() |
| && getAncestor(context.getNumberOfAncestors()) == context; |
| } |
| |
| /** |
| * Enforce identity equality only |
| * @param other other object to compare with |
| * @return this == other |
| */ |
| @Override |
| public final boolean equals(final Object other) { |
| return this == other; |
| } |
| |
| /** |
| * Enforce returning identity based hascode |
| * @return {@link System#identityHashCode(Object) System.identityHashCode(this)} |
| */ |
| @Override |
| public final int hashCode() { |
| return System.identityHashCode(this); |
| } |
| } |
| |