001: package net.xoetrope.builder.editor.syntaxhighlight;
002:
003: /*
004: * HTMLTokenMarker.java - HTML token marker
005: * Copyright (C) 1998, 1999 Slava Pestov
006: *
007: * You may use and modify this package for any purpose. Redistribution is
008: * permitted, in both source and binary form, provided that this notice
009: * remains intact in all source distributions of this package.
010: */
011:
012: import javax.swing.text.Segment;
013:
014: /**
015: * HTML token marker.
016: *
017: * @author Slava Pestov
018: * @version $Id: HTMLTokenMarker.java,v 1.22 2005/01/05 17:20:48 luano Exp $
019: */
020: public class HTMLTokenMarker extends TokenMarker {
021: public static final byte JAVASCRIPT = Token.INTERNAL_FIRST;
022:
023: public HTMLTokenMarker() {
024: this (true);
025: }
026:
027: public HTMLTokenMarker(boolean js) {
028: this .js = js;
029: keywords = JavaScriptTokenMarker.getKeywords();
030: }
031:
032: public byte markTokensImpl(byte token, Segment line, int lineIndex) {
033: char[] array = line.array;
034: int offset = line.offset;
035: lastOffset = offset;
036: lastKeyword = offset;
037: int length = line.count + offset;
038: boolean backslash = false;
039:
040: loop: for (int i = offset; i < length; i++) {
041: int i1 = (i + 1);
042:
043: char c = array[i];
044: if (c == '\\') {
045: backslash = !backslash;
046: continue;
047: }
048:
049: switch (token) {
050: case Token.NULL: // HTML text
051: backslash = false;
052: switch (c) {
053: case '<':
054: addToken(i - lastOffset, token);
055: lastOffset = lastKeyword = i;
056: if (SyntaxUtilities.regionMatches(false, line, i1,
057: "!--")) {
058: i += 3;
059: token = Token.COMMENT1;
060: } else if (js
061: && SyntaxUtilities.regionMatches(true,
062: line, i1, "script>")) {
063: addToken(8, Token.KEYWORD1);
064: lastOffset = lastKeyword = (i += 8);
065: token = JAVASCRIPT;
066: } else {
067: token = Token.KEYWORD1;
068: }
069: break;
070: case '&':
071: addToken(i - lastOffset, token);
072: lastOffset = lastKeyword = i;
073: token = Token.KEYWORD2;
074: break;
075: }
076: break;
077: case Token.KEYWORD1: // Inside a tag
078: backslash = false;
079: if (c == '>') {
080: addToken(i1 - lastOffset, token);
081: lastOffset = lastKeyword = i1;
082: token = Token.NULL;
083: }
084: break;
085: case Token.KEYWORD2: // Inside an entity
086: backslash = false;
087: if (c == ';') {
088: addToken(i1 - lastOffset, token);
089: lastOffset = lastKeyword = i1;
090: token = Token.NULL;
091: break;
092: }
093: break;
094: case Token.COMMENT1: // Inside a comment
095: backslash = false;
096: if (SyntaxUtilities
097: .regionMatches(false, line, i, "-->")) {
098: addToken((i + 3) - lastOffset, token);
099: lastOffset = lastKeyword = i + 3;
100: token = Token.NULL;
101: }
102: break;
103: case JAVASCRIPT: // Inside a JavaScript
104: switch (c) {
105: case '<':
106: backslash = false;
107: doKeyword(line, i, c);
108: if (SyntaxUtilities.regionMatches(true, line, i1,
109: "/script>")) {
110: addToken(i - lastOffset, Token.NULL);
111: addToken(9, Token.KEYWORD1);
112: lastOffset = lastKeyword = (i += 9);
113: token = Token.NULL;
114: }
115: break;
116: case '"':
117: if (backslash)
118: backslash = false;
119: else {
120: doKeyword(line, i, c);
121: addToken(i - lastOffset, Token.NULL);
122: lastOffset = lastKeyword = i;
123: token = Token.LITERAL1;
124: }
125: break;
126: case '\'':
127: if (backslash)
128: backslash = false;
129: else {
130: doKeyword(line, i, c);
131: addToken(i - lastOffset, Token.NULL);
132: lastOffset = lastKeyword = i;
133: token = Token.LITERAL2;
134: }
135: break;
136: case '/':
137: backslash = false;
138: doKeyword(line, i, c);
139: if (length - i > 1) {
140: addToken(i - lastOffset, Token.NULL);
141: lastOffset = lastKeyword = i;
142: if (array[i1] == '/') {
143: addToken(length - i, Token.COMMENT2);
144: lastOffset = lastKeyword = length;
145: break loop;
146: } else if (array[i1] == '*') {
147: token = Token.COMMENT2;
148: }
149: }
150: break;
151: default:
152: backslash = false;
153: if (!Character.isLetterOrDigit(c) && c != '_')
154: doKeyword(line, i, c);
155: break;
156: }
157: break;
158: case Token.LITERAL1: // JavaScript "..."
159: if (backslash)
160: backslash = false;
161: else if (c == '"') {
162: addToken(i1 - lastOffset, Token.LITERAL1);
163: lastOffset = lastKeyword = i1;
164: token = JAVASCRIPT;
165: }
166: break;
167: case Token.LITERAL2: // JavaScript '...'
168: if (backslash)
169: backslash = false;
170: else if (c == '\'') {
171: addToken(i1 - lastOffset, Token.LITERAL1);
172: lastOffset = lastKeyword = i1;
173: token = JAVASCRIPT;
174: }
175: break;
176: case Token.COMMENT2: // Inside a JavaScript comment
177: backslash = false;
178: if (c == '*' && length - i > 1 && array[i1] == '/') {
179: addToken((i += 2) - lastOffset, Token.COMMENT2);
180: lastOffset = lastKeyword = i;
181: token = JAVASCRIPT;
182: }
183: break;
184: default:
185: throw new InternalError("Invalid state: " + token);
186: }
187: }
188:
189: switch (token) {
190: case Token.LITERAL1:
191: case Token.LITERAL2:
192: addToken(length - lastOffset, Token.INVALID);
193: token = JAVASCRIPT;
194: break;
195: case Token.KEYWORD2:
196: addToken(length - lastOffset, Token.INVALID);
197: token = Token.NULL;
198: break;
199: case JAVASCRIPT:
200: doKeyword(line, length, '\0');
201: addToken(length - lastOffset, Token.NULL);
202: break;
203: default:
204: addToken(length - lastOffset, token);
205: break;
206: }
207:
208: return token;
209: }
210:
211: // private members
212: private KeywordMap keywords;
213: private boolean js;
214: private int lastOffset;
215: private int lastKeyword;
216:
217: private boolean doKeyword(Segment line, int i, char c) {
218: int i1 = i + 1;
219:
220: int len = i - lastKeyword;
221: byte id = keywords.lookup(line, lastKeyword, len);
222: if (id != Token.NULL) {
223: if (lastKeyword != lastOffset)
224: addToken(lastKeyword - lastOffset, Token.NULL);
225: addToken(len, id);
226: lastOffset = i;
227: }
228: lastKeyword = i1;
229: return false;
230: }
231: }
|