/******************************************************************************* * Copyright (c) 2003, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdi.internal; import java.util.ArrayList; import java.util.List; import org.eclipse.osgi.util.NLS; import com.sun.jdi.AbsentInformationException; /** * */ public class SourceDebugExtensionParser { private static class Lexer { static final int UNKNOWN = 0; static final int SMAP = 1; static final int NON_ASTERISK_STRING = 2; static final int NUMBER = 3; static final int CR = 4; static final int ASTERISK_CHAR = 5; static final int ASTERISK_C = 6; static final int ASTERISK_E = 7; static final int ASTERISK_F = 8; static final int ASTERISK_L = 9; static final int ASTERISK_O = 10; static final int ASTERISK_S = 11; static final int ASTERISK_V = 12; // never used // static final int WHITE_SPACE= 13; static final int COLON = 14; static final int COMMA = 15; static final int SHARP = 16; static final int PLUS = 17; private char[] fSmap; private int fPointer; private char fChar; private char[] fLexem; private int fLexemType; private boolean fEOF; public Lexer(String smap) { fSmap = smap.toCharArray(); fLexemType = UNKNOWN; fPointer = -1; nextChar(); } /** * Compute the next lexem. * * @return the type of the next lexem. */ public int nextLexem() throws AbsentInformationException { if (fEOF) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_0); } startWith(); return fLexemType; } private char nextChar() { if (++fPointer == fSmap.length) { fEOF = true; return '\000'; } fChar = fSmap[fPointer]; return fChar; } private void startWith() throws AbsentInformationException { switch (fChar) { case '\n': case '\r': startWithCR(); break; case '*': startWithAsterisk(); break; case ':': fLexem = new char[] { ':' }; fLexemType = COLON; nextChar(); break; case ',': fLexem = new char[] { ',' }; fLexemType = COMMA; nextChar(); break; case '#': fLexem = new char[] { '#' }; fLexemType = SHARP; nextChar(); break; case '+': fLexem = new char[] { '+' }; fLexemType = PLUS; nextChar(); break; default: startWithOtherChar(); break; } } /** * */ private void startWithOtherChar() { int lexemStart = fPointer; consumeWhiteSpace(); if (fChar >= '0' && fChar <= '9') { // a number number(lexemStart); } else { nonAsteriskString(lexemStart); } } /** * @param lexemStart */ private void nonAsteriskString(int lexemStart) { while (fChar != '\n' && fChar != '\r' && !fEOF) { nextChar(); } int length = fPointer - lexemStart; fLexem = new char[length]; System.arraycopy(fSmap, lexemStart, fLexem, 0, length); if (length == 4 && fLexem[0] == 'S' && fLexem[1] == 'M' && fLexem[2] == 'A' && fLexem[3] == 'P') { fLexemType = SMAP; } else { fLexemType = NON_ASTERISK_STRING; } } /** * @param lexemStart */ private void number(int lexemStart) { while (fChar >= '0' && fChar <= '9') { nextChar(); } consumeWhiteSpace(); fLexemType = NUMBER; int length = fPointer - lexemStart; fLexem = new char[length]; System.arraycopy(fSmap, lexemStart, fLexem, 0, length); } /** * */ private void startWithAsterisk() throws AbsentInformationException { nextChar(); if (fEOF) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_0); } switch (fChar) { case 'C': fLexemType = ASTERISK_C; break; case 'E': fLexemType = ASTERISK_E; break; case 'F': fLexemType = ASTERISK_F; break; case 'L': fLexemType = ASTERISK_L; break; case 'O': fLexemType = ASTERISK_O; break; case 'S': fLexemType = ASTERISK_S; break; case 'V': fLexemType = ASTERISK_V; break; default: fLexemType = ASTERISK_CHAR; break; } fLexem = new char[] { '*', fChar }; nextChar(); } /** * */ private void startWithCR() { if (fChar == '\r') { if (nextChar() == '\n') { fLexem = new char[] { '\r', '\n' }; nextChar(); } else { fLexem = new char[] { '\r' }; } } else { fLexem = new char[] { fChar }; nextChar(); } fLexemType = CR; } /** * */ private void consumeWhiteSpace() { while (fChar == ' ' || fChar == '\t') { nextChar(); } } /** * @return the value of the current lexem. */ public char[] lexem() { return fLexem; } /** * @return the type of the current lexem. */ public int lexemType() { return fLexemType; } } /** * The reference type to which this source debug extension is associated. */ private ReferenceTypeImpl fReferenceType; private List<String> fDefinedStrata; // parser data; private ReferenceTypeImpl.Stratum fCurrentStratum; private boolean fFileSectionDefinedForCurrentStratum; private boolean fLineSectionDefinedForCurrentStratum; private int fCurrentLineFileId; public static void parse(String smap, ReferenceTypeImpl referenceType) throws AbsentInformationException { new SourceDebugExtensionParser(referenceType).parseSmap(smap); } /** * SourceDebugExtension constructor. */ private SourceDebugExtensionParser(ReferenceTypeImpl referenceType) { fReferenceType = referenceType; fDefinedStrata = new ArrayList<>(); fDefinedStrata.add(VirtualMachineImpl.JAVA_STRATUM_NAME); } /** * */ private void parseSmap(String smap) throws AbsentInformationException { Lexer lexer = new Lexer(smap); parseHeader(lexer); parseSections(lexer); if (!fDefinedStrata.contains(fReferenceType.defaultStratum())) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_2); } } /** * @param lexer */ private void parseHeader(Lexer lexer) throws AbsentInformationException { int lexemType = lexer.nextLexem(); if (lexemType != Lexer.SMAP) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_3); } if (lexer.nextLexem() != Lexer.CR) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_4); } if (isAsteriskLexem(lexer.nextLexem())) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_5); } fReferenceType.setOutputFileName(getNonAsteriskString(lexer)); if (isAsteriskLexem(lexer.lexemType())) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_6); } fReferenceType.setDefaultStratumId(getNonAsteriskString(lexer)); } /** * @param lexer */ private void parseSections(Lexer lexer) throws AbsentInformationException { while (lexer.lexemType() != Lexer.ASTERISK_E) { parseStratumSection(lexer); } } /** * @param lexer */ private void parseStratumSection(Lexer lexer) throws AbsentInformationException { if (lexer.lexemType() != Lexer.ASTERISK_S) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_7); } if (isAsteriskLexem(lexer.nextLexem())) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_8); } String stratumId = getNonAsteriskString(lexer); if (fDefinedStrata.contains(stratumId)) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_9, new String[] { stratumId })); } fCurrentStratum = new ReferenceTypeImpl.Stratum(stratumId); fFileSectionDefinedForCurrentStratum = false; fLineSectionDefinedForCurrentStratum = false; int lexemType = lexer.lexemType(); while (lexemType != Lexer.ASTERISK_E && lexemType != Lexer.ASTERISK_S) { switch (lexemType) { case Lexer.ASTERISK_F: if (fFileSectionDefinedForCurrentStratum) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_10, new String[] { stratumId })); } parseFileSection(lexer); fFileSectionDefinedForCurrentStratum = true; break; case Lexer.ASTERISK_L: if (fLineSectionDefinedForCurrentStratum) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_11, new String[] { stratumId })); } parseLineSection(lexer); fLineSectionDefinedForCurrentStratum = true; break; case Lexer.ASTERISK_V: parseVendorSection(lexer); break; case Lexer.ASTERISK_CHAR: parseFutureSection(lexer); break; default: throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_12, new String[] { new String(lexer.lexem()) })); } lexemType = lexer.lexemType(); } if (!fFileSectionDefinedForCurrentStratum) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_13, new String[] { stratumId })); } if (!fLineSectionDefinedForCurrentStratum) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_14, new String[] { stratumId })); } fDefinedStrata.add(stratumId); fReferenceType.addStratum(fCurrentStratum); } /** * @param lexer */ private void parseFileSection(Lexer lexer) throws AbsentInformationException { if (lexer.nextLexem() != Lexer.CR) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_12, new String[] { new String(lexer.lexem()) })); } lexer.nextLexem(); while (!isAsteriskLexem(lexer.lexemType())) { parseFileInfo(lexer); } } /** * @param lexer */ private void parseFileInfo(Lexer lexer) throws AbsentInformationException { int lexemType = lexer.lexemType(); if (lexemType == Lexer.NUMBER) { int fileId = integerValue(lexer.lexem()); if (isAsteriskLexem(lexer.nextLexem())) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_16); } fCurrentStratum.addFileInfo(fileId, getNonAsteriskString(lexer)); } else if (lexemType == Lexer.PLUS) { if (lexer.nextLexem() != Lexer.NUMBER) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_17); } int fileId = integerValue(lexer.lexem()); if (isAsteriskLexem(lexer.nextLexem())) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_16); } String fileName = getNonAsteriskString(lexer); if (isAsteriskLexem(lexer.lexemType())) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_19); } fCurrentStratum.addFileInfo(fileId, fileName, getNonAsteriskString(lexer)); } else { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_12, new String[] { new String(lexer.lexem()) })); } } /** * @param lexer */ private void parseLineSection(Lexer lexer) throws AbsentInformationException { fCurrentLineFileId = 0; if (lexer.nextLexem() != Lexer.CR) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_12, new String[] { new String(lexer.lexem()) })); } lexer.nextLexem(); while (!isAsteriskLexem(lexer.lexemType())) { parseLineInfo(lexer); } } /** * @param lexer */ private void parseLineInfo(Lexer lexer) throws AbsentInformationException { if (lexer.lexemType() != Lexer.NUMBER) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_22); } int inputStartLine = integerValue(lexer.lexem()); int lexemType = lexer.nextLexem(); if (lexemType == Lexer.SHARP) { if (lexer.nextLexem() != Lexer.NUMBER) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_23); } fCurrentLineFileId = integerValue(lexer.lexem()); lexemType = lexer.nextLexem(); } int repeatCount; if (lexemType == Lexer.COMMA) { if (lexer.nextLexem() != Lexer.NUMBER) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_24); } repeatCount = integerValue(lexer.lexem()); lexemType = lexer.nextLexem(); } else { repeatCount = 1; } if (lexemType != Lexer.COLON) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_25); } if (lexer.nextLexem() != Lexer.NUMBER) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_26); } int outputStartLine = integerValue(lexer.lexem()); lexemType = lexer.nextLexem(); int outputLineIncrement; if (lexemType == Lexer.COMMA) { if (lexer.nextLexem() != Lexer.NUMBER) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_27); } outputLineIncrement = integerValue(lexer.lexem()); lexemType = lexer.nextLexem(); } else { outputLineIncrement = 1; } if (lexemType != Lexer.CR) { throw new AbsentInformationException( JDIMessages.SourceDebugExtensionParser_28); } lexer.nextLexem(); fCurrentStratum.addLineInfo(inputStartLine, fCurrentLineFileId, repeatCount, outputStartLine, outputLineIncrement); } /** * @param lexer */ private void parseVendorSection(Lexer lexer) throws AbsentInformationException { if (lexer.nextLexem() != Lexer.CR) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_12, new String[] { new String(lexer.lexem()) })); } lexer.nextLexem(); while (!isAsteriskLexem(lexer.lexemType())) { // do nothing in this case, just consume the lexems. getNonAsteriskString(lexer); } } /** * @param lexer */ private void parseFutureSection(Lexer lexer) throws AbsentInformationException { if (lexer.nextLexem() != Lexer.CR) { throw new AbsentInformationException(NLS.bind( JDIMessages.SourceDebugExtensionParser_12, new String[] { new String(lexer.lexem()) })); } lexer.nextLexem(); while (!isAsteriskLexem(lexer.lexemType())) { // do nothing in this case, just consume the lexems. getNonAsteriskString(lexer); } } private String getNonAsteriskString(Lexer lexer) throws AbsentInformationException { StringBuffer string = new StringBuffer(); int lexemType = lexer.lexemType(); while (lexemType != Lexer.CR) { string.append(lexer.lexem()); lexemType = lexer.nextLexem(); } lexer.nextLexem(); // remove the leading white spaces int i = -1, length = string.length(); char c; while (++i < length && ((c = string.charAt(i)) == ' ' || c == '\t')) { //continue until we chew up all the whitespace or hit the end of the line } return string.delete(0, i).toString(); } private int integerValue(char[] lexem) { int i = 0; char c = lexem[0]; while (c == ' ' || c == '\t') { c = lexem[++i]; } int value = 0; while (c >= '0' && c <= '9') { value = value * 10 + c - '0'; if (++i == lexem.length) { break; } c = lexem[i]; } return value; } private boolean isAsteriskLexem(int lexemType) { switch (lexemType) { case Lexer.ASTERISK_C: case Lexer.ASTERISK_E: case Lexer.ASTERISK_F: case Lexer.ASTERISK_L: case Lexer.ASTERISK_O: case Lexer.ASTERISK_S: case Lexer.ASTERISK_V: case Lexer.ASTERISK_CHAR: return true; default: return false; } } }