Commit includes ControlLoopPolicy API and bugfixes
[policy/engine.git] / ECOMP-ControlloopPolicy / src / main / java / org / openecomp / policy / controlloop / compiler / ControlLoopCompiler.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ECOMP Policy Engine
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.policy.controlloop.compiler;
22
23 import java.io.InputStream;
24 import java.io.Serializable;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30
31 import org.jgrapht.DirectedGraph;
32 import org.jgrapht.graph.ClassBasedEdgeFactory;
33 import org.jgrapht.graph.DefaultEdge;
34 import org.jgrapht.graph.DirectedMultigraph;
35 import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
36 import org.openecomp.policy.common.logging.flexlogger.Logger;
37 import org.openecomp.policy.controlloop.policy.ControlLoop;
38 import org.openecomp.policy.controlloop.policy.ControlLoopPolicy;
39 import org.openecomp.policy.controlloop.policy.FinalResult;
40 import org.openecomp.policy.controlloop.policy.Policy;
41 import org.openecomp.policy.controlloop.policy.PolicyResult;
42 import org.openecomp.policy.controlloop.policy.TargetType;
43 import org.yaml.snakeyaml.Yaml;
44 import org.yaml.snakeyaml.constructor.Constructor;
45
46 import com.google.common.collect.ImmutableList;
47 import com.google.common.collect.ImmutableMap;
48
49 public class ControlLoopCompiler {
50         private static Logger LOGGER = FlexLogger.getLogger(ControlLoopCompiler.class.getName());
51         
52         public static ControlLoopPolicy compile(ControlLoopPolicy policy, ControlLoopCompilerCallback callback) throws CompilerException {
53                 //
54                 // Ensure the control loop is sane
55                 //
56                 validateControlLoop(policy.getControlLoop(), callback);
57                 //
58                 // Validate the policies
59                 //
60                 validatePolicies(policy, callback);
61                 
62                 return policy;
63         }
64         
65         public static ControlLoopPolicy compile(InputStream yamlSpecification, ControlLoopCompilerCallback callback) throws CompilerException {
66                 Yaml yaml = new Yaml(new Constructor(ControlLoopPolicy.class));
67                 Object obj = yaml.load(yamlSpecification);
68                 if (obj == null) {
69                         throw new CompilerException("Could not parse yaml specification.");
70                 }
71                 if (! (obj instanceof ControlLoopPolicy)) {
72                         throw new CompilerException("Yaml could not parse specification into required ControlLoopPolicy object");
73                 }
74                 return ControlLoopCompiler.compile((ControlLoopPolicy) obj, callback);
75         }
76         
77         private static void validateControlLoop(ControlLoop controlLoop, ControlLoopCompilerCallback callback) throws CompilerException {
78                 if (controlLoop == null && callback != null) {
79                         callback.onError("controlLoop cannot be null");
80                 }
81                 if ((controlLoop.getControlLoopName() == null || controlLoop.getControlLoopName().length() < 1) && callback != null) {
82                         callback.onError("Missing controlLoopName");
83                 }
84                 if ((!controlLoop.getVersion().contentEquals(ControlLoop.getVERSION())) && callback != null) {
85                         callback.onError("Unsupported version for this compiler");
86                 }
87                 if (controlLoop.getTrigger_policy() == null || controlLoop.getTrigger_policy().length() < 1) {
88                         throw new CompilerException("trigger_policy is not valid");
89                 }
90         }
91
92         private static void validatePolicies(ControlLoopPolicy policy, ControlLoopCompilerCallback callback) throws CompilerException {
93                 if (policy == null) {
94                         throw new CompilerException("policy cannot be null");
95                 }
96                 //
97                 // verify controlLoop overall timeout should be no less than the sum of operational policy timeouts
98                 //
99                 if (policy.getPolicies() == null) {
100             callback.onWarning("controlLoop is an open loop.");   
101         }
102         else{
103             int sum = 0;
104                     for (Policy operPolicy : policy.getPolicies()) {
105                         sum += operPolicy.getTimeout().intValue();
106                     }
107                     if (policy.getControlLoop().getTimeout().intValue() < sum && callback != null) {
108                         callback.onError("controlLoop overall timeout is less than the sum of operational policy timeouts.");
109                     }
110                     //
111                     // For this version we can use a directed multigraph, in the future we may not be able to
112                     //
113                     DirectedGraph<NodeWrapper, LabeledEdge> graph = new DirectedMultigraph<>(new ClassBasedEdgeFactory<NodeWrapper, LabeledEdge>(LabeledEdge.class));
114                     //
115                     // Check to see if the trigger Event is for OpenLoop, we do so by
116                     // attempting to create a FinalResult object from it. If its a policy id, this should
117                     // return null.
118                     //
119                     FinalResult triggerResult = FinalResult.toResult(policy.getControlLoop().getTrigger_policy());
120                     TriggerNodeWrapper triggerNode;
121                     //
122                     // Did this turn into a FinalResult object?
123                     //
124                     if (triggerResult != null) {
125                         //
126                         // Ensure they didn't use some other FinalResult code
127                         //
128                         if (triggerResult != FinalResult.FINAL_OPENLOOP) {
129                                 throw new CompilerException("Unexpected Final Result for trigger_policy, should only be " + FinalResult.FINAL_OPENLOOP.toString() + " or a valid Policy ID");
130                         }
131                         //
132                         // They really shouldn't have any policies attached.
133                         //
134                         if ((policy.getPolicies() != null || policy.getPolicies().isEmpty())&& callback != null ) {
135                                 callback.onWarning("Open Loop policy contains policies. The policies will never be invoked.");
136                         }
137                         return;
138                         //
139                     } else {
140                         //
141                         // Ok, not a FinalResult object so let's assume that it is a Policy. Which it should be.
142                         //
143                         triggerNode = new TriggerNodeWrapper(policy.getControlLoop().getControlLoopName());
144                     }
145                     //
146                     // Add in the trigger node
147                     //
148                     graph.addVertex(triggerNode);
149                     //
150                     // Add in our Final Result nodes. All paths should end to these nodes.
151                     //
152                     FinalResultNodeWrapper finalSuccess = new FinalResultNodeWrapper(FinalResult.FINAL_SUCCESS);
153                     FinalResultNodeWrapper finalFailure = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE);
154                     FinalResultNodeWrapper finalFailureTimeout = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE_TIMEOUT);
155                     FinalResultNodeWrapper finalFailureRetries = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE_RETRIES);
156                     FinalResultNodeWrapper finalFailureException = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE_EXCEPTION);
157                     FinalResultNodeWrapper finalFailureGuard = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE_GUARD);
158                     graph.addVertex(finalSuccess);
159                     graph.addVertex(finalFailure);
160                     graph.addVertex(finalFailureTimeout);
161                     graph.addVertex(finalFailureRetries);
162                     graph.addVertex(finalFailureException);
163                     graph.addVertex(finalFailureGuard);
164                     //
165                     // Work through the policies and add them in as nodes.
166                     //
167                     Map<Policy, PolicyNodeWrapper> mapNodes = new HashMap<>();
168                     for (Policy operPolicy : policy.getPolicies()) {
169                         //
170                         // Is it still ok to add?
171                         //
172                         if (!okToAdd(operPolicy, callback)) {
173                                 //
174                                 // Do not add it in
175                                 //
176                                 continue;
177                         }
178                         //
179                         // Create wrapper policy node and save it into our map so we can
180                         // easily retrieve it.
181                         //
182                         PolicyNodeWrapper node = new PolicyNodeWrapper(operPolicy);
183                         mapNodes.put(operPolicy, node);
184                         graph.addVertex(node);
185                         //
186                         // Is this the trigger policy?
187                         //
188                         if (operPolicy.getId().equals(policy.getControlLoop().getTrigger_policy())) {
189                                 //
190                                 // Yes add an edge from our trigger event node to this policy
191                                 //
192                                 graph.addEdge(triggerNode, node, new LabeledEdge(triggerNode, node, new TriggerEdgeWrapper("ONSET")));
193                         }
194                     }
195                     //
196                     // last sweep to connect remaining edges for policy results
197                     //
198                     for (Policy operPolicy : policy.getPolicies()) {
199                         PolicyNodeWrapper node = mapNodes.get(operPolicy);
200                         //
201                         // Just ensure this has something
202                         //
203                         if (node == null) {
204                                 continue;
205                         }
206                         if (FinalResult.isResult(operPolicy.getSuccess(), FinalResult.FINAL_SUCCESS)) {
207                                 graph.addEdge(node, finalSuccess, new LabeledEdge(node, finalSuccess, new FinalResultEdgeWrapper(FinalResult.FINAL_SUCCESS)));
208                         } else {
209                                 PolicyNodeWrapper toNode = findPolicyNode(mapNodes, operPolicy.getSuccess());
210                                 if (toNode == null) {
211                                         throw new CompilerException("Operation Policy " + operPolicy.getId() + " success is connected to unknown policy " + operPolicy.getSuccess());
212                                 } else {
213                                  graph.addEdge(node, toNode, new LabeledEdge(node, toNode, new PolicyResultEdgeWrapper(PolicyResult.SUCCESS)));
214                                 }
215                         }
216                         if (FinalResult.isResult(operPolicy.getFailure(), FinalResult.FINAL_FAILURE)) {
217                                 graph.addEdge(node, finalFailure, new LabeledEdge(node, finalFailure, new FinalResultEdgeWrapper(FinalResult.FINAL_FAILURE)));
218                         } else {
219                                 PolicyNodeWrapper toNode = findPolicyNode(mapNodes, operPolicy.getFailure());
220                                 if (toNode == null) {
221                                         throw new CompilerException("Operation Policy " + operPolicy.getId() + " failure is connected to unknown policy " + operPolicy.getFailure());
222                                 } else {
223                                         graph.addEdge(node, toNode, new LabeledEdge(node, toNode, new PolicyResultEdgeWrapper(PolicyResult.FAILURE)));
224                                 }
225                         }
226                         if (FinalResult.isResult(operPolicy.getFailure_timeout(), FinalResult.FINAL_FAILURE_TIMEOUT)) {
227                                 graph.addEdge(node, finalFailureTimeout, new LabeledEdge(node, finalFailureTimeout, new FinalResultEdgeWrapper(FinalResult.FINAL_FAILURE_TIMEOUT)));
228                         } else {
229                                 PolicyNodeWrapper toNode = findPolicyNode(mapNodes, operPolicy.getFailure_timeout());
230                                 if (toNode == null) {
231                                         throw new CompilerException("Operation Policy " + operPolicy.getId() + " failure_timeout is connected to unknown policy " + operPolicy.getFailure_timeout());
232                                 } else {
233                                         graph.addEdge(node, toNode, new LabeledEdge(node, toNode, new PolicyResultEdgeWrapper(PolicyResult.FAILURE_TIMEOUT)));
234                                 }
235                         }
236                         if (FinalResult.isResult(operPolicy.getFailure_retries(), FinalResult.FINAL_FAILURE_RETRIES)) {
237                                 graph.addEdge(node, finalFailureRetries, new LabeledEdge(node, finalFailureRetries, new FinalResultEdgeWrapper(FinalResult.FINAL_FAILURE_RETRIES)));
238                         } else {
239                                 PolicyNodeWrapper toNode = findPolicyNode(mapNodes, operPolicy.getFailure_retries());
240                                 if (toNode == null) {
241                                         throw new CompilerException("Operation Policy " + operPolicy.getId() + " failure_retries is connected to unknown policy " + operPolicy.getFailure_retries());
242                                 } else {
243                                         graph.addEdge(node, toNode, new LabeledEdge(node, toNode, new PolicyResultEdgeWrapper(PolicyResult.FAILURE_RETRIES)));
244                                 }
245                         }
246                         if (FinalResult.isResult(operPolicy.getFailure_exception(), FinalResult.FINAL_FAILURE_EXCEPTION)) {
247                                 graph.addEdge(node, finalFailureException, new LabeledEdge(node, finalFailureException, new FinalResultEdgeWrapper(FinalResult.FINAL_FAILURE_EXCEPTION)));
248                         } else {
249                                 PolicyNodeWrapper toNode = findPolicyNode(mapNodes, operPolicy.getFailure_exception());
250                                 if (toNode == null) {
251                                         throw new CompilerException("Operation Policy " + operPolicy.getId() + " failure_exception is connected to unknown policy " + operPolicy.getFailure_exception());
252                                 } else {
253                                         graph.addEdge(node, toNode, new LabeledEdge(node, toNode, new PolicyResultEdgeWrapper(PolicyResult.FAILURE_EXCEPTION)));
254                                 }
255                         }
256                         if (FinalResult.isResult(operPolicy.getFailure_guard(), FinalResult.FINAL_FAILURE_GUARD)) {
257                                 graph.addEdge(node, finalFailureGuard, new LabeledEdge(node, finalFailureGuard, new FinalResultEdgeWrapper(FinalResult.FINAL_FAILURE_GUARD)));
258                         } else {
259                                 PolicyNodeWrapper toNode = findPolicyNode(mapNodes, operPolicy.getFailure_guard());
260                                 if (toNode == null) {
261                                         throw new CompilerException("Operation Policy " + operPolicy.getId() + " failure_guard is connected to unknown policy " + operPolicy.getFailure_guard());
262                                 } else {
263                                         graph.addEdge(node, toNode, new LabeledEdge(node, toNode, new PolicyResultEdgeWrapper(PolicyResult.FAILURE_GUARD)));
264                                 }
265                         }
266                 }
267                     //
268                     // Now validate all the nodes/edges
269                     //
270                     for (NodeWrapper node : graph.vertexSet()) {
271                         if (node instanceof TriggerNodeWrapper) {
272                                 LOGGER.info("Trigger Node " + node.toString());
273                                 if (graph.inDegreeOf(node) > 0 ) {
274                                         //
275                                         // Really should NEVER get here unless someone messed up the code above.
276                                         //
277                                         throw new CompilerException("No inputs to event trigger");
278                                 }
279                                 //
280                                 // Should always be 1, except in the future we may support multiple events
281                                 //
282                                 if (graph.outDegreeOf(node) > 1) {
283                                         throw new CompilerException("The event trigger should only go to ONE node");
284                                 }
285                         } else if (node instanceof FinalResultNodeWrapper) {
286                                 LOGGER.info("FinalResult Node " + node.toString());
287                                 //
288                                 // FinalResult nodes should NEVER have an out edge
289                                 //
290                                 if (graph.outDegreeOf(node) > 0) {
291                                         throw new CompilerException("FinalResult nodes should never have any out edges.");
292                                 }
293                         } else if (node instanceof PolicyNodeWrapper) {
294                                 LOGGER.info("Policy Node " + node.toString());
295                                 //
296                                 // All Policy Nodes should have the 5 out degrees defined.
297                                 //
298                                 if (graph.outDegreeOf(node) != 6) {
299                                         throw new CompilerException("Policy node should ALWAYS have 6 out degrees.");
300                                 }
301                                 //
302                                 // All Policy Nodes should have at least 1 in degrees 
303                                 // 
304                                 if (graph.inDegreeOf(node) == 0 && callback != null) {
305                                         callback.onWarning("Policy " + node.getID() + " is not reachable.");
306                                 }
307                         }
308                         for (LabeledEdge edge : graph.outgoingEdgesOf(node)){
309                                 LOGGER.info(edge.from.getID() + " invokes " + edge.to.getID() + " upon " + edge.edge.getID());
310                         }
311                     }
312             }   
313         }
314         
315         private static boolean okToAdd(Policy operPolicy, ControlLoopCompilerCallback callback) {
316                 //
317         // Check the policy id and make sure its sane
318         //
319         boolean okToAdd = true;
320         if (operPolicy.getId() == null || operPolicy.getId().length() < 1) {
321                 if (callback != null) {
322                         callback.onError("Operational Policy has an bad ID");
323                 }
324                 okToAdd = false;
325         }
326         //
327         // Check if they decided to make the ID a result object
328         //
329         if (PolicyResult.toResult(operPolicy.getId()) != null) {
330                 if (callback != null) {
331                         callback.onError("Policy id is set to a PolicyResult " + operPolicy.getId());
332                 }
333                 okToAdd = false;
334         }
335         if (FinalResult.toResult(operPolicy.getId()) != null) {
336                 if (callback != null) {
337                         callback.onError("Policy id is set to a FinalResult " + operPolicy.getId());
338                 }
339                 okToAdd = false;
340         }
341         //
342         // Check that the actor/recipe/target are valid
343         // 
344         if (operPolicy.getActor() == null) {
345                 if (callback != null) {
346                         callback.onError("Policy actor is null");
347                 }
348                 okToAdd = false;
349         }
350         //
351         // Construct a list for all valid actors
352         //
353         ImmutableList<String> actors = ImmutableList.of("APPC", "AOTS", "MSO", "SDNO", "SDNR", "AAI");
354         //
355         if (operPolicy.getActor() != null && (!actors.contains(operPolicy.getActor())) ) {
356                 if (callback != null) {
357                         callback.onError("Policy actor is invalid");
358                 }
359                 okToAdd = false;
360         }
361         if (operPolicy.getRecipe() == null) {
362                 if (callback != null) {
363                         callback.onError("Policy recipe is null");
364                 }
365                 okToAdd = false;
366         }
367         //
368         // NOTE: We need a way to find the acceptable recipe values (either Enum or a database that has these)
369         // 
370         ImmutableMap<String, List<String>> recipes = new ImmutableMap.Builder<String, List<String>>()
371                                 .put("APPC", ImmutableList.of("Restart", "Rebuild", "Migrate", "ModifyConfig"))
372                         .put("AOTS", ImmutableList.of("checkMaintenanceWindow", "checkENodeBTicketHours", "checkEquipmentStatus", "checkEimStatus", "checkEquipmentMaintenance"))
373                         .put("MSO", ImmutableList.of("VF Module Create"))
374                         .put("SDNO", ImmutableList.of("health-diagnostic-type", "health-diagnostic", "health-diagnostic-history", "health-diagnostic-commands", "health-diagnostic-aes"))
375                         .put("SDNR", ImmutableList.of("Restart", "Reboot"))
376                         .build();
377         //
378         if (operPolicy.getRecipe() != null && (!recipes.getOrDefault(operPolicy.getActor(), Collections.emptyList()).contains(operPolicy.getRecipe()))) {
379                 if (callback != null) {
380                         callback.onError("Policy recipe is invalid");
381                 }
382                 okToAdd = false;
383         }
384         if (operPolicy.getTarget() == null) {
385                 if (callback != null) {
386                         callback.onError("Policy target is null");
387                 }
388                 okToAdd = false;
389         }
390         if (operPolicy.getTarget() != null && operPolicy.getTarget().getType() != TargetType.VM && operPolicy.getTarget().getType() != TargetType.VFC && operPolicy.getTarget().getType() != TargetType.PNF) {
391                 if (callback != null) {
392                         callback.onError("Policy target is invalid");
393                 }
394                 okToAdd = false;
395         }
396         //
397         // Check that policy results are connected to either default final * or another policy
398         //
399         if (FinalResult.toResult(operPolicy.getSuccess()) != null && operPolicy.getSuccess() != FinalResult.FINAL_SUCCESS.toString()) {
400                 if (callback != null) {
401                         callback.onError("Policy success is neither another policy nor FINAL_SUCCESS");
402                 }
403                 okToAdd = false;
404         }
405         if (FinalResult.toResult(operPolicy.getFailure()) != null && operPolicy.getFailure() != FinalResult.FINAL_FAILURE.toString()) {
406                 if (callback != null) {
407                         callback.onError("Policy failure is neither another policy nor FINAL_FAILURE");
408                 }
409                 okToAdd = false;
410         }
411         if (FinalResult.toResult(operPolicy.getFailure_retries()) != null && operPolicy.getFailure_retries() != FinalResult.FINAL_FAILURE_RETRIES.toString()) {
412                 if (callback != null) {
413                         callback.onError("Policy failure retries is neither another policy nor FINAL_FAILURE_RETRIES");
414                 }
415                 okToAdd = false;
416         }
417         if (FinalResult.toResult(operPolicy.getFailure_timeout()) != null && operPolicy.getFailure_timeout() != FinalResult.FINAL_FAILURE_TIMEOUT.toString()) {
418                 if (callback != null) {
419                         callback.onError("Policy failure timeout is neither another policy nor FINAL_FAILURE_TIMEOUT");
420                 }
421                 okToAdd = false;
422         }
423         if (FinalResult.toResult(operPolicy.getFailure_exception()) != null && operPolicy.getFailure_exception() != FinalResult.FINAL_FAILURE_EXCEPTION.toString()) {
424                 if (callback != null) {
425                         callback.onError("Policy failure exception is neither another policy nor FINAL_FAILURE_EXCEPTION");
426                 }
427                 okToAdd = false;
428         }
429         if (FinalResult.toResult(operPolicy.getFailure_guard()) != null && operPolicy.getFailure_guard() != FinalResult.FINAL_FAILURE_GUARD.toString()) {
430                 if (callback != null) {
431                         callback.onError("Policy failure guard is neither another policy nor FINAL_FAILURE_GUARD");
432                 }
433                 okToAdd = false;
434         }
435         return okToAdd;
436         }
437
438         private static PolicyNodeWrapper findPolicyNode(Map<Policy, PolicyNodeWrapper> mapNodes, String id) {
439                 for (Entry<Policy, PolicyNodeWrapper> entry : mapNodes.entrySet()) {
440                         if (entry.getKey().getId().equals(id)) {
441                                 return entry.getValue();
442                         }
443                 }
444                 return null;
445         }
446         
447         @FunctionalInterface
448         private interface NodeWrapper extends Serializable{
449                 public String   getID();
450         }
451         
452         private static class TriggerNodeWrapper implements NodeWrapper {
453                 private static final long serialVersionUID = -187644087811478349L;
454                 private String closedLoopControlName;
455                 
456                 public TriggerNodeWrapper(String closedLoopControlName) {
457                         this.closedLoopControlName = closedLoopControlName;
458                 }
459
460                 @Override
461                 public String toString() {
462                         return "TriggerNodeWrapper [closedLoopControlName=" + closedLoopControlName + "]";
463                 }
464
465                 @Override
466                 public String getID() {
467                         return closedLoopControlName;
468                 }
469                 
470         }
471                 
472         private static class FinalResultNodeWrapper implements NodeWrapper {
473                 private static final long serialVersionUID = 8540008796302474613L;
474                 private FinalResult result;
475
476                 public FinalResultNodeWrapper(FinalResult result) {
477                         this.result = result;
478                 }
479
480                 @Override
481                 public String toString() {
482                         return "FinalResultNodeWrapper [result=" + result + "]";
483                 }
484
485                 @Override
486                 public String getID() {
487                         return result.toString();
488                 }
489         }
490         
491         private static class PolicyNodeWrapper implements NodeWrapper {
492                 private static final long serialVersionUID = 8170162175653823082L;
493                 private Policy policy;
494                 
495                 public PolicyNodeWrapper(Policy operPolicy) {
496                         this.policy = operPolicy;
497                 }
498
499                 @Override
500                 public String toString() {
501                         return "PolicyNodeWrapper [policy=" + policy + "]";
502                 }
503
504                 @Override
505                 public String getID() {
506                         return policy.getId();
507                 }
508         }
509         
510         @FunctionalInterface
511         private interface EdgeWrapper extends Serializable{
512                 public String getID();
513                 
514         }
515         
516         private static class TriggerEdgeWrapper implements EdgeWrapper {
517                 private static final long serialVersionUID = 2678151552623278863L;
518                 private String trigger;
519                 
520                 public TriggerEdgeWrapper(String trigger) {
521                         this.trigger = trigger;
522                 }
523
524                 @Override
525                 public String getID() {
526                         return trigger;
527                 }
528
529                 @Override
530                 public String toString() {
531                         return "TriggerEdgeWrapper [trigger=" + trigger + "]";
532                 }
533                 
534         }
535         
536         private static class PolicyResultEdgeWrapper implements EdgeWrapper {
537                 private static final long serialVersionUID = 6078569477021558310L;
538                 private PolicyResult policyResult;
539
540                 public PolicyResultEdgeWrapper(PolicyResult policyResult) {
541                         super();
542                         this.policyResult = policyResult;
543                 }
544
545                 @Override
546                 public String toString() {
547                         return "PolicyResultEdgeWrapper [policyResult=" + policyResult + "]";
548                 }
549
550                 @Override
551                 public String getID() {
552                         return policyResult.toString();
553                 }
554                 
555                 
556         }
557         
558         private static class FinalResultEdgeWrapper implements EdgeWrapper {
559                 private static final long serialVersionUID = -1486381946896779840L;
560                 private FinalResult finalResult;
561                 public FinalResultEdgeWrapper(FinalResult result) {
562                         this.finalResult = result;
563                 }
564
565                 @Override
566                 public String toString() {
567                         return "FinalResultEdgeWrapper [finalResult=" + finalResult + "]";
568                 }
569                 
570                 @Override
571                 public String getID() {
572                         return finalResult.toString();
573                 }
574         }
575         
576         
577         private static class LabeledEdge extends DefaultEdge {
578                 private static final long serialVersionUID = 579384429573385524L;
579                 
580                 private NodeWrapper from;
581                 private NodeWrapper to;
582                 private EdgeWrapper edge;
583                 
584                 public LabeledEdge(NodeWrapper from, NodeWrapper to, EdgeWrapper edge) {
585                         this.from = from;
586                         this.to = to;
587                         this.edge = edge;
588                 }
589                 
590                 @SuppressWarnings("unused")
591                 public NodeWrapper from() {
592                         return from;
593                 }
594                 
595                 @SuppressWarnings("unused")
596                 public NodeWrapper to() {
597                         return to;
598                 }
599                 
600                 @SuppressWarnings("unused")
601                 public EdgeWrapper edge() {
602                         return edge;
603                 }
604
605                 @Override
606                 public String toString() {
607                         return "LabeledEdge [from=" + from + ", to=" + to + ", edge=" + edge + "]";
608                 }
609         }
610
611 }