001    /*
002     * Copyright 2006 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.IOException;
022    import java.io.OutputStream;
023    import java.io.Writer;
024    import java.io.OutputStreamWriter;
025    
026    import javax.xml.XMLConstants;
027    
028    import net.dpml.state.Trigger.TriggerEvent;
029    
030    /**
031     * Construct a state graph.
032     */
033    public class StateEncoder
034    {
035        private static final String XML_HEADER = 
036          "<?xml version=\"1.0\"?>";
037        
038        private static final String STATE_SCHEMA_URN = "link:xsd:dpml/lang/dpml-state#1.0";
039        
040       /**
041        * Externalize the part to XML.
042        * @param state the state graph to externalize
043        * @param output the output stream 
044        * @exception IOException if an IO error occurs
045        */
046        public void export( State state, OutputStream output ) throws IOException
047        {
048            final Writer writer = new OutputStreamWriter( output );
049    
050            writer.write( XML_HEADER );
051            writer.write( "\n\n" );
052            writer.write( "<state xmlns=\"" 
053              + STATE_SCHEMA_URN 
054              + "\""
055              + "\n    xmlns:xsi=\"" 
056              + XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
057              + "\"" );
058            if( state.isTerminal() )
059            {
060                writer.write( " terminal=\"true\">" );
061            }
062            else
063            {
064                writer.write( ">" );
065            }
066            writer.write( "\n" );
067            writeBody( writer, state, "" );
068            writer.write( "\n" );
069            writer.write( "\n</state>" );
070            writer.write( "\n" );
071            writer.flush();
072            output.close();
073        }
074        
075       /**
076        * Write the state.
077        * @param writer the stream writer
078        * @param state the state to externalize
079        * @param pad the pad offset
080        * @exception IOException if an IO error occurs
081        */
082        public void writeState( Writer writer, State state, String pad ) throws IOException
083        {
084            if( isEmpty( state ) )
085            {
086                return;
087            }
088            else
089            {
090                writer.write( "\n" + pad + "<state xmlns=\"" + STATE_SCHEMA_URN + "\"" );
091                if( state.isTerminal() )
092                {
093                    writer.write( " terminal=\"true\"" );
094                }
095                writer.write( ">" );
096                writeBody( writer, state, pad + "  " );
097                writer.write( "\n" + pad + "</state>" );
098            }
099        }
100        
101        private void writeBody( Writer writer, State state, String pad )  throws IOException
102        {
103            Trigger[] triggers = state.getTriggers();
104            Transition[] transitions = state.getTransitions();
105            Operation[] operations = state.getOperations();
106            Interface[] interfaces = state.getInterfaces();
107            State[] states = state.getStates();
108            
109            writeTriggers( writer, triggers, pad );
110            writeTransitions( writer, transitions, pad );
111            writeOperations( writer, operations, pad );
112            writeInterfaces( writer, interfaces, pad );
113            writeStates( writer, states, pad );
114        }
115        
116        private void writeTriggers( Writer writer, Trigger[] triggers, String pad )  throws IOException
117        {
118            if( triggers.length == 0 )
119            {
120                return;
121            }
122            else
123            {
124                for( int i=0; i<triggers.length; i++ )
125                {
126                    Trigger trigger = triggers[i];
127                    writeTrigger( writer, trigger, pad );
128                }
129            }
130        }
131        
132        private void writeTrigger( Writer writer, Trigger trigger, String pad )  throws IOException
133        {
134            TriggerEvent event = trigger.getEvent();
135            writer.write( "\n" + pad + "<trigger event=\"" );
136            writer.write( event.getName() );
137            writer.write( "\">" );
138            Action action = trigger.getAction();
139            writeAction( writer, action, pad + "  " );
140            writer.write( "\n" + pad + "</trigger>" );
141        }
142        
143        private void writeTransitions( Writer writer, Transition[] transitions, String pad )  throws IOException
144        {
145            if( transitions.length == 0 )
146            {
147                return;
148            }
149            else
150            {
151                for( int i=0; i<transitions.length; i++ )
152                {
153                    Transition transition = transitions[i];
154                    writeTransition( writer, transition, pad );
155                }
156            }
157        }
158        
159        private void writeOperations( Writer writer, Operation[] operations, String pad )  throws IOException
160        {
161            if( operations.length == 0 )
162            {
163                return;
164            }
165            else
166            {
167                for( int i=0; i<operations.length; i++ )
168                {
169                    Operation operation = operations[i];
170                    writeOperation( writer, operation, pad );
171                }
172            }
173        }
174        
175        private void writeInterfaces( Writer writer, Interface[] interfaces, String pad )  throws IOException
176        {
177            if( interfaces.length == 0 )
178            {
179                return;
180            }
181            else
182            {
183                for( int i=0; i<interfaces.length; i++ )
184                {
185                    Interface spec = interfaces[i];
186                    writeInterface( writer, spec, pad );
187                }
188            }
189        }
190        
191        private void writeStates( Writer writer, State[] states, String pad )  throws IOException
192        {
193            if( states.length == 0 )
194            {
195                return;
196            }
197            else
198            {
199                for( int i=0; i<states.length; i++ )
200                {
201                    State state = states[i];
202                    writeNestedState( writer, state, pad );
203                }
204            }
205        }
206        
207        private void writeTransition( Writer writer, Transition transition, String pad )  throws IOException
208        {
209            String name = transition.getName();
210            String target = transition.getTargetName();
211            writer.write( "\n" + pad + "<transition name=\"" + name + "\" target=\"" + target + "\"" );
212            Operation operation = transition.getOperation();
213            if( null == operation )
214            {
215                writer.write( "/>" );
216            }
217            else
218            {
219                writer.write( ">" );
220                writeOperation( writer, operation, pad + "  " );
221                writer.write( "\n" + pad + "</transition>" );
222            }
223        }
224    
225        private void writeOperation( Writer writer, Operation operation, String pad )  throws IOException
226        {
227            String name = operation.getName();
228            String method = operation.getMethodName();
229            writer.write( "\n" + pad + "<operation name=\"" + name + "\"" );
230            if( null != method )
231            {
232                writer.write( " method=\"" + method + "\"" );
233            }
234            writer.write( "/>" );
235        }
236        
237        private void writeInterface( Writer writer, Interface spec, String pad )  throws IOException
238        {
239            String classname = spec.getClassname();
240            writer.write( "\n" + pad + "<interface class=\"" + classname + "\"/>" );
241        }
242        
243        private void writeAction( Writer writer, Action action, String pad )  throws IOException
244        {
245            if( action instanceof Transition )
246            {
247                Transition transition = (Transition) action;
248                writeTransition( writer, transition, pad );
249            }
250            else if( action instanceof Operation )
251            {
252                Operation operation = (Operation) action;
253                writeOperation( writer, operation, pad );
254            }
255            else if( action instanceof Interface )
256            {
257                Interface spec = (Interface) action;
258                writeInterface( writer, spec, pad );
259            }
260            else if( action instanceof ExecAction )
261            {
262                ExecAction exec = (ExecAction) action;
263                writeExecAction( writer, exec, pad );
264            }
265            else if( action instanceof ApplyAction )
266            {
267                ApplyAction apply = (ApplyAction) action;
268                writeApplyAction( writer, apply, pad );
269            }
270            else
271            {
272                final String error = 
273                  "Unrecognized action class ["
274                  + action.getClass().getName()
275                  + "].";
276                throw new IOException( error );
277            }
278        }
279        private void writeExecAction( Writer writer, ExecAction action, String pad )  throws IOException
280        {
281            String id = action.getID();
282            writer.write( "\n" + pad + "<exec id=\"" + id + "\"/>" );
283        }
284        
285        private void writeApplyAction( Writer writer, ApplyAction action, String pad )  throws IOException
286        {
287            String id = action.getID();
288            writer.write( "\n" + pad + "<apply id=\"" + id + "\"/>" );
289        }
290        
291        private void writeNestedState( Writer writer, State state, String pad )  throws IOException
292        {
293            String name = state.getName();
294            writer.write( "\n" + pad + "<state name=\"" + name + "\"" );
295            if( state.isTerminal() )
296            {
297                writer.write( " terminal=\"true\">" );
298            }
299            else
300            {
301                writer.write( ">" );
302            }
303            writeBody( writer, state, pad + "  " );
304            writer.write( "\n" + pad + "</state>" );
305        }
306    
307        private boolean isEmpty( State state )
308        {
309            if( state.getTriggers().length > 0 )
310            {
311                return false;
312            }
313            if( state.getTransitions().length > 0 )
314            {
315                return false;
316            }
317            if( state.getOperations().length > 0 )
318            {
319                return false;
320            }
321            if( state.getInterfaces().length > 0 )
322            {
323                return false;
324            }
325            if( state.getStates().length > 0 )
326            {
327                return false;
328            }
329            return true;
330        }
331    }