| /* |
| * 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.css.lib; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.antlr.runtime.*; |
| import org.netbeans.modules.css.lib.api.ProblemDescription; |
| import org.netbeans.modules.css.lib.api.ProblemDescription.Type; |
| import org.openide.util.NbBundle; |
| import static org.netbeans.modules.css.lib.Bundle.*; |
| |
| /** |
| * Note: Funny aspect of the ANTLR lexer is that it doesn't create any kind of |
| * error tokens. So if there's a character in the input which cannot be properly |
| * made a part of a token it is simply skipped. The result is that the sequence |
| * of tokens is not continuous and there might be "holes". |
| * |
| * @author marekfukala |
| */ |
| public class ExtCss3Lexer extends Css3Lexer { |
| |
| private List<ProblemDescription> problems = new ArrayList<>(); |
| static boolean isLessSource_unit_tests = false; |
| static boolean isScssSource_unit_tests = false; |
| |
| private boolean isLessSource = isLessSource_unit_tests; |
| private boolean isScssSource = isScssSource_unit_tests; |
| |
| public ExtCss3Lexer(CharStream input, RecognizerSharedState state) { |
| super(input, state); |
| } |
| |
| public ExtCss3Lexer(CharStream input) { |
| super(input); |
| } |
| |
| public ExtCss3Lexer(CharSequence charSequence, String mimeType) { |
| this(charSequence); |
| if(mimeType != null) { |
| this.isLessSource = mimeType.equals("text/less"); |
| this.isScssSource = mimeType.equals("text/scss"); |
| } |
| } |
| |
| /** |
| * Preferred constructor. |
| * |
| * Make the Css3Lexer case insensitive by default - the characters passed to |
| * the lexer are converted to upper case. The token images are in the |
| * original case though. |
| */ |
| public ExtCss3Lexer(CharSequence charSequence) { |
| |
| this(new ANTLRStringStream(charSequence.toString()) { |
| @Override |
| public int LA(int i) { |
| if (i == 0) { |
| return 0; // undefined |
| } |
| if (i < 0) { |
| i++; // e.g., translate LA(-1) to use offset 0 |
| } |
| |
| if ((p + i - 1) >= n) { |
| |
| return CharStream.EOF; |
| } |
| return Character.toUpperCase(data[p + i - 1]); |
| } |
| }); |
| |
| } |
| |
| @Override |
| //overridden since we need to produce error tokens for unrecognized input, |
| //by default such content is only skipped and the resulting token sequence |
| //contains "holes". |
| // |
| //this way of solving the proble seems to be the official one: |
| //http://www.antlr.org/wiki/pages/viewpage.action?pageId=5341230 |
| public Token nextToken() { |
| while (true) { |
| state.token = null; |
| state.channel = Token.DEFAULT_CHANNEL; |
| state.tokenStartCharIndex = input.index(); |
| state.tokenStartCharPositionInLine = input.getCharPositionInLine(); |
| state.tokenStartLine = input.getLine(); |
| state.text = null; |
| if (input.LA(1) == CharStream.EOF) { |
| Token eof = new CommonToken(input, Token.EOF, |
| Token.DEFAULT_CHANNEL, |
| input.index(), input.index()); |
| eof.setLine(getLine()); |
| eof.setCharPositionInLine(getCharPositionInLine()); |
| return eof; |
| } |
| try { |
| mTokens(); |
| if (state.token == null) { |
| emit(); |
| } else if (state.token == Token.SKIP_TOKEN) { |
| continue; |
| } |
| return state.token; |
| } catch (RecognitionException re) { |
| reportError(re); |
| if (re instanceof NoViableAltException) { |
| recover(re); |
| } |
| // create token that holds mismatched char |
| Token t = new CommonToken(input, Token.INVALID_TOKEN_TYPE, |
| Token.DEFAULT_CHANNEL, |
| state.tokenStartCharIndex, |
| getCharIndex() - 1); |
| t.setLine(state.tokenStartLine); |
| t.setCharPositionInLine(state.tokenStartCharPositionInLine); |
| emit(t); |
| return state.token; |
| } |
| } |
| } |
| |
| @Override |
| @NbBundle.Messages({ |
| "# {0} - the unexpected character", |
| "MSG_Error_Unexpected_Char=Unexpected character(s) {0} found" |
| }) |
| public void displayRecognitionError(String[] tokenNames, RecognitionException e) { |
| StringBuilder b = new StringBuilder(); |
| b.append(getErrorHeader(e)); |
| b.append(' '); |
| if (e instanceof NoViableAltException) { |
| //lexing error - unexpected character in the char stream |
| char unexpectedChar = (char) input.LA(1); |
| b.append(MSG_Error_Unexpected_Char(unexpectedChar)); |
| ProblemDescription pp = new ProblemDescription(e.input.index(), e.input.index() + 1, b.toString(), ProblemDescription.Keys.LEXING.name(), Type.ERROR); |
| problems.add(pp); |
| } else { |
| b.append(getErrorHeader(e)); |
| b.append(getErrorMessage(e, tokenNames)); |
| } |
| |
| } |
| |
| public List<ProblemDescription> getProblems() { |
| return problems; |
| } |
| |
| @Override |
| protected boolean isLessSource() { |
| return isLessSource; |
| } |
| |
| @Override |
| protected boolean isScssSource() { |
| return isScssSource; |
| } |
| } |