    001    package com.hammurapi.eventbus;
    002
    003    import java.io.File;
    004    import java.io.FileWriter;
    005    import java.io.IOException;
    006    import java.io.Writer;
    007    import java.util.Collection;
    008    import java.util.Date;
    009    import java.util.LinkedHashMap;
    010    import java.util.LinkedList;
    011    import java.util.List;
    012    import java.util.Map;
    013    import java.util.Set;
    014    import java.util.concurrent.Callable;
    015
    016    import com.hammurapi.eventbus.AbstractEventBus.Snapshot;
    017    import com.hammurapi.extract.Predicate;
    018
    019    /**
    020     * Outputs dispatch network in DOT format.
    021     * @author Pavel Vlasov
    022     *
    023     * @param <E>
    024     * @param <P>
    025     * @param <C>
    026     * @param <K>
    027     */
    028    public class DispatchNetworkDotSnapshot<E, P extends Comparable<P>, C, K, H extends EventBus.Handle<E,P,C>, S extends EventStore<E,P,C,H,S>> implements Snapshot<E, P, C, K, H, S> {
    029
    030            private File out;
    031            private Writer writer;
    032
    033            private List<Callable<Object>> refCommands = new LinkedList<Callable<Object>>();
    034
    035            public DispatchNetworkDotSnapshot(File out) {
    036                    this.out = out;
    037            }
    038
    039            @Override
    040            public synchronized void start() {
    041                    try {
    042                            writer = new FileWriter(out);
    043                            writer.write("digraph \""+new Date()+"\" {\n");
    044                            for (Map.Entry<String, String> attr: graphAttributes.entrySet()) {
    045                                    writer.write("\t"+attr.getKey()+"="+attr.getValue()+";\n");
    046                            }
    047                    } catch (IOException e) {
    048                            throw new EventBusException(e);
    049                    }
    050            }
    051
    052            @Override
    053            public synchronized void end(boolean success) {
    054                    try {
    055                            if (success && out!=null) {
    056                                    for (Callable<Object> c: refCommands) {
    057                                            c.call();
    058                                    }
    059                            }
    060                            writer.write("}");
    061                            writer.close();
    062                            writer = null;
    063                    } catch (Exception e) {
    064                            throw new EventBusException(e);
    065                    }
    066            }
    067
    068            @Override
    069            public synchronized void handler(K id, EventHandler<E, P, C, H, S> eventHandler) {
    070                    try {
    071                            writer.write("\t"+id+" [");
    072                            String hName = eventHandler.getClass().getName();
    073                            int idx = hName.lastIndexOf('.');
    074                            if (idx!=-1) {
    075                                    hName = hName.substring(idx+1);
    076                            }
    077                            StringBuilder sb = new StringBuilder("shape=box, style=filled, fillcolor=burlywood1, label=\"");
    078                            encode(hName, sb);
    079                            sb.append(" ("+id+")\", tooltip=\"");
    080                            encode(String.valueOf(eventHandler), sb);
    081                            sb.append("\"");
    082                            writer.write(sb.toString());
    083                            writer.write("];\n");
    084                    } catch (IOException e) {
    085                            throw new EventBusException(e);
    086                    }
    087            }
    088
    089            private Map<String,String> graphAttributes = new LinkedHashMap<String, String>();
    090
    091            /**
    092             * Sets gpraph attribute, see DOT language documentation (http://www.graphviz.org/pdf/dotguide.pdf) for details.
    093             */
    094            public void setGraphAttribute(String name, String value) {
    095                    graphAttributes.put(name, value);
    096            }
    097
    098            @Override
    099            public synchronized void predicateNode(
    100                            final K id,
    101                            Predicate<E, C> predicate,
    102                            final Collection<K> trueChildren,
    103                            final Collection<K> trueHandlers,
    104                            final Collection<K> falseChildren,
    105                            final Collection<K> falseHandlers,
    106                            boolean isRoot) {
    107                    try {
    108                            writer.write("\t"+id+" [");
    109                            if (isRoot) {
    110                                    writer.write("shape=circle, style=filled, fillcolor=aliceblue, label=\"Root\"");
    111                            } else {
    112                                    String pName = predicate.getClass().getName();
    113                                    int idx = pName.lastIndexOf('.');
    114                                    if (idx!=-1) {
    115                                            pName = pName.substring(idx+1);
    116                                    }
    117                                    StringBuilder sb = new StringBuilder("shape=house, style=filled, fillcolor=aliceblue, label=\"");
    118                                    encode(pName,sb);
    119                                    sb.append(" ("+id+")\", tooltip=\"");
    120                                    encode(String.valueOf(predicate), sb);
    121                                    sb.append("\"");
    122                                    writer.write(sb.toString());
    123                            }
    124                            writer.write("];\n");
    125                            refCommands.add(new Callable<Object>() {
    126
    127                                    @Override
    128                                    public Object call() throws Exception {
    129                                            for (K tcid: trueChildren) {
    130                                                    writer.write("\t"+id+" -> "+tcid+" [color=green];\n");
    131                                            }
    132
    133                                            for (K thid: trueHandlers) {
    134                                                    writer.write("\t"+id+" -> "+thid+" [color=green];\n");
    135                                            }
    136
    137                                            for (K fcid: falseChildren) {
    138                                                    writer.write("\t"+id+" -> "+fcid+" [color=red];\n");
    139                                            }
    140
    141                                            for (K fhid: falseHandlers) {
    142                                                    writer.write("\t"+id+" -> "+fhid+" [color=red];\n");
    143                                            }
    144
    145                                            return null;
    146                                    }
    147
    148                            });
    149                    } catch (IOException e) {
    150                            throw new EventBusException(e);
    151                    }
    152            }
    153
    154            @Override
    155            public synchronized void joinInput(final K id, final K joinNodeId, int index) {
    156                    try {
    157                            writer.write("\t"+id+" [");
    158                            writer.write("shape=circle, style=filled, fillcolor=chartreuse, label=\""+index+"\"");
    159                            writer.write("];\n");
    160
    161                            refCommands.add(new Callable<Object>() {
    162
    163                                    @Override
    164                                    public Object call() throws Exception {
    165                                            writer.write("\t"+id+" -> "+joinNodeId+";\n");
    166                                            return null;
    167                                    }
    168
    169                            });
    170                    } catch (IOException e) {
    171                            throw new EventBusException(e);
    172                    }
    173            }
    174
    175            @Override
    176            public synchronized void joinNode(
    177                            final K id,
    178                            Predicate<E, C> predicate,
    179                            Set<Integer> outputIndices,
    180                            final K eventHandlerId,
    181                            final K nextJoinNodeId) {
    182                    try {
    183                            writer.write("\t"+id+" [");
    184                            writer.write("shape=invhouse, style=filled, fillcolor=chartreuse, label=\""+outputIndices+"\"");
    185                            if (predicate!=null) {
    186                                    StringBuilder sb = new StringBuilder(", tooltip=\"");
    187                                    encode(String.valueOf(predicate), sb);
    188                                    sb.append("\"");
    189                                    writer.write(sb.toString());
    190                            }
    191                            writer.write("];\n");
    192                            refCommands.add(new Callable<Object>() {
    193
    194                                    @Override
    195                                    public Object call() throws Exception {
    196                                            if (eventHandlerId==null) {
    197                                                    writer.write("\t"+id+" -> "+nextJoinNodeId+";\n");
    198                                            } else {
    199                                                    writer.write("\t"+id+" -> "+eventHandlerId+";\n");
    200                                            }
    201                                            return null;
    202                                    }
    203
    204                            });
    205                    } catch (IOException e) {
    206                            throw new EventBusException(e);
    207                    }
    208            }
    209
    210            private void encode(String str, StringBuilder out) {
    211
    212                    for (char ch: str.toCharArray()) {
    213                            if (Character.isLetterOrDigit(ch) || Character.isWhitespace(ch)) {
    214                                    out.append(ch);
    215                            } else {
    216                                    out.append("&#");
    217                                    out.append((int) ch);
    218                                    out.append(";");
    219                            }
    220                            if (out.length()>500) {
    221                                    out.append("...");
    222                                    break;
    223                            }
    224                    }
    225
    226            }
    227
    228    }