Java tutorial
/* * Copyright 2009-2011 Collaborative Research Centre SFB 632 * * 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 annis.visualizers.component.tree; import annis.libgui.MatchedNodeColors; import annis.libgui.visualizers.VisualizerInput; import annis.model.AnnisNode; import annis.model.Annotation; import annis.model.Edge; import annis.service.ifaces.AnnisResult; import annis.visualizers.component.AbstractImageVisualizer; import annis.visualizers.component.tree.backends.staticimg.AbstractImageGraphicsItem; import annis.visualizers.component.tree.backends.staticimg.Java2dBackend; import com.vaadin.ui.Notification; import edu.uci.ics.jung.graph.DirectedGraph; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.imageio.ImageIO; import net.xeoh.plugins.base.annotations.PluginImplementation; /** * Visualizes a constituent syntax tree. * * <p> * Mappings:<br /> * The annotation names to be displayed in non terminal nodes can be * set e.g. using <b>node_key:cat</b> for an annotation called cat (the default), and * similarly the edge labels using <b>edge_key:func</b> for an edge label called * <b>func</b> (the default). Instructions are separated using semicolons. <br /><br /> * * With the mapping <b>terminal_name</b> and <b>terminal_ns</b> you can * select span nodes with the corresponding annotations as terminal elements * instead of the default tokens. * </p> * @author Thomas Krause <krauseto@hu-berlin.de> */ @PluginImplementation public class TigerTreeVisualizer extends AbstractImageVisualizer { private static final int SIDE_MARGIN = 20; private static final int TOP_MARGIN = 40; private static final int TREE_DISTANCE = 40; private transient Java2dBackend backend; private final DefaultLabeler labeler; private transient DefaultStyler styler; private AnnisGraphTools graphtools; public static final String TERMINAL_NAME_KEY = "terminal_name"; public static final String TERMINAL_NS_KEY = "terminal_ns"; public class DefaultStyler implements TreeElementStyler { private final BasicStroke DEFAULT_PEN_STYLE = new BasicStroke(1); public static final int LABEL_PADDING = 2; public static final int HEIGHT_STEP = 40; public static final int TOKEN_SPACING = 15; public static final int VEDGE_OVERLAP_THRESHOLD = 20; private final Java2dBackend backend; public DefaultStyler(Java2dBackend backend_) { this.backend = backend_; } public int getLabelPadding() { return LABEL_PADDING; } public GraphicsBackend.Font getFont(AnnisNode n, VisualizerInput input) { if (AnnisGraphTools.isTerminal(n, input)) { return backend.getFont(Font.SANS_SERIF, 12, java.awt.Font.PLAIN); } else { return backend.getFont(Font.SANS_SERIF, 15, java.awt.Font.BOLD); } } public GraphicsBackend.Font getFont(Edge e) { return backend.getFont(Font.SANS_SERIF, 10, java.awt.Font.PLAIN); } @Override public Shape getShape(AnnisNode n, VisualizerInput input) { if (isQueryMatch(n, input)) { // get CSS color name String backColorName = input.getMarkableExactMap().get("" + n.getId()); Color backColor = Color.RED; try { backColor = MatchedNodeColors.valueOf(backColorName).getColor(); } catch (IllegalArgumentException ex) { } if (AnnisGraphTools.isTerminal(n, input)) { return new Shape.Rectangle(Color.WHITE, backColor, DEFAULT_PEN_STYLE, getLabelPadding()); } else { return new Shape.Ellipse(Color.WHITE, backColor, DEFAULT_PEN_STYLE, getLabelPadding()); } } else { if (AnnisGraphTools.isTerminal(n, input)) { return new Shape.Invisible(getLabelPadding()); } else { return new Shape.Ellipse(Color.BLACK, Color.WHITE, DEFAULT_PEN_STYLE, getLabelPadding()); } } } private boolean isQueryMatch(AnnisNode n, VisualizerInput input) { return input.getMarkableExactMap().containsKey(Long.toString(n.getId())); } @Override public Shape getShape(Edge e, VisualizerInput input) { if (graphtools.hasEdgeSubtype(e, graphtools.getSecEdgeSubType())) { return new Shape.Rectangle(getEdgeColor(e, input), Color.WHITE, DEFAULT_PEN_STYLE, getLabelPadding()); } else { return new Shape.Rectangle(new Color(0.4f, 0.4f, 0.4f), Color.WHITE, DEFAULT_PEN_STYLE, getLabelPadding()); } } @Override public Color getTextBrush(AnnisNode n, VisualizerInput input) { if (isQueryMatch(n, input)) { return Color.WHITE; } else { return Color.BLACK; } } @Override public Color getTextBrush(Edge n) { return Color.BLACK; } @Override public int getHeightStep() { return HEIGHT_STEP; } @Override public Color getEdgeColor(Edge e, VisualizerInput input) { if (graphtools.hasEdgeSubtype(e, graphtools.getSecEdgeSubType())) { return new Color(0.5f, 0.5f, 0.8f, 0.7f); } else { return new Color(0.3f, 0.3f, 0.3f); } } @Override public int getTokenSpacing() { return TOKEN_SPACING; } @Override public int getVEdgeOverlapThreshold() { return VEDGE_OVERLAP_THRESHOLD; } @Override public Stroke getStroke(Edge e, VisualizerInput input) { if (graphtools.hasEdgeSubtype(e, graphtools.getSecEdgeSubType())) { return new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, new float[] { 2, 2 }, 0); } else { return new BasicStroke(2); } } } private static class DefaultLabeler implements TreeElementLabeler, Serializable { @Override public String getLabel(AnnisNode n, VisualizerInput input) { if (AnnisGraphTools.isTerminal(n, input)) { String terminalName = input.getMappings().getProperty(TERMINAL_NAME_KEY); if (terminalName == null) { String spannedText = n.getSpannedText(); if (spannedText == null || "".equals(spannedText)) { spannedText = " "; } return spannedText; } else { String terminalNamespace = input.getMappings().getProperty(TERMINAL_NS_KEY); return extractAnnotation(n.getNodeAnnotations(), terminalNamespace, terminalName); } } else { return extractAnnotation(n.getNodeAnnotations(), input.getMappings().getProperty("node_anno_ns", input.getNamespace()), input.getMappings().getProperty("node_key", "cat")); } } @Override public String getLabel(Edge e, VisualizerInput input) { return extractAnnotation(e.getAnnotations(), input.getMappings().getProperty("edge_anno_ns", input.getNamespace()), input.getMappings().getProperty("edge_key", "func")); } private String extractAnnotation(Set<Annotation> annotations, String namespace, String featureName) { String result = AnnisGraphTools.extractAnnotation(annotations, namespace, featureName); if (result == null) { result = "--"; } return result; } } public TigerTreeVisualizer() { labeler = new DefaultLabeler(); initTransients(); } private void initTransients() { styler = new DefaultStyler(getBackend()); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); initTransients(); } @Override public String getShortName() { return "tree"; } @Override public void writeOutput(VisualizerInput input, OutputStream outstream) { AnnisResult result = input.getResult(); graphtools = new AnnisGraphTools(input); List<AbstractImageGraphicsItem> layouts = new LinkedList<AbstractImageGraphicsItem>(); double width = 0; double maxheight = 0; for (DirectedGraph<AnnisNode, Edge> g : graphtools.getSyntaxGraphs()) { if (g.getEdgeCount() > 0 && g.getVertexCount() > 0) { ConstituentLayouter<AbstractImageGraphicsItem> cl = new ConstituentLayouter<AbstractImageGraphicsItem>( g, getBackend(), labeler, styler, input, graphtools); AbstractImageGraphicsItem item = cl.createLayout(new LayoutOptions(VerticalOrientation.TOP_ROOT, AnnisGraphTools.detectLayoutDirection(result.getGraph()))); Rectangle2D treeSize = item.getBounds(); maxheight = Math.max(maxheight, treeSize.getHeight()); width += treeSize.getWidth(); layouts.add(item); } } BufferedImage image; if (width == 0 || maxheight == 0) { Notification.show("Can't generate tree visualization.", Notification.Type.WARNING_MESSAGE); image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); } else { image = new BufferedImage((int) (width + (layouts.size() - 1) * TREE_DISTANCE + 2 * SIDE_MARGIN), (int) (maxheight + 2 * TOP_MARGIN), BufferedImage.TYPE_INT_ARGB); Graphics2D canvas = createCanvas(image); double xOffset = SIDE_MARGIN; for (AbstractImageGraphicsItem item : layouts) { AffineTransform t = canvas.getTransform(); Rectangle2D bounds = item.getBounds(); canvas.translate(xOffset, TOP_MARGIN + maxheight - bounds.getHeight()); renderTree(item, canvas); xOffset += bounds.getWidth() + TREE_DISTANCE; canvas.setTransform(t); } } try { ImageIO.write(image, "png", outstream); } catch (IOException e) { throw new RuntimeException(e); } } private void renderTree(AbstractImageGraphicsItem item, Graphics2D canvas) { List<AbstractImageGraphicsItem> allItems = new ArrayList<AbstractImageGraphicsItem>(); item.getAllChildren(allItems); Collections.sort(allItems, new Comparator<AbstractImageGraphicsItem>() { @Override public int compare(AbstractImageGraphicsItem o1, AbstractImageGraphicsItem o2) { return o1.getZValue() - o2.getZValue(); } }); for (AbstractImageGraphicsItem c : allItems) { c.draw(canvas); } } private Graphics2D createCanvas(BufferedImage image) { Graphics2D canvas = (Graphics2D) image.getGraphics(); canvas.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); canvas.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); canvas.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); return canvas; } @Override public String getContentType() { return "image/png"; } private Java2dBackend getBackend() { if (backend == null) { backend = new Java2dBackend(); } return backend; } }