public class GraphWriter {
	protected XMLStreamWriter xtw;
	protected int openElements = 0;
	protected int idSequence;
	protected final ElementWriter elementWriter = new InternalWriter();
	protected final String encoding;
	public GraphWriter(OutputStream out){
		this(out, StandardCharsets.UTF_8.name());
	}
	public GraphWriter(OutputStream out, String encoding) {
		this.encoding = encoding;
		XMLOutputFactory xof = XMLOutputFactory.newInstance();
		try {
			xtw = xof.createXMLStreamWriter(out, encoding);
		} catch (XMLStreamException e) {
			throw new RuntimeException(e);
		}
	}

	public String getNextId() {
		int id = idSequence++;
		return String.valueOf(id);
	}

	public void node(Node node, String id) {
		node.writeStart(elementWriter, id);
		node.writeEnd(elementWriter);
	}

	public void startNode(Node node, String id) {
		node.writeStart(elementWriter, id);
	}

	public void endNode(Node node) {
		node.writeEnd(elementWriter);
	}

	public void edge(Edge edge, String id, String source, String target) {
		edge.writeStart(elementWriter, id, source, target);
		edge.writeEnd(elementWriter);
	}

	public void startDocument() {
		try {
			xtw.writeStartDocument(encoding, "1.0");
			elementWriter.startElement("graphml");
			xtw.writeNamespace("xmlns", "http://graphml.graphdrawing.org/xmlns");
			xtw.writeNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
			xtw.writeNamespace("y", "http://www.yworks.com/xml/graphml");
			xtw.writeAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation",
					"http://graphml.graphdrawing.org/xmlns");
			for (Key key : DefaultKeys.values()) {
				key.writeTo(elementWriter);
			}
		} catch (XMLStreamException e) {
			throw new RuntimeException(e);
		}
	}

	public void writeKeys(Collection<Key> keys) {
		for (Key key : keys) {
			key.writeTo(elementWriter);
		}
	}

	public String startGraph(Graph graph){
		String nextId = getNextId();
		startGraph(graph, nextId);
		return nextId;
	}
	
	public void startGraph(Graph graph, String id) {
		graph.writeStart(elementWriter, id);
	}

	public void endGraph(Graph graph) {
		graph.writeEnd(elementWriter);
	}

	public void endDocument() {
		while (openElements > 0) {
			elementWriter.endElement();
		}
		try {
			xtw.writeEndDocument();
			xtw.flush();
			xtw.close();
		} catch (XMLStreamException e) {
			throw new RuntimeException(e);
		}
	}

	private class InternalWriter implements ElementWriter {

		@Override
		public void endElement() {
			if (openElements == 0) {
				throw new IllegalStateException("no open element found");
			}
			try {
				openElements--;
				xtw.writeEndElement();
			} catch (XMLStreamException e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public void writeText(String description) {
			try {
				xtw.writeCharacters(description);
			} catch (XMLStreamException e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public void startData(Key key) {
			startElement("data");
			writeAttribute("key", key.getId());
		}

		@Override
		public void startElement(String localName) {
			openElements++;

			try {
				xtw.writeStartElement(localName);
			} catch (XMLStreamException e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public void startElement(String namespaceURI, String localName) {
			openElements++;

			try {
				xtw.writeStartElement(namespaceURI, localName);
			} catch (XMLStreamException e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public void writeAttribute(String localName, String value) {
			if (value == null) {
				return;
			}
			try {
				xtw.writeAttribute(localName, value);
			} catch (XMLStreamException e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public void writeAttribute(String localName, Boolean value) {
			if (value == null) {
				return;
			}
			try {
				xtw.writeAttribute(localName, String.valueOf(value));
			} catch (XMLStreamException e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public void writeAttribute(String localName, Double value) {
			if (value == null) {
				return;
			}
			try {
				xtw.writeAttribute(localName, String.valueOf(value));
			} catch (XMLStreamException e) {
				throw new RuntimeException(e);
			}

		}

		@Override
		public void writeAttribute(String localName, Integer value) {
			if (value == null) {
				return;
			}
			try {
				xtw.writeAttribute(localName, String.valueOf(value));
			} catch (XMLStreamException e) {
				throw new RuntimeException(e);
			}
		}
	}

} public class BaseGraph implements Graph {
	public String description;
	public boolean defaultDirected;
	public static final Graph DIRECTED = new BaseGraph().withDefaultDirected(true);
	public static final Graph UNDIRECTED = new BaseGraph();
	
	public BaseGraph(){
	}

	public BaseGraph withDescription(String description){
		this.description = description;
		return this;
	}
	
	public BaseGraph withDefaultDirected(boolean defaultDirected){
		this.defaultDirected = defaultDirected;
		return this;
	}
	
	@Override
	public void writeStart(ElementWriter w, String id) {
		w.startElement("graph");
		w.writeAttribute("id", id);
		w.writeAttribute("edgedefault", defaultDirected ? "directed" : "undirected");
		if (description != null){
			w.startData(DefaultKeys.DESCRIPTION_FOR_GRAPH);
			w.writeText(description);
			w.endElement();
		}
	}

	@Override
	public void writeEnd(ElementWriter w) {
		w.endElement();
	}
} public class YedEdge<T extends EdgeGraphics> extends AbstractBaseEdge {
	public final T edgeGraphics;

	public YedEdge(final T edgeGraphics) {
		this.edgeGraphics = edgeGraphics;
	}

	@Override
	public void writeTo(ElementWriter w) {
		w.startData(YedKeys.EDGE_GRAPHICS);
		edgeGraphics.writeTo(w);
		w.endElement();
	}
} public class YedGroupNode<T extends NodeGraphics> extends YedNode<T>  {
	
	public YedGroupNode(final T nodeGraphics){
		super(nodeGraphics);
	}
	
	@Override
	public void writeStart(ElementWriter w, String id) {
		w.startElement("node");
		w.writeAttribute("id", id);
		w.writeAttribute("yfiles.foldertype", "group");
		writeTo(w);
	}
} public enum YedKeys implements Key{
	PORT_GRAPHICS("y1", "port", "yfiles.type", "portgraphics"),
	PORT_GEOMETRY("y2", "port", "yfiles.type", "portgeometry"),
	PORT_USERDATA("y3", "port", "yfiles.type", "portuserdata"),
	NODE_GRAPHICS("y4", "node", "yfiles.type", "nodegraphics"),
	RESOURCES_FOR_GRAPHML("y5", "graphml", "yfiles.type", "resources"),
	EDGE_GRAPHICS("y6", "edge", "yfiles.type", "edgegraphics");

	public final String id;
	public final String forName;
	public final String attrTypeName;
	public final String attrTypeValue;
	
	YedKeys(String id, String forName, String attrTypeName, String attrTypeValue){
		this.id = id;
		this.forName = forName;
		this.attrTypeName = attrTypeName;
		this.attrTypeValue = attrTypeValue;
	}
	@Override
	public String getId() {
		return id;
	}
	@Override
	public void writeTo(ElementWriter w) {
		w.startElement("key");
		w.writeAttribute("id", id);
		w.writeAttribute("for", forName);
		w.writeAttribute(attrTypeName, attrTypeValue);
		w.endElement();
	}
} public class YedNode<T extends NodeGraphics> extends AbstractBaseNode {
	public final T nodeGraphics;
	
	public YedNode(final T nodeGraphics){
		this.nodeGraphics = nodeGraphics;
	}
	@Override
	public void writeTo(ElementWriter w) {
		w.startData(YedKeys.NODE_GRAPHICS);
		this.nodeGraphics.writeTo(w);		
  		w.endElement(); 
	}
} public class EdgeLabel extends AbstractBaseLabel implements LeafRenderable, YedConstants {
	private LabelModel labelModel;
	public boolean autoFlippingLabel;
	public Double distance=2.0;
	public Double ratio=0.5;
	public EdgeLabel(){
	}
	public EdgeLabel(String text){
		this.text = text;
	}
	
	public void setLabelModel(LabelModel labelModel){
		this.labelModel = labelModel;
		this.modelName = ModelName.CUSTOM;
	}
	@Override
	public void writeTo(ElementWriter w) {
		if (text == null) {
			return;
		}
		w.startElement(Y, "EdgeLabel");
		writerAttributes(w);
		w.writeAttribute("preferredPlacement", "anywhere");
		if (autoFlippingLabel){
			w.writeAttribute("configuration", "AutoFlippingLabel");
		}
		w.writeAttribute("ratio", ratio);
		w.writeAttribute("distance", distance);
		w.writeText(text);
		if (labelModel != null){
			w.startElement(Y, "LabelModel");
			labelModel.writeTo(w);
			w.endElement();
		}
		
		w.startElement(Y, "ModelParameter");
		if (labelModel != null){
			labelModel.writeParameter(w);
		}
		w.endElement();

		w.endElement();
	}

	public static EdgeLabel centered(String text) {
		return withModelAndPosition(text, ModelName.CENTERED, ModelPosition.CENTER);
	}
	public static EdgeLabel sourceCenter(String text) {
		return withModelAndPosition(text, ModelName.THREE_CENTER, ModelPosition.THREE_SOURCE_CENTER);
	}
	public static EdgeLabel sourceHead(String text) {
		return withModelAndPosition(text, ModelName.SIX_POS, ModelPosition.SIX_POS_SOURCE_HEAD);
	}
	public static EdgeLabel targetCenter(String text) {
		return withModelAndPosition(text, ModelName.THREE_CENTER, ModelPosition.THREE_TARGET_CENTER);
	}
	public static EdgeLabel targetHead(String text) {
		return withModelAndPosition(text, ModelName.SIX_POS, ModelPosition.SIX_POS_TARGET_HEAD);
	}
	public static EdgeLabel withModelAndPosition(String text, ModelName modelName, ModelPosition modelPosition) {
		EdgeLabel label = new EdgeLabel(text);
		label.modelName = modelName;
		label.modelPosition = modelPosition;
		return label;
	}
} public class GenericNode extends AbstractNodeGraphicsBase implements NodeGraphics, YedConstants{
	public enum Configuration{
		FLOWCHART_DIRECT_DATA("com.yworks.flowchart.directData"),
		FLOWCHART_PROCESS("com.yworks.flowchart.process"),
		FLOWCHART_DATABASE("com.yworks.flowchart.dataBase"),
		FLOWCHART_DECISION("com.yworks.flowchart.decision"),
		FLOWCHART_DOCUMENT("com.yworks.flowchart.document"),
		FLOWCHART_START1("com.yworks.flowchart.start1"),
		FLOWCHART_START2("com.yworks.flowchart.start2"),
		FLOWCHART_DATA("com.yworks.flowchart.data"),
		FLOWCHART_PREDEFINED_PROCESS("com.yworks.flowchart.predefinedProcess"),
		FLOWCHART_STORED_DATA("com.yworks.flowchart.storedData"),
		FLOWCHART_INTERNAL_STORAGE("com.yworks.flowchart.internalStorage"),
		FLOWCHART_SEQUENTIAL_DATA("com.yworks.flowchart.sequentialData"),
		FLOWCHART_MANUAL_INPUT("com.yworks.flowchart.manualInput"),
		FLOWCHART_CARD("com.yworks.flowchart.card"),
		FLOWCHART_PAPER_TYPE("com.yworks.flowchart.paperType"),
		FLOWCHART_CLOUD("com.yworks.flowchart.cloud"),
		FLOWCHART_DELAY("com.yworks.flowchart.delay"),
		FLOWCHART_DISPLAY("com.yworks.flowchart.display"),
		FLOWCHART_MANUAL_OPERATION("com.yworks.flowchart.manualOperation"),
		FLOWCHART_PREPARATION("com.yworks.flowchart.preparation"),
		FLOWCHART_LOOP_LIMIT("com.yworks.flowchart.loopLimit"),
		FLOWCHART_LOOP_LIMIT_END("com.yworks.flowchart.loopLimitEnd"),
		FLOWCHART_TERMINATOR("com.yworks.flowchart.terminator"),
		FLOWCHART_ON_PAGE_REFERENCE("com.yworks.flowchart.onPageReference"),
		FLOWCHART_OFF_PAGE_REFERENCE("com.yworks.flowchart.offPageReference"),
		FLOWCHART_ANNOTATION("com.yworks.flowchart.annotation"),
		FLOWCHART_USER_MESSAGE("com.yworks.flowchart.userMessage"),
		FLOWCHART_NETWORK_MESSAGE("com.yworks.flowchart.networkMessage");
		public final String value;
		private Configuration(String value){
			this.value = value;
		}
	}
	
	public Configuration configuration;
	
	public GenericNode(){
		this.geometry.width = 80d;
		this.geometry.height = 40d;
	}

	public GenericNode(Configuration configuration) {
		this();
		this.configuration = configuration;
	}

	public GenericNode withConfiguration(Configuration configuration){
		this.configuration = configuration;
		return this;
	}
	
	@Override
	public void writeTo(ElementWriter w) {
		w.startElement(Y, "GenericNode");
		w.writeAttribute("configuration", configuration.value);
		writeGraphics(w);
		
  		w.endElement(); // shape node
	}

	public static GenericNode as(Configuration configuration, String text) {
		GenericNode
			node = new GenericNode(configuration);
		node.addLabel(new NodeLabel(text));
		return node;
	}

	public GenericNode withConfigurationAndLabelText(Configuration configuration, String text) {
		withConfiguration(configuration);
		withLabelText(text);	
		return this;
	}
} public enum Configuration{
	FLOWCHART_DIRECT_DATA("com.yworks.flowchart.directData"),
	FLOWCHART_PROCESS("com.yworks.flowchart.process"),
	FLOWCHART_DATABASE("com.yworks.flowchart.dataBase"),
	FLOWCHART_DECISION("com.yworks.flowchart.decision"),
	FLOWCHART_DOCUMENT("com.yworks.flowchart.document"),
	FLOWCHART_START1("com.yworks.flowchart.start1"),
	FLOWCHART_START2("com.yworks.flowchart.start2"),
	FLOWCHART_DATA("com.yworks.flowchart.data"),
	FLOWCHART_PREDEFINED_PROCESS("com.yworks.flowchart.predefinedProcess"),
	FLOWCHART_STORED_DATA("com.yworks.flowchart.storedData"),
	FLOWCHART_INTERNAL_STORAGE("com.yworks.flowchart.internalStorage"),
	FLOWCHART_SEQUENTIAL_DATA("com.yworks.flowchart.sequentialData"),
	FLOWCHART_MANUAL_INPUT("com.yworks.flowchart.manualInput"),
	FLOWCHART_CARD("com.yworks.flowchart.card"),
	FLOWCHART_PAPER_TYPE("com.yworks.flowchart.paperType"),
	FLOWCHART_CLOUD("com.yworks.flowchart.cloud"),
	FLOWCHART_DELAY("com.yworks.flowchart.delay"),
	FLOWCHART_DISPLAY("com.yworks.flowchart.display"),
	FLOWCHART_MANUAL_OPERATION("com.yworks.flowchart.manualOperation"),
	FLOWCHART_PREPARATION("com.yworks.flowchart.preparation"),
	FLOWCHART_LOOP_LIMIT("com.yworks.flowchart.loopLimit"),
	FLOWCHART_LOOP_LIMIT_END("com.yworks.flowchart.loopLimitEnd"),
	FLOWCHART_TERMINATOR("com.yworks.flowchart.terminator"),
	FLOWCHART_ON_PAGE_REFERENCE("com.yworks.flowchart.onPageReference"),
	FLOWCHART_OFF_PAGE_REFERENCE("com.yworks.flowchart.offPageReference"),
	FLOWCHART_ANNOTATION("com.yworks.flowchart.annotation"),
	FLOWCHART_USER_MESSAGE("com.yworks.flowchart.userMessage"),
	FLOWCHART_NETWORK_MESSAGE("com.yworks.flowchart.networkMessage");
	public final String value;
	private Configuration(String value){
		this.value = value;
	}
} public class NodeLabel extends AbstractBaseLabel implements LeafRenderable, YedConstants {
	
	public NodeLabel(){
	}

	public NodeLabel(String text){
		this.text = text;
	}
	@Override
	public void writeTo(ElementWriter w) {
		if (text == null){
			return;
		}
		w.startElement(Y, "NodeLabel");
		writerAttributes(w);
		w.writeText(text);
		w.endElement();
	}
/*	
    <y:NodeLabel 
    borderDistance="0.0" 
*/
} public class PolyLineEdge implements EdgeGraphics, YedConstants{
	public Path path = new Path();
	public LineStyle lineStyle = new LineStyle();
	public List<EdgeLabel> labels;
	public ArrowStyle source = ArrowStyle.NONE;
	public ArrowStyle target = ArrowStyle.STANDARD;

	@Override
	public void writeTo(ElementWriter w) {
		w.startElement(Y, "PolyLineEdge");

		path.writeTo(w);
		lineStyle.writeTo(w);

		w.startElement(Y, "Arrows");
		w.writeAttribute("source", source.value);
		w.writeAttribute("type", "line");
		w.writeAttribute("target", target.value);
		w.endElement();		
		
		if (labels != null){
			for (EdgeLabel label: labels){
				label.writeTo(w);
			}
		}
		
		w.startElement(Y, "BendStyle");
		w.writeAttribute("smoothed", false);
		w.endElement();		
		
		w.endElement();
	}
	
	public void addLabel(EdgeLabel label){
		if (labels == null){
			labels = new ArrayList<>();
		}
		labels.add(label);
	}
} public class ProxyAutoBoundsNode implements NodeGraphics, YedConstants{
	public GroupNode groupNode = new GroupNode();

	@Override
	public void writeTo(ElementWriter w) {
		w.startElement(Y, "ProxyAutoBoundsNode");
		w.startElement(Y, "Realizers");
		w.writeAttribute("active", 0);
		
		groupNode.writeTo(w);
		
        w.endElement(); // Realizers
   		w.endElement(); // ProxyAutoBoundsNode
	}
} public class RotatedDiscreteEdgeLabelModel implements LabelModel {
	/** label placement near the middle of the edge, directly on the edge's path */
	public static final int	CENTER=128;
	/** allowed positions to the center of the edge's path */
	public static final int	CENTERED=128;
	/** label placement near the middle of the edge, left/above the edge's path */
	public static final int	HEAD=2;
	/** label placement near the source, directly on the edge's path */
	public static final int	SCENTER=64;
	/** label placement near the source, left/above the edge's path */
	public static final int	SHEAD=1;
	/**  allowed positions to a set of six positions beside the edge path */
	public static final int	SIX_POS=63;
	/** label placement near the source, right/below the edge's path */
	public static final int	STAIL=8;
	/** label placement near the middle of the edge, right/below the edge's path */
	public static final int	TAIL=16;
	/** label placement near the target, directly on the edge's path */
	public static final int	TCENTER=256;
	/** label placement near the target, left/above the edge's path */
	public static final int	THEAD=4;
	/**  positions to a set of three positions directly on the edge's path */
	public static final int	THREE_CENTER=448;
	/** label placement near the target, right/below the edge's path */
	public static final int	TTAIL=32;
	/** allowed positions to the two locations near the center of the edge, beside the edge path */
	public static final int	TWO_POS=18;

	public boolean autoRotationEnabled=true;
	public double angle=0d;
	public double distance;
	public int candidateMask=HEAD|TAIL;
	public boolean positionRelativeToSegment=false;
	public RotatedDiscreteEdgeLabelModelParameter parameter = new RotatedDiscreteEdgeLabelModelParameter();
	
	@Override
	public void writeTo(ElementWriter w) {
		w.startElement(Y, "RotatedDiscreteEdgeLabelModel");
		w.writeAttribute("angle", angle);
		w.writeAttribute("autoRotationEnabled", autoRotationEnabled);
		w.writeAttribute("candidateMask", candidateMask);
		w.writeAttribute("distance", distance);
		w.writeAttribute("positionRelativeToSegment", positionRelativeToSegment);
		w.endElement();

		// TODO Auto-generated method stub
		/* <y:RotatedDiscreteEdgeLabelModel 
		 * angle="0.0" 
		 * autoRotationEnabled="true" 
		 * candidateMask="18" 
		 * distance="10.0" 
		 * positionRelativeToSegment="false"/>
		 */
	}

	public static class RotatedDiscreteEdgeLabelModelParameter implements LeafRenderable, YedConstants{
		public ModelPosition position=ModelPosition.TWO_POS_HEAD;
		@Override
		public void writeTo(ElementWriter w) {
			w.startElement(Y, "RotatedDiscreteEdgeLabelModelParameter");
			w.writeAttribute("position", position.value);
			w.endElement();		
		}
	}
	
	@Override
	public void writeParameter(ElementWriter w) {
		parameter.writeTo(w);
	}

} public class ShapeNode extends AbstractNodeGraphicsBase implements NodeGraphics, YedConstants{

	@Override
	public void writeTo(ElementWriter w) {
		w.startElement(Y, "ShapeNode");
		
		writeGraphics(w);
		
  		w.endElement(); // shape node
	}
	
	public static ShapeNode asRectangle(double x, double y, double width, double height, String text){
		ShapeNode nodeGraphics = new ShapeNode();
		nodeGraphics.geometry.x = x;
		nodeGraphics.geometry.y = y;
		nodeGraphics.geometry.width = width;
		nodeGraphics.geometry.height = height;
		nodeGraphics.addLabel(new NodeLabel(text));
		return nodeGraphics;
	}	

	public static ShapeNode asRectangle(double width, double height, String text){
		ShapeNode nodeGraphics = new ShapeNode();
		nodeGraphics.geometry.width = width;
		nodeGraphics.geometry.height = height;
		nodeGraphics.addLabel(new NodeLabel(text));
		return nodeGraphics;
	}	
} public enum AutoSizePolicy {
	CONTENT("content"),
	NODE_WIDTH("node_width"),
	NODE_HEIGHT("node_height"),
	NODE_SIZE("node_size");
	public final String value;
	private AutoSizePolicy(String value){
		this.value = value;
	}
} public enum LineType {
	LINE("line"),
	DASHED("dashed"),
	DOTTED("dotted"),
	DASHED_DOTTED("dashed_dotted");
	public final String value;
	LineType(String value){
		this.value = value;
	}
} public enum ModelName {
	CUSTOM("custom"),
	CORNERS("corners"),
	INTERNAL("internal"),
	SANDWICH("sandwich"),
	EIGHT_POS("eight_pos"),
	EDGE_OPPOSITE("edge_opposite"),
	// edge models
	CENTERED("centered"),
	TWO_POS("two_pos"),
	THREE_CENTER("three_center"),
	SIX_POS("six_pos"),
	CENTER_SLIDER("center_slider"),
	SIDE_SLIDER("side_slider");
	public final String value;
	private ModelName(String value){
		this.value = value;
	}
} public enum ModelPosition {
	NORTH("n"),
	NORTH_EAST("ne"),
	EAST("e"),
	SOUTH_EAST("se"),
	SOUTH("s"),
	SOUTH_WEST("se"),
	WEST("w"),
	NORTH_WEST("nw"),
	CENTER("c"),
	TOP("t"),
	BOTTOM("b"),
	LEFT("l"),
	RIGHT("r"),
	TOP_RIGHT("tr"),
	TOP_LEFT("tl"),
	BOTTOM_RIGHT("br"),
	BOTTOM_LEFT("bl"),
	
	TWO_POS_HEAD("head"),
	TWO_POS_TAIL("tail"),
	
	THREE_CENTER("center"),
	THREE_SOURCE_CENTER("scentr"),
	THREE_TARGET_CENTER("tcentr"),
	
	SIX_POS_HEAD("head"),
	SIX_POS_TAIL("tail"),
	SIX_POS_SOURCE_HEAD("shead"),
	SIX_POS_SOURCE_TAIL("stail"),
	SIX_POS_TARGET_HEAD("thead"),
	SIX_POS_TARGET_TAIL("ttail");
	public final String value;
	private ModelPosition(String value){
		this.value = value;
	}
} public enum ShapeType {
	RECTANGLE("rectangle"),
	RECTANGLE3D("rectangle3d"),
	TRIANGLE("triangle"),
	ROUNDRECTANGLE("roundrectangle"),
	DIAMOND("diamond"),
	OCTAGON("octagon"),
	PARALLELOGRAM("parallelogram"),
	HEXAGON("hexagon"),
	ELLIPSE("ellipse"),
	TRAPEZOID("trapezoid"),
	TRAPEZOID2("trapezoid2");
	
	public final String value;
	ShapeType(final String value){
		this.value = value;
	}
}
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.zip.GZIPOutputStream;
import org.junit.Test;
import de.graphml.writer.GraphWriter;
import de.graphml.writer.model.BaseGraph;
import de.graphml.writer.yed.YedEdge;
import de.graphml.writer.yed.YedGroupNode;
import de.graphml.writer.yed.YedKeys;
import de.graphml.writer.yed.YedNode;
import de.graphml.writer.yed.graphics.EdgeLabel;
import de.graphml.writer.yed.graphics.GenericNode;
import de.graphml.writer.yed.graphics.GenericNode.Configuration;
import de.graphml.writer.yed.graphics.NodeLabel;
import de.graphml.writer.yed.graphics.PolyLineEdge;
import de.graphml.writer.yed.graphics.ProxyAutoBoundsNode;
import de.graphml.writer.yed.graphics.RotatedDiscreteEdgeLabelModel;
import de.graphml.writer.yed.graphics.ShapeNode;
import de.graphml.writer.yed.style.AutoSizePolicy;
import de.graphml.writer.yed.style.LineType;
import de.graphml.writer.yed.style.ModelName;
import de.graphml.writer.yed.style.ModelPosition;
import de.graphml.writer.yed.style.ShapeType;
package de.graphml.writer.test;
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.
 */



public class GraphMLWriterTest {
	@Test
	public void writeSimpleGraph() throws Exception {
		GraphWriter graphWriter = new GraphWriter(new FileOutputStream("target/simple.graphml"));
		graphWriter.startDocument();
		graphWriter.writeKeys(Arrays.asList(YedKeys.values()));

		graphWriter.startGraph(BaseGraph.DIRECTED);
		YedNode<ShapeNode> node = new YedNode<>(ShapeNode.asRectangle(0d, 0d, 250d, 30d, "Data"));
		// write the node
		graphWriter.node(node, graphWriter.getNextId());
		
		// re-set text for next node
		node.nodeGraphics.firstLabel().text = "Task1";
		// adjust y coordinate
		node.nodeGraphics.geometry.y = 250d;
		// write the next node
		graphWriter.node(node, graphWriter.getNextId());
		
		PolyLineEdge
			edgeGraphics = new PolyLineEdge();
		edgeGraphics.addLabel(EdgeLabel.sourceHead("source head"));
		edgeGraphics.addLabel(EdgeLabel.centered("flow"));
		edgeGraphics.addLabel(EdgeLabel.targetHead("target"));
		
		YedEdge<PolyLineEdge>