/**
* Copyright (c) 2013 Puppet Labs, Inc. and other contributors, as listed below.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Puppet Labs
*/
package com.puppetlabs.geppetto.graph;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.CancellationException;
import java.util.zip.GZIPOutputStream;
import com.puppetlabs.graph.ICancel;
import com.puppetlabs.graph.graphviz.GraphvizFormat;
import com.puppetlabs.graph.graphviz.GraphvizLayout;
import com.puppetlabs.graph.graphviz.IGraphviz;
import com.puppetlabs.graph.graphviz.IGraphviz.SVGOutputFilter;
import com.puppetlabs.graph.utils.IOutputStreamFilterFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* A service facade for Graphviz SVG production that configures a Guice injector and performs
* optional post processing of SVG stream.
*
* The only state held by the SVGProducer is the injector. It is safe to hold on to an instance
* of this class and use it multiple times. If instantiated via Guice it is a singleton.
*
*/
@Singleton
public class SVGProducer {
// graphviz runner
@Inject
private IGraphviz graphviz;
@Inject
@SVGOutputFilter
private IOutputStreamFilterFactory streamFilterFactory;
/**
* Transforms the text in DOT language in the given dotStream to SVG and writes the resulting SVG
* text to the given svgStream. If compress is true, the SVG output is written as SVGZ.
*
* When transformation is completed, the given svgStream is in a state where further writes are
* possible. It is the caller's responsibility to close the stream.
*
* It is possible to inject a filter configuration into the SVG stream by binding an {@link IOutputStreamFilterFactory}
* annotated with
*
* @Named(DependencyGraphModule.GRAPH_SVG_OUTPUT).
*
* @param dotStream
* stream with text in DOT language
* @param svgStream
* stream where SVG(Z) will be written
* @param compress
* output is SVGZ if true
* @param monitor
* used to check for cancellation, and for work pings, will configure SubMonitor with unknown work. Does not call done
* on
* the passed monitor - this is the callers responsibility unless a SubMonitor was passed.
*/
@SuppressWarnings("resource")
public void produceSVG(InputStream dotStream, OutputStream svgStream, boolean compress, IProgressMonitor monitor)
throws IOException {
// monitor
SubMonitor m2 = SubMonitor.convert(monitor);
// convert every 1000 checks for cancel into a worked(1)
final ICancel cancel = new ProgressMonitorCancelIndicator(m2.newChild(IProgressMonitor.UNKNOWN), 1000);
// configure in and out streams
// ByteArrayOutputStream tmp = new ByteArrayOutputStream();
// byte[] buffer = new byte[512];
// for(;;) {
// int read = dotStream.read(buffer);
// if(read == -1)
// break;
// tmp.write(buffer, 0, read);
// }
GZIPOutputStream zipStream = null;
if(compress) {
zipStream = new GZIPOutputStream(svgStream);
svgStream = zipStream;
}
// If filtering is wanted
svgStream = streamFilterFactory.configureFilterFor(svgStream);
// produce SVG from DOT
try {
if(graphviz.writeGraphvizOutput(cancel, svgStream, GraphvizFormat.svg, null, GraphvizLayout.dot, dotStream) == null)
throw new IOException("Graphviz SVG production failed - view logs");
}
catch(CancellationException e) {
// translate to expected exception when using IProgressMonitor
throw new OperationCanceledException();
}
// if the svgStream requires a finish to flush trailing data, it needs to be finished before the
// zip stream (if used).
if(svgStream instanceof IOutputStreamFilterFactory.IFinishable)
((IOutputStreamFilterFactory.IFinishable) svgStream).finish();
// Make sure everything is written as we may otherwise lose trailing uncompressed bits
if(compress)
zipStream.finish();
// seems prudent to flush, but *do not* close, as the user may want to continue writing data
// to the given stream.
svgStream.flush();
}
}