/******************************************************************************* * Copyright 2013 * Ubiquitous Knowledge Processing (UKP) Lab * Technische Universität Darmstadt * * Licensed 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 de.tudarmstadt.ukp.csniper.treevisualizer; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.uima.UimaContext; import org.apache.uima.analysis_engine.AnalysisEngineProcessException; import org.apache.uima.fit.component.JCasAnnotator_ImplBase; import org.apache.uima.fit.descriptor.ConfigurationParameter; import org.apache.uima.fit.util.JCasUtil; import org.apache.uima.jcas.JCas; import org.apache.uima.jcas.cas.FSArray; import org.apache.uima.jcas.tcas.Annotation; import org.apache.uima.resource.ResourceInitializationException; import annis.frontend.servlets.visualizers.Visualizer; import annis.frontend.servlets.visualizers.tree.TigerTreeVisualizer; import annis.model.AnnisNode; import annis.model.AnnotationGraph; import annis.model.Edge; import annis.model.Edge.EdgeType; import de.tudarmstadt.ukp.dkpro.core.api.metadata.type.DocumentMetaData; import de.tudarmstadt.ukp.dkpro.core.api.parameter.ComponentParameters; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Token; import de.tudarmstadt.ukp.dkpro.core.api.syntax.type.constituent.Constituent; import de.tudarmstadt.ukp.dkpro.core.api.syntax.type.constituent.ROOT; /** * Draws constituent trees to png files.<br> * Requires a parser to be run before. * * @author Erik-Lân Do Dinh */ public class TreeVisualizer extends JCasAnnotator_ImplBase { public static final String PARAM_OUTPUT_PATH = ComponentParameters.PARAM_TARGET_LOCATION; @ConfigurationParameter(name = PARAM_OUTPUT_PATH, mandatory = true) private File targetLocation; public static final String PARAM_NAMESPACE = "namespace"; @ConfigurationParameter(name = PARAM_NAMESPACE, mandatory = true) private String namespace; private Log log = LogFactory.getLog(getClass()); private Map<Annotation, AnnisNode> nodes; private List<Edge> edges; private JCas jcas; @Override public void initialize(UimaContext aContext) throws ResourceInitializationException { super.initialize(aContext); try { FileUtils.forceMkdir(targetLocation); } catch (IOException e) { throw new ResourceInitializationException(e); } } @Override public void process(JCas aJCas) throws AnalysisEngineProcessException { jcas = aJCas; OutputStream os = null; int i = 0; for (ROOT root : JCasUtil.select(jcas, ROOT.class)) { try { DocumentMetaData meta = DocumentMetaData.get(aJCas); File image = new File(targetLocation, meta.getDocumentId() + "_" + i + ".png"); if (log.isInfoEnabled()) { log.info("Started creating image [" + image.getAbsolutePath() + "]."); } os = new FileOutputStream(image); Visualizer ttv = new TigerTreeVisualizer(); ttv.setResult(new AnnisResultIncompleteImpl(createGraph(root))); ttv.setMappings(new Properties()); ttv.setNamespace(namespace); ttv.writeOutput(os); if (log.isInfoEnabled()) { log.info("Finished writing image [" + image.getAbsolutePath() + "]."); } } catch (FileNotFoundException e) { throw new AnalysisEngineProcessException(e); } finally { i++; IOUtils.closeQuietly(os); } } } private AnnotationGraph createGraph(ROOT aRoot) { nodes = new HashMap<Annotation, AnnisNode>(); edges = new ArrayList<Edge>(); AnnotationGraph graph = new AnnotationGraph(); AnnisNode rootV = getAnnisNode(aRoot); nodes.put(aRoot, rootV); traverse(aRoot); for (AnnisNode node : nodes.values()) { graph.addNode(node); } return graph; } private void traverse(Constituent parent) { FSArray children = parent.getChildren(); AnnisNode parentNode = getAnnisNode(parent); annis.model.Annotation nodeAnno = new annis.model.Annotation(namespace, "cat", parent.getConstituentType()); parentNode.addNodeAnnotation(nodeAnno); nodes.put(parent, parentNode); for (int i = 0; i < children.size(); i++) { Annotation child = parent.getChildren(i); // create/save node AnnisNode childNode = getAnnisNode(child); nodes.put(child, childNode); // create/save edge Edge edge = new Edge(); edge.setName("edge"); edge.setSource(parentNode); edge.setDestination(childNode); edge.setEdgeType(EdgeType.DOMINANCE); if ((child instanceof Constituent)) { // TODO show syntactic functions only if available String synFunc = ((Constituent) child).getSyntacticFunction(); synFunc = synFunc == null ? "#" : synFunc; annis.model.Annotation edgeAnno = new annis.model.Annotation(namespace, "func", synFunc); edge.addAnnotation(edgeAnno); } edges.add(edge); // add edges to nodes parentNode.addOutgoingEdge(edge); childNode.addIncomingEdge(edge); // traverse deeper if (child instanceof Constituent) { traverse((Constituent) child); } } } private AnnisNode getAnnisNode(Annotation a) { AnnisNode node = nodes.get(a); if (node == null) { long id = a.hashCode(); node = new AnnisNode(id); node.setRoot(a.getClass() == ROOT.class); node.setNamespace(namespace); node.setSpannedText(a.getCoveredText()); if (a instanceof Constituent) { node.setToken(false); } else { node.setToken(true); Annotation dummy = new Annotation(jcas, 0, a.getBegin()); Long idx = (long) JCasUtil.selectCovered(jcas, Token.class, dummy).size(); node.setTokenIndex(idx); } } return node; } }