001: /*******************************************************************************
002: * Copyright (c) 2003, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdi.internal;
011:
012: import com.ibm.icu.text.MessageFormat;
013: import java.util.ArrayList;
014: import java.util.List;
015:
016: import com.sun.jdi.AbsentInformationException;
017:
018: /**
019: *
020: */
021: public class SourceDebugExtensionParser {
022:
023: private static class Lexer {
024:
025: static final int UNKNOWN = 0;
026: static final int SMAP = 1;
027: static final int NON_ASTERISK_STRING = 2;
028: static final int NUMBER = 3;
029: static final int CR = 4;
030: static final int ASTERISK_CHAR = 5;
031: static final int ASTERISK_C = 6;
032: static final int ASTERISK_E = 7;
033: static final int ASTERISK_F = 8;
034: static final int ASTERISK_L = 9;
035: static final int ASTERISK_O = 10;
036: static final int ASTERISK_S = 11;
037: static final int ASTERISK_V = 12;
038: static final int WHITE_SPACE = 13;
039: static final int COLON = 14;
040: static final int COMMA = 15;
041: static final int SHARP = 16;
042: static final int PLUS = 17;
043:
044: private char[] fSmap;
045: private int fPointer;
046: private char fChar;
047:
048: private char[] fLexem;
049: private int fLexemType;
050:
051: private boolean fEOF;
052:
053: public Lexer(String smap) {
054: fSmap = smap.toCharArray();
055: fLexemType = UNKNOWN;
056: fPointer = -1;
057: nextChar();
058: }
059:
060: /**
061: * Compute the next lexem.
062: *
063: * @return the type of the next lexem.
064: */
065: public int nextLexem() throws AbsentInformationException {
066: if (fEOF) {
067: throw new AbsentInformationException(
068: JDIMessages.SourceDebugExtensionParser_0);
069: }
070: startWith();
071: return fLexemType;
072: }
073:
074: private char nextChar() {
075: if (++fPointer == fSmap.length) {
076: fEOF = true;
077: return '\000';
078: }
079: fChar = fSmap[fPointer];
080: return fChar;
081: }
082:
083: private void startWith() throws AbsentInformationException {
084: switch (fChar) {
085: case '\n':
086: case '\r':
087: startWithCR();
088: break;
089: case '*':
090: startWithAsterisk();
091: break;
092: case ':':
093: fLexem = new char[] { ':' };
094: fLexemType = COLON;
095: nextChar();
096: break;
097: case ',':
098: fLexem = new char[] { ',' };
099: fLexemType = COMMA;
100: nextChar();
101: break;
102: case '#':
103: fLexem = new char[] { '#' };
104: fLexemType = SHARP;
105: nextChar();
106: break;
107: case '+':
108: fLexem = new char[] { '+' };
109: fLexemType = PLUS;
110: nextChar();
111: break;
112: default:
113: startWithOtherChar();
114: break;
115: }
116: }
117:
118: /**
119: *
120: */
121: private void startWithOtherChar() {
122: int lexemStart = fPointer;
123: consumeWhiteSpace();
124: if (fChar >= '0' && fChar <= '9') { // a number
125: number(lexemStart);
126: } else {
127: nonAsteriskString(lexemStart);
128: }
129: }
130:
131: /**
132: * @param lexemStart
133: */
134: private void nonAsteriskString(int lexemStart) {
135: while (fChar != '\n' && fChar != '\r' && !fEOF) {
136: nextChar();
137: }
138: int length = fPointer - lexemStart;
139: fLexem = new char[length];
140: System.arraycopy(fSmap, lexemStart, fLexem, 0, length);
141: if (length == 4 && fLexem[0] == 'S' && fLexem[1] == 'M'
142: && fLexem[2] == 'A' && fLexem[3] == 'P') {
143: fLexemType = SMAP;
144: } else {
145: fLexemType = NON_ASTERISK_STRING;
146: }
147: }
148:
149: /**
150: * @param lexemStart
151: */
152: private void number(int lexemStart) {
153: while (fChar >= '0' && fChar <= '9') {
154: nextChar();
155: }
156: consumeWhiteSpace();
157: fLexemType = NUMBER;
158: int length = fPointer - lexemStart;
159: fLexem = new char[length];
160: System.arraycopy(fSmap, lexemStart, fLexem, 0, length);
161: }
162:
163: /**
164: *
165: */
166: private void startWithAsterisk()
167: throws AbsentInformationException {
168: nextChar();
169: if (fEOF) {
170: throw new AbsentInformationException(
171: JDIMessages.SourceDebugExtensionParser_0);
172: }
173: switch (fChar) {
174: case 'C':
175: fLexemType = ASTERISK_C;
176: break;
177: case 'E':
178: fLexemType = ASTERISK_E;
179: break;
180: case 'F':
181: fLexemType = ASTERISK_F;
182: break;
183: case 'L':
184: fLexemType = ASTERISK_L;
185: break;
186: case 'O':
187: fLexemType = ASTERISK_O;
188: break;
189: case 'S':
190: fLexemType = ASTERISK_S;
191: break;
192: case 'V':
193: fLexemType = ASTERISK_V;
194: break;
195: default:
196: fLexemType = ASTERISK_CHAR;
197: break;
198: }
199: fLexem = new char[] { '*', fChar };
200: nextChar();
201: }
202:
203: /**
204: *
205: */
206: private void startWithCR() {
207: if (fChar == '\r') {
208: if (nextChar() == '\n') {
209: fLexem = new char[] { '\r', '\n' };
210: nextChar();
211: } else {
212: fLexem = new char[] { '\r' };
213: }
214: } else {
215: fLexem = new char[] { fChar };
216: nextChar();
217: }
218: fLexemType = CR;
219: }
220:
221: /**
222: *
223: */
224: private void consumeWhiteSpace() {
225: while (fChar == ' ' || fChar == '\t') {
226: nextChar();
227: }
228: }
229:
230: /**
231: * @return the value of the current lexem.
232: */
233: public char[] lexem() {
234: return fLexem;
235: }
236:
237: /**
238: * @return the type of the current lexem.
239: */
240: public int lexemType() {
241: return fLexemType;
242: }
243:
244: }
245:
246: /**
247: * The reference type to which this source debug extension is associated.
248: */
249: private ReferenceTypeImpl fReferenceType;
250:
251: private List fDefinedStrata;
252:
253: // parser data;
254: private ReferenceTypeImpl.Stratum fCurrentStratum;
255: private boolean fFileSectionDefinedForCurrentStratum;
256: private boolean fLineSectionDefinedForCurrentStratum;
257: private int fCurrentLineFileId;
258:
259: public static void parse(String smap,
260: ReferenceTypeImpl referenceType)
261: throws AbsentInformationException {
262: new SourceDebugExtensionParser(referenceType).parseSmap(smap);
263: }
264:
265: /**
266: * SourceDebugExtension constructor.
267: */
268: private SourceDebugExtensionParser(ReferenceTypeImpl referenceType) {
269: fReferenceType = referenceType;
270: fDefinedStrata = new ArrayList();
271: fDefinedStrata.add(VirtualMachineImpl.JAVA_STRATUM_NAME);
272: }
273:
274: /**
275: *
276: */
277: private void parseSmap(String smap)
278: throws AbsentInformationException {
279: Lexer lexer = new Lexer(smap);
280: parseHeader(lexer);
281: parseSections(lexer);
282: if (!fDefinedStrata.contains(fReferenceType.defaultStratum())) {
283: throw new AbsentInformationException(
284: JDIMessages.SourceDebugExtensionParser_2);
285: }
286: }
287:
288: /**
289: * @param lexer
290: */
291: private void parseHeader(Lexer lexer)
292: throws AbsentInformationException {
293: int lexemType = lexer.nextLexem();
294: if (lexemType != Lexer.SMAP) {
295: throw new AbsentInformationException(
296: JDIMessages.SourceDebugExtensionParser_3);
297: }
298: if (lexer.nextLexem() != Lexer.CR) {
299: throw new AbsentInformationException(
300: JDIMessages.SourceDebugExtensionParser_4);
301: }
302: if (isAsteriskLexem(lexer.nextLexem())) {
303: throw new AbsentInformationException(
304: JDIMessages.SourceDebugExtensionParser_5);
305: }
306: fReferenceType.setOutputFileName(getNonAsteriskString(lexer));
307: if (isAsteriskLexem(lexer.lexemType())) {
308: throw new AbsentInformationException(
309: JDIMessages.SourceDebugExtensionParser_6);
310: }
311: fReferenceType.setDefaultStratumId(getNonAsteriskString(lexer));
312: }
313:
314: /**
315: * @param lexer
316: */
317: private void parseSections(Lexer lexer)
318: throws AbsentInformationException {
319: while (lexer.lexemType() != Lexer.ASTERISK_E) {
320: parseStratumSection(lexer);
321: }
322: }
323:
324: /**
325: * @param lexer
326: */
327: private void parseStratumSection(Lexer lexer)
328: throws AbsentInformationException {
329: if (lexer.lexemType() != Lexer.ASTERISK_S) {
330: throw new AbsentInformationException(
331: JDIMessages.SourceDebugExtensionParser_7);
332: }
333: if (isAsteriskLexem(lexer.nextLexem())) {
334: throw new AbsentInformationException(
335: JDIMessages.SourceDebugExtensionParser_8);
336: }
337: String stratumId = getNonAsteriskString(lexer);
338: if (fDefinedStrata.contains(stratumId)) {
339: throw new AbsentInformationException(MessageFormat.format(
340: JDIMessages.SourceDebugExtensionParser_9,
341: new String[] { stratumId }));
342: }
343: fCurrentStratum = new ReferenceTypeImpl.Stratum(stratumId);
344: fFileSectionDefinedForCurrentStratum = false;
345: fLineSectionDefinedForCurrentStratum = false;
346: int lexemType = lexer.lexemType();
347: while (lexemType != Lexer.ASTERISK_E
348: && lexemType != Lexer.ASTERISK_S) {
349: switch (lexemType) {
350: case Lexer.ASTERISK_F:
351: if (fFileSectionDefinedForCurrentStratum) {
352: throw new AbsentInformationException(
353: MessageFormat
354: .format(
355: JDIMessages.SourceDebugExtensionParser_10,
356: new String[] { stratumId }));
357: }
358: parseFileSection(lexer);
359: fFileSectionDefinedForCurrentStratum = true;
360: break;
361: case Lexer.ASTERISK_L:
362: if (fLineSectionDefinedForCurrentStratum) {
363: throw new AbsentInformationException(
364: MessageFormat
365: .format(
366: JDIMessages.SourceDebugExtensionParser_11,
367: new String[] { stratumId }));
368: }
369: parseLineSection(lexer);
370: fLineSectionDefinedForCurrentStratum = true;
371: break;
372: case Lexer.ASTERISK_V:
373: parseVendorSection(lexer);
374: break;
375: case Lexer.ASTERISK_CHAR:
376: parseFutureSection(lexer);
377: break;
378: default:
379: throw new AbsentInformationException(
380: MessageFormat
381: .format(
382: JDIMessages.SourceDebugExtensionParser_12,
383: new String[] { new String(lexer
384: .lexem()) }));
385: }
386: lexemType = lexer.lexemType();
387: }
388: if (!fFileSectionDefinedForCurrentStratum) {
389: throw new AbsentInformationException(MessageFormat.format(
390: JDIMessages.SourceDebugExtensionParser_13,
391: new String[] { stratumId }));
392: }
393: if (!fLineSectionDefinedForCurrentStratum) {
394: throw new AbsentInformationException(MessageFormat.format(
395: JDIMessages.SourceDebugExtensionParser_14,
396: new String[] { stratumId }));
397: }
398: fDefinedStrata.add(stratumId);
399: fReferenceType.addStratum(fCurrentStratum);
400: }
401:
402: /**
403: * @param lexer
404: */
405: private void parseFileSection(Lexer lexer)
406: throws AbsentInformationException {
407: if (lexer.nextLexem() != Lexer.CR) {
408: throw new AbsentInformationException(MessageFormat.format(
409: JDIMessages.SourceDebugExtensionParser_12,
410: new String[] { new String(lexer.lexem()) }));
411: }
412: lexer.nextLexem();
413: while (!isAsteriskLexem(lexer.lexemType())) {
414: parseFileInfo(lexer);
415: }
416: }
417:
418: /**
419: * @param lexer
420: */
421: private void parseFileInfo(Lexer lexer)
422: throws AbsentInformationException {
423: int lexemType = lexer.lexemType();
424: if (lexemType == Lexer.NUMBER) {
425: int fileId = integerValue(lexer.lexem());
426: if (isAsteriskLexem(lexer.nextLexem())) {
427: throw new AbsentInformationException(
428: JDIMessages.SourceDebugExtensionParser_16);
429: }
430: fCurrentStratum.addFileInfo(fileId,
431: getNonAsteriskString(lexer));
432: } else if (lexemType == Lexer.PLUS) {
433: if (lexer.nextLexem() != Lexer.NUMBER) {
434: throw new AbsentInformationException(
435: JDIMessages.SourceDebugExtensionParser_17);
436: }
437: int fileId = integerValue(lexer.lexem());
438: if (isAsteriskLexem(lexer.nextLexem())) {
439: throw new AbsentInformationException(
440: JDIMessages.SourceDebugExtensionParser_16);
441: }
442: String fileName = getNonAsteriskString(lexer);
443: if (isAsteriskLexem(lexer.lexemType())) {
444: throw new AbsentInformationException(
445: JDIMessages.SourceDebugExtensionParser_19);
446: }
447: fCurrentStratum.addFileInfo(fileId, fileName,
448: getNonAsteriskString(lexer));
449: } else {
450: throw new AbsentInformationException(MessageFormat.format(
451: JDIMessages.SourceDebugExtensionParser_12,
452: new String[] { new String(lexer.lexem()) }));
453: }
454: }
455:
456: /**
457: * @param lexer
458: */
459: private void parseLineSection(Lexer lexer)
460: throws AbsentInformationException {
461: fCurrentLineFileId = 0;
462: if (lexer.nextLexem() != Lexer.CR) {
463: throw new AbsentInformationException(MessageFormat.format(
464: JDIMessages.SourceDebugExtensionParser_12,
465: new String[] { new String(lexer.lexem()) }));
466: }
467: lexer.nextLexem();
468: while (!isAsteriskLexem(lexer.lexemType())) {
469: parseLineInfo(lexer);
470: }
471: }
472:
473: /**
474: * @param lexer
475: */
476: private void parseLineInfo(Lexer lexer)
477: throws AbsentInformationException {
478: if (lexer.lexemType() != Lexer.NUMBER) {
479: throw new AbsentInformationException(
480: JDIMessages.SourceDebugExtensionParser_22);
481: }
482: int inputStartLine = integerValue(lexer.lexem());
483: int lexemType = lexer.nextLexem();
484: if (lexemType == Lexer.SHARP) {
485: if (lexer.nextLexem() != Lexer.NUMBER) {
486: throw new AbsentInformationException(
487: JDIMessages.SourceDebugExtensionParser_23);
488: }
489: fCurrentLineFileId = integerValue(lexer.lexem());
490: lexemType = lexer.nextLexem();
491: }
492: int repeatCount;
493: if (lexemType == Lexer.COMMA) {
494: if (lexer.nextLexem() != Lexer.NUMBER) {
495: throw new AbsentInformationException(
496: JDIMessages.SourceDebugExtensionParser_24);
497: }
498: repeatCount = integerValue(lexer.lexem());
499: lexemType = lexer.nextLexem();
500: } else {
501: repeatCount = 1;
502: }
503: if (lexemType != Lexer.COLON) {
504: throw new AbsentInformationException(
505: JDIMessages.SourceDebugExtensionParser_25);
506: }
507: if (lexer.nextLexem() != Lexer.NUMBER) {
508: throw new AbsentInformationException(
509: JDIMessages.SourceDebugExtensionParser_26);
510: }
511: int outputStartLine = integerValue(lexer.lexem());
512: lexemType = lexer.nextLexem();
513: int outputLineIncrement;
514: if (lexemType == Lexer.COMMA) {
515: if (lexer.nextLexem() != Lexer.NUMBER) {
516: throw new AbsentInformationException(
517: JDIMessages.SourceDebugExtensionParser_27);
518: }
519: outputLineIncrement = integerValue(lexer.lexem());
520: lexemType = lexer.nextLexem();
521: } else {
522: outputLineIncrement = 1;
523: }
524: if (lexemType != Lexer.CR) {
525: throw new AbsentInformationException(
526: JDIMessages.SourceDebugExtensionParser_28);
527: }
528: lexer.nextLexem();
529: fCurrentStratum.addLineInfo(inputStartLine, fCurrentLineFileId,
530: repeatCount, outputStartLine, outputLineIncrement);
531: }
532:
533: /**
534: * @param lexer
535: */
536: private void parseVendorSection(Lexer lexer)
537: throws AbsentInformationException {
538: if (lexer.nextLexem() != Lexer.CR) {
539: throw new AbsentInformationException(MessageFormat.format(
540: JDIMessages.SourceDebugExtensionParser_12,
541: new String[] { new String(lexer.lexem()) }));
542: }
543: lexer.nextLexem();
544: while (!isAsteriskLexem(lexer.lexemType())) {
545: // do nothing in this case, just consume the lexems.
546: getNonAsteriskString(lexer);
547: }
548: }
549:
550: /**
551: * @param lexer
552: */
553: private void parseFutureSection(Lexer lexer)
554: throws AbsentInformationException {
555: if (lexer.nextLexem() != Lexer.CR) {
556: throw new AbsentInformationException(MessageFormat.format(
557: JDIMessages.SourceDebugExtensionParser_12,
558: new String[] { new String(lexer.lexem()) }));
559: }
560: lexer.nextLexem();
561: while (!isAsteriskLexem(lexer.lexemType())) {
562: // do nothing in this case, just consume the lexems.
563: getNonAsteriskString(lexer);
564: }
565: }
566:
567: private String getNonAsteriskString(Lexer lexer)
568: throws AbsentInformationException {
569: StringBuffer string = new StringBuffer();
570: int lexemType = lexer.lexemType();
571: while (lexemType != Lexer.CR) {
572: string.append(lexer.lexem());
573: lexemType = lexer.nextLexem();
574: }
575: lexer.nextLexem();
576: // remove the leading white spaces
577: int i = -1, length = string.length();
578: char c;
579: while (++i < length
580: && ((c = string.charAt(i)) == ' ' || c == '\t'))
581: ;
582: return string.delete(0, i).toString();
583: }
584:
585: private int integerValue(char[] lexem) {
586: int i = 0;
587: char c = lexem[0];
588: while (c == ' ' || c == '\t') {
589: c = lexem[++i];
590: }
591: int value = 0;
592: while (c >= '0' && c <= '9') {
593: value = value * 10 + c - '0';
594: if (++i == lexem.length) {
595: break;
596: }
597: c = lexem[i];
598: }
599: return value;
600: }
601:
602: private boolean isAsteriskLexem(int lexemType) {
603: switch (lexemType) {
604: case Lexer.ASTERISK_C:
605: case Lexer.ASTERISK_E:
606: case Lexer.ASTERISK_F:
607: case Lexer.ASTERISK_L:
608: case Lexer.ASTERISK_O:
609: case Lexer.ASTERISK_S:
610: case Lexer.ASTERISK_V:
611: case Lexer.ASTERISK_CHAR:
612: return true;
613: default:
614: return false;
615: }
616: }
617:
618: }
|