/* * Copyright (c) 2007-2009, Osmorc Development Team * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * 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. * * Neither the name of 'Osmorc Development Team' 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. */ package org.osmorc.manifest.lang; import com.intellij.lexer.LexerBase; import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.Map; /** * @author Robert F. Beeger (robert@beeger.net) */ public class ManifestLexer extends LexerBase { public ManifestLexer() { } public void start(CharSequence buffer, int startOffset, int endOffset, int initialState) { this.buffer = buffer; this.endOffset = endOffset; currentState = initialState; tokenStart = startOffset; parseNextToken(); } public void advance() { tokenStart = tokenEnd; parseNextToken(); } public int getState() { return currentState; } @Nullable public IElementType getTokenType() { return tokenType; } public int getTokenStart() { return tokenStart; } public int getTokenEnd() { return tokenEnd; } public int getBufferEnd() { return endOffset; } public CharSequence getBufferSequence() { return buffer; } private void parseNextToken() { if (tokenStart < endOffset) { if (isNewline(tokenStart)) { tokenType = isLineStart(tokenStart) ? ManifestTokenType.SECTION_END : ManifestTokenType.NEWLINE; tokenEnd = tokenStart + 1; currentState = INITIAL_STATE; } else if (currentState == WAITING_FOR_HEADER_ASSIGNMENT_STATE || currentState == WAITING_FOR_HEADER_ASSIGNMENT_AFTER_BAD_CHARACTER_STATE) { if (isColon(tokenStart)) { tokenType = ManifestTokenType.COLON; currentState = WAITING_FOR_SPACE_AFTER_HEADER_NAME_STATE; } else { tokenType = TokenType.BAD_CHARACTER; currentState = WAITING_FOR_HEADER_ASSIGNMENT_AFTER_BAD_CHARACTER_STATE; } tokenEnd = tokenStart + 1; } else if (currentState == WAITING_FOR_SPACE_AFTER_HEADER_NAME_STATE) { if (isSpace(tokenStart)) { tokenType = ManifestTokenType.SIGNIFICANT_SPACE; } else { tokenType = TokenType.BAD_CHARACTER; } currentState = INITIAL_STATE; tokenEnd = tokenStart + 1; } else if (isHeaderStart(tokenStart)) { if (isAlphaNum(tokenStart)) { tokenEnd = tokenStart + 1; while (tokenEnd < endOffset && isHeaderChar(tokenEnd)) { tokenEnd++; } } tokenType = ManifestTokenType.HEADER_NAME; currentState = WAITING_FOR_HEADER_ASSIGNMENT_STATE; } else if (isContinuationStart(tokenStart)) { tokenType = ManifestTokenType.SIGNIFICANT_SPACE; tokenEnd = tokenStart + 1; currentState = INITIAL_STATE; } else if (isSpecialCharacter(tokenStart)) { tokenType = getTokenTypeForSpecialCharacter(tokenStart); tokenEnd = tokenStart + 1; currentState = INITIAL_STATE; } else { tokenEnd = tokenStart; while (tokenEnd < endOffset && !isSpecialCharacter(tokenEnd) && !isNewline(tokenEnd)) { tokenEnd++; } tokenType = ManifestTokenType.HEADER_VALUE_PART; } } else { tokenType = null; tokenEnd = tokenStart; } } private boolean isNewline(int position) { return '\n' == buffer.charAt(position); } private boolean isHeaderStart(int position) { return isLineStart(position) && !Character.isWhitespace(buffer.charAt(position)); } private boolean isAlphaNum(int position) { return Character.isLetterOrDigit(buffer.charAt(position)); } private boolean isHeaderChar(int position) { return isAlphaNum(position) || buffer.charAt(position) == '-' || buffer.charAt(position) == '_'; } private boolean isContinuationStart(int position) { return isLineStart(position) && !isHeaderStart(position); } private boolean isLineStart(int position) { return (position == 0 || isNewline(position - 1)); } private boolean isSpace(int position) { return buffer.charAt(position) == ' '; } private boolean isColon(int position) { return buffer.charAt(position) == ':'; } private boolean isSpecialCharacter(int position) { return SPECIAL_CHARACTERS_TOKEN_MAPPING.get(buffer.charAt(position)) != null; } private IElementType getTokenTypeForSpecialCharacter(int position) { return SPECIAL_CHARACTERS_TOKEN_MAPPING.get(buffer.charAt(position)); } private CharSequence buffer; private int endOffset; private int tokenStart; private int tokenEnd; private int currentState; private IElementType tokenType; private static final int INITIAL_STATE = 0; private static final int WAITING_FOR_HEADER_ASSIGNMENT_STATE = 1; private static final int WAITING_FOR_HEADER_ASSIGNMENT_AFTER_BAD_CHARACTER_STATE = 2; private static final int WAITING_FOR_SPACE_AFTER_HEADER_NAME_STATE = 3; private static final Map<Character, IElementType> SPECIAL_CHARACTERS_TOKEN_MAPPING; static { SPECIAL_CHARACTERS_TOKEN_MAPPING = new HashMap<Character, IElementType>(); SPECIAL_CHARACTERS_TOKEN_MAPPING.put(':', ManifestTokenType.COLON); SPECIAL_CHARACTERS_TOKEN_MAPPING.put(';', ManifestTokenType.SEMICOLON); SPECIAL_CHARACTERS_TOKEN_MAPPING.put(',', ManifestTokenType.COMMA); SPECIAL_CHARACTERS_TOKEN_MAPPING.put('=', ManifestTokenType.EQUALS); SPECIAL_CHARACTERS_TOKEN_MAPPING.put('\"', ManifestTokenType.QUOTE); } }