Java tutorial
/* * Created 2009/02/08 * Copyright (C) 2003-2009 Naoki Iwami (naoki@limy.org) * * This file is part of Limy Eclipse Plugin. * * Limy Eclipse Plugin is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Limy Eclipse Plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Limy Eclipse Plugin. If not, see <http://www.gnu.org/licenses/>. */ package org.limy.eclipse.qalab.outline.sequence; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.RenderingHints.Key; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Double; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PushbackReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import org.apache.commons.io.FileUtils; import org.apache.velocity.VelocityContext; import org.apache.velocity.context.Context; import org.eclipse.core.resources.IResource; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.limy.eclipse.common.LimyEclipsePluginUtils; import org.limy.eclipse.common.io.LimyIOUtils; import org.limy.eclipse.qalab.LimyQalabPlugin; import org.limy.eclipse.qalab.common.LimyQalabEnvironment; import org.limy.eclipse.qalab.common.LimyQalabUtils; import org.limy.eclipse.qalab.outline.ASTUtils; import org.limy.eclipse.qalab.outline.BasePopupImage; import org.limy.eclipse.qalab.outline.CanvasMouseMoveListener; import org.limy.eclipse.qalab.outline.ClickablePointInfo; import org.limy.eclipse.qalab.outline.CommonKeyExecutor; import org.limy.eclipse.qalab.outline.DialogSupport; import org.limy.eclipse.qalab.outline.GraphPopupDialog; import org.limy.eclipse.qalab.outline.ImageCreator; import org.limy.eclipse.qalab.outline.JavaElementClickPoint; import org.limy.eclipse.qalab.outline.JavaElementMouseListener; import org.limy.eclipse.qalab.outline.ListenerCreator; import org.limy.eclipse.qalab.outline.PopupImage; import org.limy.eclipse.qalab.outline.QalabKeyListener; import org.limy.velocity.VelocitySupport; import antlr.ANTLRException; import antlr.collections.AST; import com.puppycrawl.tools.checkstyle.TreeWalker; import com.puppycrawl.tools.checkstyle.api.FileContents; import com.puppycrawl.tools.checkstyle.api.TokenTypes; import com.zanthan.sequence.diagram.Diagram; import com.zanthan.sequence.diagram.MethodExecution; import com.zanthan.sequence.diagram.NodeFactory; import com.zanthan.sequence.layout.LayoutData; import com.zanthan.sequence.parser.Parser; import com.zanthan.sequence.parser.ParserException; import com.zanthan.sequence.parser.ParserFactory; import com.zanthan.sequence.preferences.Prefs; import com.zanthan.sequence.swing.ExceptionHandler; import com.zanthan.sequence.swing.display.SwingPainter; import com.zanthan.sequence.swing.display.SwingStringMeasure; import com.zanthan.sequence.swing.model.Model; /** * V?[PX?}??SNX?B * <p> * http://www.zanthan.com/itymbi/archives/cat_sequence.html * </p> * @author Naoki Iwami */ public class SequenceImageCreator implements ImageCreator, DialogSupport, ListenerCreator { // ------------------------ Fields /** ? */ private LimyQalabEnvironment env; /** ??\bh */ private IMethod method; /** O??C??[W */ private PopupImage image; /** ?o?pngt@C */ private File pngFile; /** SequenceCAEgf?[^ */ private LayoutData layoutData; /** V?[PX?}? */ private SequenceBean root; // ------------------------ Constructors public SequenceImageCreator(LimyQalabEnvironment env, IMethod method) { this.env = env; this.method = method; } // ------------------------ Implement Methods (ImageCreator) public PopupImage changeLocation() { return image; // } public PopupImage create() throws IOException { // C??[W?? IResource javaResource = method.getResource(); File file = javaResource.getLocation().toFile(); try { String content = LimyIOUtils.getContent(file); String[] lines = content.split("\\n"); FileContents contents = new FileContents(file.getAbsolutePath(), lines); AST ast = TreeWalker.parse(contents); ASTUtils.debugSourceAst(ast); parseAst(ast); BasePopupImage popupImage = new BasePopupImage(); popupImage.setImageFile(pngFile); popupImage.setElements(createElements()); image = popupImage; return image; } catch (ANTLRException e) { LimyEclipsePluginUtils.log(e); } catch (JavaModelException e) { LimyEclipsePluginUtils.log(e); } return null; } // ------------------------ Implement Methods (DialogSupport) public PopupImage changeHorizontal() { return image; // } public String getDialogTitle() { return "V?[PX?}\ (Enable key 'v')"; } public IJavaElement getTargetElement() { return method; } // ------------------------ Implement Methods (ListenerCreator) public KeyListener createKeyListener(GraphPopupDialog dialog) { return new QalabKeyListener(new CommonKeyExecutor(this, this, dialog)); } public MouseListener createMouseListener(GraphPopupDialog dialog) { return new JavaElementMouseListener(image); } public MouseMoveListener createMouseMoveListener(GraphPopupDialog dialog) { return new CanvasMouseMoveListener(dialog.getCanvas(), image); } // ------------------------ Private Methods private void parseAst(AST ast) throws JavaModelException, IOException { AST methodAst = ASTUtils.searchMethodFromSource(ast, method); if (methodAst == null) { throw new IOException("Ci?[NX?B"); } root = new SequenceBean(null, method); SequenceBean target = root; // S?\bh?o?Bcatch/finally?? AST[] methodCalls = ASTUtils.searchMultiAllSiblingEx(methodAst, new int[] { TokenTypes.LITERAL_CATCH, TokenTypes.LITERAL_FINALLY }, TokenTypes.METHOD_CALL); for (AST methodCall : methodCalls) { AST dotAst = methodCall.getFirstChild(); if (dotAst.getType() == TokenTypes.IDENT) { // b()`?BNX?\bh?o // ICodeAssist g??\bh? int sourceOffset = method.getSourceRange().getOffset(); int pos = -1; while (true) { pos = method.getSource().indexOf(dotAst.getText(), pos + 1); if (pos < 0) { break; } IJavaElement[] elements = method.getCompilationUnit().codeSelect(sourceOffset + pos, dotAst.getText().length()); for (IJavaElement element : elements) { // X?[p?[NX?\bh?o???A // ?INXRei?BC?zu target.addChild( new SequenceBean(null, element, method.getDeclaringType().getElementName())); pos = method.getSource().length() - 1; break; } } } else { // a.b()` AST callingAst = dotAst.getFirstChild(); AST calledAst = callingAst.getNextSibling(); // ICodeAssist g??\bh? int sourceOffset = method.getSourceRange().getOffset(); int pos = -1; while (true) { pos = method.getSource().indexOf(calledAst.getText(), pos + 1); if (pos < 0) { break; } IJavaElement[] elements = method.getCompilationUnit().codeSelect(sourceOffset + pos, calledAst.getText().length()); for (IJavaElement element : elements) { String instanceName = callingAst.getText(); if (instanceName.equals("(") || instanceName.equals(".")) { instanceName = null; } target.addChild(new SequenceBean(instanceName, element)); pos = method.getSource().length() - 1; break; } } } } try { writeSequence(root); } catch (ParserException e) { throw new IOException(e.getMessage()); } } /** * V?[PX?}?? layoutData, pngFile ?o?B * @param root V?[PX?}Bean * @throws IOException I/OO * @throws ParserException */ private void writeSequence(SequenceBean root) throws IOException, ParserException { StringWriter out = new StringWriter(); Context context = new VelocityContext(); context.put("root", root); VelocitySupport.write(new File(LimyQalabPlugin.getDefault().getPluginRoot(), "resource/sequence/index.vm") .getAbsolutePath(), context, out); File txtFile = LimyQalabUtils.createTempFile(env.getProject(), "sequence.txt"); pngFile = LimyQalabUtils.createTempFile(env.getProject(), "sequence.png"); FileUtils.writeByteArrayToFile(txtFile, out.toString().getBytes()); Parser parser = ParserFactory.getInstance().getDefaultParser(); NodeFactory nodeFactory = ParserFactory.getInstance().getNodeFactoryForParser(parser); Diagram diagram = new Diagram(parser, nodeFactory); PushbackReader reader = new PushbackReader(new FileReader(txtFile)); try { diagram.parse(reader); } finally { reader.close(); } Model model = new Model(new ExceptionHandler() { public void exception(Exception e) { e.printStackTrace(); } }, diagram); BufferedImage bi = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = bi.createGraphics(); layoutData = new LayoutData(new SwingStringMeasure(graphics)); diagram.layout(layoutData); int height = layoutData.getHeight(); int width = layoutData.getWidth(); BufferedImage png = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D pngGraphics = png.createGraphics(); pngGraphics.setClip(0, 0, width, height); Map<Key, Object> hintsMap = new HashMap<Key, Object>(); hintsMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); pngGraphics.addRenderingHints(hintsMap); pngGraphics.setBackground(Prefs.getColorValue(Prefs.BACKGROUND_COLOR)); pngGraphics.fillRect(0, 0, width, height); SwingPainter painter = new SwingPainter(); painter.setGraphics(pngGraphics); model.layout(layoutData); layoutData.paint(painter); ImageIO.write(png, "png", pngFile); } private Collection<? extends ClickablePointInfo> createElements() { Collection<ClickablePointInfo> results = new ArrayList<ClickablePointInfo>(); List<MethodExecution> methodExecutions = layoutData.getMethodExecutions(); SequenceBean lastBean = null; for (MethodExecution methodExecution : methodExecutions) { Double rect = new Rectangle2D.Double(methodExecution.getMinX(), methodExecution.getStartY(), methodExecution.getMaxX() - methodExecution.getMinX(), methodExecution.getEndY() - methodExecution.getStartY()); ClickablePointInfo info = new JavaElementClickPoint( root.searchElement(lastBean, methodExecution.getName())); info.setRect(rect); info.setTooltipText(methodExecution.getName()); results.add(info); } return results; } }