001 //$HeadURL: https://sushibar/svn/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 037 package org.deegree.tools.xml; 038 039 import static java.lang.System.out; 040 import static org.deegree.framework.log.LoggerFactory.getLogger; 041 import static org.deegree.framework.xml.DOMPrinter.nodeToString; 042 import static org.deegree.framework.xml.XMLTools.getNode; 043 import static org.deegree.framework.xml.XMLTools.getNodes; 044 import static org.deegree.ogcbase.CommonNamespaces.getNamespaceContext; 045 046 import java.io.BufferedReader; 047 import java.io.File; 048 import java.io.IOException; 049 import java.io.InputStreamReader; 050 import java.net.MalformedURLException; 051 import java.net.URL; 052 import java.util.LinkedList; 053 import java.util.List; 054 import java.util.StringTokenizer; 055 056 import org.deegree.framework.log.ILogger; 057 import org.deegree.framework.util.Pair; 058 import org.deegree.framework.util.StringPair; 059 import org.deegree.framework.xml.NamespaceContext; 060 import org.deegree.framework.xml.XMLFragment; 061 import org.deegree.framework.xml.XMLParsingException; 062 import org.jaxen.JaxenException; 063 import org.jaxen.XPath; 064 import org.jaxen.dom.DOMXPath; 065 import org.w3c.dom.Node; 066 import org.xml.sax.SAXException; 067 068 /** 069 * <code>SimpleValidator</code> is a simple xpath based "validator". It can be used to crudely 070 * check XML documents for existing nodes. A sample rule file can be found right beneath in this 071 * package. 072 * 073 * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a> 074 * @author last edited by: $Author:$ 075 * 076 * @version $Revision:$, $Date:$ 077 */ 078 public class SimpleValidator { 079 080 static final NamespaceContext nsContext = getNamespaceContext(); 081 082 private static final ILogger LOG = getLogger( SimpleValidator.class ); 083 084 private LinkedList<Rule> rules; 085 086 /** 087 * Initializes from configuration URL. File should be encoded in UTF-8. 088 * 089 * @param config 090 * @throws IOException 091 */ 092 public SimpleValidator( URL config ) throws IOException { 093 rules = new LinkedList<Rule>(); 094 095 BufferedReader in = new BufferedReader( new InputStreamReader( config.openStream() ) ); 096 097 StringBuffer buf = new StringBuffer( 65536 ); 098 String s; 099 while ( ( s = in.readLine() ) != null ) { 100 if ( s.startsWith( "#" ) ) { 101 continue; 102 } 103 buf.append( s ).append( " " ); 104 } 105 106 in.close(); 107 108 StringTokenizer tok = new StringTokenizer( buf.toString() ); 109 String cur = tok.nextToken(); 110 while ( tok.hasMoreTokens() ) { 111 Pair<Rule, String> p = parseRule( tok, cur ); 112 cur = p.second; 113 rules.add( p.first ); 114 } 115 116 LOG.logDebug( "Parsed rule file with " + rules.size() + " rules." ); 117 } 118 119 private static Pair<Rule, String> parseRule( StringTokenizer tok, String first ) 120 throws IOException { 121 String id = first; 122 String s = tok.nextToken(); 123 124 if ( s.equalsIgnoreCase( "if" ) ) { 125 String test = tok.nextToken(); 126 if ( !tok.nextToken().equalsIgnoreCase( "then" ) ) { 127 throw new IOException( "Missing 'then' after " + test + "." ); 128 } 129 130 Pair<Rule, String> then = parseRule( tok, tok.nextToken() ); 131 132 return new Pair<Rule, String>( new Rule( id, test, then.first ), then.second ); 133 } 134 135 if ( s.equalsIgnoreCase( "oneof" ) ) { 136 String base = tok.nextToken(); 137 List<String> choices = new LinkedList<String>(); 138 139 s = tok.nextToken(); 140 while ( tok.hasMoreTokens() && s.equalsIgnoreCase( "choice" ) ) { 141 choices.add( tok.nextToken() ); 142 if ( tok.hasMoreTokens() ) { 143 s = tok.nextToken(); 144 } 145 } 146 147 return new Pair<Rule, String>( new Rule( id, base, choices ), s ); 148 } 149 150 boolean isBoolean = false; 151 152 if ( s.equalsIgnoreCase( "istrue" ) ) { 153 s = tok.nextToken(); 154 // if ( !s.startsWith( "boolean(" ) ) { 155 // s = "boolean(" + s + ")"; 156 // } 157 isBoolean = true; 158 } 159 160 String next = tok.hasMoreTokens() ? tok.nextToken() : null; 161 return new Pair<Rule, String>( new Rule( id, s, isBoolean ), next ); 162 } 163 164 /** 165 * @param n 166 * @return a list of errors. A pair will include the id of the failed rule, and the context node 167 * as string (if applicable) or null (if not). 168 */ 169 public LinkedList<StringPair> validate( Node n ) { 170 LinkedList<StringPair> list = new LinkedList<StringPair>(); 171 172 for ( Rule r : rules ) { 173 if ( !r.eval( n ) ) { 174 list.addAll( r.errors ); 175 } 176 } 177 178 return list; 179 } 180 181 /** 182 * @param args 183 */ 184 public static void main( String[] args ) { 185 if ( args.length < 2 ) { 186 out.println( "Usage:" ); 187 out.println( "org.deegree.tools.xml.SimpleValidator <rulesfile> <xmlfiletovalidate> [-v]" ); 188 return; 189 } 190 191 boolean verbose = args.length > 2 && args[2].equals( "-v" ); 192 193 try { 194 XMLFragment doc = new XMLFragment( new File( args[1] ) ); 195 SimpleValidator val = new SimpleValidator( new File( args[0] ).toURI().toURL() ); 196 197 LinkedList<StringPair> errors = val.validate( doc.getRootElement() ); 198 if ( errors.size() > 0 ) { 199 out.println( "Errors:" ); 200 for ( StringPair p : errors ) { 201 if ( verbose ) { 202 out.println( p ); 203 } else { 204 out.print( p.first ); 205 if ( errors.indexOf( p ) != errors.size() - 1 ) { 206 out.print( ", " ); 207 } 208 } 209 } 210 if ( !verbose ) { 211 out.println(); 212 } 213 } else { 214 out.println( "All rules passed." ); 215 } 216 } catch ( MalformedURLException e ) { 217 out.println( "Error: one of the files is not a valid filename." ); 218 out.println( "Usage:" ); 219 out.println( "org.deegree.tools.xml.SimpleValidator <rulesfile> <xmlfiletovalidate> [-v]" ); 220 } catch ( IOException e ) { 221 out.println( "Error: second file cannot be read." ); 222 out.println( "Usage:" ); 223 out.println( "org.deegree.tools.xml.SimpleValidator <rulesfile> <xmlfiletovalidate> [-v]" ); 224 } catch ( SAXException e ) { 225 out.println( "Error: second file is not parsable XML." ); 226 out.println( "Usage:" ); 227 out.println( "org.deegree.tools.xml.SimpleValidator <rulesfile> <xmlfiletovalidate> [-v]" ); 228 } 229 } 230 231 private static class Rule { 232 233 String id; 234 235 List<StringPair> errors; 236 237 private String xpath; 238 239 private Rule then; 240 241 private List<String> choices; 242 243 private boolean isBoolean; 244 245 Rule( String id, String xpath ) { 246 this.xpath = xpath; 247 this.id = id; 248 errors = new LinkedList<StringPair>(); 249 } 250 251 Rule( String id, String xpath, Rule then ) { 252 this( id, xpath ); 253 this.then = then; 254 } 255 256 Rule( String id, String base, List<String> choices ) { 257 this( id, base ); 258 this.choices = choices; 259 } 260 261 Rule( String id, String xpath, boolean isBoolean ) { 262 this( id, xpath ); 263 this.isBoolean = isBoolean; 264 } 265 266 boolean eval( Node n ) { 267 try { 268 269 if ( isBoolean ) { 270 try { 271 XPath xpath = new DOMXPath( this.xpath ); 272 xpath.setNamespaceContext( nsContext ); 273 boolean res = xpath.booleanValueOf( n ); 274 if ( !res ) { 275 errors.add( new StringPair( id, "The IsTrue expression evaluated to false." ) ); 276 } 277 return res; 278 } catch ( JaxenException e ) { 279 errors.add( new StringPair( id, "The xpath expression contained an error: " 280 + e.getLocalizedMessage() ) ); 281 return false; 282 } 283 } 284 285 if ( choices != null ) { 286 boolean isOk = false; 287 288 Node baseNode = getNode( n, xpath, nsContext ); 289 290 if ( baseNode == null ) { 291 errors.add( new StringPair( id, "(node not found)" ) ); 292 return false; 293 } 294 295 for ( String x : choices ) { 296 Node tmp = getNode( baseNode, x, nsContext ); 297 isOk = isOk || tmp != null; 298 } 299 300 if ( !isOk ) { 301 errors.add( new StringPair( id, nodeToString( baseNode, "UTF-8" ) ) ); 302 } 303 304 return isOk; 305 } 306 307 List<Node> tmps = getNodes( n, xpath, nsContext ); 308 309 if ( then == null ) { 310 if ( tmps.size() == 0 ) { 311 errors.add( new StringPair( id, null ) ); 312 } 313 return tmps.size() != 0; 314 } 315 316 if ( tmps.size() == 0 ) { 317 return true; 318 } 319 320 boolean res = true; 321 322 for ( Node tmp : tmps ) { 323 if ( then.eval( tmp ) ) { 324 continue; 325 } 326 327 res = false; 328 329 errors.add( new StringPair( then.id, nodeToString( tmp, "UTF-8" ) ) ); 330 } 331 332 return res; 333 } catch ( XMLParsingException e ) { 334 return false; 335 } 336 } 337 338 } 339 340 }