001    package com.hammurapi.flow.runtime;
002    
003    import java.util.ArrayList;
004    import java.util.Collection;
005    import java.util.Iterator;
006    import java.util.List;
007    
008    import org.eclipse.emf.common.util.TreeIterator;
009    import org.eclipse.emf.ecore.EObject;
010    import org.eclipse.emf.ecore.util.EcoreUtil;
011    
012    import com.hammurapi.config.ConfigFactory;
013    import com.hammurapi.config.Factory;
014    import com.hammurapi.config.MethodCall;
015    import com.hammurapi.config.Named;
016    import com.hammurapi.config.NamedCollection;
017    import com.hammurapi.config.NamedObjectDefinition;
018    import com.hammurapi.config.ObjectDefinition;
019    import com.hammurapi.config.PropertySource;
020    import com.hammurapi.config.bootstrap.ConfigurationException;
021    import com.hammurapi.flow.Flow;
022    import com.hammurapi.flow.Pin;
023    
024    /**
025     * Transforms flow definition to config definition.
026     * @author Pavel Vlasov
027     *
028     */
029    public class FlowCompiler {
030            
031            private static final String TRUE = "true";
032            private static final String COMPILED = "compiled";
033    
034            /**
035             * Compiles flow definition to object definition.
036             * @param flow Flow definition.  
037             * @throws ConfigurationException
038             */
039            public com.hammurapi.flow.Flow compile(com.hammurapi.flow.Flow flow) throws ConfigurationException {
040                    if (isCompiled(flow)) {
041                            return flow;
042                    }
043                    Flow ret = (Flow) EcoreUtil.copy(flow);         
044                    compile(ret, true);
045                    return ret;
046            }
047                    
048            public static boolean isCompiled(Flow flow) {
049                    for (Named property: flow.getProperty()) {
050                            if (property instanceof NamedObjectDefinition && COMPILED.equals(property.getName())) {
051                                    return TRUE.equals(((NamedObjectDefinition) property).getValue());
052                            }
053                    }
054                    return false;
055            }
056    
057            private void compile(com.hammurapi.flow.Flow flow, boolean topLevel) throws ConfigurationException {            
058                    // Inject nodes and transitions
059                    List<com.hammurapi.flow.Node> nodes = new ArrayList<com.hammurapi.flow.Node>();
060                    List<com.hammurapi.flow.Transition> transitions = new ArrayList<com.hammurapi.flow.Transition>();
061                    for (com.hammurapi.flow.FlowElement fe: flow.getFlowElement()) {
062                            if (fe instanceof com.hammurapi.flow.Node) {
063                                    nodes.add((com.hammurapi.flow.Node) fe);
064                            } else if (fe instanceof com.hammurapi.flow.Transition) {
065                                    transitions.add((com.hammurapi.flow.Transition) fe);
066                            } else {
067                                    throw new ConfigurationException("Unexpected flow element type: "+fe.getClass()+" "+fe);
068                            }
069                    }
070                    
071                    if (flow.getFacadeInterface()!=null) {
072                            NamedObjectDefinition facadeInterfaceProperty = ConfigFactory.eINSTANCE.createNamedObjectDefinition();
073                            facadeInterfaceProperty.setName("facadeInterface");
074                            facadeInterfaceProperty.setValue(flow.getFacadeInterface());
075                            facadeInterfaceProperty.setType(Class.class.getName());
076                            flow.getProperty().add(facadeInterfaceProperty);
077                    }
078                    
079                    NamedCollection nodesCollection = ConfigFactory.eINSTANCE.createNamedCollection();
080                    nodesCollection.setName("nodes");
081                    for (com.hammurapi.flow.Node node: nodes) {
082                            nodesCollection.getElement().add(node);
083                    }
084                    flow.getProperty().add(nodesCollection);
085                    
086                    NamedCollection transitionsCollection = ConfigFactory.eINSTANCE.createNamedCollection();
087                    transitionsCollection.setName("transitions");
088                    for (com.hammurapi.flow.Transition transition: transitions) {
089                            transitionsCollection.getElement().add(transition);
090                    }
091                    flow.getProperty().add(transitionsCollection);
092                    
093                    if (topLevel) {
094                            MethodCall beforeConnectCall = ConfigFactory.eINSTANCE.createMethodCall();
095                            beforeConnectCall.setName("beforeConnect");
096                            flow.getProperty().add(beforeConnectCall);
097                    }
098                    
099                    for (com.hammurapi.flow.Transition transition: transitions) {
100                            int transitionIdx = transitions.indexOf(transition);
101                            addConnectCall(flow, nodes, transition, transitionIdx, true);
102                            addConnectCall(flow, nodes, transition, transitionIdx, false);
103                    }
104                    
105                    for (com.hammurapi.flow.Node node: nodes) {
106                            for (com.hammurapi.flow.Pin pin: node.getPin()) {
107                                    addPinCall(node, pin);
108                            }
109                    }
110                    
111                    // Compile nested flows
112                    for (com.hammurapi.flow.Node node: nodes) {
113                            if (node instanceof com.hammurapi.flow.Flow) {
114                                    compile((com.hammurapi.flow.Flow) node, false);
115                            }
116                    }
117                    
118                    // Remove design time properties
119                    Collection<PropertySource> propertySources = new ArrayList<PropertySource>();
120                    TreeIterator<EObject> tit = flow.eAllContents();
121                    while (tit.hasNext()) {
122                            EObject next = tit.next();
123                            if (next instanceof PropertySource) {
124                                    propertySources.add((PropertySource) next);
125                            }
126                    }
127                    
128                    for (PropertySource ps: propertySources) {
129                            Iterator<Named> pit = ps.getProperty().iterator();
130                            while (pit.hasNext()) {
131                                    Named property = pit.next();
132                                    if (!property.isRuntime()) {
133                                            pit.remove();
134                                    }
135                            }
136                    }
137                    
138                    if (topLevel) {
139                            MethodCall afterConnectCall = ConfigFactory.eINSTANCE.createMethodCall();
140                            afterConnectCall.setName("afterConnect");
141                            flow.getProperty().add(afterConnectCall);
142                            
143                            // Mark as compiled.
144                            NamedObjectDefinition compiledFlag = ConfigFactory.eINSTANCE.createNamedObjectDefinition();
145                            flow.getProperty().add(compiledFlag);
146                            compiledFlag.setName(COMPILED);
147                            compiledFlag.setValue(TRUE);
148                            compiledFlag.setRuntime(false);
149                    }
150            }
151    
152            private void addConnectCall(ObjectDefinition def,
153                            List<com.hammurapi.flow.Node> nodes,
154                            com.hammurapi.flow.Transition transition,
155                            int transitionIdx, 
156                            boolean isInbound) {
157                    MethodCall connectCall = ConfigFactory.eINSTANCE.createMethodCall();
158                    connectCall.setName("connect");
159                    def.getProperty().add(connectCall);
160                    
161                    Pin pin;
162                    if (isInbound) {
163                            pin = transition.getToPin();
164                    } else {
165                            pin = transition.getFromPin();
166                    }
167                    
168                    ObjectDefinition nodeIdx = ConfigFactory.eINSTANCE.createObjectDefinition();
169                    nodeIdx.setType(Integer.class.getName());
170                    nodeIdx.setValue(String.valueOf(nodes.indexOf(pin.getNode())));
171                    connectCall.getArgument().add(nodeIdx);
172                    
173                    ObjectDefinition pinName = ConfigFactory.eINSTANCE.createObjectDefinition();
174                    pinName.setType(String.class.getName());
175                    pinName.setValue(pin.getName());
176                    connectCall.getArgument().add(pinName);
177                    
178                    ObjectDefinition tIdx = ConfigFactory.eINSTANCE.createObjectDefinition();
179                    tIdx.setType(Integer.class.getName());
180                    tIdx.setValue(String.valueOf(transitionIdx));
181                    connectCall.getArgument().add(tIdx);
182                    
183                    ObjectDefinition inboundArg = ConfigFactory.eINSTANCE.createObjectDefinition();
184                    inboundArg.setType(Boolean.class.getName());
185                    inboundArg.setValue(String.valueOf(isInbound));
186                    connectCall.getArgument().add(inboundArg);      
187                    
188                    Factory connectKey;
189                    if (isInbound) {
190                            connectKey = transition.getToKey();
191                            transition.setToKey(null);
192                    } else {
193                            connectKey = transition.getFromKey();
194                            transition.setFromKey(null);
195                    }
196                    if (connectKey==null) {
197                            connectKey = ConfigFactory.eINSTANCE.createNull();
198                    }
199                    connectCall.getArgument().add(connectKey);
200            }
201    
202            private void addPinCall(com.hammurapi.flow.Node node, com.hammurapi.flow.Pin pin) {
203                    MethodCall addPinCall = ConfigFactory.eINSTANCE.createMethodCall();
204                    addPinCall.setName("addPin");
205                    node.getProperty().add(addPinCall);
206                                    
207                    ObjectDefinition pinName = ConfigFactory.eINSTANCE.createObjectDefinition();
208                    pinName.setType(String.class.getName());
209                    pinName.setValue(pin.getName());
210                    addPinCall.getArgument().add(pinName);
211                    
212                    Factory pinConfig = pin.getPinConfig();
213                    addPinCall.getArgument().add(pinConfig==null ? ConfigFactory.eINSTANCE.createNull() : pinConfig);
214            }
215    }