/* * Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC * All rights reserved. * * The source code of this document is proprietary work, and is not licensed for * distribution. For information about licensing, contact Sam Harwell at: * sam@tunnelvisionlabs.com */ package org.antlr.works.editor.grammar.debugger; import java.awt.Color; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.EditorKit; import javax.swing.text.JTextComponent; import javax.swing.text.MutableAttributeSet; import javax.swing.text.SimpleAttributeSet; import org.netbeans.api.editor.mimelookup.MimeRegistration; import org.netbeans.api.editor.settings.EditorStyleConstants; import org.netbeans.spi.editor.highlighting.HighlightsContainer; import org.netbeans.spi.editor.highlighting.HighlightsLayer; import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; import org.netbeans.spi.editor.highlighting.ZOrder; import org.netbeans.spi.editor.highlighting.support.OffsetsBag; /** * * @author Sam Harwell */ @MimeRegistration(mimeType=LexerDebuggerEditorKit.LEXER_DEBUGGER_MIME_TYPE, service=HighlightsLayerFactory.class) public class LexerDebuggerTokenHighlighterLayerFactory implements HighlightsLayerFactory { private static final boolean INTERNAL_PARSE = true; private static final int FULL = 0; private static final int START = 1; private static final int MIDDLE = 2; private static final int STOP = 3; private static final Color[] ModeColors = { Color.black, Color.red, Color.blue, Color.orange, Color.green, Color.magenta, Color.cyan, Color.yellow, Color.pink, }; private static final AttributeSet[] TokenFullOutlineAttributes = { createOutlineAttributeSet(FULL, 0), createOutlineAttributeSet(FULL, 1), createOutlineAttributeSet(FULL, 2), createOutlineAttributeSet(FULL, 3), createOutlineAttributeSet(FULL, 4), createOutlineAttributeSet(FULL, 5), createOutlineAttributeSet(FULL, 6), createOutlineAttributeSet(FULL, 7), createOutlineAttributeSet(FULL, 8), }; private static final AttributeSet[] TokenStartOutlineAttributes = { createOutlineAttributeSet(START, 0), createOutlineAttributeSet(START, 1), createOutlineAttributeSet(START, 2), createOutlineAttributeSet(START, 3), createOutlineAttributeSet(START, 4), createOutlineAttributeSet(START, 5), createOutlineAttributeSet(START, 6), createOutlineAttributeSet(START, 7), createOutlineAttributeSet(START, 8), }; private static final AttributeSet[] TokenMiddleOutlineAttributes = { createOutlineAttributeSet(MIDDLE, 0), createOutlineAttributeSet(MIDDLE, 1), createOutlineAttributeSet(MIDDLE, 2), createOutlineAttributeSet(MIDDLE, 3), createOutlineAttributeSet(MIDDLE, 4), createOutlineAttributeSet(MIDDLE, 5), createOutlineAttributeSet(MIDDLE, 6), createOutlineAttributeSet(MIDDLE, 7), createOutlineAttributeSet(MIDDLE, 8), }; private static final AttributeSet[] TokenStopOutlineAttributes = { createOutlineAttributeSet(STOP, 0), createOutlineAttributeSet(STOP, 1), createOutlineAttributeSet(STOP, 2), createOutlineAttributeSet(STOP, 3), createOutlineAttributeSet(STOP, 4), createOutlineAttributeSet(STOP, 5), createOutlineAttributeSet(STOP, 6), createOutlineAttributeSet(STOP, 7), createOutlineAttributeSet(STOP, 8), }; private static Color getColorForMode(int mode) { if (mode >= 0 && mode < ModeColors.length) { return ModeColors[mode]; } return ModeColors[ModeColors.length - 1]; } public static Color getColorForMode(int mode, double alpha) { return applyAlpha(getColorForMode(mode), alpha); } public static Color applyAlpha(Color color, double alpha) { int red = applyAlpha(color.getRed(), alpha); int green = applyAlpha(color.getGreen(), alpha); int blue = applyAlpha(color.getBlue(), alpha); return new Color(red, green, blue); } private static int applyAlpha(int value, double alpha) { double result = value + (1.0 - alpha) * (255 - value); int rounded = (int)Math.round(result); return Math.max(0, Math.min(255, rounded)); } private static AttributeSet createOutlineAttributeSet(int part, int mode) { Color color = getColorForMode(mode, 0.3); MutableAttributeSet attributes = new SimpleAttributeSet(); if (part == START || part == FULL) { attributes.addAttribute(EditorStyleConstants.LeftBorderLineColor, color); } if (part == STOP || part == FULL) { attributes.addAttribute(EditorStyleConstants.RightBorderLineColor, color); } return attributes; } private static AttributeSet getTokenOutlineAttributes(int part, int mode) { AttributeSet[] attributes; switch (part) { case FULL: attributes = TokenFullOutlineAttributes; break; case START: attributes = TokenStartOutlineAttributes; break; case MIDDLE: attributes = TokenMiddleOutlineAttributes; break; case STOP: attributes = TokenStopOutlineAttributes; break; default: return SimpleAttributeSet.EMPTY; } if (mode >= 0 && mode < attributes.length) { return attributes[mode]; } return attributes[attributes.length - 1]; } @Override public HighlightsLayer[] createLayers(Context context) { HighlightsContainer highlighter = loadHighlights(context.getComponent()); return new HighlightsLayer[] { HighlightsLayer.create(LexerDebuggerTokenHighlighterLayerFactory.class.getName(), ZOrder.SYNTAX_RACK, true, highlighter) }; } private static HighlightsContainer loadHighlights(JTextComponent component) { Document document = component.getDocument(); EditorKit kit = component.getUI().getEditorKit(component); if (!(kit instanceof LexerDebuggerEditorKit)) { return new OffsetsBag(document); } LexerDebuggerEditorKit dbgkit = (LexerDebuggerEditorKit)kit; TraceToken[] tokens = dbgkit.getTokens(component.getDocument()); if (tokens == null || tokens.length == 0) { return new OffsetsBag(document); } OffsetsBag highlights = new OffsetsBag(document); for (TraceToken token : tokens) { int start = token.getStartIndex(); int stop = token.getStopIndex(); addHighlights(highlights, start, stop, token.getMode()); } return highlights; // try { // if (INTERNAL_PARSE) { // GrammarLexer lexer = new GrammarLexer(new DocumentCharStreamV4((StyledDocument)document)); // ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // lexer.getInterpreter().trace = outputStream; // CommonTokenStream tokenStream = new CommonTokenStream(lexer); // tokenStream.fill(); // lexer.getInterpreter().trace.flush(); // return loadHighlights(document, new ByteArrayInputStream(outputStream.toByteArray())); // } else { // String sourceFileName = (String)document.getProperty(Document.StreamDescriptionProperty); // String traceFileName = sourceFileName + ".trace"; // File traceFile = new File(traceFileName); // return loadHighlights(document, new BufferedInputStream(new FileInputStream(traceFileName))); // } // } catch (IOException ex) { // Exceptions.printStackTrace(ex); // return new OffsetsBag(document); // } } private static void addHighlights(OffsetsBag highlights, int start, int stop, int mode) { if (stop < start) { return; } if (stop == start) { AttributeSet tokenAttributes = getTokenOutlineAttributes(FULL, mode); highlights.addHighlight(start, stop + 1, tokenAttributes); } else { AttributeSet tokenStartAttributes = getTokenOutlineAttributes(START, mode); highlights.addHighlight(start, start + 1, tokenStartAttributes); if (stop > start + 1) { AttributeSet tokenMiddleAttributes = getTokenOutlineAttributes(MIDDLE, mode); highlights.addHighlight(start + 1, stop, tokenMiddleAttributes); } AttributeSet tokenStopAttributes = getTokenOutlineAttributes(STOP, mode); highlights.addHighlight(stop, stop + 1, tokenStopAttributes); } } private static HighlightsContainer loadHighlights(Document document, InputStream reader) { OffsetsBag highlights = new OffsetsBag(document); int mode = 0; int previousStop = -1; try { while (true) { int opcodeValue = reader.read(); if (opcodeValue == -1) { break; } LexerOpCode opcode = LexerOpCode.values()[opcodeValue]; switch (opcode) { case BeginMatch: { mode = (byte)reader.read(); reader.skip(opcode.getArgumentSize() - 1); break; } case EndMatch: case AcceptState: case Predict: reader.skip(opcode.getArgumentSize()); break; case Seek: case Consume: case Lookahead1: reader.skip(opcode.getArgumentSize()); break; case Transition: case PushMode: case PopMode: reader.skip(opcode.getArgumentSize()); break; case Emit: int startIndex = readInteger(reader); int stopIndex = readInteger(reader); //int type = readInteger(reader); //int channel = readInteger(reader); reader.skip(opcode.getArgumentSize() - 4 * (Integer.SIZE / 8)); if (stopIndex > previousStop && startIndex < stopIndex + 1) { previousStop = stopIndex; addHighlights(highlights, startIndex, stopIndex, mode); } break; default: throw new UnsupportedOperationException("Invalid debugger opcode."); } } } catch (FileNotFoundException ex) { } catch (IOException ex) { } return highlights; } private static int readInteger(InputStream reader) throws IOException { int value = reader.read(); value |= reader.read() << 8; value |= reader.read() << 16; value |= reader.read() << 24; return value; } public enum LexerOpCode { BeginMatch(5), EndMatch(0), Transition(1), AcceptState(4), Predict(4), Seek(4), Consume(8), Lookahead1(0), PushMode(1), PopMode(0), Emit(16); private final int argumentSize; private LexerOpCode(int argumentSize) { this.argumentSize = argumentSize; } public int getArgumentSize() { return argumentSize; } } }