001: /*--
002:
003: Copyright (C) 2002-2005 Adrian Price.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: adrianprice@sourceforge.net.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Adrian Price (adrianprice@users.sourceforge.net).
027:
028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
038: POSSIBILITY OF SUCH DAMAGE.
039:
040: For more information on OBE, please see
041: <http://obe.sourceforge.net/>.
042:
043: */
044:
045: package org.obe.spi.model;
046:
047: import java.io.Serializable;
048: import java.util.Arrays;
049:
050: /**
051: * Stores the persistent state of an activity instance's join.
052: *
053: * @author Adrian Price
054: */
055: public abstract class JoinInstance implements Serializable {
056: private static final long serialVersionUID = -6225903231253140734L;
057: protected final String[] _transitions;
058: protected final boolean[] _states;
059: private boolean _fired;
060: private int _firedCount;
061: private transient boolean _modified;
062:
063: private static String asList(boolean[] states) {
064: StringBuffer sb = new StringBuffer();
065: sb.append('[');
066: for (int i = 0, n = states.length; i < n; i++) {
067: sb.append(states[i]);
068: if (i < n - 1)
069: sb.append(',');
070: }
071: sb.append(']');
072: return sb.toString();
073: }
074:
075: protected JoinInstance(String[] transitions) {
076: _transitions = (String[]) transitions.clone();
077: _states = new boolean[transitions.length];
078: }
079:
080: /**
081: * Handles the activation of an afferent transition.
082: *
083: * @param transitionId The ID of the transition that fired.
084: * @return <code>true</code> if the join fired.
085: */
086: public abstract boolean shouldFire(String transitionId);
087:
088: /**
089: * Informs the join that it has fired. If it has not already fired it is
090: * marked as both fired and modified, which states persist until it is
091: * reset and persisted respectively. Firing a join also increments its
092: * {@link #getFiredCount() fired count}.
093: */
094: protected final synchronized void fire() {
095: if (!_fired) {
096: _fired = _modified = true;
097: _firedCount++;
098: }
099: }
100:
101: /**
102: * Returns whether the join has fired.
103: *
104: * @return <code>true</code> if the join has fired.
105: */
106: public final synchronized boolean hasFired() {
107: return _fired;
108: }
109:
110: protected final synchronized boolean hasFired(int index) {
111: return _states[index];
112: }
113:
114: /**
115: * Sets the fired state for the specified transition. A change of state
116: * marks the join as modified.
117: *
118: * @param index Index to set.
119: * @return Whether the transition fired.
120: */
121: protected final synchronized boolean didFire(int index) {
122: boolean previousState = _states[index];
123: if (!previousState)
124: _modified = _states[index] = true;
125: return !previousState;
126: }
127:
128: /**
129: * Returns the number of times the join has fired since the workflow
130: * started.
131: *
132: * @return Fire count;
133: */
134: public final synchronized int getFiredCount() {
135: return _firedCount;
136: }
137:
138: /**
139: * Returns whether the join has been modified since it was last persisted.
140: *
141: * @return <code>true</code> if the join has been modified.
142: */
143: public final synchronized boolean isModified() {
144: return _modified;
145: }
146:
147: /**
148: * Resets the join to its initial state. The {@link #getFiredCount 'fired
149: * count'} is unaffected by the reset operation.
150: */
151: public final synchronized void reset() {
152: for (int i = 0; i < _states.length; i++) {
153: _modified |= _states[i];
154: _states[i] = false;
155: }
156: _modified |= _fired;
157: _fired = false;
158: }
159:
160: public final synchronized boolean equals(Object obj) {
161: if (this == obj)
162: return true;
163: if (obj == null || obj.getClass() != getClass())
164: return false;
165:
166: JoinInstance that = (JoinInstance) obj;
167: return _fired == that._fired
168: && Arrays.equals(_states, that._states)
169: && Arrays.equals(_transitions, that._transitions);
170: }
171:
172: public final synchronized int hashCode() {
173: int hashCode = 0;
174: for (int i = 0; i < _transitions.length; i++) {
175: hashCode ^= _transitions[i].hashCode();
176: if (_states[i])
177: hashCode++;
178: }
179: if (_fired)
180: hashCode++;
181: hashCode ^= _firedCount;
182: return hashCode;
183: }
184:
185: public final synchronized String toString() {
186: String type = getClass().getName();
187: type = type.substring(type.lastIndexOf('.') + 1);
188: return type
189: + "[transitions="
190: + (_transitions == null ? null : "length:"
191: + _transitions.length
192: + Arrays.asList(_transitions))
193: + ", states="
194: + (_states == null ? null : "length:" + _states.length
195: + asList(_states)) + ", fired=" + _fired
196: + ", firedCount=" + _firedCount + ']';
197: }
198: }
|