001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.compiler.grammar;
020:
021: import org.apache.beehive.netui.compiler.AnnotationGrammar;
022: import org.apache.beehive.netui.compiler.FlowControllerInfo;
023: import org.apache.beehive.netui.compiler.LocalFileEntityResolver;
024: import org.apache.beehive.netui.compiler.typesystem.declaration.AnnotationValue;
025: import org.xml.sax.InputSource;
026: import org.xml.sax.SAXException;
027: import org.xml.sax.SAXParseException;
028: import org.xml.sax.helpers.DefaultHandler;
029: import org.w3c.dom.Document;
030:
031: import javax.xml.parsers.DocumentBuilder;
032: import javax.xml.parsers.DocumentBuilderFactory;
033: import java.io.File;
034: import java.util.ArrayList;
035: import java.util.Collections;
036: import java.util.HashMap;
037: import java.util.Iterator;
038: import java.util.List;
039: import java.util.Map;
040:
041: /**
042: * A type that requires a valid XML file. Can accept a specific XML schema, and will fall back to DTD-checking.
043: */
044: public class ValidXmlFileType extends WebappPathType {
045: private String _schemaFileName;
046: private static Map _parseResults = Collections
047: .synchronizedMap(new HashMap());
048:
049: public ValidXmlFileType(String schemaFileName,
050: String requiredRuntimeVersion,
051: AnnotationGrammar parentGrammar, FlowControllerInfo fcInfo) {
052: super (false, requiredRuntimeVersion, parentGrammar, fcInfo);
053: _schemaFileName = schemaFileName;
054: }
055:
056: protected boolean checkAnyExtension() {
057: return true;
058: }
059:
060: protected boolean doFatalError() {
061: return true;
062: }
063:
064: protected boolean ignoreDirectories() {
065: return false;
066: }
067:
068: protected boolean allowFileInPageFlowSourceDir() {
069: return true;
070: }
071:
072: protected void runAdditionalChecks(File file, AnnotationValue value) {
073:
074: //
075: // We cache the results of parsing the file until the file is actually modified,
076: // so we don't end up continually re-parsing it.
077: //
078: ParseResults prevResults = (ParseResults) _parseResults
079: .get(file.getPath());
080: long lastModTime = file.lastModified();
081:
082: if (prevResults == null
083: || lastModTime > prevResults.getFileModTime()) {
084: try {
085: DocumentBuilderFactory factory = DocumentBuilderFactory
086: .newInstance();
087: LocalFileEntityResolver entityResolver = LocalFileEntityResolver
088: .getInstance();
089:
090: // If a schema was specified, we'll validate against that; otherwise, we'll just use the DTD.
091: if (_schemaFileName != null) {
092: InputSource schemaInput = entityResolver
093: .resolveLocalEntity(_schemaFileName);
094: assert schemaInput != null : "could not get schema resource for "
095: + _schemaFileName;
096: factory.setNamespaceAware(true);
097: factory
098: .setAttribute(
099: "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
100: "http://www.w3.org/2001/XMLSchema");
101: factory
102: .setAttribute(
103: "http://java.sun.com/xml/jaxp/properties/schemaSource",
104: schemaInput);
105: }
106: factory.setValidating(true);
107: DocumentBuilder builder = factory.newDocumentBuilder();
108: builder.setEntityResolver(LocalFileEntityResolver
109: .getInstance());
110: ParseResults results = new ParseResults(lastModTime);
111: Validator handler = new Validator(results);
112: builder.setErrorHandler(handler);
113: Document doc = builder.parse(file);
114: if (doc.getDoctype() == null && _schemaFileName == null) {
115: // If the doctype is null, then we don't want to add errors -- there was no DTD identified.
116: results = new ParseResults(lastModTime);
117: }
118: _parseResults.put(file.getPath(), results);
119: addErrorDiagnostics(file, results, value);
120: } catch (SAXParseException e) {
121: _parseResults.put(file.getPath(), new ParseResults(
122: lastModTime, e));
123: addDiagnostic(file, e, value, true);
124: return;
125: } catch (Exception e) {
126: _parseResults.put(file.getPath(), new ParseResults(
127: lastModTime, e));
128: addError(value, "error.xml-read-error", new Object[] {
129: file.getPath(), e.getClass().getName(),
130: e.getMessage() });
131: return;
132: }
133: } else {
134: addErrorDiagnostics(file, prevResults, value);
135: }
136: }
137:
138: private void addErrorDiagnostics(File file, ParseResults results,
139: AnnotationValue value) {
140: List errors = results.getErrors();
141:
142: for (Iterator i = errors.iterator(); i.hasNext();) {
143: Exception e = (Exception) i.next();
144:
145: if (e instanceof SAXParseException) {
146: addDiagnostic(file, (SAXParseException) e, value, true);
147: } else {
148: addError(value, "error.xml-read-error", new Object[] {
149: file.getPath(), e.getClass().getName(),
150: e.getMessage() });
151: }
152: }
153:
154: List warnings = results.getWarnings();
155:
156: for (Iterator i = warnings.iterator(); i.hasNext();) {
157: Exception e = (Exception) i.next();
158: assert e instanceof SAXParseException : e.getClass()
159: .getName();
160: addDiagnostic(file, (SAXParseException) e, value, false);
161: }
162: }
163:
164: private void addDiagnostic(File file, SAXParseException err,
165: AnnotationValue value, boolean isError) {
166: if (err.getColumnNumber() != -1 && err.getLineNumber() != -1) {
167: Object[] args = { file.getPath(),
168: new Integer(err.getLineNumber()),
169: new Integer(err.getColumnNumber()),
170: err.getMessage() };
171:
172: if (isError) {
173: addError(value, "error.xml-parse-error", args);
174: } else {
175: addWarning(value, "error.xml-parse-error", args);
176: }
177: } else if (err.getLineNumber() != -1) {
178: Object[] args = { file.getPath(),
179: new Integer(err.getLineNumber()), err.getMessage() };
180:
181: if (isError) {
182: addError(value, "error.xml-parse-error-nocolumn", args);
183: } else {
184: addWarning(value, "error.xml-parse-error-nocolumn",
185: args);
186: }
187: } else {
188: Object[] args = { file.getPath(), err.getMessage() };
189:
190: if (isError) {
191: addError(value, "error.xml-parse-error-nolinecolumn",
192: args);
193: } else {
194: addWarning(value, "error.xml-parse-error-nolinecolumn",
195: args);
196: }
197: }
198: }
199:
200: private static class ParseResults {
201: private long _fileModTime;
202: private List _errors = null;
203: private List _warnings = null;
204:
205: public ParseResults(long fileModTime) {
206: _fileModTime = fileModTime;
207: }
208:
209: public ParseResults(long fileModTime, Exception ex) {
210: _fileModTime = fileModTime;
211: addError(ex);
212: }
213:
214: public long getFileModTime() {
215: return _fileModTime;
216: }
217:
218: public void setFileModTime(long fileModTime) {
219: _fileModTime = fileModTime;
220: }
221:
222: public void addError(Exception e) {
223: if (_errors == null) {
224: _errors = new ArrayList();
225: }
226: _errors.add(e);
227: }
228:
229: public void addWarning(Exception e) {
230: if (_warnings == null) {
231: _warnings = new ArrayList();
232: }
233: _warnings.add(e);
234: }
235:
236: public List getErrors() {
237: return _errors != null ? _errors : Collections.EMPTY_LIST;
238: }
239:
240: public List getWarnings() {
241: return _warnings != null ? _warnings
242: : Collections.EMPTY_LIST;
243: }
244: }
245:
246: private static class Validator extends DefaultHandler {
247:
248: private ParseResults _results;
249:
250: public Validator(ParseResults results) {
251: _results = results;
252: }
253:
254: public ParseResults getResults() {
255: return _results;
256: }
257:
258: public void error(SAXParseException ex) throws SAXException {
259: _results.addError(ex);
260: }
261:
262: public void fatalError(SAXParseException ex)
263: throws SAXException {
264: _results.addError(ex);
265: }
266:
267: public void warning(SAXParseException ex) throws SAXException {
268: _results.addWarning(ex);
269: }
270: }
271: }
|