/* * Copyright 2014 Groupon, Inc * Copyright 2014 The Billing Project, LLC * * Groupon licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package org.killbill.automaton.dot; import java.util.HashMap; import java.util.Map; import com.google.common.base.Joiner; import javax.annotation.Nullable; public class DOTBuilder { private static final String INDENT = " "; private static final String NEW_LINE = "\n"; private static final String SPACE = " "; private static final Joiner SPACE_JOINER = Joiner.on(SPACE); private static final String EQUAL = "="; private static final String SEMI_COLON = ";"; private final String name; private final StringBuilder output; private int nextNodeId; private int nextClusterId; private int currentIndent; private String currentIndentString; public DOTBuilder(final String name) { this.name = name; this.output = new StringBuilder(); this.nextNodeId = 0; this.nextClusterId = 0; this.currentIndent = 1; rebuildCurrentIndent(); } public int addNode(final String name) { return addNode(name, null); } public int addNode(final String name, @Nullable final Map<String, String> attributesOrNull) { // attributes is for example label="Foo" or shape=box final Map<String, String> attributes = new HashMap<String, String>(); attributes.put("label", name); if (attributesOrNull != null) { attributes.putAll(attributesOrNull); } final String id = getNodeIdSymbol(nextNodeId); nextNodeId++; output.append(currentIndentString) .append(id); addAttributesInlineWithSpace(attributes); return nextNodeId - 1; } public void addPath(final int fromNodeId, final int toNodeId) { addPath(fromNodeId, toNodeId, null); } public void addPath(final int fromNodeId, final int toNodeId, @Nullable final Map<String, String> attributes) { addPath(getNodeIdSymbol(fromNodeId), getNodeIdSymbol(toNodeId), true, attributes); } public void addPath(final String from, final String to, final boolean directed, @Nullable final Map<String, String> attributes) { final String edgeSymbol = "-" + (directed ? ">" : "-"); output.append(currentIndentString) .append(from) .append(SPACE) .append(edgeSymbol) .append(SPACE) .append(to); addAttributesInlineWithSpace(attributes); } public void openCluster(final String name) { openCluster(name, null); } public void openCluster(final String name, @Nullable final Map<String, String> attributes) { output.append(currentIndentString) .append("subgraph") .append(SPACE) .append("cluster_").append(nextClusterId) .append(SPACE) .append("{") .append(NEW_LINE); nextClusterId++; increaseCurrentIndent(); output.append(currentIndentString) .append("label") .append(EQUAL) .append("\"") .append(name) .append("\"") .append(SEMI_COLON) .append(NEW_LINE); addAttributes(attributes); } public void closeCluster() { decreaseCurrentIndent(); output.append(currentIndentString) .append("}") .append(NEW_LINE); } public void open() { open(null); } public void open(@Nullable final Map<String, String> attributes) { output.append("digraph") .append(SPACE) .append(name) .append(SPACE) .append("{") .append(NEW_LINE); addAttributesNoBrackets(attributes); } private void addAttributes(@Nullable final Map<String, String> attributes) { if (attributes != null) { output.append(currentIndentString); addAttributesInlineNoSpace(attributes); } } private void addAttributesNoBrackets(@Nullable final Map<String, String> attributes) { if (attributes != null) { output.append(currentIndentString); addAttributesInline(attributes, false, false); } } private void addAttributesInlineNoSpace(@Nullable final Map<String, String> attributes) { addAttributesInline(attributes, false); } private void addAttributesInlineWithSpace(@Nullable final Map<String, String> attributes) { addAttributesInline(attributes, true); } private void addAttributesInline(@Nullable final Map<String, String> attributes, final boolean withSpace) { addAttributesInline(attributes, withSpace, true); } private void addAttributesInline(@Nullable final Map<String, String> attributes, final boolean withSpace, final boolean withBrackets) { if (attributes != null) { final String attributesSymbol = (withSpace ? SPACE : "") + (withBrackets ? "[" : "") + SPACE_JOINER.withKeyValueSeparator(EQUAL).join(attributes) + (withBrackets ? "]" : ""); output.append(attributesSymbol); } output.append(SEMI_COLON) .append(NEW_LINE); } public void close() { output.append("}") .append(NEW_LINE); } private String getNodeIdSymbol(final int nodeId) { return "node_" + nodeId; } private void increaseCurrentIndent() { currentIndent++; rebuildCurrentIndent(); } private void decreaseCurrentIndent() { currentIndent--; rebuildCurrentIndent(); } private void rebuildCurrentIndent() { currentIndentString = ""; for (int i = 0; i < currentIndent; i++) { currentIndentString += INDENT; } } @Override public String toString() { return output.toString(); } }