1 /**
2 * AUTOHIT 2003
3 * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Additional license information can be found in the documentation.
19 * @author Erich P Gatejen
20 */
21 package autohit.creator.compiler;
22
23 import java.io.EOFException;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29
30 import javax.xml.parsers.DocumentBuilder;
31 import javax.xml.parsers.DocumentBuilderFactory;
32
33 import org.w3c.dom.Document;
34 import org.xml.sax.InputSource;
35 import org.xml.sax.SAXException;
36
37 import autohit.common.AutohitErrorCodes;
38 import autohit.common.AutohitException;
39 import autohit.common.AutohitLogInjectorWrapper;
40 import autohit.common.AutohitProperties;
41 import autohit.server.SystemContext;
42
43 /**
44 * This is the a base XML compiler. It must be extended by a specific compiler.
45 * Users of an extended class will call the compile() method in this class, which
46 * will first parse the XML then call the abstract method build(). An extended
47 * class must override the build() method and use to to compile from the
48 * xml document tree.
49 * <p>
50 * This will load/cache the DTD by providing a new Resolver that will
51 * return a string reader to the cached DTD. It assumes that each execution
52 * of "build" is for a new compile.
53 * <p>
54 * WARNING!!! For the compiler to work, the root property must be set
55 * and passed in the prop to the constructor.
56 *
57 * @author Erich P. Gatejen
58 * @version 1.1
59 * <i>Version History</i>
60 * <code>EPG - Initial - 14Apr03</code>
61 *
62 */
63 public abstract class XmlCompiler {
64
65 private final static int BUFFER_SIZE = 1024;
66
67 /**
68 * Runtime logger. Used for compile-time logging. Use the two
69 * helper methods instead of using it directly--runtimeError and
70 * runtimeWarning.
71 * @see #runtimeError(String t)
72 * @see #runtimeWarning(String t)
73 */
74 public AutohitLogInjectorWrapper runtimeLog;
75 public AutohitLogInjectorWrapper myLog;
76 private int warnings;
77 private int errors;
78
79 /**
80 * Handles parse/compile errors and warnings. Also serves as the ErrorHandler
81 * for the XML parser.
82 * @see autohit.creator.compiler.XmlParseErrorHandler
83 */
84 public XmlParseErrorHandler myErrorHandler;
85
86 /**
87 * The DTD to use when parsing the source text.
88 */
89 private XmlCompilerResolver myResolver;
90
91 /**
92 * System context
93 */
94 private SystemContext sc;
95
96 /**
97 * XML internals
98 */
99 private DocumentBuilder builder;
100 private DocumentBuilderFactory factory;
101
102 // --- PUBLIC METHODS ----------------------------------------------------
103
104 /**
105 * Constructor. You must use this and NOT the default.
106 * It will make sure that the DTD for the SimLanguage is available.
107 *
108 * If you use the defaulty constructor, the compiler will not know
109 * which DTD to use. That is a BAD THING(tm).
110 *
111 * @param dtdURI URI of the DTD used in the !DOCTYPE * SYSTEM clause in the
112 * compile targets.
113 * @param sc A system context containing valid references to a root logger
114 * and the system properties.
115 * @throws Exception any exception invalidates the compiler.
116 */
117 public XmlCompiler(String dtdURI, SystemContext sc) throws Exception {
118
119 StringBuffer tempDTD;
120 String scrubbedURI;
121
122 // See if we have a logger
123 // CHEAT!!! I'm going to seperate the logs later.
124 // TODO seperate the logs
125 runtimeLog = sc.getRootLogger();
126 myLog = runtimeLog;
127
128 // Find the dtd
129 String location =
130 sc.getPropertiesSet().getString(AutohitProperties.ROOT_PATH);
131 if (location == null) {
132 myLog.debug(
133 "COMPILER ERROR. Root property not set!",
134 AutohitErrorCodes.CODE_COMPILE_CONFIGURATION_FAULT);
135 throw new AutohitException(
136 "Root property not set.",
137 AutohitErrorCodes.CODE_COMPILE_CONFIGURATION_FAULT);
138 }
139
140 location = location + AutohitProperties.literal_DTD_PATH + "/";
141 scrubbedURI = dtdURI.substring(dtdURI.indexOf(":") + 1);
142 String dtdPath = location + scrubbedURI;
143 myLog.debug(
144 "XMLCompiler:dtdPath=" + dtdPath,
145 AutohitErrorCodes.CODE_INFORMATIONAL_OK);
146
147 // load the dtd
148 File dtdFile = new File(dtdPath);
149 InputStreamReader inFile =
150 new InputStreamReader(new FileInputStream(dtdFile));
151 tempDTD = new StringBuffer();
152 try {
153 char[] buf = new char[BUFFER_SIZE];
154 int len;
155
156 len = inFile.read(buf, 0, BUFFER_SIZE);
157 while (len > 0) {
158 tempDTD.append(buf, 0, len);
159 len = inFile.read(buf, 0, BUFFER_SIZE);
160 }
161 } catch (EOFException e) {
162 // Dont do anything. this is A-OK. For some odd reason, java.io
163 // will sometimes throw an EOF instead of just returning a -1.
164 } catch (Exception e) {
165 throw (e);
166 }
167
168 // Set up a resolver from which the XML parser can get our DTD
169 myResolver = new XmlCompilerResolver(myLog);
170 myResolver.register(scrubbedURI, tempDTD.toString());
171
172 // Set up our Error Handler
173 myErrorHandler = new XmlParseErrorHandler(this);
174
175 // Create my builders
176 factory = DocumentBuilderFactory.newInstance();
177 factory.setValidating(true);
178 //factory.setNamespaceAware(true);
179
180 builder = factory.newDocumentBuilder();
181 builder.setErrorHandler(myErrorHandler);
182 builder.setEntityResolver(myResolver);
183
184 myLog.debug(
185 "XMLCompiler: constructed.",
186 AutohitErrorCodes.CODE_INFORMATIONAL_OK);
187 }
188
189 /**
190 * Constructor. Don't use the default--ever.
191 */
192 public XmlCompiler() throws AutohitException {
193 throw new AutohitException(
194 "BAD PROGRAMMER DETECTED. PUNISH HIM/HER SEVERELY! DONT USE THE DEFAULT CONSTRUCTOR FOR autohit.creator.XmlCompiler.",
195 AutohitErrorCodes.CODE_CATASTROPHIC_FRAMEWORK_FAULT);
196 }
197
198 /**
199 * Compile a stream into object code. It will abort on a major error
200 * and return a null instead of an object. This base class does not
201 * specify the format of the object code. Any compile errors or
202 * warnings can be found in the errors field.
203 *
204 * @param is An input stream to the text that is to be compiled.
205 * @return a reference to the target object.
206 */
207 synchronized public Object compile(InputStream is) {
208
209 Document myDocument;
210 Object objectCode = null;
211 InputSource isource;
212
213 // Any exception aborts the compile
214 try {
215
216 // Setup the log for this run
217 this.setRuntimeLog(myLog); // CHEAT
218
219 // Parse. This needs to be a singleton, because I can't trust the stock parser.
220 synchronized (builder) {
221 isource = new InputSource(is);
222 isource.setSystemId("//");
223 myDocument = builder.parse(isource);
224 }
225 myLog.debug(
226 "XMLCompiler: parse successful.",
227 AutohitErrorCodes.CODE_INFORMATIONAL_OK);
228
229 objectCode = build(myDocument);
230
231 // Reset the log
232 this.resetRuntimeLog();
233
234 } catch (SAXException sxe) {
235 // None of these should happen!
236 myLog.debug(
237 "XMLCompiler: Unrecoverable parsing error.",
238 AutohitErrorCodes.CODE_COMPILE_ABORT);
239 myLog.debug(":" + sxe.getMessage());
240 } catch (IOException ioe) {
241 myLog.debug(
242 "XMLCompiler: build failed to IOException.",
243 AutohitErrorCodes.CODE_CATASTROPHIC_FRAMEWORK_FAULT);
244 myLog.debug(":" + ioe.getMessage());
245 } catch (Exception e) {
246 myLog.debug(
247 "XMLCompiler: Software Detected Fault: Unexpected general Exception.",
248 AutohitErrorCodes.CODE_SW_DETECTED_FAULT);
249 myLog.debug(":" + e.getMessage());
250 }
251 // this will return null unless it was set at the end of the try.
252 return objectCode;
253 }
254
255 /**
256 * Posts a warning to the runtime log and increments the error count.
257 * @param t the warning message
258 */
259 public void runtimeWarning(String t) {
260 runtimeLog.warning(
261 "WARNING: " + t,
262 AutohitErrorCodes.CODE_COMPILE_WARNING);
263 warnings++;
264 }
265
266 /**
267 * Posts a warning to the runtime log and increments the error count.
268 * @param t the error message
269 */
270 public void runtimeError(String t) {
271 runtimeLog.error("ERROR: " + t, AutohitErrorCodes.CODE_COMPILE_ERROR);
272 errors++;
273 }
274
275 /**
276 * Posts a debug message to the runtime log.
277 * @param t the debug message
278 */
279 public void runtimeDebug(String t) {
280 runtimeLog.debug(t, AutohitErrorCodes.CODE_INFORMATIONAL_OK);
281 }
282
283 /**
284 * Get error count.
285 * @return number of errors
286 */
287 public int numberErrors() {
288 return errors;
289 }
290
291 /**
292 * Get warning count.
293 * @return number of warnings
294 */
295 public int numberWarnings() {
296 return warnings;
297 }
298
299 /**
300 * This sets the runtime log. The runtime log will be used during
301 * compilation. The system (myLog) is used during overhead functions.
302 * These can be the same log. This method lets you point to a
303 * different log. This will clear the error and warning counts.
304 * @param cl a log injector
305 */
306 public void setRuntimeLog(AutohitLogInjectorWrapper cl) {
307 runtimeLog = cl;
308 warnings = 0;
309 errors = 0;
310 }
311
312 /**
313 * This sets the runtime log back to be the same as the system log.
314 */
315 public void resetRuntimeLog() {
316 runtimeLog = myLog;
317 }
318
319 /**
320 * Abstract build method. Override with a method that builds the object code
321 * from the XML parse tree.
322 *
323 * @param xd A parsed XML document.
324 *
325 * @return Object reference to the object code.
326 */
327 public abstract Object build(Document xd);
328
329 }
|