/* * 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.syndiag; import java.awt.Color; import java.awt.Graphics; import java.awt.font.TextAttribute; import java.text.AttributedString; import java.text.CharacterIterator; import java.util.Collections; import java.util.List; import javax.swing.text.AttributeSet; import javax.swing.text.StyleConstants; import org.antlr.netbeans.editor.text.SnapshotPositionRegion; import org.antlr.v4.runtime.Dependents; import org.antlr.v4.runtime.RuleDependencies; import org.antlr.v4.runtime.RuleDependency; import org.antlr.v4.runtime.misc.Interval; import org.antlr.v4.runtime.misc.IntervalSet; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.works.editor.grammar.experimental.GrammarParser; /** * * @author Sam Harwell */ public class SetTerminal extends Terminal { private final AttributedString attributedLabel; private final boolean inverted; @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_setElement, version=4, dependents=Dependents.SELF) public SetTerminal(List<GrammarParser.SetElementContext> elements, SnapshotPositionRegion sourceSpan, boolean inverted) { this(getAttributedLabel(elements, inverted), sourceSpan, inverted); } public SetTerminal(TerminalNode element, SnapshotPositionRegion sourceSpan) { this(getAttributedLabel(Collections.singletonList(element), false), sourceSpan, false); } private SetTerminal(AttributedString attributedLabel, SnapshotPositionRegion sourceSpan, boolean inverted) { super(getLabelText(attributedLabel), sourceSpan); this.attributedLabel = attributedLabel; this.inverted = inverted; } @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_range, version=0, dependents=Dependents.SELF), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_setElement, version=4, dependents=Dependents.SELF), }) private static AttributedString getAttributedLabel(List<? extends ParseTree> elements, boolean inverted) { IntervalSet foregroundSpans = new IntervalSet(); IntervalSet literalSpans = new IntervalSet(); IntervalSet lexerRuleSpans = new IntervalSet(); IntervalSet parserRuleSpans = new IntervalSet(); StringBuilder builder = new StringBuilder(); if (inverted) { builder.append("~("); foregroundSpans.add(0, 1); } for (int i = 0; i < elements.size(); i++) { if (i > 0) { builder.append("|"); foregroundSpans.add(builder.length() - 1); } ParseTree element = elements.get(i); if (element instanceof GrammarParser.SetElementContext) { GrammarParser.SetElementContext context = (GrammarParser.SetElementContext)element; if (context.TOKEN_REF() != null) { String tokenName = context.TOKEN_REF().getText(); builder.append(tokenName); lexerRuleSpans.add(builder.length() - tokenName.length(), builder.length() - 1); } else if (context.STRING_LITERAL() != null) { String text = context.STRING_LITERAL().getText(); builder.append(text); literalSpans.add(builder.length() - text.length(), builder.length() - 1); } else if (context.LEXER_CHAR_SET() != null) { String text = context.LEXER_CHAR_SET().getText(); if (text.length() >= 2 && text.charAt(0) == '[' && text.charAt(text.length() - 1) == ']') { builder.append(text); foregroundSpans.add(builder.length() - text.length()); literalSpans.add(builder.length() - text.length() + 1, builder.length() - 2); foregroundSpans.add(builder.length() - 1); } else { builder.append("???"); } } else if (context.range() != null) { GrammarParser.RangeContext rangeContext = context.range(); List<? extends TerminalNode> strings = rangeContext.STRING_LITERAL(); if (strings.size() == 2) { builder.append(strings.get(0).getText()); literalSpans.add(builder.length() - strings.get(0).getText().length(), builder.length() - 1); builder.append(".."); foregroundSpans.add(builder.length() - 2, builder.length() - 1); builder.append(strings.get(1).getText()); literalSpans.add(builder.length() - strings.get(1).getText().length(), builder.length() - 1); } else { builder.append("???"); } } else { builder.append("???"); } } else if (element instanceof TerminalNode) { TerminalNode node = (TerminalNode)element; String text = node.getText(); if (text.length() >= 2 && text.charAt(0) == '[' && text.charAt(text.length() - 1) == ']') { builder.append(text); foregroundSpans.add(builder.length() - text.length()); literalSpans.add(builder.length() - text.length() + 1, builder.length() - 2); foregroundSpans.add(builder.length() - 1); } else { builder.append("???"); } } else { builder.append("???"); } } if (inverted) { builder.append(")"); foregroundSpans.add(builder.length() - 1); } AttributedString label = new AttributedString(builder.toString()); applyAttributes(label, foregroundSpans.getIntervals(), "identifier"); applyAttributes(label, literalSpans.getIntervals(), "stringliteral"); applyAttributes(label, lexerRuleSpans.getIntervals(), "lexerrule"); applyAttributes(label, parserRuleSpans.getIntervals(), "parserrule"); return label; } private static void applyAttributes(AttributedString attributedString, List<Interval> intervals, String category) { if (intervals.isEmpty()) { return; } AttributeSet attributes = Diagram.lookupAttributes(category); Color foreground = attributes != null ? (Color)attributes.getAttribute(StyleConstants.Foreground) : null; if (foreground == null) { foreground = Color.BLACK; } for (Interval interval : intervals) { attributedString.addAttribute(TextAttribute.FOREGROUND, foreground, interval.a, interval.b + 1); } } private static String getLabelText(AttributedString attributedLabel) { CharacterIterator iterator = attributedLabel.getIterator(); int length = iterator.getEndIndex(); StringBuilder builder = new StringBuilder(length); for (int i = 0; i < length; i++) { builder.append(iterator.setIndex(i)); } return builder.toString(); } @Override protected void paintLabel(Graphics g, int x, int y) { AttributedString label = new AttributedString(this.attributedLabel.getIterator()); label.addAttribute(TextAttribute.FONT, g.getFont()); g.drawString(label.getIterator(), x, y); } }