KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > tigris > scarab > util > xmlissues > ImportIssues


1 package org.tigris.scarab.util.xmlissues;
2
3 /* ================================================================
4  * Copyright (c) 2000-2002 CollabNet. 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 are
8  * met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowlegement: "This product includes
19  * software developed by Collab.Net <http://www.Collab.Net/>."
20  * Alternately, this acknowlegement may appear in the software itself, if
21  * and wherever such third-party acknowlegements normally appear.
22  *
23  * 4. The hosted project names must not be used to endorse or promote
24  * products derived from this software without prior written
25  * permission. For written permission, please contact info@collab.net.
26  *
27  * 5. Products derived from this software may not use the "Tigris" or
28  * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
29  * prior written permission of Collab.Net.
30  *
31  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
32  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
34  * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
35  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
37  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
39  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
40  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42  *
43  * ====================================================================
44  *
45  * This software consists of voluntary contributions made by many
46  * individuals on behalf of Collab.Net.
47  */

48
49 import java.beans.BeanDescriptor JavaDoc;
50 import java.io.BufferedInputStream JavaDoc;
51 import java.io.File JavaDoc;
52 import java.io.FileInputStream JavaDoc;
53 import java.io.IOException JavaDoc;
54 import java.io.InputStream JavaDoc;
55 import java.io.Writer JavaDoc;
56 import java.util.Collection JavaDoc;
57 import java.util.Iterator JavaDoc;
58 import java.util.Locale JavaDoc;
59
60 import javax.xml.parsers.ParserConfigurationException JavaDoc;
61
62 import org.apache.commons.betwixt.XMLIntrospector;
63 import org.apache.commons.betwixt.io.BeanReader;
64 import org.apache.commons.betwixt.io.BeanWriter;
65 import org.apache.commons.betwixt.strategy.HyphenatedNameMapper;
66 import org.apache.commons.betwixt.strategy.NameMapper;
67 import org.apache.commons.digester.Digester;
68 import org.apache.commons.digester.Rule;
69 import org.apache.commons.fileupload.FileItem;
70 import org.apache.commons.logging.Log;
71 import org.apache.commons.logging.LogFactory;
72 import org.apache.fulcrum.localization.Localization;
73 import org.tigris.scarab.om.Module;
74 import org.tigris.scarab.util.ScarabConstants;
75 import org.tigris.scarab.util.TurbineInitialization;
76 import org.tigris.scarab.workflow.WorkflowFactory;
77 import org.xml.sax.Attributes JavaDoc;
78 import org.xml.sax.ErrorHandler JavaDoc;
79 import org.xml.sax.InputSource JavaDoc;
80 import org.xml.sax.SAXException JavaDoc;
81 import org.xml.sax.SAXParseException JavaDoc;
82
83
84 /**
85  * This is a bean'ish object which allows one to set values for importing
86  * issues, and then run the actual import.
87  *
88  * Amenable to the ant task wrapper or you can pass an explicit file for
89  * explicit import if turbine is already up and running.
90  *
91  * <p>The way the ant task wrapper works is simple: call all the appropriate
92  * set methods to define the properties. Then you will need to call the init()
93  * and execute methods to start running things. Note: If Turbine is already
94  * initialized, there is no need to call the init() method.</p>
95  *
96  * <p>Instances of this class are not thread-safe.</p>
97  *
98  * @author <a HREF="mailto:jon@latchkey.com">Jon S. Stevens</a>
99  * @author <a HREF="mailto:dlr@collab.net">Daniel Rall</a>
100  * @version $Id: ImportIssues.java 9104 2004-05-10 21:04:51Z dabbous $
101  * @since Scarab beta 14
102  */

103 public class ImportIssues
104     implements ErrorHandler JavaDoc
105 {
106     private static final Log LOG = LogFactory.getLog(ImportIssues.class);
107
108     /**
109      * The virtual URL to the document type definition (DTD) used with
110      * this version of Scarab. Though this file doesn't actually
111      * exist, it's what can be used as a friendly way to refer to
112      * Scarab's DTD in an XML file's <code>DOCTYPE</code> declaration.
113      */

114     public static final String JavaDoc SYSTEM_DTD_URI =
115         "http://scarab.tigris.org/dtd/scarab-0.16.29.dtd";
116
117     /**
118      * The absolute URL to the document type definition (DTD) used
119      * with this version of Scarab.
120      */

121     private static final String JavaDoc INTERNAL_DTD_URI =
122         "http://scarab.tigris.org/unbranded-source/browse/scarab/src/dtd/scarab.dtd?rev=1.49&content-type=text/plain";
123
124     /**
125      * The resource location of the DTD in the classpath.
126      */

127     private static final String JavaDoc DTD_RESOURCE = "/org/tigris/scarab/scarab.dtd";
128
129     /**
130      * Name of the properties file.
131      */

132     private String JavaDoc trProps = "/WEB-INF/conf/TurbineResourcesTest.properties";
133
134     /**
135      * Name of the xmlimport.properties file used for configuration of log4j.
136      */

137     private String JavaDoc configProps = "/WEB-INF/conf/xmlimport.properties";
138
139     private File JavaDoc configDir = null;
140     private boolean sendEmail = false;
141     private File JavaDoc xmlFile = null;
142
143     /**
144      * Current file attachment handling code contains a security hole
145      * which can allow a user to see any file on the host that is
146      * readable by Scarab. It is not easy to exploit this hole (you
147      * have to know about file paths on a host you likely don't have
148      * access to), and there are cases where we want to use the
149      * functionality and can be sure the hole is not being exploited.
150      * So adding a flag to disallow file attachments when importing
151      * through the UI.
152      */

153     private boolean allowFileAttachments;
154
155     /**
156      * Whether we're in validation mode, or some phase after that.
157      */

158     private boolean validationMode;
159
160     /**
161      * A list of any errors encountered during the import, likely
162      * added during the validation phase.
163      */

164     private ImportErrors importErrors;
165
166     public ImportIssues()
167     {
168         this(false);
169     }
170
171     public ImportIssues(boolean allowFileAttachments)
172     {
173         this.allowFileAttachments = allowFileAttachments;
174         this.importErrors = new ImportErrors();
175     }
176
177     /**
178      * Instance of scarabissues we ran the actual insert with.
179      *
180      * Make it available post import so importer can get at info about
181      * what has just been imported.
182      */

183     private ScarabIssues si = null;
184
185
186     public boolean getSendEmail()
187     {
188         return this.sendEmail;
189     }
190
191     public void setSendEmail(boolean state)
192     {
193         this.sendEmail = state;
194     }
195
196     public File JavaDoc getXmlFile()
197     {
198         return this.xmlFile;
199     }
200
201     public void setXmlFile(File JavaDoc xmlFile)
202     {
203         this.xmlFile = xmlFile;
204     }
205
206     public File JavaDoc getConfigDir()
207     {
208         return this.configDir;
209     }
210
211     public void setConfigDir(File JavaDoc configDir)
212     {
213         this.configDir = configDir;
214     }
215
216     public String JavaDoc getConfigFile()
217     {
218         return this.configProps;
219     }
220
221     public void setConfigFile(String JavaDoc configProps)
222     {
223         this.configProps = configProps;
224     }
225
226     public String JavaDoc getTurbineResources()
227     {
228         return this.trProps;
229     }
230
231     public void setTurbineResources(String JavaDoc trProps)
232     {
233         this.trProps = trProps;
234     }
235
236     public void init()
237         throws Exception JavaDoc
238     {
239         TurbineInitialization.setTurbineResources(getTurbineResources());
240         TurbineInitialization.setUp(getConfigDir().getAbsolutePath(),
241             getConfigFile());
242     }
243
244     /**
245      * Hook method called by <a
246      * HREF="http://ant.apache.org/">Ant's</a> Task wrapper.
247      */

248     public void execute()
249         throws Exception JavaDoc
250     {
251         runImport(getXmlFile());
252     }
253
254     /**
255      * Run an import.
256      *
257      * Assumes we're up and running inside of turbine.
258      *
259      * @param importFile File to import.
260      *
261      * @return List of errors if any.
262      *
263      * @exception Exception
264      */

265     public Collection JavaDoc runImport(File JavaDoc importFile)
266         throws Exception JavaDoc
267     {
268         return runImport(importFile, (Module) null);
269     }
270
271     /**
272      * Run an import.
273      *
274      * Assumes we're up and running inside of turbine.
275      *
276      * @param importFile File to import.
277      * @param currentModule If non-null, run check that import is going
278      * against this module.
279      *
280      * @return List of errors if any.
281      *
282      * @exception Exception
283      */

284     public Collection JavaDoc runImport(File JavaDoc importFile, Module currentModule)
285         throws Exception JavaDoc
286     {
287         return runImport(importFile.getAbsolutePath(), importFile,
288                          currentModule);
289     }
290
291     /**
292      * Run an import.
293      *
294      * Assumes we're up and running inside of turbine. Awkwardly duplicates
295      * {@link #runImport(File) import} but duplication is so we can do the reget of
296      * the input stream; FileInput "manages" backing up the Upload for us on the
297      * second get of the input stream (It creates new ByteArrayInputStream
298      * w/ the src being a byte array of the file its kept in memory or in
299      * temporary storage on disk).
300      *
301      * @param importFile FileItem reference to use importing.
302      *
303      * @return List of errors if any.
304      *
305      * @exception Exception
306      */

307     public Collection JavaDoc runImport(FileItem importFile)
308         throws Exception JavaDoc
309     {
310         return runImport(importFile, (Module) null);
311     }
312
313     /**
314      * Run an import.
315      *
316      * Assumes we're up and running inside of turbine. Awkwardly duplicates
317      * {@link #runImport(File) import} but duplication is so we can do the reget of
318      * the input stream; FileInput "manages" backing up the Upload for us on the
319      * second get of the input stream (It creates new ByteArrayInputStream
320      * w/ the src being a byte array of the file its kept in memory or in
321      * temporary storage on disk).
322      *
323      * @param importFile FileItem reference to use importing.
324      * @param currentModule If non-null, run check that import is going
325      * against this module.
326      *
327      * @return List of errors if any.
328      *
329      * @exception Exception
330      */

331     public Collection JavaDoc runImport(FileItem importFile, Module currentModule)
332         throws Exception JavaDoc
333     {
334         return runImport(importFile.getName(), importFile, currentModule);
335     }
336
337     /**
338      * @param input A <code>File</code> or <code>FileItem</code>.
339      */

340     protected Collection JavaDoc runImport(String JavaDoc filePath, Object JavaDoc input,
341                                    Module currentModule)
342         throws Exception JavaDoc
343     {
344         String JavaDoc msg = "Importing issues from XML '" + filePath + '\'';
345         LOG.debug(msg);
346         try
347         {
348             // Disable workflow and set file attachment flag
349
WorkflowFactory.setForceUseDefault(true);
350             BeanReader reader = createScarabIssuesBeanReader();
351             validate(filePath, inputStreamFor(input), reader, currentModule);
352
353             if (importErrors == null || importErrors.isEmpty())
354             {
355                 // Reget the input stream.
356
this.si = insert(filePath, inputStreamFor(input), reader);
357             }
358         }
359         catch (Exception JavaDoc e)
360         {
361             LOG.error(msg, e);
362             throw e; //EXCEPTION
363
}
364         finally
365         {
366             // Re-enable workflow.
367
WorkflowFactory.setForceUseDefault(false);
368         }
369
370         return importErrors;
371     }
372
373     /**
374      * Coerces a new <code>InputStream</code> from <code>input</code>.
375      * Necessary because the stream is read twice by
376      * <code>runImport()</code>, so the source of the stream must be
377      * passed into that method.
378      *
379      * @throws IllegalArgumentException If <code>input</code> is
380      * unrecognized.
381      */

382     private InputStream JavaDoc inputStreamFor(Object JavaDoc input)
383         throws IOException JavaDoc
384     {
385         if (input instanceof FileItem)
386         {
387             return ((FileItem) input).getInputStream();
388         }
389         else if (input instanceof File JavaDoc)
390         {
391             return new BufferedInputStream JavaDoc(new FileInputStream JavaDoc((File JavaDoc) input));
392         }
393         else
394         {
395             throw new IllegalArgumentException JavaDoc(); //EXCEPTION
396
}
397     }
398
399     /**
400      * Run validation phase. Starts by performing XML-well
401      * formed-ness and DTD validation (if present), then checks the
402      * content.
403      *
404      * @param name Filename to output in log message. May be null.
405      * @param is Input stream to read.
406      * @param reader ScarabIssues bean reader instance.
407      * @param currentModule If not <code>null</code>, check whether
408      * import is going against this module.
409      *
410      * @return Any errors encountered during XML or content
411      * validation.
412      *
413      * @exception Exception
414      */

415     protected void validate(String JavaDoc name, InputStream JavaDoc is,
416                             BeanReader reader, Module currentModule)
417         throws Exception JavaDoc
418     {
419         // While parsing the XML, we perform well formed-ness and DTD
420
// validation (if present, see Xerces dynamic feature).
421
setValidationMode(reader, true);
422         ScarabIssues si = null;
423         try
424         {
425             si = (ScarabIssues) reader.parse(is);
426         }
427         catch (SAXParseException JavaDoc e)
428         {
429             // TODO: L10N this error message from Xerces (somehow),
430
// and provide a prefix that describes that a XML parse
431
// error was encountered.
432
importErrors.add("XML parse error at line " + e.getLineNumber() +
433                              " column " + e.getColumnNumber() + ": " +
434                              e.getMessage());
435         }
436
437         // If the XML is okay, validate the actual content.
438
if (si != null)
439         {
440             // ASSUMPTION: Parse errors prevent entry into this block.
441
validateContent(si, currentModule);
442
443             // Log any errors encountered during import.
444
if (importErrors != null)
445             {
446                 int nbrErrors = importErrors.size();
447                 LOG.error("Found " + nbrErrors + " error" +
448                           (nbrErrors == 1 ? "" : "s") + " importing '" +
449                           name + "':");
450                 for (Iterator JavaDoc itr = importErrors.iterator(); itr.hasNext(); )
451                 {
452                     LOG.error(itr.next());
453                 }
454             }
455         }
456     }
457
458     /**
459      * Helper method for validate() which invokes validation routines
460      * supplied by <code>ScarabIssues</code> plus (conditionally)
461      * additional module validation.
462      */

463     private void validateContent(ScarabIssues si, Module currentModule)
464         throws Exception JavaDoc
465     {
466         if (currentModule != null)
467         {
468             // Make sure the XML module corresponds to the current
469
// module. This is later than we'd like to perform this
470
// check, since we've already parsed the XML. On the
471
// upside, si.getModule() should not return null.
472
XmlModule xmlModule = si.getModule();
473
474             // HELP: Check domain also?
475

476             String JavaDoc xmlModuleName = xmlModule.getName();
477             String JavaDoc curModuleName = currentModule.getRealName();
478             if (!curModuleName.equals(xmlModuleName))
479             {
480                 Object JavaDoc[] args = { xmlModuleName, curModuleName };
481                 String JavaDoc error = Localization.format
482                     (ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
483                      "XMLAndCurrentModuleMismatch", args);
484                 importErrors.add(error);
485             }
486
487             String JavaDoc xmlCode = xmlModule.getCode();
488             if (xmlCode == null ||
489                 !currentModule.getCode().equals(xmlCode))
490             {
491                 Object JavaDoc[] args = { xmlCode, currentModule.getCode() };
492                 String JavaDoc error = Localization.format
493                     (ScarabConstants.DEFAULT_BUNDLE_NAME, getLocale(),
494                      "XMLAndCurrentCodeMismatch", args);
495                 importErrors.add(error);
496             }
497         }
498
499         si.doValidateDependencies();
500         si.doValidateUsers();
501     }
502
503     /**
504      * Do actual issue insert.
505      *
506      * Assumes issues passed have already been validated. If they haven't
507      * been, could damage scarab.
508      *
509      * @param name Name to use in log messages (E.g. filename). May be null.
510      * @param is Input stream of xml to insert.
511      * @param reader ScarabIssues bean reader instance.
512      *
513      * @return The instance of scarabissues we inserted in case you need to
514      * display info about the issues inserted.
515      */

516     protected ScarabIssues insert(String JavaDoc name, InputStream JavaDoc is,
517             BeanReader reader)
518         throws Exception JavaDoc
519     {
520         setValidationMode(reader, false);
521         ScarabIssues si = (ScarabIssues)reader.parse(is);
522         si.doHandleDependencies();
523         LOG.debug("Successfully imported " + name + '!');
524         return si;
525     }
526
527     /**
528      * Sets the validation mode for both this instance and the
529      * specified <code>Digester</code>.
530      *
531      * @param reader The XML parser to set the validation mode for.
532      * @param state The validation mode.
533      * @see <a HREF="http://xml.apache.org/xerces-j/faq-general.html#valid">Xerces validation FAQ</a>
534      * @see <a HREF="http://xml.apache.org/xerces-j/features.html">Xerces SAX2 feature list</a>
535      */

536     private void setValidationMode(Digester reader, boolean state)
537         throws ParserConfigurationException JavaDoc, SAXException JavaDoc
538     {
539         this.validationMode = state;
540
541         // Setup the XML parser SAX2 features.
542

543         // Turn on DTD validation (these are functionally equivalent
544
// with Xerces 1.4.4 and likely most other SAX2 impls).
545
reader.setValidating(state);
546         reader.setFeature("http://xml.org/sax/features/validation", state);
547
548         // Validate the document only if a grammar is specified
549
// (http://xml.org/sax/features/validation must be state).
550
reader.setFeature("http://apache.org/xml/features/validation/dynamic",
551                           state);
552     }
553
554     /**
555      * Get instance of the ScarabIssues used importing.
556      *
557      * You'd use this method to get at the instance of scarab issues used
558      * importing for case where you want to print out info on the import thats
559      * just happened. Call after a successful import. Calling before will give
560      * undefined results.
561      *
562      * @return Instance of ScarabIssues we ran the import with.
563      */

564     public ScarabIssues getScarabIssuesBeanReader()
565     {
566         return this.si;
567     }
568
569     /**
570      * Return a bean reader for ScarabIssue.
571      *
572      * @return A bean reader.
573      */

574     protected BeanReader createScarabIssuesBeanReader()
575         throws Exception JavaDoc
576     {
577         BeanReader reader = new BeanReader()
578             {
579                 public InputSource JavaDoc resolveEntity(String JavaDoc publicId,
580                                                  String JavaDoc systemId)
581                     throws SAXException JavaDoc
582                 {
583                     InputSource JavaDoc input = null;
584                     if (publicId == null && systemId != null)
585                     {
586                         // Resolve SYSTEM DOCTYPE.
587
if (SYSTEM_DTD_URI.equalsIgnoreCase(systemId) ||
588                             INTERNAL_DTD_URI.equalsIgnoreCase(systemId))
589                         {
590                             // First look for the DTD in the classpath.
591
input = resolveDTDResource();
592
593                             if (input == null)
594                             {
595                                 // Kick resolution back to Digester.
596
input = super.resolveEntity(publicId,
597                                                             systemId);
598                             }
599                         }
600                     }
601                     return input;
602                 }
603
604                 /**
605                  * Looks for the DTD in the classpath as resouce
606                  * {@link #DTD_RESOURCE}.
607                  *
608                  * @return The DTD, or <code>null</code> if not found.
609                  */

610                 private InputSource JavaDoc resolveDTDResource()
611                 {
612                     InputStream JavaDoc stream =
613                         getClass().getResourceAsStream(DTD_RESOURCE);
614                     if (stream != null)
615                     {
616                         LOG.debug("Located DTD in classpath using " +
617                                   "resource path '" + DTD_RESOURCE + '\'');
618                         return new InputSource JavaDoc(stream);
619                     }
620                     else
621                     {
622                         LOG.debug("DTD resource '" + DTD_RESOURCE + "' not " +
623                                   "found in classpath");
624                         return null;
625                     }
626                 }
627             };
628
629         // Connecting Digster's logger to ours logs too verbosely.
630
//reader.setLogger(LOG);
631
reader.register(SYSTEM_DTD_URI, INTERNAL_DTD_URI);
632         // Be forgiving about the encodings we accept.
633
reader.setFeature("http://apache.org/xml/features/allow-java-encodings",
634                           true);
635         reader.setXMLIntrospector(createXMLIntrospector());
636         reader.registerBeanClass(ScarabIssues.class);
637         NameMapper nm = reader.getXMLIntrospector().getNameMapper();
638         reader.addRule(nm.mapTypeToElementName
639                        (new BeanDescriptor JavaDoc(ScarabIssues.class).getName()),
640                        new ScarabIssuesSetupRule());
641         reader.setErrorHandler(this);
642         return reader;
643     }
644
645     protected XMLIntrospector createXMLIntrospector()
646     {
647         XMLIntrospector introspector = new XMLIntrospector();
648
649         // set elements for attributes to true
650
introspector.setAttributesForPrimitives(false);
651
652         // wrap collections in an XML element
653
//introspector.setWrapCollectionsInElement(true);
654

655         // turn bean elements into lower case
656
introspector.setElementNameMapper(new HyphenatedNameMapper());
657
658         return introspector;
659     }
660
661     /**
662      * A rule to perform setup of the a ScarabIssues instance.
663      */

664     class ScarabIssuesSetupRule extends Rule
665     {
666         public void begin(String JavaDoc namespace, String JavaDoc name, Attributes JavaDoc attributes)
667         {
668             ScarabIssues si = (ScarabIssues) getDigester().peek();
669             si.allowFileAttachments(allowFileAttachments);
670             si.inValidationMode(validationMode);
671             si.importErrors = importErrors;
672         }
673     }
674
675     /**
676      * Method to output the bean object as XML.
677      *
678      * Not used right now.
679      */

680     protected void write(Object JavaDoc bean, Writer JavaDoc out)
681         throws Exception JavaDoc
682     {
683         BeanWriter writer = new BeanWriter(out);
684         writer.setXMLIntrospector(createXMLIntrospector());
685         writer.enablePrettyPrint();
686         writer.setWriteIDs(false);
687         writer.write(bean);
688     }
689
690     private Locale JavaDoc getLocale()
691     {
692         return ScarabConstants.DEFAULT_LOCALE;
693     }
694
695
696     // ---- org.xml.sax.ErrorHandler implementation ------------------------
697

698     /** Receive notification of a recoverable error. */
699     public void error(SAXParseException JavaDoc e)
700         throws SAXParseException JavaDoc
701     {
702         LOG.error("Parse Error at line " + e.getLineNumber() +
703                   " column " + e.getColumnNumber() + ": " + e.getMessage(), e);
704         throw e; //EXCEPTION
705
}
706
707     /** Receive notification of a non-recoverable error. */
708     public void fatalError(SAXParseException JavaDoc e)
709         throws SAXParseException JavaDoc
710     {
711         LOG.error("Parse Fatal Error at line " + e.getLineNumber() +
712                   " column " + e.getColumnNumber() + ": " + e.getMessage(), e);
713         throw e; //EXCEPTION
714
}
715
716     /** Receive notification of a warning. */
717     public void warning(SAXParseException JavaDoc e)
718     {
719         // Warnings are non-fatal. At some point we should report
720
// these back to the end user.
721
LOG.debug("Parse Warning at line " + e.getLineNumber() +
722                   " column " + e.getColumnNumber() + ": " + e.getMessage());
723     }
724 }
725
Popular Tags