001    /*
002     * Copyright 2005 Stephen J. McConnell.
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.state;
020    
021    import java.io.Serializable;
022    import java.util.Arrays;
023    
024    /**
025     * Default implementation of an application state descriptor.
026     * 
027     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
028     * @version 1.0.4
029     */
030    public class DefaultState implements State, Serializable
031    {
032        private final String m_name;
033        private final Transition[] m_transitions;
034        private final Operation[] m_operations;
035        private final Interface[] m_interfaces;
036        private final State[] m_states;
037        private final Trigger[] m_triggers;
038        private final boolean m_terminal;
039        
040        private transient State m_parent;
041    
042       /**
043        * Creation of a new state.
044        */
045        public DefaultState()
046        {
047            this( "root" );
048        }
049        
050       /**
051        * Creation of a new state.
052        * @param name the state name
053        */
054        public DefaultState( final String name )
055        {
056            this( name, new Trigger[0], new Transition[0], new Interface[0], new Operation[0], new State[0], true );
057        }
058        
059       /**
060        * Creation of a new non-terminal state.
061        * @param name the state name
062        * @param triggers an array of triggers
063        * @param transitions an array of state transitions
064        * @param interfaces an array of management interface declarations
065        * @param operations an array of operations
066        * @param states an array of substates
067        */
068        public DefaultState( 
069          final String name, final Trigger[] triggers, final Transition[] transitions, 
070          final Interface[] interfaces, final Operation[] operations, final State[] states )
071        {
072            this( name, triggers, transitions, interfaces, operations, states, false );
073        }
074        
075       /**
076        * Creation of a new state.
077        * @param name the state name
078        * @param triggers an array of triggers
079        * @param transitions an array of state transitions
080        * @param interfaces an array of management interface declarations
081        * @param operations an array of operations
082        * @param states an array of substates
083        * @param terminal the terminal flag
084        */
085        public DefaultState( 
086          final String name, final Trigger[] triggers, final Transition[] transitions, 
087          final Interface[] interfaces, final Operation[] operations, final State[] states, boolean terminal )
088        {
089            if( null == name )
090            {
091                throw new NullPointerException( "name" );
092            }
093            
094            for( int i=0; i<operations.length; i++ )
095            {
096                Operation operation = operations[i];
097                if( null == operation )
098                {
099                    throw new NullPointerException( "operation/" + i );
100                }
101            }
102            
103            m_name = name;
104            m_triggers = triggers;
105            m_transitions = transitions;
106            m_operations = operations;
107            m_interfaces = interfaces;
108            m_states = states;
109            m_terminal = terminal;
110            
111            for( int i=0; i<transitions.length; i++ )
112            {
113                Transition transition = transitions[i];
114                if( null == transition )
115                {
116                    throw new NullPointerException( "transition/" + i );
117                }
118                transition.setState( this );
119            }
120            
121            for( int i=0; i<triggers.length; i++ )
122            {
123                Trigger trigger = triggers[i];
124                if( null == trigger )
125                {
126                    throw new NullPointerException( "trigger/" + i );
127                }
128                Action action = trigger.getAction();
129                if( action instanceof Transition )
130                {
131                    Transition transition = (Transition) action;
132                    transition.setState( this );
133                }
134            }
135            
136            for( int i=0; i<interfaces.length; i++ )
137            {
138                Interface description = interfaces[i];
139                if( null == description )
140                {
141                    throw new NullPointerException( "interface/" + i );
142                }
143            }
144            
145            for( int i=0; i<states.length; i++ )
146            {
147                State state = states[i];
148                if( null == state )
149                {
150                    throw new NullPointerException( "state/ " + i );
151                }
152                state.setParent( this );
153            }
154        }
155        
156       /**
157        * Return the parent state to this state or null if this is the root of a 
158        * state graph.
159        * @return the parent state
160        */
161        public State getParent()
162        {
163            return m_parent;
164        }
165    
166       /**
167        * Set the parent state. 
168        * @param state the parent state
169        */
170        public void setParent( State state )
171        {
172            if( null == m_parent )
173            {
174                m_parent = state;
175            }
176            else
177            {
178                final String error = 
179                  "Illegal attempt to reassign parent.";
180                throw new IllegalStateException( error );
181            }
182        }
183        
184       /**
185        * Return the name of the state.
186        * @return the state name
187        */
188        public String getName()
189        {
190            return m_name;
191        }
192        
193       /**
194        * Return the array of triggers associated with the state.
195        * @return the trigger array
196        */
197        public Trigger[] getTriggers()
198        {
199            return m_triggers;
200        }
201        
202       /**
203        * Return the state path.  The path is composed of a sequence of states
204        * from the root to this state.
205        * @return the state path
206        */
207        public State[] getStatePath()
208        {
209            if( null == m_parent )
210            {
211                return new State[]{this};
212            }
213            else
214            {
215                State[] path = m_parent.getStatePath();
216                State[] result = new State[ path.length + 1 ];
217                System.arraycopy( path, 0, result, 0, path.length );
218                result[ path.length ] = this;
219                return result;
220            }
221        }
222        
223       /**
224        * Return the substates within this state.
225        * @return the substate array
226        */
227        public State[] getStates()
228        {
229            return m_states;
230        }
231        
232       /**
233        * Return the array of transtions associated with the state.
234        * @return the transition array
235        */
236        public Transition[] getTransitions()
237        {
238            return m_transitions;
239        }
240        
241       /**
242        * Return the array of operations associated with the state.
243        * @return the operation array
244        */
245        public Operation[] getOperations()
246        {
247            return m_operations;
248        }
249        
250       /**
251        * Return the array of management interfaces associated with the state.
252        * @return the interfaces array
253        */
254        public Interface[] getInterfaces()
255        {
256            return m_interfaces;
257        }
258        
259       /**
260        * Return the terminal flag.
261        * @return true if terminal
262        */
263        public boolean getTerminal()
264        {
265            return isTerminal();
266        }
267        
268       /**
269        * Test is the state is a terminal state.
270        * @return true if terminal
271        */
272        public boolean isTerminal()
273        {
274            return m_terminal;
275        }
276        
277       /**
278        * Return a string representation of the instance.
279        * @return the string value
280        */
281        public String toString()
282        {
283            StringBuffer buffer = new StringBuffer();
284            State[] path = getStatePath();
285            for( int i=0; i<path.length; i++ )
286            {
287                State state = path[i];
288                if( i>0 )
289                {
290                    buffer.append( "/" );
291                }
292                buffer.append( state.getName() );
293            }
294            return buffer.toString();
295            //return "[" + m_name + "]";
296        }
297        
298       /**
299        * Compare this object to another for equality.
300        * @param other the other object
301        * @return true if the object is equal to this object
302        */
303        public boolean equals( Object other )
304        {
305            if( null == other )
306            {
307                return false;
308            }
309            else if( other instanceof DefaultState )
310            {
311                DefaultState state = (DefaultState) other;
312                if( !m_name.equals( state.getName() ) )
313                {
314                    return false;
315                }
316                else if( m_terminal != state.isTerminal() )
317                {
318                    return false;
319                }
320                else if( !Arrays.equals( m_triggers, state.getTriggers() ) )
321                {
322                    return false;
323                }
324                else if( !Arrays.equals( m_transitions, state.getTransitions() ) )
325                {
326                    return false;
327                }
328                else if( !Arrays.equals( m_operations, state.getOperations() ) )
329                {
330                    return false;
331                }
332                else
333                {
334                    return Arrays.equals( m_states, state.getStates() );
335                }
336            }
337            else
338            {
339                return false;
340            }
341        }
342        
343       /**
344        * Compute the hashcode for this instance.
345        * @return the hashcode value
346        */
347        public int hashCode()
348        {
349            if( null == m_parent )
350            {
351                return m_name.hashCode();
352            }
353            else
354            {
355                int hash = m_parent.hashCode();
356                hash ^= m_name.hashCode();
357                return hash;
358            }
359        }
360    
361        private boolean equals( Object a, Object b )
362        {
363            if( null == a )
364            {
365                return null == b;
366            }
367            else
368            {
369                return a.equals( b );
370            }
371        }
372    }