001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005:
006: /*
007: * Created on April 20, 2005
008: *
009: */
010: package org.vfny.geoserver.util;
011:
012: import org.apache.xerces.parsers.SAXParser;
013: import org.xml.sax.InputSource;
014: import org.xml.sax.SAXException;
015: import org.xml.sax.SAXParseException;
016: import org.xml.sax.helpers.DefaultHandler;
017: import java.io.BufferedReader;
018: import java.io.InputStream;
019: import java.io.InputStreamReader;
020: import java.io.Reader;
021: import java.net.URL;
022: import java.util.ArrayList;
023: import java.util.List;
024: import java.util.logging.Level;
025: import java.util.logging.Logger;
026: import javax.servlet.ServletContext;
027:
028: public class SLDValidator {
029: static Logger LOGGER = org.geotools.util.logging.Logging
030: .getLogger("org.vfny.geoserver");
031:
032: public SLDValidator() {
033: }
034:
035: /**
036: * validates against the "normal" location of the schema (ie.
037: * ".../schemas/sld/StyleLayerDescriptor.xsd" uses the geoserver_home
038: * patch
039: *
040: * @param xml
041: * @param servContext servlet context
042: *
043: * @return
044: */
045: public List validateSLD(InputStream xml, ServletContext servContext) {
046: // a riminder not to use the data directory for the schemas
047: //String url = GeoserverDataDirectory.getGeoserverDataDirectory(servContext).toString();
048: return validateSLD(new InputSource(xml), servContext);
049:
050: /*try {
051: URL schemaFile = servContext.getResource("/schemas/sld/StyledLayerDescriptor.xsd");
052: LOGGER.info("Validating SLD with " + schemaFile.toString());
053:
054: return validateSLD(xml, schemaFile.toString());
055: } catch (Exception e) {
056: LOGGER.severe(e.getLocalizedMessage());
057: ArrayList al = new ArrayList();
058: al.add(new SAXException(e));
059:
060: return al;
061: }*/
062: }
063:
064: public static String getErrorMessage(InputStream xml, List errors) {
065: return getErrorMessage(new InputStreamReader(xml), errors);
066: }
067:
068: /**
069: * returns a better formated error message - suitable for framing. There's
070: * a more complex version in StylesEditorAction. This will kick out a VERY
071: * LARGE errorMessage.
072: *
073: * @param xml
074: * @param errors
075: *
076: * @return DOCUMENT ME!
077: */
078: public static String getErrorMessage(Reader xml, List errors) {
079: BufferedReader reader = null;
080: StringBuffer result = new StringBuffer();
081: result.append("Your SLD is not valid.\n");
082: result
083: .append("Most common problems are: \n(1) no namespaces - use <ows:GetMap>, <sld:Rule>, <ogc:Filter>, <gml:Point> - the part before the ':' is important\n");
084: result
085: .append("(2) capitialization - use '<And>' not '<and>' \n");
086: result
087: .append("(3) Order - The order of elements is important \n");
088: result
089: .append("(4) Make sure your first tag imports the correct namespaces. ie. xmlns:sld=\"http://www.opengis.net/sld\" for EVERY NAMESPACE \n");
090: result.append("\n");
091:
092: try {
093: reader = new BufferedReader(xml);
094:
095: String line = reader.readLine();
096: int linenumber = 1;
097: int exceptionNum = 0;
098:
099: //check for lineNumber -1 errors --> invalid XML
100: if (errors.size() > 0) {
101: SAXParseException sax = (SAXParseException) errors
102: .get(0);
103:
104: if (sax.getLineNumber() < 0) {
105: result.append(" INVALID XML: "
106: + sax.getLocalizedMessage() + "\n");
107: result.append(" \n");
108: exceptionNum = 1; // skip ahead (you only ever get one error in this case)
109: }
110: }
111:
112: while (line != null) {
113: line.replace('\n', ' ');
114: line.replace('\r', ' ');
115:
116: String header = linenumber + ": ";
117: result.append(header + line + "\n"); // record the current line
118:
119: boolean keep_going = true;
120:
121: while (keep_going) {
122: if ((exceptionNum < errors.size())) {
123: SAXParseException sax = (SAXParseException) errors
124: .get(exceptionNum);
125:
126: if (sax.getLineNumber() <= linenumber) {
127: String head = "---------------------"
128: .substring(0, header.length() - 1);
129: String body = "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
130:
131: int colNum = sax.getColumnNumber(); //protect against col 0 problems
132:
133: if (colNum < 1) {
134: colNum = 1;
135: }
136:
137: if (colNum > body.length()) {
138: body = body + body + body + body + body
139: + body; // make it longer (not usually required, but might be for SLD_BODY=... which is all one line)
140:
141: if (colNum > body.length()) {
142: colNum = body.length();
143: }
144: }
145:
146: result.append(head
147: + body.substring(0, colNum - 1)
148: + "^\n");
149: result.append(" (line "
150: + sax.getLineNumber() + ", column "
151: + sax.getColumnNumber() + ")"
152: + sax.getLocalizedMessage() + "\n");
153: exceptionNum++;
154: } else {
155: keep_going = false; //report later (sax.getLineNumber() > linenumber)
156: }
157: } else {
158: keep_going = false; // no more errors to report
159: }
160: }
161:
162: line = reader.readLine(); //will be null at eof
163: linenumber++;
164: }
165:
166: for (int t = exceptionNum; t < errors.size(); t++) {
167: SAXParseException sax = (SAXParseException) errors
168: .get(t);
169: result.append(" (line " + sax.getLineNumber()
170: + ", column " + sax.getColumnNumber() + ")"
171: + sax.getLocalizedMessage() + "\n");
172: }
173: } catch (Exception e) {
174: e.printStackTrace();
175: } finally {
176: try {
177: if (reader != null) {
178: reader.close();
179: }
180: } catch (Exception e) {
181: e.printStackTrace();
182: }
183: }
184:
185: return result.toString();
186: }
187:
188: /*public List validateSLD(InputStream xml, String SchemaUrl) {
189: return validateSLD(new InputSource(xml), SchemaUrl);
190: }*/
191:
192: /*public List validateSLD(InputSource xml, ServletContext servContext) {
193: File schemaFile = new File(servContext.getRealPath("/"),
194: "/schemas/sld/StyledLayerDescriptor.xsd");
195:
196: try {
197: return validateSLD(xml, schemaFile.toURL().toString());
198: } catch (Exception e) {
199: ArrayList al = new ArrayList();
200: al.add(new SAXException(e));
201:
202: return al;
203: }
204: }*/
205:
206: /**
207: * validate a .sld against the schema
208: *
209: * @param xml input stream representing the .sld file
210: * @param SchemaUrl location of the schemas. Normally use
211: * ".../schemas/sld/StyleLayerDescriptor.xsd"
212: *
213: * @return list of SAXExceptions (0 if the file's okay)
214: */
215: public List validateSLD(InputSource xml, ServletContext servContext) {
216: SAXParser parser = new SAXParser();
217:
218: try {
219: // this takes care of spaces in the path to the file
220: URL schemaFile = servContext
221: .getResource("/schemas/sld/StyledLayerDescriptor.xsd");
222:
223: if (LOGGER.isLoggable(Level.INFO)) {
224: LOGGER.info(new StringBuffer("Validating SLD with ")
225: .append(schemaFile.toString()).toString());
226: }
227:
228: String schemaUrl = schemaFile.toString();
229:
230: // 1. tell the parser to validate the XML document vs the schema
231: // 2. does not validate the schema (the GML schema is *not* valid. This is
232: // an OGC blunder)
233: // 3. tells the validator that the tags without a namespace are actually
234: // SLD tags.
235: // 4. tells the validator to 'override' the SLD schema that a user may
236: // include with the one inside geoserver.
237: parser.setFeature("http://xml.org/sax/features/validation",
238: true);
239: parser.setFeature(
240: "http://apache.org/xml/features/validation/schema",
241: true);
242: parser
243: .setFeature(
244: "http://apache.org/xml/features/validation/schema-full-checking",
245: false);
246:
247: parser
248: .setProperty(
249: "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",
250: schemaUrl);
251: parser
252: .setProperty(
253: "http://apache.org/xml/properties/schema/external-schemaLocation",
254: "http://www.opengis.net/sld " + schemaUrl);
255:
256: //parser.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation","http://www.opengis.net/ows "+SchemaUrl);
257: Validator handler = new Validator();
258: parser.setErrorHandler(handler);
259: parser.parse(xml);
260:
261: return handler.errors;
262: } catch (java.io.IOException ioe) {
263: ArrayList al = new ArrayList();
264: al.add(new SAXParseException(ioe.getLocalizedMessage(),
265: null));
266:
267: return al;
268: } catch (SAXException e) {
269: ArrayList al = new ArrayList();
270: al
271: .add(new SAXParseException(e.getLocalizedMessage(),
272: null));
273:
274: return al;
275: }
276: }
277:
278: // errors in the document will be put in "errors".
279: // if errors.size() ==0 then there were no errors.
280: private class Validator extends DefaultHandler {
281: public ArrayList errors = new ArrayList();
282:
283: public void error(SAXParseException exception)
284: throws SAXException {
285: errors.add(exception);
286: }
287:
288: public void fatalError(SAXParseException exception)
289: throws SAXException {
290: errors.add(exception);
291: }
292:
293: public void warning(SAXParseException exception)
294: throws SAXException {
295: //do nothing
296: }
297: }
298: }
|