| /******************************************************************************* |
| * 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<String>(); |
| 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; |
| } |
| } |
| |
| } |