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 }