1/*
2 * [The "BSD license"]
3 *  Copyright (c) 2010 Terence Parr
4 *  All rights reserved.
5 *
6 *  Redistribution and use in source and binary forms, with or without
7 *  modification, are permitted provided that the following conditions
8 *  are met:
9 *  1. Redistributions of source code must retain the above copyright
10 *      notice, this list of conditions and the following disclaimer.
11 *  2. Redistributions in binary form must reproduce the above copyright
12 *      notice, this list of conditions and the following disclaimer in the
13 *      documentation and/or other materials provided with the distribution.
14 *  3. The name of the author may not be used to endorse or promote products
15 *      derived from this software without specific prior written permission.
16 *
17 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28package org.antlr.tool;
29
30import org.antlr.Tool;
31import org.antlr.analysis.DFAState;
32import org.antlr.analysis.DecisionProbe;
33import org.antlr.misc.BitSet;
34import org.antlr.runtime.RecognitionException;
35import org.antlr.runtime.Token;
36import org.stringtemplate.v4.ST;
37import org.stringtemplate.v4.STErrorListener;
38import org.stringtemplate.v4.STGroup;
39import org.stringtemplate.v4.STGroupFile;
40import org.stringtemplate.v4.misc.STMessage;
41
42import java.io.BufferedReader;
43import java.io.IOException;
44import java.io.InputStream;
45import java.io.InputStreamReader;
46import java.lang.reflect.Field;
47import java.lang.reflect.InvocationTargetException;
48import java.util.*;
49
50/** Defines all the errors ANTLR can generator for both the tool and for
51 *  issues with a grammar.
52 *
53 *  Here is a list of language names:
54 *
55 *  http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt
56 *
57 *  Here is a list of country names:
58 *
59 *  http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
60 *
61 *  I use constants not strings to identify messages as the compiler will
62 *  find any errors/mismatches rather than leaving a mistyped string in
63 *  the code to be found randomly in the future.  Further, Intellij can
64 *  do field name expansion to save me some typing.  I have to map
65 *  int constants to template names, however, which could introduce a mismatch.
66 *  Someone could provide a .stg file that had a template name wrong.  When
67 *  I load the group, then, I must verify that all messages are there.
68 *
69 *  This is essentially the functionality of the resource bundle stuff Java
70 *  has, but I don't want to load a property file--I want to load a template
71 *  group file and this is so simple, why mess with their junk.
72 *
73 *  I use the default Locale as defined by java to compute a group file name
74 *  in the org/antlr/tool/templates/messages dir called en_US.stg and so on.
75 *
76 *  Normally we want to use the default locale, but often a message file will
77 *  not exist for it so we must fall back on the US local.
78 *
79 *  During initialization of this class, all errors go straight to System.err.
80 *  There is no way around this.  If I have not set up the error system, how
81 *  can I do errors properly?  For example, if the string template group file
82 *  full of messages has an error, how could I print to anything but System.err?
83 *
84 *  TODO: how to map locale to a file encoding for the stringtemplate group file?
85 *  ST knows how to pay attention to the default encoding so it
86 *  should probably just work unless a GUI sets the local to some chinese
87 *  variation but System.getProperty("file.encoding") is US.  Hmm...
88 *
89 *  TODO: get antlr.g etc.. parsing errors to come here.
90 */
91public class ErrorManager {
92	// TOOL ERRORS
93	// file errors
94	public static final int MSG_CANNOT_WRITE_FILE = 1;
95	public static final int MSG_CANNOT_CLOSE_FILE = 2;
96	public static final int MSG_CANNOT_FIND_TOKENS_FILE = 3;
97	public static final int MSG_ERROR_READING_TOKENS_FILE = 4;
98	public static final int MSG_DIR_NOT_FOUND = 5;
99	public static final int MSG_OUTPUT_DIR_IS_FILE = 6;
100	public static final int MSG_CANNOT_OPEN_FILE = 7;
101	public static final int MSG_FILE_AND_GRAMMAR_NAME_DIFFER = 8;
102	public static final int MSG_FILENAME_EXTENSION_ERROR = 9;
103
104	public static final int MSG_INTERNAL_ERROR = 10;
105	public static final int MSG_INTERNAL_WARNING = 11;
106	public static final int MSG_ERROR_CREATING_ARTIFICIAL_RULE = 12;
107	public static final int MSG_TOKENS_FILE_SYNTAX_ERROR = 13;
108	public static final int MSG_CANNOT_GEN_DOT_FILE = 14;
109	public static final int MSG_BAD_AST_STRUCTURE = 15;
110	public static final int MSG_BAD_ACTION_AST_STRUCTURE = 16;
111
112	// code gen errors
113	public static final int MSG_MISSING_CODE_GEN_TEMPLATES = 20;
114	public static final int MSG_MISSING_CYCLIC_DFA_CODE_GEN_TEMPLATES = 21;
115	public static final int MSG_CODE_GEN_TEMPLATES_INCOMPLETE = 22;
116	public static final int MSG_CANNOT_CREATE_TARGET_GENERATOR = 23;
117	//public static final int MSG_CANNOT_COMPUTE_SAMPLE_INPUT_SEQ = 24;
118
119	// GRAMMAR ERRORS
120	public static final int MSG_SYNTAX_ERROR = 100;
121	public static final int MSG_RULE_REDEFINITION = 101;
122	public static final int MSG_LEXER_RULES_NOT_ALLOWED = 102;
123	public static final int MSG_PARSER_RULES_NOT_ALLOWED = 103;
124	public static final int MSG_CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL = 104;
125	public static final int MSG_NO_TOKEN_DEFINITION = 105;
126	public static final int MSG_UNDEFINED_RULE_REF = 106;
127	public static final int MSG_LITERAL_NOT_ASSOCIATED_WITH_LEXER_RULE = 107;
128	public static final int MSG_CANNOT_ALIAS_TOKENS_IN_LEXER = 108;
129	public static final int MSG_ATTRIBUTE_REF_NOT_IN_RULE = 111;
130	public static final int MSG_INVALID_RULE_SCOPE_ATTRIBUTE_REF = 112;
131	public static final int MSG_UNKNOWN_ATTRIBUTE_IN_SCOPE = 113;
132	public static final int MSG_UNKNOWN_SIMPLE_ATTRIBUTE = 114;
133	public static final int MSG_INVALID_RULE_PARAMETER_REF = 115;
134	public static final int MSG_UNKNOWN_RULE_ATTRIBUTE = 116;
135	public static final int MSG_ISOLATED_RULE_SCOPE = 117;
136	public static final int MSG_SYMBOL_CONFLICTS_WITH_GLOBAL_SCOPE = 118;
137	public static final int MSG_LABEL_CONFLICTS_WITH_RULE = 119;
138	public static final int MSG_LABEL_CONFLICTS_WITH_TOKEN = 120;
139	public static final int MSG_LABEL_CONFLICTS_WITH_RULE_SCOPE_ATTRIBUTE = 121;
140	public static final int MSG_LABEL_CONFLICTS_WITH_RULE_ARG_RETVAL = 122;
141	public static final int MSG_ATTRIBUTE_CONFLICTS_WITH_RULE = 123;
142	public static final int MSG_ATTRIBUTE_CONFLICTS_WITH_RULE_ARG_RETVAL = 124;
143	public static final int MSG_LABEL_TYPE_CONFLICT = 125;
144	public static final int MSG_ARG_RETVAL_CONFLICT = 126;
145	public static final int MSG_NONUNIQUE_REF = 127;
146	public static final int MSG_FORWARD_ELEMENT_REF = 128;
147	public static final int MSG_MISSING_RULE_ARGS = 129;
148	public static final int MSG_RULE_HAS_NO_ARGS = 130;
149	public static final int MSG_ARGS_ON_TOKEN_REF = 131;
150	public static final int MSG_RULE_REF_AMBIG_WITH_RULE_IN_ALT = 132;
151	public static final int MSG_ILLEGAL_OPTION = 133;
152	public static final int MSG_LIST_LABEL_INVALID_UNLESS_RETVAL_STRUCT = 134;
153	public static final int MSG_UNDEFINED_TOKEN_REF_IN_REWRITE = 135;
154	public static final int MSG_REWRITE_ELEMENT_NOT_PRESENT_ON_LHS = 136;
155	public static final int MSG_UNDEFINED_LABEL_REF_IN_REWRITE = 137;
156	public static final int MSG_NO_GRAMMAR_START_RULE = 138;
157	public static final int MSG_EMPTY_COMPLEMENT = 139;
158	public static final int MSG_UNKNOWN_DYNAMIC_SCOPE = 140;
159	public static final int MSG_UNKNOWN_DYNAMIC_SCOPE_ATTRIBUTE = 141;
160	public static final int MSG_ISOLATED_RULE_ATTRIBUTE = 142;
161	public static final int MSG_INVALID_ACTION_SCOPE = 143;
162	public static final int MSG_ACTION_REDEFINITION = 144;
163	public static final int MSG_DOUBLE_QUOTES_ILLEGAL = 145;
164	public static final int MSG_INVALID_TEMPLATE_ACTION = 146;
165	public static final int MSG_MISSING_ATTRIBUTE_NAME = 147;
166	public static final int MSG_ARG_INIT_VALUES_ILLEGAL = 148;
167	public static final int MSG_REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION = 149;
168	public static final int MSG_NO_RULES = 150;
169	public static final int MSG_WRITE_TO_READONLY_ATTR = 151;
170	public static final int MSG_MISSING_AST_TYPE_IN_TREE_GRAMMAR = 152;
171	public static final int MSG_REWRITE_FOR_MULTI_ELEMENT_ALT = 153;
172	public static final int MSG_RULE_INVALID_SET = 154;
173	public static final int MSG_HETERO_ILLEGAL_IN_REWRITE_ALT = 155;
174	public static final int MSG_NO_SUCH_GRAMMAR_SCOPE = 156;
175	public static final int MSG_NO_SUCH_RULE_IN_SCOPE = 157;
176	public static final int MSG_TOKEN_ALIAS_CONFLICT = 158;
177	public static final int MSG_TOKEN_ALIAS_REASSIGNMENT = 159;
178	public static final int MSG_TOKEN_VOCAB_IN_DELEGATE = 160;
179	public static final int MSG_INVALID_IMPORT = 161;
180	public static final int MSG_IMPORTED_TOKENS_RULE_EMPTY = 162;
181	public static final int MSG_IMPORT_NAME_CLASH = 163;
182	public static final int MSG_AST_OP_WITH_NON_AST_OUTPUT_OPTION = 164;
183	public static final int MSG_AST_OP_IN_ALT_WITH_REWRITE = 165;
184    public static final int MSG_WILDCARD_AS_ROOT = 166;
185    public static final int MSG_CONFLICTING_OPTION_IN_TREE_FILTER = 167;
186	public static final int MSG_ILLEGAL_OPTION_VALUE = 168;
187	public static final int MSG_ALL_OPS_NEED_SAME_ASSOC = 169;
188
189	// GRAMMAR WARNINGS
190	public static final int MSG_GRAMMAR_NONDETERMINISM = 200; // A predicts alts 1,2
191	public static final int MSG_UNREACHABLE_ALTS = 201;       // nothing predicts alt i
192	public static final int MSG_DANGLING_STATE = 202;         // no edges out of state
193	public static final int MSG_INSUFFICIENT_PREDICATES = 203;
194	public static final int MSG_DUPLICATE_SET_ENTRY = 204;    // (A|A)
195	public static final int MSG_ANALYSIS_ABORTED = 205;
196	public static final int MSG_RECURSION_OVERLOW = 206;
197	public static final int MSG_LEFT_RECURSION = 207;
198	public static final int MSG_UNREACHABLE_TOKENS = 208; // nothing predicts token
199	public static final int MSG_TOKEN_NONDETERMINISM = 209; // alts of Tokens rule
200	public static final int MSG_LEFT_RECURSION_CYCLES = 210;
201	public static final int MSG_NONREGULAR_DECISION = 211;
202
203
204    // Dependency sorting errors
205    //
206    public static final int MSG_CIRCULAR_DEPENDENCY = 212; // t1.g -> t2.g -> t3.g ->t1.g
207
208	public static final int MAX_MESSAGE_NUMBER = 212;
209
210	/** Do not do perform analysis if one of these happens */
211	public static final BitSet ERRORS_FORCING_NO_ANALYSIS = new BitSet() {
212		{
213			add(MSG_RULE_REDEFINITION);
214			add(MSG_UNDEFINED_RULE_REF);
215			add(MSG_LEFT_RECURSION_CYCLES);
216			add(MSG_REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION);
217			add(MSG_NO_RULES);
218			add(MSG_NO_SUCH_GRAMMAR_SCOPE);
219			add(MSG_NO_SUCH_RULE_IN_SCOPE);
220			add(MSG_LEXER_RULES_NOT_ALLOWED);
221            add(MSG_WILDCARD_AS_ROOT);
222            add(MSG_CIRCULAR_DEPENDENCY);
223            // TODO: ...
224		}
225	};
226
227	/** Do not do code gen if one of these happens */
228	public static final BitSet ERRORS_FORCING_NO_CODEGEN = new BitSet() {
229		{
230			add(MSG_NONREGULAR_DECISION);
231			add(MSG_RECURSION_OVERLOW);
232			add(MSG_UNREACHABLE_ALTS);
233			add(MSG_FILE_AND_GRAMMAR_NAME_DIFFER);
234			add(MSG_INVALID_IMPORT);
235			add(MSG_AST_OP_WITH_NON_AST_OUTPUT_OPTION);
236            add(MSG_CIRCULAR_DEPENDENCY);
237			// TODO: ...
238		}
239	};
240
241	/** Only one error can be emitted for any entry in this table.
242	 *  Map<String,Set> where the key is a method name like danglingState.
243	 *  The set is whatever that method accepts or derives like a DFA.
244	 */
245	public static final Map emitSingleError = new HashMap() {
246		{
247			put("danglingState", new HashSet());
248		}
249	};
250
251	/** Messages should be sensitive to the locale. */
252	private static Locale locale;
253	private static String formatName;
254
255	/** Each thread might need it's own error listener; e.g., a GUI with
256	 *  multiple window frames holding multiple grammars.
257	 */
258	private static Map threadToListenerMap = new HashMap();
259
260	static class ErrorState {
261		public int errors;
262		public int warnings;
263		public int infos;
264		/** Track all msgIDs; we use to abort later if necessary
265		 *  also used in Message to find out what type of message it is via getMessageType()
266		 */
267		public BitSet errorMsgIDs = new BitSet();
268		public BitSet warningMsgIDs = new BitSet();
269		// TODO: figure out how to do info messages. these do not have IDs...kr
270		//public BitSet infoMsgIDs = new BitSet();
271	}
272
273	/** Track the number of errors regardless of the listener but track
274	 *  per thread.
275	 */
276	private static Map threadToErrorStateMap = new HashMap();
277
278	/** Each thread has its own ptr to a Tool object, which knows how
279	 *  to panic, for example.  In a GUI, the thread might just throw an Error
280	 *  to exit rather than the suicide System.exit.
281	 */
282	private static Map threadToToolMap = new HashMap();
283
284	/** The group of templates that represent all possible ANTLR errors. */
285	private static STGroup messages;
286	/** The group of templates that represent the current message format. */
287	private static STGroup format;
288
289	/** From a msgID how can I get the name of the template that describes
290	 *  the error or warning?
291	 */
292	private static String[] idToMessageTemplateName = new String[MAX_MESSAGE_NUMBER+1];
293
294	static ANTLRErrorListener theDefaultErrorListener = new ANTLRErrorListener() {
295		public void info(String msg) {
296			if (formatWantsSingleLineMessage()) {
297				msg = msg.replaceAll("\n", " ");
298			}
299			System.err.println(msg);
300		}
301
302		public void error(Message msg) {
303			String outputMsg = msg.toString();
304			if (formatWantsSingleLineMessage()) {
305				outputMsg = outputMsg.replaceAll("\n", " ");
306			}
307			System.err.println(outputMsg);
308		}
309
310		public void warning(Message msg) {
311			String outputMsg = msg.toString();
312			if (formatWantsSingleLineMessage()) {
313				outputMsg = outputMsg.replaceAll("\n", " ");
314			}
315			System.err.println(outputMsg);
316		}
317
318		public void error(ToolMessage msg) {
319			String outputMsg = msg.toString();
320			if (formatWantsSingleLineMessage()) {
321				outputMsg = outputMsg.replaceAll("\n", " ");
322			}
323			System.err.println(outputMsg);
324		}
325	};
326
327	/** Handle all ST error listeners here (code gen, Grammar, and this class
328	 *  use templates.
329	 */
330	static STErrorListener initSTListener =
331		new STErrorListener() {
332			public void compileTimeError(STMessage msg) {
333				System.err.println("ErrorManager init error: "+msg);
334			}
335
336			public void runTimeError(STMessage msg) {
337				System.err.println("ErrorManager init error: "+msg);
338			}
339
340			public void IOError(STMessage msg) {
341				System.err.println("ErrorManager init error: "+msg);
342			}
343
344			public void internalError(STMessage msg) {
345				System.err.println("ErrorManager init error: "+msg);
346			}
347
348		};
349
350	/** During verification of the messages group file, don't gen errors.
351	 *  I'll handle them here.  This is used only after file has loaded ok
352	 *  and only for the messages STG.
353	 */
354	static STErrorListener blankSTListener =
355		new STErrorListener() {
356			public void compileTimeError(STMessage msg) {			}
357			public void runTimeError(STMessage msg) {			}
358			public void IOError(STMessage msg) {			}
359			public void internalError(STMessage msg) {			}
360		};
361
362	/** Errors during initialization related to ST must all go to System.err.
363	 */
364	static STErrorListener theDefaultSTListener =
365		new STErrorListener() {
366			public void compileTimeError(STMessage msg) {
367				ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, msg.toString(), msg.cause);
368			}
369
370			public void runTimeError(STMessage msg) {
371				ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, msg.toString(), msg.cause);
372			}
373
374			public void IOError(STMessage msg) {
375				ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, msg.toString(), msg.cause);
376			}
377
378			public void internalError(STMessage msg) {
379				ErrorManager.error(ErrorManager.MSG_INTERNAL_ERROR, msg.toString(), msg.cause);
380			}
381		};
382
383	// make sure that this class is ready to use after loading
384	static {
385		initIdToMessageNameMapping();
386		// it is inefficient to set the default locale here if another
387		// piece of code is going to set the locale, but that would
388		// require that a user call an init() function or something.  I prefer
389		// that this class be ready to go when loaded as I'm absentminded ;)
390		setLocale(Locale.getDefault());
391		// try to load the message format group
392		// the user might have specified one on the command line
393		// if not, or if the user has given an illegal value, we will fall back to "antlr"
394		setFormat("antlr");
395	}
396
397    public static STErrorListener getSTErrorListener() {
398		return theDefaultSTListener;
399	}
400
401	/** We really only need a single locale for entire running ANTLR code
402	 *  in a single VM.  Only pay attention to the language, not the country
403	 *  so that French Canadians and French Frenchies all get the same
404	 *  template file, fr.stg.  Just easier this way.
405	 */
406	public static void setLocale(Locale locale) {
407		ErrorManager.locale = locale;
408		String language = locale.getLanguage();
409		String fileName = "org/antlr/tool/templates/messages/languages/"+language+".stg";
410		try {
411			messages = new STGroupFile(fileName);
412		}
413		catch (IllegalArgumentException iae) {
414			if ( language.equals(Locale.US.getLanguage()) ) {
415				rawError("ANTLR installation corrupted; cannot find English messages file "+fileName);
416				panic();
417			}
418			else {
419				setLocale(Locale.US); // recurse on this rule, trying the US locale
420			}
421		}
422
423		messages.setListener(blankSTListener);
424		boolean messagesOK = verifyMessages();
425		if ( !messagesOK && language.equals(Locale.US.getLanguage()) ) {
426			rawError("ANTLR installation corrupted; English messages file "+language+".stg incomplete");
427			panic();
428		}
429		else if ( !messagesOK ) {
430			setLocale(Locale.US); // try US to see if that will work
431		}
432	}
433
434	/** The format gets reset either from the Tool if the user supplied a command line option to that effect
435	 *  Otherwise we just use the default "antlr".
436	 */
437	public static void setFormat(String formatName) {
438		ErrorManager.formatName = formatName;
439		String fileName = "org/antlr/tool/templates/messages/formats/"+formatName+".stg";
440		format = new STGroupFile(fileName);
441		format.setListener(initSTListener);
442		if ( !format.isDefined("message") ) { // pick random msg to load
443			if ( formatName.equals("antlr") ) {
444				rawError("no such message format file "+fileName+" retrying with default ANTLR format");
445				setFormat("antlr"); // recurse on this rule, trying the default message format
446				return;
447			}
448			else {
449				setFormat("antlr"); // recurse on this rule, trying the default message format
450			}
451		}
452
453		format.setListener(blankSTListener);
454		boolean formatOK = verifyFormat();
455		if ( !formatOK && formatName.equals("antlr") ) {
456			rawError("ANTLR installation corrupted; ANTLR messages format file "+formatName+".stg incomplete");
457			panic();
458		}
459		else if ( !formatOK ) {
460			setFormat("antlr"); // recurse on this rule, trying the default message format
461		}
462	}
463
464	/** Encodes the error handling found in setLocale, but does not trigger
465	 *  panics, which would make GUI tools die if ANTLR's installation was
466	 *  a bit screwy.  Duplicated code...ick.
467	public static Locale getLocaleForValidMessages(Locale locale) {
468		ErrorManager.locale = locale;
469		String language = locale.getLanguage();
470		String fileName = "org/antlr/tool/templates/messages/"+language+".stg";
471		ClassLoader cl = Thread.currentThread().getContextClassLoader();
472		InputStream is = cl.getResourceAsStream(fileName);
473		if ( is==null && language.equals(Locale.US.getLanguage()) ) {
474			return null;
475		}
476		else if ( is==null ) {
477			return getLocaleForValidMessages(Locale.US); // recurse on this rule, trying the US locale
478		}
479
480		boolean messagesOK = verifyMessages();
481		if ( !messagesOK && language.equals(Locale.US.getLanguage()) ) {
482			return null;
483		}
484		else if ( !messagesOK ) {
485			return getLocaleForValidMessages(Locale.US); // try US to see if that will work
486		}
487		return true;
488	}
489	 */
490
491	/** In general, you'll want all errors to go to a single spot.
492	 *  However, in a GUI, you might have two frames up with two
493	 *  different grammars.  Two threads might launch to process the
494	 *  grammars--you would want errors to go to different objects
495	 *  depending on the thread.  I store a single listener per
496	 *  thread.
497	 */
498	public static void setErrorListener(ANTLRErrorListener listener) {
499		threadToListenerMap.put(Thread.currentThread(), listener);
500	}
501
502    public static void removeErrorListener() {
503        threadToListenerMap.remove(Thread.currentThread());
504    }
505
506	public static void setTool(Tool tool) {
507		threadToToolMap.put(Thread.currentThread(), tool);
508	}
509
510	/** Given a message ID, return a ST that somebody can fill
511	 *  with data.  We need to convert the int ID to the name of a template
512	 *  in the messages ST group.
513	 */
514	public static ST getMessage(int msgID) {
515        String msgName = idToMessageTemplateName[msgID];
516		return messages.getInstanceOf(msgName);
517	}
518	public static String getMessageType(int msgID) {
519		if (getErrorState().warningMsgIDs.member(msgID)) {
520			return messages.getInstanceOf("warning").render();
521		}
522		else if (getErrorState().errorMsgIDs.member(msgID)) {
523			return messages.getInstanceOf("error").render();
524		}
525		assertTrue(false, "Assertion failed! Message ID " + msgID + " created but is not present in errorMsgIDs or warningMsgIDs.");
526		return "";
527	}
528
529	/** Return a ST that refers to the current format used for
530	 * emitting messages.
531	 */
532	public static ST getLocationFormat() {
533		return format.getInstanceOf("location");
534	}
535	public static ST getReportFormat() {
536		return format.getInstanceOf("report");
537	}
538	public static ST getMessageFormat() {
539		return format.getInstanceOf("message");
540	}
541	public static boolean formatWantsSingleLineMessage() {
542		return format.getInstanceOf("wantsSingleLineMessage").render().equals("true");
543	}
544
545	public static ANTLRErrorListener getErrorListener() {
546		ANTLRErrorListener el =
547			(ANTLRErrorListener)threadToListenerMap.get(Thread.currentThread());
548		if ( el==null ) {
549			return theDefaultErrorListener;
550		}
551		return el;
552	}
553
554	public static ErrorState getErrorState() {
555		ErrorState ec =
556			(ErrorState)threadToErrorStateMap.get(Thread.currentThread());
557		if ( ec==null ) {
558			ec = new ErrorState();
559			threadToErrorStateMap.put(Thread.currentThread(), ec);
560		}
561		return ec;
562	}
563
564	public static int getNumErrors() {
565		return getErrorState().errors;
566	}
567
568	public static void resetErrorState() {
569        threadToListenerMap = new HashMap();
570        ErrorState ec = new ErrorState();
571		threadToErrorStateMap.put(Thread.currentThread(), ec);
572	}
573
574	public static void info(String msg) {
575		getErrorState().infos++;
576		getErrorListener().info(msg);
577	}
578
579	public static void error(int msgID) {
580		getErrorState().errors++;
581		getErrorState().errorMsgIDs.add(msgID);
582		getErrorListener().error(new ToolMessage(msgID));
583	}
584
585	public static void error(int msgID, Throwable e) {
586		getErrorState().errors++;
587		getErrorState().errorMsgIDs.add(msgID);
588		getErrorListener().error(new ToolMessage(msgID,e));
589	}
590
591	public static void error(int msgID, Object arg) {
592		getErrorState().errors++;
593		getErrorState().errorMsgIDs.add(msgID);
594		getErrorListener().error(new ToolMessage(msgID, arg));
595	}
596
597	public static void error(int msgID, Object arg, Object arg2) {
598		getErrorState().errors++;
599		getErrorState().errorMsgIDs.add(msgID);
600		getErrorListener().error(new ToolMessage(msgID, arg, arg2));
601	}
602
603	public static void error(int msgID, Object arg, Throwable e) {
604		getErrorState().errors++;
605		getErrorState().errorMsgIDs.add(msgID);
606		getErrorListener().error(new ToolMessage(msgID, arg, e));
607	}
608
609	public static void warning(int msgID, Object arg) {
610		getErrorState().warnings++;
611		getErrorState().warningMsgIDs.add(msgID);
612		getErrorListener().warning(new ToolMessage(msgID, arg));
613	}
614
615	public static void nondeterminism(DecisionProbe probe,
616									  DFAState d)
617	{
618		getErrorState().warnings++;
619		Message msg = new GrammarNonDeterminismMessage(probe,d);
620		getErrorState().warningMsgIDs.add(msg.msgID);
621		getErrorListener().warning(msg);
622	}
623
624	public static void danglingState(DecisionProbe probe,
625									 DFAState d)
626	{
627		getErrorState().errors++;
628		Message msg = new GrammarDanglingStateMessage(probe,d);
629		getErrorState().errorMsgIDs.add(msg.msgID);
630		Set seen = (Set)emitSingleError.get("danglingState");
631		if ( !seen.contains(d.dfa.decisionNumber+"|"+d.getAltSet()) ) {
632			getErrorListener().error(msg);
633			// we've seen this decision and this alt set; never again
634			seen.add(d.dfa.decisionNumber+"|"+d.getAltSet());
635		}
636	}
637
638	public static void analysisAborted(DecisionProbe probe)
639	{
640		getErrorState().warnings++;
641		Message msg = new GrammarAnalysisAbortedMessage(probe);
642		getErrorState().warningMsgIDs.add(msg.msgID);
643		getErrorListener().warning(msg);
644	}
645
646	public static void unreachableAlts(DecisionProbe probe,
647									   List alts)
648	{
649		getErrorState().errors++;
650		Message msg = new GrammarUnreachableAltsMessage(probe,alts);
651		getErrorState().errorMsgIDs.add(msg.msgID);
652		getErrorListener().error(msg);
653	}
654
655	public static void insufficientPredicates(DecisionProbe probe,
656											  DFAState d,
657											  Map<Integer, Set<Token>> altToUncoveredLocations)
658	{
659		getErrorState().warnings++;
660		Message msg = new GrammarInsufficientPredicatesMessage(probe,d,altToUncoveredLocations);
661		getErrorState().warningMsgIDs.add(msg.msgID);
662		getErrorListener().warning(msg);
663	}
664
665	public static void nonLLStarDecision(DecisionProbe probe) {
666		getErrorState().errors++;
667		Message msg = new NonRegularDecisionMessage(probe, probe.getNonDeterministicAlts());
668		getErrorState().errorMsgIDs.add(msg.msgID);
669		getErrorListener().error(msg);
670	}
671
672	public static void recursionOverflow(DecisionProbe probe,
673										 DFAState sampleBadState,
674										 int alt,
675										 Collection targetRules,
676										 Collection callSiteStates)
677	{
678		getErrorState().errors++;
679		Message msg = new RecursionOverflowMessage(probe,sampleBadState, alt,
680										 targetRules, callSiteStates);
681		getErrorState().errorMsgIDs.add(msg.msgID);
682		getErrorListener().error(msg);
683	}
684
685	/*
686	// TODO: we can remove I think.  All detected now with cycles check.
687	public static void leftRecursion(DecisionProbe probe,
688									 int alt,
689									 Collection targetRules,
690									 Collection callSiteStates)
691	{
692		getErrorState().warnings++;
693		Message msg = new LeftRecursionMessage(probe, alt, targetRules, callSiteStates);
694		getErrorState().warningMsgIDs.add(msg.msgID);
695		getErrorListener().warning(msg);
696	}
697	*/
698
699	public static void leftRecursionCycles(Collection cycles) {
700		getErrorState().errors++;
701		Message msg = new LeftRecursionCyclesMessage(cycles);
702		getErrorState().errorMsgIDs.add(msg.msgID);
703		getErrorListener().error(msg);
704	}
705
706	public static void grammarError(int msgID,
707									Grammar g,
708									Token token,
709									Object arg,
710									Object arg2)
711	{
712		getErrorState().errors++;
713		Message msg = new GrammarSemanticsMessage(msgID,g,token,arg,arg2);
714		getErrorState().errorMsgIDs.add(msgID);
715		getErrorListener().error(msg);
716	}
717
718	public static void grammarError(int msgID,
719									Grammar g,
720									Token token,
721									Object arg)
722	{
723		grammarError(msgID,g,token,arg,null);
724	}
725
726	public static void grammarError(int msgID,
727									Grammar g,
728									Token token)
729	{
730		grammarError(msgID,g,token,null,null);
731	}
732
733	public static void grammarWarning(int msgID,
734									  Grammar g,
735									  Token token,
736									  Object arg,
737									  Object arg2)
738	{
739		getErrorState().warnings++;
740		Message msg = new GrammarSemanticsMessage(msgID,g,token,arg,arg2);
741		getErrorState().warningMsgIDs.add(msgID);
742		getErrorListener().warning(msg);
743	}
744
745	public static void grammarWarning(int msgID,
746									  Grammar g,
747									  Token token,
748									  Object arg)
749	{
750		grammarWarning(msgID,g,token,arg,null);
751	}
752
753	public static void grammarWarning(int msgID,
754									  Grammar g,
755									  Token token)
756	{
757		grammarWarning(msgID,g,token,null,null);
758	}
759
760	public static void syntaxError(int msgID,
761								   Grammar grammar,
762								   Token token,
763								   Object arg,
764								   RecognitionException re)
765	{
766		getErrorState().errors++;
767		getErrorState().errorMsgIDs.add(msgID);
768		getErrorListener().error(
769			new GrammarSyntaxMessage(msgID,grammar,token,arg,re)
770		);
771	}
772
773	public static void internalError(Object error, Throwable e) {
774		StackTraceElement location = getLastNonErrorManagerCodeLocation(e);
775		String msg = "Exception "+e+"@"+location+": "+error;
776		error(MSG_INTERNAL_ERROR, msg);
777	}
778
779	public static void internalError(Object error) {
780		StackTraceElement location =
781			getLastNonErrorManagerCodeLocation(new Exception());
782		String msg = location+": "+error;
783		error(MSG_INTERNAL_ERROR, msg);
784	}
785
786	public static boolean doNotAttemptAnalysis() {
787		return !getErrorState().errorMsgIDs.and(ERRORS_FORCING_NO_ANALYSIS).isNil();
788	}
789
790	public static boolean doNotAttemptCodeGen() {
791		return doNotAttemptAnalysis() ||
792			   !getErrorState().errorMsgIDs.and(ERRORS_FORCING_NO_CODEGEN).isNil();
793	}
794
795	/** Return first non ErrorManager code location for generating messages */
796	private static StackTraceElement getLastNonErrorManagerCodeLocation(Throwable e) {
797		StackTraceElement[] stack = e.getStackTrace();
798		int i = 0;
799		for (; i < stack.length; i++) {
800			StackTraceElement t = stack[i];
801			if ( t.toString().indexOf("ErrorManager")<0 ) {
802				break;
803			}
804		}
805		StackTraceElement location = stack[i];
806		return location;
807	}
808
809	// A S S E R T I O N  C O D E
810
811	public static void assertTrue(boolean condition, String message) {
812		if ( !condition ) {
813			internalError(message);
814		}
815	}
816
817	// S U P P O R T  C O D E
818
819	protected static boolean initIdToMessageNameMapping() {
820		// make sure a message exists, even if it's just to indicate a problem
821		for (int i = 0; i < idToMessageTemplateName.length; i++) {
822			idToMessageTemplateName[i] = "INVALID MESSAGE ID: "+i;
823		}
824		// get list of fields and use it to fill in idToMessageTemplateName mapping
825		Field[] fields = ErrorManager.class.getFields();
826		for (int i = 0; i < fields.length; i++) {
827			Field f = fields[i];
828			String fieldName = f.getName();
829			if ( !fieldName.startsWith("MSG_") ) {
830				continue;
831			}
832			String templateName =
833				fieldName.substring("MSG_".length(),fieldName.length());
834			int msgID = 0;
835			try {
836				// get the constant value from this class object
837				msgID = f.getInt(ErrorManager.class);
838			}
839			catch (IllegalAccessException iae) {
840				System.err.println("cannot get const value for "+f.getName());
841				continue;
842			}
843			if ( fieldName.startsWith("MSG_") ) {
844                idToMessageTemplateName[msgID] = templateName;
845			}
846		}
847		return true;
848	}
849
850	/** Use reflection to find list of MSG_ fields and then verify a
851	 *  template exists for each one from the locale's group.
852	 */
853	protected static boolean verifyMessages() {
854		boolean ok = true;
855		Field[] fields = ErrorManager.class.getFields();
856		for (int i = 0; i < fields.length; i++) {
857			Field f = fields[i];
858			String fieldName = f.getName();
859			String templateName =
860				fieldName.substring("MSG_".length(),fieldName.length());
861			if ( fieldName.startsWith("MSG_") ) {
862				if ( !messages.isDefined(templateName) ) {
863					System.err.println("Message "+templateName+" in locale "+
864									   locale+" not found");
865					ok = false;
866				}
867			}
868		}
869		// check for special templates
870		if (!messages.isDefined("warning")) {
871			System.err.println("Message template 'warning' not found in locale "+ locale);
872			ok = false;
873		}
874		if (!messages.isDefined("error")) {
875			System.err.println("Message template 'error' not found in locale "+ locale);
876			ok = false;
877		}
878		return ok;
879	}
880
881	/** Verify the message format template group */
882	protected static boolean verifyFormat() {
883		boolean ok = true;
884		if (!format.isDefined("location")) {
885			System.err.println("Format template 'location' not found in " + formatName);
886			ok = false;
887		}
888		if (!format.isDefined("message")) {
889			System.err.println("Format template 'message' not found in " + formatName);
890			ok = false;
891		}
892		if (!format.isDefined("report")) {
893			System.err.println("Format template 'report' not found in " + formatName);
894			ok = false;
895		}
896		return ok;
897	}
898
899	/** If there are errors during ErrorManager init, we have no choice
900	 *  but to go to System.err.
901	 */
902	static void rawError(String msg) {
903		System.err.println(msg);
904	}
905
906	static void rawError(String msg, Throwable e) {
907		rawError(msg);
908		e.printStackTrace(System.err);
909	}
910
911	/** I *think* this will allow Tool subclasses to exit gracefully
912	 *  for GUIs etc...
913	 */
914	public static void panic() {
915		Tool tool = (Tool)threadToToolMap.get(Thread.currentThread());
916		if ( tool==null ) {
917			// no tool registered, exit
918			throw new Error("ANTLR ErrorManager panic");
919		}
920		else {
921			tool.panic();
922		}
923	}
924}
925