2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common
8 * Development and Distribution License("CDDL") (collectively, the
9 * "License"). You may not use this file except in compliance with the
10 * License. You can obtain a copy of the License at
11 * http://www.netbeans.org/cddl-gplv2.html
12 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13 * specific language governing permissions and limitations under the
14 * License. When distributing the software, include this License Header
15 * Notice in each file and include the License file at
16 * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17 * particular file as subject to the "Classpath" exception as provided
18 * by Sun in the GPL Version 2 section of the License file that
19 * accompanied this code. If applicable, add the following below the
20 * License Header, with the fields enclosed by brackets [] replaced by
21 * your own identifying information:
22 * "Portions Copyrighted [year] [name of copyright owner]"
24 * If you wish your version of this file to be governed by only the CDDL
25 * or only the GPL Version 2, indicate your decision by adding
26 * "[Contributor] elects to include this software in this distribution
27 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28 * single choice of license, a recipient has the option to distribute
29 * your version of this file under either the CDDL, the GPL Version 2 or
30 * to extend the choice of license to its licensees as provided above.
31 * However, if you add GPL Version 2 code and therefore, elected the GPL
32 * Version 2 license, then the option applies only if the new code is
33 * made subject to such option by the copyright holder.
37 * Portions Copyrighted 2008 Sun Microsystems, Inc.
39 package org.netbeans.modules.php.smarty.editor.lexer;
41 import org.netbeans.api.lexer.InputAttributes;
42 import org.netbeans.api.lexer.LanguagePath;
43 import org.netbeans.api.lexer.Token;
44 import org.netbeans.lib.editor.util.CharSequenceUtilities;
45 import org.netbeans.modules.php.smarty.editor.TplMetaData;
46 import org.netbeans.modules.php.smarty.editor.utlis.LexerUtils;
47 import org.netbeans.modules.php.smarty.editor.utlis.TplUtils;
48 import org.netbeans.spi.lexer.Lexer;
49 import org.netbeans.spi.lexer.LexerInput;
50 import org.netbeans.spi.lexer.LexerRestartInfo;
51 import org.netbeans.spi.lexer.TokenFactory;
55 * @author Martin Fousek
57 public class TplTopLexer implements Lexer<TplTopTokenId> {
59 private final TplTopColoringLexer scanner;
60 private TokenFactory<TplTopTokenId> tokenFactory;
61 private final InputAttributes inputAttributes;
62 private final TplMetaData tplMetaData;
64 private class CompoundState {
65 private State lexerState;
66 private SubState lexerSubState;
68 public CompoundState(State lexerState, SubState lexerSubState) {
69 this.lexerState = lexerState;
70 this.lexerSubState = lexerSubState;
74 public boolean equals(Object obj) {
78 if (getClass() != obj.getClass()) {
81 final CompoundState other = (CompoundState) obj;
82 if (this.lexerState != other.lexerState) {
85 if (this.lexerSubState != other.lexerSubState) {
92 public int hashCode() {
94 hash = 17 * hash + this.lexerState.ordinal();
95 hash = 17 * hash + this.lexerSubState.ordinal();
100 public String toString() {
101 return "State(hash=" + hashCode() + ",s=" + lexerState + ",ss=" + lexerSubState + ")"; //NOI18N
106 private TplTopLexer(LexerRestartInfo<TplTopTokenId> info) {
107 CompoundState state = null;
108 if (info.state() == null) {
109 state = new CompoundState(State.INIT, SubState.NO_SUB_STATE);
111 state = (CompoundState)info.state();
113 this.tokenFactory = info.tokenFactory();
114 this.inputAttributes = info.inputAttributes();
115 if (inputAttributes != null) {
116 this.tplMetaData = (TplMetaData) inputAttributes.getValue(LanguagePath.get(TplTopTokenId.language()), TplMetaData.class);
118 this.tplMetaData = TplUtils.getProjectPropertiesForFileObject(null);
120 scanner = new TplTopColoringLexer(info, state, tplMetaData);
124 * Create new top lexer.
125 * @param info where was the parsing started
126 * @return new lexer for additional parsing
128 public static synchronized TplTopLexer create(LexerRestartInfo<TplTopTokenId> info) {
129 return new TplTopLexer(info);
132 public Token<TplTopTokenId> nextToken() {
133 TplTopTokenId tokenId = scanner.nextToken();
134 Token<TplTopTokenId> token = null;
135 if (tokenId != null) {
136 token = tokenFactory.createToken(tokenId);
141 public Object state() {
142 return scanner.getState();
145 public void release() {
161 private enum SubState {
167 private class TplTopColoringLexer {
170 private final LexerInput input;
171 private SubState subState;
172 private final TplMetaData metadata;
174 public TplTopColoringLexer(LexerRestartInfo<TplTopTokenId> info, CompoundState state, TplMetaData metadata) {
175 this.input = info.input();
176 this.state = state.lexerState;
177 this.subState = state.lexerSubState;
178 if (metadata != null)
179 this.metadata = metadata;
181 this.metadata = TplUtils.getProjectPropertiesForFileObject(null);
184 public TplTopTokenId nextToken() {
185 int c = input.read();
188 int openDelimiterLength = getOpenDelimiterLength();
189 int closeDelimiterLength = getCloseDelimiterLength();
190 if (c == LexerInput.EOF) {
193 while (c != LexerInput.EOF) {
195 text = input.readText();
196 textLength = text.length();
200 if (isSmartyOpenDelimiter(text)) {
201 state = State.OPEN_DELIMITER;
202 input.backup(openDelimiterLength);
203 if (textLength > openDelimiterLength) {
204 return TplTopTokenId.T_HTML;
208 // return TplTopTokenId.T_HTML;
213 if (textLength < openDelimiterLength) {
216 state = State.AFTER_DELIMITER;
217 if (subState == subState.NO_SUB_STATE) {
218 return TplTopTokenId.T_SMARTY_OPEN_DELIMITER;
220 if (input.readLength() > openDelimiterLength) {
221 input.backup(input.readLength() - openDelimiterLength);
222 if (subState == subState.LITERAL)
223 return TplTopTokenId.T_HTML;
225 return TplTopTokenId.T_PHP;
230 case AFTER_DELIMITER:
231 if (LexerUtils.isWS(c)) {
232 if (subState == subState.NO_SUB_STATE) {
233 return TplTopTokenId.T_SMARTY;
239 String lastWord = readNextWord(c);
242 if (lastWord.startsWith("/literal")) {
243 subState = SubState.NO_SUB_STATE;
244 state = State.OPEN_DELIMITER;
245 input.backup(input.readLength());
248 input.backup(input.readLength()-1);
249 state = State.IN_LITERAL;
251 return TplTopTokenId.T_HTML;
253 if (lastWord.startsWith("/php")) {
254 subState = SubState.NO_SUB_STATE;
255 state = State.OPEN_DELIMITER;
256 input.backup(input.readLength());
259 state = State.IN_PHP;
261 return TplTopTokenId.T_PHP;
263 if (lastWord.charAt(0) == '*') {
264 state = State.IN_COMMENT;
265 input.backup(lastWord.length()-1);
266 return TplTopTokenId.T_COMMENT;
268 else if (lastWord.startsWith("literal")) {
269 subState = SubState.LITERAL;
270 state = State.AFTER_SUBSTATE;
271 input.backup(lastWord.length()-7);
272 return TplTopTokenId.T_SMARTY;
274 else if (lastWord.startsWith("php")) {
275 subState = SubState.PHP_CODE;
276 state = State.AFTER_SUBSTATE;
277 input.backup(lastWord.length()-3);
278 return TplTopTokenId.T_SMARTY;
281 state = State.IN_SMARTY;
282 input.backup(lastWord.length());
290 state = State.AFTER_SUBSTATE;
291 return TplTopTokenId.T_COMMENT;
293 return TplTopTokenId.T_COMMENT;
296 if (LexerUtils.isWS(c)) {
297 return TplTopTokenId.T_SMARTY;
299 else if (isSmartyCloseDelimiter(text)) {
300 state = State.CLOSE_DELIMITER;
301 input.backup(closeDelimiterLength);
307 case CLOSE_DELIMITER:
308 if (textLength < closeDelimiterLength) {
313 state = State.IN_LITERAL;
316 state = State.IN_PHP;
322 return TplTopTokenId.T_SMARTY_CLOSE_DELIMITER;
325 if (isSmartyOpenDelimiter(text)) {
326 state = State.OPEN_DELIMITER;
327 input.backup(openDelimiterLength);
328 if (input.readLength() > 0)
329 return TplTopTokenId.T_PHP;
331 if (input.readLength() > 1) {
332 return TplTopTokenId.T_PHP;
337 if (isSmartyOpenDelimiter(text)) {
338 state = State.OPEN_DELIMITER;
339 input.backup(openDelimiterLength);
340 if (input.readLength() > 0) {
341 return TplTopTokenId.T_HTML;
344 if (LexerUtils.isWS(c)) {
345 return TplTopTokenId.T_HTML;
350 if (isSmartyCloseDelimiter(text)) {
351 if (textLength == closeDelimiterLength) {
353 return TplTopTokenId.T_SMARTY_CLOSE_DELIMITER;
356 state = State.CLOSE_DELIMITER;
357 input.backup(closeDelimiterLength);
358 if (input.readLength() != 0) {
359 return TplTopTokenId.T_SMARTY;
365 return TplTopTokenId.T_SMARTY;
367 return TplTopTokenId.T_SMARTY;
369 // state = State.OUTER;
371 // if (input.readLength() > 1) {
372 // return TplTopTokenId.T_SMARTY;
380 return getTokenId(state);
383 private TplTopTokenId getTokenId(State state) {
386 return TplTopTokenId.T_SMARTY;
388 return TplTopTokenId.T_SMARTY_OPEN_DELIMITER;
389 case CLOSE_DELIMITER:
390 return TplTopTokenId.T_SMARTY_CLOSE_DELIMITER;
392 return TplTopTokenId.T_HTML;
397 return new CompoundState(state, subState);
400 private boolean isSmartyOpenDelimiter(CharSequence text) {
401 return CharSequenceUtilities.endsWith(text, metadata.getOpenDelimiter());
404 private boolean isSmartyCloseDelimiter(CharSequence text) {
405 return CharSequenceUtilities.endsWith(text, metadata.getCloseDelimiter());
408 private int getOpenDelimiterLength() {
409 return metadata.getOpenDelimiter().length();
412 private int getCloseDelimiterLength() {
413 return metadata.getCloseDelimiter().length();
416 private String readNextWord(int lastChar) {
417 String word = Character.toString((char)lastChar);
419 while (!LexerUtils.isWS(c = input.read()) && c != LexerInput.EOF) {
420 word += Character.toString((char)c);