org.netbeans.modules.html.knockout.KODataBindLexer Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.html.knockout;
import org.netbeans.modules.html.knockout.api.KODataBindTokenId;
import org.netbeans.api.lexer.Token;
import org.netbeans.spi.lexer.Lexer;
import org.netbeans.spi.lexer.LexerInput;
import org.netbeans.spi.lexer.LexerRestartInfo;
import org.netbeans.spi.lexer.TokenFactory;
/**
*
* @author marekfukala
*/
public class KODataBindLexer implements Lexer {
private enum State {
INIT,
IN_WS_BEFORE_KEY,
IN_KEY,
AFTER_KEY,
WS_AFTER_KEY,
IN_VALUE,
IN_VALUE_ESCAPE,
AFTER_VALUE,;
}
private static class CompoundState {
private State state;
private byte parenDepth;
private boolean inSingleQuotedString;
private boolean inDoubleQuotedString;
public CompoundState(State state, byte parenDepth, boolean inSingleQuotedString, boolean inDoubleQuotedString) {
this.state = state;
this.parenDepth = parenDepth;
this.inSingleQuotedString = inSingleQuotedString;
this.inDoubleQuotedString = inDoubleQuotedString;
}
@Override
public int hashCode() {
int hash = 7;
hash = 11 * hash + (this.state != null ? this.state.hashCode() : 0);
hash = 11 * hash + this.parenDepth;
hash = 11 * hash + (this.inSingleQuotedString ? 1 : 0);
hash = 11 * hash + (this.inDoubleQuotedString ? 1 : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CompoundState other = (CompoundState) obj;
if (this.state != other.state) {
return false;
}
if (this.parenDepth != other.parenDepth) {
return false;
}
if (this.inSingleQuotedString != other.inSingleQuotedString) {
return false;
}
if (this.inDoubleQuotedString != other.inDoubleQuotedString) {
return false;
}
return true;
}
}
private final LexerInput input;
private final TokenFactory tokenFactory;
private State state;
private byte parenDepth = 0; //parenthesis depth in "IN_VALUE" state
private boolean inSingleQuotedString;
private boolean inDoubleQuotedString;
@Override
public Object state() {
return new CompoundState(state, parenDepth, inSingleQuotedString, inDoubleQuotedString);
}
public KODataBindLexer(LexerRestartInfo info) {
this.tokenFactory = info.tokenFactory();
this.input = info.input();
if (info.state() == null) {
state = State.INIT;
} else {
CompoundState compoundState = (CompoundState) info.state();
state = compoundState.state;
parenDepth = compoundState.parenDepth;
}
}
@Override
public Token nextToken() {
int c;
while (true) {
c = input.read();
if (c == LexerInput.EOF) {
if (input.readLengthEOF() == 1) {
return null; //just EOL is read
} else {
//there is something else in the buffer except EOL we will return last token now
input.backup(1); //backup the EOL, we will return null in next nextToken() call
break;
}
}
switch (state) {
case INIT:
if (Character.isLetter(c) || c == '_' || c == '$') {
state = State.IN_KEY;
} else if (Character.isWhitespace(c)) {
input.backup(1); //backup the ws
state = State.IN_WS_BEFORE_KEY;
} else {
return tokenFactory.createToken(KODataBindTokenId.ERROR);
}
break;
case IN_WS_BEFORE_KEY:
if (Character.isLetter(c) || c == '_' || c == '$') {
state = State.IN_KEY;
input.backup(1); //backup the first key char
return tokenFactory.createToken(KODataBindTokenId.WS);
} else if (Character.isWhitespace(c)) {
//stay
} else {
//error
state = State.INIT;
return tokenFactory.createToken(KODataBindTokenId.ERROR);
}
break;
case IN_KEY:
if (!Character.isLetter(c) && !Character.isDigit(c) && c != '_' && c != '$') {
if (c == ':') {
state = State.AFTER_KEY;
input.backup(1); //backup the colon
return tokenFactory.createToken(KODataBindTokenId.KEY);
} else if (Character.isWhitespace(c)) {
state = State.WS_AFTER_KEY;
input.backup(1); //backup the ws
return tokenFactory.createToken(KODataBindTokenId.KEY);
} else {
state = State.INIT;
return tokenFactory.createToken(KODataBindTokenId.ERROR);
}
}
//stay in IN_KEY
break;
case AFTER_KEY:
assert c == ':';
state = State.IN_VALUE;
return tokenFactory.createToken(KODataBindTokenId.COLON);
case WS_AFTER_KEY:
if (Character.isWhitespace(c)) {
//stay
} else if (c == ':') {
state = State.AFTER_KEY;
input.backup(1); //backup the colon
return tokenFactory.createToken(KODataBindTokenId.WS);
} else {
state = State.INIT;
return tokenFactory.createToken(KODataBindTokenId.ERROR);
}
break;
case IN_VALUE:
switch (c) {
case ',':
if (parenDepth == 0 && !inSingleQuotedString && !inDoubleQuotedString) {
state = State.AFTER_VALUE;
input.backup(1); //backup the comma
if (input.readLength() > 0) {
//return value token if it is not empty like here: "key:,"
return tokenFactory.createToken(KODataBindTokenId.VALUE);
}
}
break;
case '(':
case '{':
case '[':
parenDepth++;
break;
case ')':
case '}':
case ']':
parenDepth--;
break;
case '\'':
inSingleQuotedString = !inSingleQuotedString;
break;
case '"':
inDoubleQuotedString = !inDoubleQuotedString;
break;
case '\\':
if(inSingleQuotedString || inDoubleQuotedString) {
state = State.IN_VALUE_ESCAPE;
}
break;
default:
break;
}
break;
case IN_VALUE_ESCAPE:
//just go back to the IN_VALUE state => ignore the semantic of the char after backslash
state = State.IN_VALUE;
break;
case AFTER_VALUE:
assert c == ',';
state = State.INIT;
return tokenFactory.createToken(KODataBindTokenId.COMMA);
}
}
//"last buffer" handling
switch (state) {
case INIT:
if (input.readLength() == 0) {
return null;
}
break;
case IN_WS_BEFORE_KEY:
return tokenFactory.createToken(KODataBindTokenId.WS);
case IN_KEY:
case AFTER_KEY:
case WS_AFTER_KEY:
return tokenFactory.createToken(KODataBindTokenId.KEY);
case IN_VALUE:
case IN_VALUE_ESCAPE:
case AFTER_VALUE:
return tokenFactory.createToken(KODataBindTokenId.VALUE);
}
return null;
}
@Override
public void release() {
//no-op
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy