From: <ni...@us...> - 2007-12-06 16:40:36
|
Revision: 39 http://mspsim.svn.sourceforge.net/mspsim/?rev=39&view=rev Author: nifi Date: 2007-12-06 08:40:33 -0800 (Thu, 06 Dec 2007) Log Message: ----------- highlighting c source viewer Added Paths: ----------- mspsim/se/sics/mspsim/extutil/ mspsim/se/sics/mspsim/extutil/highlight/ mspsim/se/sics/mspsim/extutil/highlight/CScanner.java mspsim/se/sics/mspsim/extutil/highlight/HighlightSourceViewer.java mspsim/se/sics/mspsim/extutil/highlight/JavaScanner.java mspsim/se/sics/mspsim/extutil/highlight/LineNumberedBorder.java mspsim/se/sics/mspsim/extutil/highlight/Scan.java mspsim/se/sics/mspsim/extutil/highlight/Scanner.java mspsim/se/sics/mspsim/extutil/highlight/Symbol.java mspsim/se/sics/mspsim/extutil/highlight/SyntaxHighlighter.java mspsim/se/sics/mspsim/extutil/highlight/TextScanner.java mspsim/se/sics/mspsim/extutil/highlight/Token.java mspsim/se/sics/mspsim/extutil/highlight/TokenTypes.java Added: mspsim/se/sics/mspsim/extutil/highlight/CScanner.java =================================================================== --- mspsim/se/sics/mspsim/extutil/highlight/CScanner.java (rev 0) +++ mspsim/se/sics/mspsim/extutil/highlight/CScanner.java 2007-12-06 16:40:33 UTC (rev 39) @@ -0,0 +1,944 @@ +package se.sics.mspsim.extutil.highlight; +// Public domain, no restrictions, Ian Holyer, University of Bristol. + +/** + * <p> + * Provide a hand-written scanner for the Java language. + */ + +public class CScanner extends Scanner { + + private boolean debug = false; + + /** Create a Java scanner, for Java version 1.5 by default. */ + public CScanner() { + super(); + initKind(); + initUniKind(); + } + + /** Create a Java scanner, for a given version between "1.1" and "1.5". */ + public CScanner(String version) { + super(); + initKind(); + initUniKind(); + } + + /** Override the read method from the Scanner class. */ + protected int read() { + int type, saveStart = 0; + if (debug) + saveStart = start; + + if (start >= end) + return WHITESPACE; + + switch (state) { + case MID_COMMENT: + case END_COMMENT: + type = readComment(MID_COMMENT); + if (type == END_COMMENT) + state = WHITESPACE; + else + state = MID_COMMENT; + return type; + default: + char c = buffer[start]; + if (c == '\\') + c = next(); + if (c < 128) + type = kind[c]; + else + type = unikind[Character.getType(c)]; + switch (type) { + case WHITESPACE: + start = start + charlength; + charlength = 1; + while (start < end) { + c = buffer[start]; + if (c == '\\') + c = next(); + int k; + if (c < 128) + k = kind[c]; + else + k = unikind[Character.getType(c)]; + if (k != WHITESPACE) + break; + start = start + charlength; + charlength = 1; + } + break; + case UNRECOGNIZED: + case BRACKET: + case SEPARATOR: + start = start + charlength; + charlength = 1; + break; + case OPERATOR: + start = start + charlength; + charlength = 1; + type = readOperator(c); + break; + case CHARACTER: + start = start + charlength; + charlength = 1; + type = readCharLiteral(); + break; + case STRING: + start = start + charlength; + charlength = 1; + type = readStringLiteral(); + break; + case IDENTIFIER: + start = start + charlength; + charlength = 1; + while (start < end) { + c = buffer[start]; + if (c == '\\') + c = next(); + int k; + if (c < 128) + k = kind[c]; + else + k = unikind[Character.getType(c)]; + if (k != IDENTIFIER && k != NUMBER) + break; + start = start + charlength; + charlength = 1; + } + break; + case NUMBER: + start = start + charlength; + charlength = 1; + type = readNumber(c); + break; + case PUNCTUATION: + start = start + charlength; + charlength = 1; + type = readDot(); + break; + case COMMENT: + start = start + charlength; + charlength = 1; + type = readSlash(); + if (type == START_COMMENT) + state = MID_COMMENT; + break; + } + } + if (debug) { + System.out.print(TokenTypes.typeNames[type]); + System.out.println(" " + saveStart + "," + end + "(" + + (start - saveStart) + ")"); + } + return type; + } + + private int readOperator(char c) { + if (start >= end) + return OPERATOR; + char c2; + + switch (c) { + case '~': + case '?': + case ':': + break; + case '+': + case '-': + case '&': + case '|': + c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + if (c2 != c && c2 != '=') + break; + start = start + charlength; + charlength = 1; + break; + case '=': + case '*': + case '!': + case '^': + case '%': + case '/': + c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + if (c2 != '=') + break; + start = start + charlength; + charlength = 1; + break; + case '<': + case '>': + c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + if (c2 == '=') { + start = start + charlength; + charlength = 1; + } else if (c2 == c) { + start = start + charlength; + charlength = 1; + if (start >= end) + break; + char c3 = buffer[start]; + if (c3 == '\\') + c3 = next(); + if (c3 == '=') { + start = start + charlength; + charlength = 1; + } else if (c == '>' && c3 == '>') // >>> + { + start = start + charlength; + charlength = 1; + if (start >= end) + break; + char c4 = buffer[start]; + if (c4 == '\\') + c4 = next(); + if (c4 != '=') + break; + start = start + charlength; + charlength = 1; + } + } + break; + } + return OPERATOR; + } + + private int readCharLiteral() { + if (start >= end) + return bad(CHARACTER); + char c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + + switch (c2) { + case '\\': + start = start + charlength; + charlength = 1; + boolean ok = readEscapeSequence(); + if (!ok) + return bad(CHARACTER); + break; + case '\'': + case '\n': + return bad(CHARACTER); + default: + start = start + charlength; + charlength = 1; + break; + } + if (start >= end) + return bad(CHARACTER); + char c3 = buffer[start]; + if (c3 == '\\') + c3 = next(); + if (c3 != '\'') + return bad(CHARACTER); + start = start + charlength; + charlength = 1; + return CHARACTER; + } + + private int readStringLiteral() { + if (start >= end) + return bad(STRING); + char c = buffer[start]; + if (c == '\\') + c = next(); + + while (c != '"') { + switch (c) { + case '\\': + start = start + charlength; + charlength = 1; + boolean ok = readEscapeSequence(); + if (!ok) + return bad(STRING); + break; + case '\n': + return bad(STRING); + default: + start = start + charlength; + charlength = 1; + if (start >= end) + return bad(STRING); + break; + } + c = buffer[start]; + if (c == '\\') + c = next(); + } + if (c != '"') + return bad(STRING); + start = start + charlength; + charlength = 1; + return STRING; + } + + private int readSlash() { + if (start >= end) + return OPERATOR; + char c = buffer[start]; + if (c == '\\') + c = next(); + if (c == '/') { + while (c != '\n') { + start = start + charlength; + charlength = 1; + if (start >= end) + return COMMENT; + c = buffer[start]; + if (c == '\\') + c = next(); + } + start = start + charlength; + charlength = 1; + return COMMENT; + } else if (c == '*') { + start = start + charlength; + charlength = 1; + return readComment(START_COMMENT); + } + return readOperator('/'); + } + + // Read one line of a /*...*/ comment, given the expected type + int readComment(int type) { + if (start >= end) + return type; + char c = buffer[start]; + if (c == '\\') + c = next(); + + while (true) { + while (c != '*' && c != '\n') { + start = start + charlength; + charlength = 1; + if (start >= end) + return type; + c = buffer[start]; + if (c == '\\') + c = next(); + } + start = start + charlength; + charlength = 1; + if (c == '\n') + return type; + if (start >= end) + return type; + c = buffer[start]; + if (c == '\\') + c = next(); + if (c == '/') { + start = start + charlength; + charlength = 1; + if (type == START_COMMENT) { + return COMMENT; + } + return END_COMMENT; + } + } + } + + // Read a number, without checking whether it is out of range + // Doesn't deal with e.g. 0777.9 or 07779f + private int readNumber(char c) { + if (c == '0') { + int saveStart = start, saveLength = charlength; + start = start + charlength; + charlength = 1; + if (start >= end) + return NUMBER; + char c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + switch (c2) { + case 'x': + case 'X': + start = start + charlength; + charlength = 1; + boolean ok = readDigits(16); + if (!ok) + return bad(NUMBER); + readSuffix(); + return NUMBER; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + readDigits(8); + readSuffix(); + return NUMBER; + case '.': + case 'e': + case 'E': + start = saveStart; + charlength = saveLength; + break; + case 'f': + case 'F': + case 'd': + case 'D': + start = start + charlength; + charlength = 1; + return NUMBER; + case 'l': + case 'L': + start = start + charlength; + charlength = 1; + return NUMBER; + } + } + boolean hasDigits = false; + if ('0' <= c && c <= '9') { + hasDigits = true; + readDigits(10); + if (start >= end) + return NUMBER; + c = buffer[start]; + if (c == '\\') + c = next(); + if (c == 'l' || c == 'L') { + start = start + charlength; + charlength = 1; + return NUMBER; + } + } + if (c == '.') { + start = start + charlength; + charlength = 1; + if (start >= end) + return NUMBER; + c = buffer[start]; + if (c == '\\') + c = next(); + if ('0' <= c && c <= '9') { + hasDigits = true; + readDigits(10); + if (start >= end) + return NUMBER; + c = buffer[start]; + if (c == '\\') + c = next(); + } + } + if (!hasDigits) + return bad(NUMBER); + switch (c) { + case 'e': + case 'E': + start = start + charlength; + charlength = 1; + if (start >= end) + return bad(NUMBER); + c = buffer[start]; + if (c == '\\') + c = next(); + if (c == '+' || c == '-') { + start = start + charlength; + charlength = 1; + if (start >= end) + return bad(NUMBER); + c = buffer[start]; + if (c == '\\') + c = next(); + } + readDigits(10); + break; + case 'f': + case 'F': + case 'd': + case 'D': + start = start + charlength; + charlength = 1; + return NUMBER; + } + return NUMBER; + } + + boolean readDigits(int radix) { + if (start >= end) + return false; + char c = buffer[start]; + if (c == '\\') + c = next(); + if (Character.digit(c, radix) == -1) + return false; + while (Character.digit(c, radix) != -1) { + start = start + charlength; + charlength = 1; + if (start >= end) + return true; + c = buffer[start]; + if (c == '\\') + c = next(); + } + return true; + } + + void readSuffix() { + if (start >= end) + return; + char c = buffer[start]; + if (c == '\\') + c = next(); + switch (c) { + case 'f': + case 'F': + case 'd': + case 'D': + case 'l': + case 'L': + start = start + charlength; + charlength = 1; + } + } + + private int readDot() { + if (start >= end) + return SEPARATOR; + char c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + if (Character.isDigit(c2)) { + return readNumber('.'); + } + if (start + 1 >= end) // || version < 15) + return SEPARATOR; + if (c2 != '.' || buffer[start + 1] != '.') + return SEPARATOR; + start = start + 2; + return SEPARATOR; + } + + private boolean readEscapeSequence() { + if (start >= end) + return false; + char c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + + switch (c2) { + case 'b': + case 't': + case 'n': + case 'f': + case 'r': + case '\"': + case '\'': + case '\\': + start = start + charlength; + charlength = 1; + return true; + case '0': + case '1': + case '2': + case '3': + return readOctal(3); + case '4': + case '5': + case '6': + case '7': + return readOctal(2); + default: + return false; + } + } + + boolean readOctal(int maxlength) { + if (start >= end) + return false; + char c = buffer[start]; + if (c == '\\') + c = next(); + + int i, val = 0; + for (i = 0; i < maxlength; i++) { + if (Character.digit(c, 8) != -1) { + val = 8 * val + Character.digit(c, 8); + start = start + charlength; + charlength = 1; + if (start >= end) + break; + c = buffer[start]; + if (c == '\\') + c = next(); + } else + break; + } + if ((i == 0) || (val > 0xFF)) + return false; + return true; + } + + // A malformed or incomplete token has a negative type + private int bad(int type) { + return -type; + } + + // Look ahead at the next character or unicode escape. + // For efficiency, replace c = next(); with + // c = buffer[start]; if (c == '\\') c = next(); + // To accept the character after looking at it, use: + // start = start + charlength; charlength = 1; + + // Record the number of source code characters used up. To deal with an odd + // or even number of backslashes preceding a unicode escape, whenever a + // second backslash is coming up, mark its position as a pair. + + private int charlength = 1; + + private int pair = 0; + + private char next() { + if (start >= end) + return 26; // EOF + char c = buffer[start]; + if (c != '\\') + return c; + if (start == pair) { + pair = 0; + return '\\'; + } + if (start + 1 >= end) + return '\\'; + + c = buffer[start + 1]; + if (c == '\\') + pair = start + 1; + if (c != 'u') + return '\\'; + + int pos = start + 2; + while (pos < end && buffer[pos] == 'u') + pos++; + if (pos + 4 > end) { + charlength = end - start; + return '\0'; + } + + c = 0; + for (int j = 0; j < 4; j++) { + int d = Character.digit(buffer[pos + j], 16); + if (d < 0) { + charlength = pos + j - start; + return '\0'; + } + c = (char) (c * 16 + d); + } + charlength = pos + 4 - start; + return c; + } + + // Override initSymbolTable + + protected void initSymbolTable() { + lookup(KEYWORD, "abstract"); + lookup(KEYWORD, "assert"); + lookup(KEYWORD, "boolean"); + lookup(KEYWORD, "break"); + lookup(KEYWORD, "byte"); + lookup(KEYWORD, "case"); + lookup(KEYWORD, "catch"); + lookup(KEYWORD, "char"); + lookup(KEYWORD, "class"); + lookup(KEYWORD, "const"); + lookup(KEYWORD, "continue"); + lookup(KEYWORD, "default"); + lookup(KEYWORD, "do"); + lookup(KEYWORD, "double"); + lookup(KEYWORD, "else"); + lookup(KEYWORD, "enum"); + lookup(KEYWORD, "extends"); + lookup(KEYWORD, "float"); + lookup(KEYWORD, "for"); + lookup(KEYWORD, "goto"); + lookup(KEYWORD, "if"); + lookup(KEYWORD, "int"); + lookup(KEYWORD, "long"); + lookup(KEYWORD, "new"); + lookup(KEYWORD, "private"); + lookup(KEYWORD, "protected"); + lookup(KEYWORD, "public"); + lookup(KEYWORD, "return"); + lookup(KEYWORD, "short"); + lookup(KEYWORD, "static"); + lookup(KEYWORD, "super"); + lookup(KEYWORD, "switch"); + lookup(KEYWORD, "synchronized"); + lookup(KEYWORD, "this"); + lookup(KEYWORD, "throw"); + lookup(KEYWORD, "throws"); + lookup(KEYWORD, "transient"); + lookup(KEYWORD, "try"); + lookup(KEYWORD, "void"); + lookup(KEYWORD, "volatile"); + lookup(KEYWORD, "while"); + + lookup(LITERAL, "TRUE"); + lookup(LITERAL, "FALSE"); + lookup(LITERAL, "NULL"); + lookup(LITERAL, "int8_t"); + lookup(LITERAL, "int16_t"); + lookup(LITERAL, "int32_t"); + lookup(LITERAL, "uint8_t"); + lookup(LITERAL, "uint16_t"); + lookup(LITERAL, "uint32_t"); + lookup(LITERAL, "u8_t"); + lookup(LITERAL, "u16_t"); + lookup(LITERAL, "u32_t"); + } + + // *** Override lookup, but what about unicode escape translation? + + private Symbol temp = new Symbol(0, null); + + protected Symbol lookup(int type, String name) { + if (type != IDENTIFIER) + return super.lookup(type, name); + temp.type = KEYWORD; + temp.name = name; + Symbol sym = symbolTable.get(temp); + if (sym != null) + return sym; + temp.type = LITERAL; + sym = symbolTable.get(temp); + if (sym != null) + return sym; + return super.lookup(type, name); + } + + // Classify the ascii characters using an array of kinds, and classify all + // other unicode characters using an array indexed by unicode category. + // See the source file java/lang/Character.java for the categories. + // To find the classification of a character, use: + // if (c < 128) k = kind[c]; else k = unikind[Character.getType(c)]; + + private static final byte[] kind = new byte[128]; + + private static final byte[] unikind = new byte[31]; + + // Initialise the two classification arrays using static initializer code. + // Token types from the TokenTypes class are used to classify characters. + + private void initKind() { + for (char c = 0; c < 128; c++) + kind[c] = -1; + for (char c = 0; c < 128; c++) + switch (c) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 11: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 27: + case 28: + case 29: + case 30: + case 31: + case 127: + case '#': + case '@': + case '`': + case '\\': + kind[c] = UNRECOGNIZED; + break; + case '\t': + case '\n': + case ' ': + case '\f': + case 26: + kind[c] = WHITESPACE; + break; + case '!': + case '%': + case '&': + case '*': + case '+': + case '-': + case ':': + case '<': + case '=': + case '>': + case '?': + case '^': + case '|': + case '~': + kind[c] = OPERATOR; + break; + case '"': + kind[c] = STRING; + break; + case '\'': + kind[c] = CHARACTER; + break; + case '.': + kind[c] = PUNCTUATION; + break; + case '/': + kind[c] = COMMENT; + break; + case '$': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + kind[c] = IDENTIFIER; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + kind[c] = NUMBER; + break; + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + kind[c] = BRACKET; + break; + case ',': + case ';': + kind[c] = SEPARATOR; + break; + } + for (char c = 0; c < 128; c++) + if (kind[c] == -1) + System.out.println("Char " + ((int) c) + " hasn't been classified"); + } + + private void initUniKind() { + for (byte b = 0; b < 31; b++) + unikind[b] = -1; + for (byte b = 0; b < 31; b++) + switch (b) { + case Character.UNASSIGNED: + case Character.ENCLOSING_MARK: + case Character.OTHER_NUMBER: + case Character.SPACE_SEPARATOR: + case Character.LINE_SEPARATOR: + case Character.PARAGRAPH_SEPARATOR: + case Character.CONTROL: + case 17: // category 17 is unused + case Character.PRIVATE_USE: + case Character.SURROGATE: + case Character.DASH_PUNCTUATION: + case Character.START_PUNCTUATION: + case Character.END_PUNCTUATION: + case Character.OTHER_PUNCTUATION: + case Character.MATH_SYMBOL: + case Character.MODIFIER_SYMBOL: + case Character.OTHER_SYMBOL: + case Character.INITIAL_QUOTE_PUNCTUATION: + case Character.FINAL_QUOTE_PUNCTUATION: + unikind[b] = UNRECOGNIZED; + break; + case Character.UPPERCASE_LETTER: + case Character.LOWERCASE_LETTER: + case Character.TITLECASE_LETTER: + case Character.MODIFIER_LETTER: + case Character.OTHER_LETTER: + case Character.LETTER_NUMBER: + case Character.CONNECTOR_PUNCTUATION: // maybe NUMBER + case Character.CURRENCY_SYMBOL: + // Characters where Other_ID_Start is true + unikind[b] = IDENTIFIER; + break; + case Character.NON_SPACING_MARK: + case Character.COMBINING_SPACING_MARK: + case Character.DECIMAL_DIGIT_NUMBER: + case Character.FORMAT: + unikind[b] = NUMBER; + break; + } + for (byte b = 0; b < 31; b++) + if (unikind[b] == -1) + System.out.println("Unicode cat " + b + " hasn't been classified"); + } + +} Added: mspsim/se/sics/mspsim/extutil/highlight/HighlightSourceViewer.java =================================================================== --- mspsim/se/sics/mspsim/extutil/highlight/HighlightSourceViewer.java (rev 0) +++ mspsim/se/sics/mspsim/extutil/highlight/HighlightSourceViewer.java 2007-12-06 16:40:33 UTC (rev 39) @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: $ + * + * ----------------------------------------------------------------- + * + * HighlightSourceViewer + * + * Authors : Adam Dunkels, Joakim Eriksson, Niclas Finne + * Created : 6 dec 2007 + * Updated : $Date: 6 dec 2007 $ + * $Revision: 1.0 $ + */ + +package se.sics.mspsim.extutil.highlight; + +import java.awt.Container; +import java.io.FileReader; +import java.io.IOException; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +import se.sics.mspsim.util.SourceViewer; + +/** + * + */ +public class HighlightSourceViewer implements SourceViewer { + + private JFrame window; + private SyntaxHighlighter highlighter; + + public HighlightSourceViewer() { + // + } + + private void setup() { + if (window == null) { + window = new JFrame("Source Viewer"); + window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + Scanner scanner = new CScanner(); + highlighter = new SyntaxHighlighter(24, 80, scanner); + highlighter.setBorder(new LineNumberedBorder(LineNumberedBorder.LEFT_SIDE, LineNumberedBorder.RIGHT_JUSTIFY)); + JScrollPane scroller = new JScrollPane(highlighter); + scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + Container pane = window.getContentPane(); + pane.add(scroller); + window.pack(); + } + } + + public boolean isVisible() { + return window != null && window.isVisible(); + } + + public void setVisible(boolean isVisible) { + setup(); + window.setVisible(isVisible); + } + + public void viewFile(final String file) { + setup(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + try { + FileReader reader = new FileReader(file); + try { + highlighter.read(reader, null); + // Workaround for bug 4782232 in Java 1.4 + highlighter.setCaretPosition(1); + highlighter.setCaretPosition(0); + } finally { + reader.close(); + } + } catch (IOException err) { + err.printStackTrace(); + JOptionPane.showMessageDialog(window, "Failed to read the file '" + file + '\'', "Could not read file", JOptionPane.ERROR_MESSAGE); + } + } + }); + } + + public void viewLine(final int line) { + if (highlighter != null) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (line >= 0 && line < highlighter.getLineCount()) { + highlighter.setCaretPosition(highlighter.getLineStartOffset(line)); + } + } + }); + } + } + + public static void main(String[] args) { + HighlightSourceViewer sv = new HighlightSourceViewer(); + sv.setVisible(true); + sv.viewFile(args[0]); + } +} Added: mspsim/se/sics/mspsim/extutil/highlight/JavaScanner.java =================================================================== --- mspsim/se/sics/mspsim/extutil/highlight/JavaScanner.java (rev 0) +++ mspsim/se/sics/mspsim/extutil/highlight/JavaScanner.java 2007-12-06 16:40:33 UTC (rev 39) @@ -0,0 +1,962 @@ +package se.sics.mspsim.extutil.highlight; +// Public domain, no restrictions, Ian Holyer, University of Bristol. + +/** + * <p> + * Provide a hand-written scanner for the Java language. + */ + +public class JavaScanner extends Scanner { + + // The version of Java supported. + private int version = 15; + + private boolean debug = false; + + /** Create a Java scanner, for Java version 1.5 by default. */ + public JavaScanner() { + super(); + initKind(); + initUniKind(); + } + + /** Create a Java scanner, for a given version between "1.1" and "1.5". */ + public JavaScanner(String version) { + super(); + initKind(); + initUniKind(); + if (version.equals("1.1")) + this.version = 11; + else if (version.equals("1.2")) + this.version = 12; + else if (version.equals("1.3")) + this.version = 13; + else if (version.equals("1.4")) + this.version = 14; + else if (version.equals("1.5")) + this.version = 15; + else + throw new Error("Unknown version of Java: " + version); + } + + /** Override the read method from the Scanner class. */ + protected int read() { + int type, saveStart = 0; + if (debug) + saveStart = start; + + if (start >= end) + return WHITESPACE; + + switch (state) { + case MID_COMMENT: + case END_COMMENT: + type = readComment(MID_COMMENT); + if (type == END_COMMENT) + state = WHITESPACE; + else + state = MID_COMMENT; + return type; + default: + char c = buffer[start]; + if (c == '\\') + c = next(); + if (c < 128) + type = kind[c]; + else + type = unikind[Character.getType(c)]; + switch (type) { + case WHITESPACE: + start = start + charlength; + charlength = 1; + while (start < end) { + c = buffer[start]; + if (c == '\\') + c = next(); + int k; + if (c < 128) + k = kind[c]; + else + k = unikind[Character.getType(c)]; + if (k != WHITESPACE) + break; + start = start + charlength; + charlength = 1; + } + break; + case UNRECOGNIZED: + case BRACKET: + case SEPARATOR: + start = start + charlength; + charlength = 1; + break; + case OPERATOR: + start = start + charlength; + charlength = 1; + type = readOperator(c); + break; + case CHARACTER: + start = start + charlength; + charlength = 1; + type = readCharLiteral(); + break; + case STRING: + start = start + charlength; + charlength = 1; + type = readStringLiteral(); + break; + case IDENTIFIER: + start = start + charlength; + charlength = 1; + while (start < end) { + c = buffer[start]; + if (c == '\\') + c = next(); + int k; + if (c < 128) + k = kind[c]; + else + k = unikind[Character.getType(c)]; + if (k != IDENTIFIER && k != NUMBER) + break; + start = start + charlength; + charlength = 1; + } + break; + case NUMBER: + start = start + charlength; + charlength = 1; + type = readNumber(c); + break; + case PUNCTUATION: + start = start + charlength; + charlength = 1; + type = readDot(); + break; + case COMMENT: + start = start + charlength; + charlength = 1; + type = readSlash(); + if (type == START_COMMENT) + state = MID_COMMENT; + break; + } + } + if (debug) { + System.out.print(TokenTypes.typeNames[type]); + System.out.println(" " + saveStart + "," + end + "(" + + (start - saveStart) + ")"); + } + return type; + } + + private int readOperator(char c) { + if (start >= end) + return OPERATOR; + char c2; + + switch (c) { + case '~': + case '?': + case ':': + break; + case '+': + case '-': + case '&': + case '|': + c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + if (c2 != c && c2 != '=') + break; + start = start + charlength; + charlength = 1; + break; + case '=': + case '*': + case '!': + case '^': + case '%': + case '/': + c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + if (c2 != '=') + break; + start = start + charlength; + charlength = 1; + break; + case '<': + case '>': + c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + if (c2 == '=') { + start = start + charlength; + charlength = 1; + } else if (c2 == c) { + start = start + charlength; + charlength = 1; + if (start >= end) + break; + char c3 = buffer[start]; + if (c3 == '\\') + c3 = next(); + if (c3 == '=') { + start = start + charlength; + charlength = 1; + } else if (c == '>' && c3 == '>') // >>> + { + start = start + charlength; + charlength = 1; + if (start >= end) + break; + char c4 = buffer[start]; + if (c4 == '\\') + c4 = next(); + if (c4 != '=') + break; + start = start + charlength; + charlength = 1; + } + } + break; + } + return OPERATOR; + } + + private int readCharLiteral() { + if (start >= end) + return bad(CHARACTER); + char c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + + switch (c2) { + case '\\': + start = start + charlength; + charlength = 1; + boolean ok = readEscapeSequence(); + if (!ok) + return bad(CHARACTER); + break; + case '\'': + case '\n': + return bad(CHARACTER); + default: + start = start + charlength; + charlength = 1; + break; + } + if (start >= end) + return bad(CHARACTER); + char c3 = buffer[start]; + if (c3 == '\\') + c3 = next(); + if (c3 != '\'') + return bad(CHARACTER); + start = start + charlength; + charlength = 1; + return CHARACTER; + } + + private int readStringLiteral() { + if (start >= end) + return bad(STRING); + char c = buffer[start]; + if (c == '\\') + c = next(); + + while (c != '"') { + switch (c) { + case '\\': + start = start + charlength; + charlength = 1; + boolean ok = readEscapeSequence(); + if (!ok) + return bad(STRING); + break; + case '\n': + return bad(STRING); + default: + start = start + charlength; + charlength = 1; + if (start >= end) + return bad(STRING); + break; + } + c = buffer[start]; + if (c == '\\') + c = next(); + } + if (c != '"') + return bad(STRING); + start = start + charlength; + charlength = 1; + return STRING; + } + + private int readSlash() { + if (start >= end) + return OPERATOR; + char c = buffer[start]; + if (c == '\\') + c = next(); + if (c == '/') { + while (c != '\n') { + start = start + charlength; + charlength = 1; + if (start >= end) + return COMMENT; + c = buffer[start]; + if (c == '\\') + c = next(); + } + start = start + charlength; + charlength = 1; + return COMMENT; + } else if (c == '*') { + start = start + charlength; + charlength = 1; + return readComment(START_COMMENT); + } + return readOperator('/'); + } + + // Read one line of a /*...*/ comment, given the expected type + int readComment(int type) { + if (start >= end) + return type; + char c = buffer[start]; + if (c == '\\') + c = next(); + + while (true) { + while (c != '*' && c != '\n') { + start = start + charlength; + charlength = 1; + if (start >= end) + return type; + c = buffer[start]; + if (c == '\\') + c = next(); + } + start = start + charlength; + charlength = 1; + if (c == '\n') + return type; + if (start >= end) + return type; + c = buffer[start]; + if (c == '\\') + c = next(); + if (c == '/') { + start = start + charlength; + charlength = 1; + if (type == START_COMMENT) { + return COMMENT; + } + return END_COMMENT; + } + } + } + + // Read a number, without checking whether it is out of range + // Doesn't deal with e.g. 0777.9 or 07779f + private int readNumber(char c) { + if (c == '0') { + int saveStart = start, saveLength = charlength; + start = start + charlength; + charlength = 1; + if (start >= end) + return NUMBER; + char c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + switch (c2) { + case 'x': + case 'X': + start = start + charlength; + charlength = 1; + boolean ok = readDigits(16); + if (!ok) + return bad(NUMBER); + readSuffix(); + return NUMBER; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + readDigits(8); + readSuffix(); + return NUMBER; + case '.': + case 'e': + case 'E': + start = saveStart; + charlength = saveLength; + break; + case 'f': + case 'F': + case 'd': + case 'D': + start = start + charlength; + charlength = 1; + return NUMBER; + case 'l': + case 'L': + start = start + charlength; + charlength = 1; + return NUMBER; + } + } + boolean hasDigits = false; + if ('0' <= c && c <= '9') { + hasDigits = true; + readDigits(10); + if (start >= end) + return NUMBER; + c = buffer[start]; + if (c == '\\') + c = next(); + if (c == 'l' || c == 'L') { + start = start + charlength; + charlength = 1; + return NUMBER; + } + } + if (c == '.') { + start = start + charlength; + charlength = 1; + if (start >= end) + return NUMBER; + c = buffer[start]; + if (c == '\\') + c = next(); + if ('0' <= c && c <= '9') { + hasDigits = true; + readDigits(10); + if (start >= end) + return NUMBER; + c = buffer[start]; + if (c == '\\') + c = next(); + } + } + if (!hasDigits) + return bad(NUMBER); + switch (c) { + case 'e': + case 'E': + start = start + charlength; + charlength = 1; + if (start >= end) + return bad(NUMBER); + c = buffer[start]; + if (c == '\\') + c = next(); + if (c == '+' || c == '-') { + start = start + charlength; + charlength = 1; + if (start >= end) + return bad(NUMBER); + c = buffer[start]; + if (c == '\\') + c = next(); + } + readDigits(10); + break; + case 'f': + case 'F': + case 'd': + case 'D': + start = start + charlength; + charlength = 1; + return NUMBER; + } + return NUMBER; + } + + boolean readDigits(int radix) { + if (start >= end) + return false; + char c = buffer[start]; + if (c == '\\') + c = next(); + if (Character.digit(c, radix) == -1) + return false; + while (Character.digit(c, radix) != -1) { + start = start + charlength; + charlength = 1; + if (start >= end) + return true; + c = buffer[start]; + if (c == '\\') + c = next(); + } + return true; + } + + void readSuffix() { + if (start >= end) + return; + char c = buffer[start]; + if (c == '\\') + c = next(); + switch (c) { + case 'f': + case 'F': + case 'd': + case 'D': + case 'l': + case 'L': + start = start + charlength; + charlength = 1; + } + } + + private int readDot() { + if (start >= end) + return SEPARATOR; + char c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + if (Character.isDigit(c2)) { + return readNumber('.'); + } + if (start + 1 >= end || version < 15) + return SEPARATOR; + if (c2 != '.' || buffer[start + 1] != '.') + return SEPARATOR; + start = start + 2; + return SEPARATOR; + } + + private boolean readEscapeSequence() { + if (start >= end) + return false; + char c2 = buffer[start]; + if (c2 == '\\') + c2 = next(); + + switch (c2) { + case 'b': + case 't': + case 'n': + case 'f': + case 'r': + case '\"': + case '\'': + case '\\': + start = start + charlength; + charlength = 1; + return true; + case '0': + case '1': + case '2': + case '3': + return readOctal(3); + case '4': + case '5': + case '6': + case '7': + return readOctal(2); + default: + return false; + } + } + + boolean readOctal(int maxlength) { + if (start >= end) + return false; + char c = buffer[start]; + if (c == '\\') + c = next(); + + int i, val = 0; + for (i = 0; i < maxlength; i++) { + if (Character.digit(c, 8) != -1) { + val = 8 * val + Character.digit(c, 8); + start = start + charlength; + charlength = 1; + if (start >= end) + break; + c = buffer[start]; + if (c == '\\') + c = next(); + } else + break; + } + if ((i == 0) || (val > 0xFF)) + return false; + return true; + } + + // A malformed or incomplete token has a negative type + private int bad(int type) { + return -type; + } + + // Look ahead at the next character or unicode escape. + // For efficiency, replace c = next(); with + // c = buffer[start]; if (c == '\\') c = next(); + // To accept the character after looking at it, use: + // start = start + charlength; charlength = 1; + + // Record the number of source code characters used up. To deal with an odd + // or even number of backslashes preceding a unicode escape, whenever a + // second backslash is coming up, mark its position as a pair. + + private int charlength = 1; + + private int pair = 0; + + private char next() { + if (start >= end) + return 26; // EOF + char c = buffer[start]; + if (c != '\\') + return c; + if (start == pair) { + pair = 0; + return '\\'; + } + if (start + 1 >= end) + return '\\'; + + c = buffer[start + 1]; + if (c == '\\') + pair = start + 1; + if (c != 'u') + return '\\'; + + int pos = start + 2; + while (pos < end && buffer[pos] == 'u') + pos++; + if (pos + 4 > end) { + charlength = end - start; + return '\0'; + } + + c = 0; + for (int j = 0; j < 4; j++) { + int d = Character.digit(buffer[pos + j], 16); + if (d < 0) { + charlength = pos + j - start; + return '\0'; + } + c = (char) (c * 16 + d); + } + charlength = pos + 4 - start; + return c; + } + + // Override initSymbolTable + + protected void initSymbolTable() { + lookup(KEYWORD, "abstract"); + if (version >= 14) + lookup(KEYWORD, "assert"); + lookup(KEYWORD, "boolean"); + lookup(KEYWORD, "break"); + lookup(KEYWORD, "byte"); + lookup(KEYWORD, "case"); + lookup(KEYWORD, "catch"); + lookup(KEYWORD, "char"); + lookup(KEYWORD, "class"); + lookup(KEYWORD, "const"); + lookup(KEYWORD, "continue"); + lookup(KEYWORD, "default"); + lookup(KEYWORD, "do"); + lookup(KEYWORD, "double"); + lookup(KEYWORD, "else"); + if (version >= 15) + lookup(KEYWORD, "enum"); + lookup(KEYWORD, "extends"); + lookup(KEYWORD, "final"); + lookup(KEYWORD, "finally"); + lookup(KEYWORD, "float"); + lookup(KEYWORD, "for"); + lookup(KEYWORD, "goto"); + lookup(KEYWORD, "if"); + lookup(KEYWORD, "implements"); + lookup(KEYWORD, "import"); + lookup(KEYWORD, "instanceof"); + lookup(KEYWORD, "int"); + lookup(KEYWORD, "interface"); + lookup(KEYWORD, "long"); + lookup(KEYWORD, "native"); + lookup(KEYWORD, "new"); + lookup(KEYWORD, "package"); + lookup(KEYWORD, "private"); + lookup(KEYWORD, "protected"); + lookup(KEYWORD, "public"); + lookup(KEYWORD, "return"); + lookup(KEYWORD, "short"); + lookup(KEYWORD, "static"); + if (version >= 12) + lookup(KEYWORD, "strictfp"); + lookup(KEYWORD, "super"); + lookup(KEYWORD, "switch"); + lookup(KEYWORD, "synchronized"); + lookup(KEYWORD, "this"); + lookup(KEYWORD, "throw"); + lookup(KEYWORD, "throws"); + lookup(KEYWORD, "transient"); + lookup(KEYWORD, "try"); + lookup(KEYWORD, "void"); + lookup(KEYWORD, "volatile"); + lookup(KEYWORD, "while"); + + lookup(LITERAL, "true"); + lookup(LITERAL, "false"); + lookup(LITERAL, "null"); + } + + // *** Override lookup, but what about unicode escape translation? + + private Symbol temp = new Symbol(0, null); + + protected Symbol lookup(int type, String name) { + if (type != IDENTIFIER) + return super.lookup(type, name); + temp.type = KEYWORD; + temp.name = name; + Symbol sym = symbolTable.get(temp); + if (sym != null) + return sym; + temp.type = LITERAL; + sym = symbolTable.get(temp); + if (sym != null) + return sym; + return super.lookup(type, name); + } + + // Classify the ascii characters using an array of kinds, and classify all + // other unicode characters using an array indexed by unicode category. + // See the source file java/lang/Character.java for the categories. + // To find the classification of a character, use: + // if (c < 128) k = kind[c]; else k = unikind[Character.getType(c)]; + + private static final byte[] kind = new byte[128]; + + private static final byte[] unikind = new byte[31]; + + // Initialise the two classification arrays using static initializer code. + // Token types from the TokenTypes class are used to classify characters. + + private void initKind() { + for (char c = 0; c < 128; c++) + kind[c] = -1; + for (char c = 0; c < 128; c++) + switch (c) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 11: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 27: + case 28: + case 29: + case 30: + case 31: + case 127: + case '#': + case '@': + case '`': + case '\\': + kind[c] = UNRECOGNIZED; + break; + case '\t': + case '\n': + case ' ': + case '\f': + case 26: + kind[c] = WHITESPACE; + break; + case '!': + case '%': + case '&': + case '*': + case '+': + case '-': + case ':': + case '<': + case '=': + case '>': + case '?': + case '^': + case '|': + case '~': + kind[c] = OPERATOR; + break; + case '"': + kind[c] = STRING; + break; + case '\'': + kind[c] = CHARACTER; + break; + case '.': + kind[c] = PUNCTUATION; + break; + case '/': + kind[c] = COMMENT; + break; + case '$': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + kind[c] = IDENTIFIER; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + kind[c] = NUMBER; + break; + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + kind[c] = BRACKET; + break; + case ',': + case ';': + kind[c] = SEPARATOR; + break; + } + for (char c = 0; c < 128; c++) + if (kind[c] == -1) + System.out.println("Char " + ((int) c) + " hasn't been classified"); + } + + private void initUniKind() { + for (byte b = 0; b < 31; b++) + unikind[b] = -1; + for (byte b = 0; b < 31; b++) + switch (b) { + case Character.UNASSIGNED: + case Character.ENCLOSING_MARK: + case Character.OTHER_NUMBER: + case Character.SPACE_SEPARATOR: + case Character.LINE_SEPARATOR: + case Character.PARAGRAPH_SEPARATOR: + case Character.CONTROL: + case 17: // category 17 is unused + case Character.PRIVATE_USE: + case Character.SURROGATE: + case Character.DASH_PUNCTUATION: + case Character.START_PUNCTUATION: + case Character.END_PUNCTUATION: + case Character.OTHER_PUNCTUATION: + case Character.MATH_SYMBOL: + case Character.MODIFIER_SYMBOL: + case Character.OTHER_SYMBOL: + case Character.INITIAL_QUOTE_PUNCTUATION: + case Character.FINAL_QUOTE_PUNCTUATION: + unikind[b] = UNRECOGNIZED; + break; + case Character.UPPERCASE_LETTER: + case Character.LOWERCASE_LETTER: + case Character.TITLECASE_LETTER: + case Character.MODIFIER_LETTER: + case Character.OTHER_LETTER: + case Character.LETTER_NUMBER: + case Character.CONNECTOR_PUNCTUATION: // maybe NUMBER + case Character.CURRENCY_SYMBOL: + // Characters where Other_ID_Start is true + unikind[b] = IDENTIFIER; + break; + case Character.NON_SPACING_MARK: + case Character.COMBINING_SPACING_MARK: + case Character.DECIMAL_DIGIT_NUMBER: + case Character.FORMAT: + unikind[b] = NUMBER; + break; + } + for (byte b = 0; b < 31; b++) + if (unikind[b] == -1) + System.out.println("Unicode cat " + b + " hasn't been classified"); + } + +} Added: mspsim/se/sics/mspsim/extutil/highlight/LineNumberedBorder.java =================================================================== --- mspsim/se/sics/mspsim/extutil/highlight/LineNumberedBorder.java (rev 0) +++ mspsim/se/sics/mspsim/extutil/highlight/LineNumberedBorder.java 2007-12-06 16:40:33 UTC (rev 39) @@ -0,0 +1,258 @@ +package se.sics.mspsim.extutil.highlight; +import java.awt.Component; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.border.AbstractBorder; + +/** + * Draws line numbers next to each line, in the same font as the text. + * Currently, this can only be used with a <tt>SyntaxHighlighter</tt> , since it + * relies on the <tt>getRows()</tt> and <tt>getLineCount()</tt> methods. A + * possible extension, create an interface to return this rows/linecount. + * + * @author Paul Durbin (Mc...@ya...) + * @created January 29, 2002 + */ +public class LineNumberedBorder extends AbstractBorder { + + private static final long serialVersionUID = -3812536735962506061L; + + /** + * The line numbers should be drawn on the left side of the component. + */ + public static int LEFT_SIDE = -2; + + /** + * The line numbers should be drawn on the right side of the component. + */ + public static int RIGHT_SIDE = -1; + + /** + * The line number should be right justified. + */ + public static int RIGHT_JUSTIFY = 0; + + /** + * The line number should be left justified. + */ + public static int LEFT_JUSTIFY = 1; + + /** + * Indicates the justification of the text of the line number. + */ + private int lineNumberJustification = RIGHT_JUSTIFY; + + /** + * Indicates the location of the line numbers, w.r.t. the component. + */ + private int location = LEFT_SIDE; + + public LineNumberedBorder(int location, int justify) { + setLocation(location); + setLineNumberJustification(justify); + } + + public Insets getBorderInsets(Component c) { + return getBorderInsets(c, new Insets(0, 0, 0, 0)); + } + + /** + * This modifies the insets, by adding space for the line number on the left. + * Should be modified to add space on the right, depending upon Locale. + * + * @param c + * Description of the Parameter + * @param insets + * Description of the Parameter + * @return The borderInsets value + */ + public Insets getBorderInsets(Component c, Insets insets) { + // if c is not a SyntaxHighlighter...nothing is done... + if (c instanceof SyntaxHighlighter) { + int width = lineNumberWidth((SyntaxHighlighter) c); + if (location == LEFT_SIDE) { + insets.left = width; + } else { + insets.right = width; + } + } + return insets; + } + + public int getLineNumberJustification() { + return lineNumberJustification; + } + + public void setLineNumberJustification(int justify) { + if (justify == RIGHT_JUSTIFY || justify == LEFT_JUSTIFY) { + lineNumberJustification = justify; + } + } + + public int getLocation() { + return location; + } + + public void setLocation(int loc) { + if (loc == RIGHT_SIDE || loc == LEFT_SIDE) { + location = loc; + } + } + + /** + * Returns the width, in pixels, of the maximum line number, plus a trailing + * space. + * + * @param textArea + * Description of the Parameter + * @return Description of the Return Value + */ + private int lineNumberWidth(SyntaxHighlighter textArea) { + // + // note: should this be changed to use all nines for the lineCount? + // for example, if the number of rows is 111...999 could be wider + // (in pixels) in a proportionally spaced font... + // + int lineCount = Math.max(textArea.getRows(), textArea.getLineCount() + 1); + return textArea.getFontMetrics(textArea.getFont()).stringWidth( + lineCount + " "); + } + + // + // NOTE: This method is called every time the cursor blinks... + // so...optimize (later and if possible) for speed... + // + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) { + + java.awt.Rectangle clip = g.getClipBounds(); + + FontMetrics fm = g.getFontMetrics(); + int fontHeight = fm.getHeight(); + + // starting location at the "top" of the page... + // y is the starting baseline for the font... + // should "font leading" be applied? + int ybaseline = y + fm.getAscent(); + + // + // now determine if it is the "top" of the page...or somewhere else + // + int startingLineNumber = (clip.y / fontHeight) + 1; + + // + // use any one of the following if's: + // + // if (startingLineNumber != 1) + if (ybaseline < clip.y) { + // + // not within the clip rectangle...move it... + // determine how many fontHeight's there are between + // y and clip.y...then add that many fontHeights + // + ybaseline = + y + startingLineNumber * fontHeight - (fontHeight - fm.getAscent()); + } + + // + // options: + // . write the number rows in the document (current) + // . write the number of existing lines in the document (to do) + // see getLineCount() + // + + // determine which the "drawing" should end... + // add fontHeight: make sure...part of the line number is drawn + // + // could also do this by determining what the last line + // number to draw. + // then the "while" loop whould change accordingly. + // + // int yend = y + clip.height + fontHeight; + // int yend = ybaseline + height + fontHeight; // original + int yend = ybaseline + height; + if (yend > (y + height)) { + yend = y + height; + } + + SyntaxHighlighter jta = (SyntaxHighlighter) c; + int lineWidth = lineNumberWidth(jta); + + // base x position of the line number + int lnxstart = x; + if (location == LEFT_SIDE) { + // x (LEFT) or (x + lineWidth) (RIGHT) + // (depends upon justification) + if (lineNumberJustification == LEFT_JUSTIFY) { + lnxstart = x; + } else { + // RIGHT JUSTIFY + lnxstart = x + lineWidth; + } + } else { + // RIGHT SIDE + // (y + width) - lineWidth (LEFT) or (y + width) (RIGHT) + // (depends upon justification) + if (lineNumberJustification == LEFT_JUSTIFY) { + lnxstart = (y + width) - lineWidth; + } else { + // RIGHT JUSTIFY + lnxstart = (y + width); + } + } + + g.setColor(c.getForeground()); + // + // loop until out of the "visible" region... + // + int length = + ("" + Math.max(jta.getRows(), jta.getLineCount() + 1)).length(); + while (ybaseline < yend) { + // + // options: + // . left justify the line numbers + // . right justify the line numbers + // + + if (lineNumberJustification == LEFT_JUSTIFY) { + g.drawString(startingLineNumber + " ", lnxstart, ybaseline); + } else { + // right justify + String label = padLabel(startingLineNumber, length, true); + g.drawString(label, lnxstart - fm.stringWidth(label), ybaseline); + } + + ybaseline += fontHeight; + startingLineNumber++; + } + } + + // paintComponent + + /** + * Create the string for the line number. NOTE: The <tt>length</tt> param + * does not include the <em>optional</em> space added after the line number. + * + * @param lineNumber + * to stringize + * @param length + * the length desired of the string + * @param addSpace + * Description of the Parameter + * @return the line number for drawing + */ + private static String padLabel(int lineNumber, int length, boolean addSpace) { + StringBuffer buffer = new StringBuffer(); + buffer.append(lineNumber); + for (int count = (length - buffer.length()); count > 0; count--) { + buffer.insert(0, ' '); + } + if (addSpace) { + buffer.append(' '); + } + return buffer.toString(); + } +} +// LineNumberedBorder Added: mspsim/se/sics/mspsim/extutil/highlight/Scan.java =================================================================== --- mspsim/se/sics/mspsim/extutil/highlight/Scan.java (rev 0) +++ mspsim/se/sics/mspsim/extutil/highlight/Scan.java 2007-12-06 16:40:33 UTC (rev 39) @@ -0,0 +1,38 @@ +package se.sics.mspsim.extutil.highlight; +// Illustrate the use of the scanner by reading in a file and displaying its +// tokens. Public domain, no restrictions, Ian Holyer, University of Bristol. + +import java.io.*; + +public class Scan { + // Get the filename from the command line + public static void main(String[] args) throws IOException { + Scan program = ne... [truncated message content] |