1 package com.thaiopensource.validate.mns; 2 3 import com.thaiopensource.util.Localizer; 4 import com.thaiopensource.util.PropertyMap; 5 import com.thaiopensource.util.Uri; 6 import com.thaiopensource.util.PropertyMapBuilder; 7 import com.thaiopensource.validate.IncorrectSchemaException; 8 import com.thaiopensource.validate.Schema; 9 import com.thaiopensource.validate.ValidateProperty; 10 import com.thaiopensource.validate.Validator; 11 import com.thaiopensource.validate.AbstractSchema; 12 import com.thaiopensource.validate.auto.SchemaFuture; 13 import com.thaiopensource.xml.sax.XmlBaseHandler; 14 import com.thaiopensource.xml.sax.DelegatingContentHandler; 15 import com.thaiopensource.xml.sax.CountingErrorHandler; 16 import com.thaiopensource.xml.util.Name; 17 import com.thaiopensource.xml.util.StringSplitter; 18 import com.thaiopensource.xml.util.WellKnownNamespaces; 19 import org.xml.sax.Attributes; 20 import org.xml.sax.ErrorHandler; 21 import org.xml.sax.InputSource; 22 import org.xml.sax.Locator; 23 import org.xml.sax.SAXException; 24 import org.xml.sax.SAXParseException; 25 import org.xml.sax.XMLReader; 26 import org.xml.sax.helpers.LocatorImpl; 27 28 import java.io.IOException; 29 import java.util.Enumeration; 30 import java.util.Hashtable; 31 import java.util.Stack; 32 33 class SchemaImpl extends AbstractSchema { 34 static final String MNS_URI = "http://www.thaiopensource.com/ns/mns"; 35 private final Hashtable modeMap = new Hashtable(); 36 private Mode startMode; 37 private static final String DEFAULT_MODE_NAME = "#default"; 38 private final boolean attributesSchema; 39 40 static private final class WrappedIOException extends RuntimeException { 41 private final IOException exception; 42 43 private WrappedIOException(IOException exception) { 44 this.exception = exception; 45 } 46 47 private IOException getException() { 48 return exception; 49 } 50 } 51 52 static class ElementAction { 53 private final Schema schema; 54 private final Mode mode; 55 private final ContextMap contextMap; 56 private final ElementsOrAttributes prune; 57 private final Hashset covered = new Hashset(); 58 59 ElementAction(String ns, Schema schema, Mode mode, ContextMap contextMap, ElementsOrAttributes prune) { 60 this.schema = schema; 61 this.mode = mode; 62 this.contextMap = contextMap; 63 this.prune = prune; 64 covered.add(ns); 65 } 66 67 Mode getMode() { 68 return mode; 69 } 70 71 ContextMap getContextMap() { 72 return contextMap; 73 } 74 75 Schema getSchema() { 76 return schema; 77 } 78 79 ElementsOrAttributes getPrune() { 80 return prune; 81 } 82 83 Hashset getCoveredNamespaces() { 84 return covered; 85 } 86 } 87 88 static class Mode { 89 private Locator whereDefined; 90 private boolean defined = false; 91 private ElementsOrAttributes lax; 92 private boolean strictDefined = false; 93 private final Hashtable elementMap = new Hashtable(); 94 private final Hashtable attributesMap = new Hashtable(); 95 96 Mode(ElementsOrAttributes lax) { 97 this.lax = lax; 98 } 99 100 ElementsOrAttributes getLax() { 101 return lax; 102 } 103 104 Schema getAttributesSchema(String ns) { 105 return (Schema)attributesMap.get(ns); 106 } 107 108 ElementAction getElementAction(String ns) { 109 return (ElementAction)elementMap.get(ns); 110 } 111 } 112 113 private class Handler extends DelegatingContentHandler implements SchemaFuture { 114 private final SchemaReceiverImpl sr; 115 private ElementAction currentElementAction; 116 private boolean hadError = false; 117 private final ErrorHandler eh; 118 private final CountingErrorHandler ceh; 119 private final Localizer localizer = new Localizer(SchemaImpl.class); 120 private Locator locator; 121 private final XmlBaseHandler xmlBaseHandler = new XmlBaseHandler(); 122 private int foreignDepth = 0; 123 private String contextNs; 124 private Mode contextMode; 125 private String elementNs; 126 private String defaultSchemaType; 127 private final Stack nameStack = new Stack(); 128 private boolean isRoot; 129 private int pathDepth = 0; 130 private Validator validator; 131 132 133 Handler(SchemaReceiverImpl sr) { 134 this.sr = sr; 135 this.eh = ValidateProperty.ERROR_HANDLER.get(sr.getProperties()); 136 this.ceh = new CountingErrorHandler(eh); 137 } 138 139 public void setDocumentLocator(Locator locator) { 140 xmlBaseHandler.setLocator(locator); 141 this.locator = locator; 142 } 143 144 public void startDocument() throws SAXException { 145 try { 146 PropertyMapBuilder builder = new PropertyMapBuilder(sr.getProperties()); 147 ValidateProperty.ERROR_HANDLER.put(builder, ceh); 148 validator = sr.getMnsSchema().createValidator(builder.toPropertyMap()); 149 } 150 catch (IOException e) { 151 throw new WrappedIOException(e); 152 } 153 catch (IncorrectSchemaException e) { 154 throw new RuntimeException("internal error in RNG schema for MNS"); 155 } 156 setDelegate(validator.getContentHandler()); 157 if (locator != null) 158 super.setDocumentLocator(locator); 159 super.startDocument(); 160 } 161 162 public Schema getSchema() throws IncorrectSchemaException, SAXException { 163 if (validator == null || ceh.getHadErrorOrFatalError()) 164 throw new IncorrectSchemaException(); 165 for (Enumeration enum = modeMap.keys(); enum.hasMoreElements();) { 166 String modeName = (String)enum.nextElement(); 167 Mode mode = (Mode)modeMap.get(modeName); 168 if (!mode.defined && !modeName.equals(DEFAULT_MODE_NAME)) 169 error("undefined_mode", modeName, mode.whereDefined); 170 } 171 if (hadError) 172 throw new IncorrectSchemaException(); 173 return SchemaImpl.this; 174 } 175 176 public RuntimeException unwrapException(RuntimeException e) throws SAXException, IOException, IncorrectSchemaException { 177 if (e instanceof WrappedIOException) 178 throw ((WrappedIOException)e).getException(); 179 return e; 180 } 181 182 public void startElement(String uri, String localName, 183 String qName, Attributes attributes) 184 throws SAXException { 185 super.startElement(uri, localName, qName, attributes); 186 xmlBaseHandler.startElement(); 187 String xmlBase = attributes.getValue(WellKnownNamespaces.XML, "base"); 188 if (xmlBase != null) 189 xmlBaseHandler.xmlBaseAttribute(xmlBase); 190 if (!MNS_URI.equals(uri) || foreignDepth > 0) { 191 foreignDepth++; 192 return; 193 } 194 if (ceh.getHadErrorOrFatalError()) 195 return; 196 if (localName.equals("rules")) 197 parseRules(attributes); 198 else if (localName.equals("cover")) 199 parseCover(attributes); 200 else if (localName.equals("context")) 201 parseContext(attributes); 202 else if (localName.equals("root")) 203 parseRoot(attributes); 204 else if (localName.equals("element")) 205 parseElement(attributes); 206 else if (localName.equals("lax")) 207 parseLax(attributes); 208 else 209 parseValidate(localName.equals("validateAttributes"), attributes); 210 } 211 212 public void endElement(String namespaceURI, String localName, 213 String qName) 214 throws SAXException { 215 super.endElement(namespaceURI, localName, qName); 216 xmlBaseHandler.endElement(); 217 if (foreignDepth > 0) { 218 foreignDepth--; 219 return; 220 } 221 if (pathDepth > 0) { 222 pathDepth--; 223 if (pathDepth == 0) 224 endPath(); 225 } 226 } 227 228 229 private void parseRules(Attributes attributes) { 230 String modeName = attributes.getValue("", "startMode"); 231 if (modeName == null) 232 modeName = DEFAULT_MODE_NAME; 233 defaultSchemaType = getSchemaType(attributes); 234 startMode = lookupCreateMode(modeName); 235 } 236 237 private void parseCover(Attributes attributes) throws SAXException { 238 String ns = getNs(attributes, false); 239 currentElementAction.covered.add(ns); 240 } 241 242 private void parseLax(Attributes attributes) throws SAXException { 243 String[] modeNames = getInModes(attributes); 244 Mode[] modes = getModes(modeNames); 245 ElementsOrAttributes lax = toElementsOrAttributes(attributes.getValue("", "allow"), 246 ElementsOrAttributes.BOTH); 247 for (int i = 0; i < modes.length; i++) { 248 if (modes[i].strictDefined) 249 error("lax_multiply_defined", modeNames[i]); 250 else { 251 modes[i].lax = lax; 252 modes[i].strictDefined = true; 253 } 254 } 255 } 256 257 private void parseValidate(boolean isAttribute, Attributes attributes) throws SAXException { 258 String[] modeNames = getInModes(attributes); 259 Mode[] modes = getModes(modeNames); 260 String ns = getNs(attributes, isAttribute); 261 String schemaUri = getSchema(attributes); 262 String schemaType = getSchemaType(attributes); 263 if (schemaType == null) 264 schemaType = defaultSchemaType; 265 try { 266 if (isAttribute) { 267 Schema schema = sr.createChildSchema(new InputSource(schemaUri), schemaType, true); 268 for (int i = 0; i < modes.length; i++) { 269 if (modes[i].attributesMap.get(ns) != null) 270 error("validate_attributes_multiply_defined", modeNames[i], ns); 271 else 272 modes[i].attributesMap.put(ns, schema); 273 } 274 } 275 else { 276 Schema schema = sr.createChildSchema(new InputSource(schemaUri), schemaType, false); 277 currentElementAction = new ElementAction(ns, 278 schema, 279 getUseMode(attributes), 280 new ContextMap(), 281 getPrune(attributes)); 282 contextNs = ns; 283 for (int i = 0; i < modes.length; i++) { 284 if (modes[i].elementMap.get(ns) != null) 285 error("validate_element_multiply_defined", modeNames[i], ns); 286 else 287 modes[i].elementMap.put(ns, currentElementAction); 288 } 289 } 290 } 291 catch (IncorrectSchemaException e) { 292 hadError = true; 293 } 294 catch (IOException e) { 295 throw new WrappedIOException(e); 296 } 297 } 298 299 private void parseContext(Attributes attributes) throws SAXException { 300 String ns = getNs(attributes, false); 301 if (ns != null) 302 contextNs = ns; 303 elementNs = contextNs; 304 contextMode = getUseMode(attributes); 305 } 306 307 private void parseRoot(Attributes attributes) throws SAXException { 308 String ns = getNs(attributes, false); 309 if (ns != null) 310 elementNs = ns; 311 isRoot = true; 312 pathDepth++; 313 } 314 315 private void parseElement(Attributes attributes) throws SAXException { 316 String ns = getNs(attributes, false); 317 if (ns != null) 318 elementNs = ns; 319 if (!currentElementAction.covered.contains(elementNs)) 320 error("context_ns_not_covered", elementNs); 321 nameStack.push(new Name(elementNs, attributes.getValue("", "name").trim())); 322 pathDepth++; 323 } 324 325 private void endPath() throws SAXException { 326 if (!currentElementAction.contextMap.put(isRoot, nameStack, contextMode)) 327 error("path_multiply_defined", displayPath(isRoot, nameStack)); 328 elementNs = contextNs; 329 isRoot = false; 330 nameStack.setSize(0); 331 } 332 333 private String displayPath(boolean isRoot, Stack nameStack) { 334 StringBuffer buf = new StringBuffer(); 335 for (int i = 0, len = nameStack.size(); i < len; i++) { 336 if (i > 0 || isRoot) 337 buf.append('/'); 338 Name name = (Name)nameStack.elementAt(i); 339 if (name.getNamespaceUri().length() > 0) { 340 buf.append('{'); 341 buf.append(name.getNamespaceUri()); 342 buf.append('}'); 343 } 344 buf.append(name.getLocalName()); 345 } 346 return buf.toString(); 347 } 348 349 private String getSchema(Attributes attributes) throws SAXException { 350 String schemaUri = attributes.getValue("", "schema"); 351 if (Uri.hasFragmentId(schemaUri)) 352 error("schema_fragment_id"); 353 return Uri.resolve(xmlBaseHandler.getBaseUri(), 354 Uri.escapeDisallowedChars(schemaUri)); 355 } 356 357 private String getSchemaType(Attributes attributes) { 358 return attributes.getValue("", "schemaType"); 359 } 360 361 private ElementsOrAttributes getPrune(Attributes attributes) { 362 return toElementsOrAttributes(attributes.getValue("", "prune"), 363 ElementsOrAttributes.NEITHER); 364 } 365 366 private ElementsOrAttributes toElementsOrAttributes(String value, ElementsOrAttributes defaultValue) { 367 if (value == null) 368 return defaultValue; 369 ElementsOrAttributes eoa = ElementsOrAttributes.NEITHER; 370 if (value.indexOf("elements") >= 0) 371 eoa = eoa.addElements(); 372 if (value.indexOf("attributes") >= 0) 373 eoa = eoa.addAttributes(); 374 return eoa; 375 } 376 377 private Mode getUseMode(Attributes attributes) { 378 String modeName = attributes.getValue("", "useMode"); 379 if (modeName == null) 380 modeName = DEFAULT_MODE_NAME; 381 Mode mode = lookupCreateMode(modeName); 382 if (mode.whereDefined == null && locator != null) 383 mode.whereDefined = new LocatorImpl(locator); 384 return mode; 385 } 386 387 private String getNs(Attributes attributes, boolean forbidEmpty) throws SAXException { 388 String ns = attributes.getValue("", "ns"); 389 if (ns != null && !Uri.isAbsolute(ns) && (forbidEmpty || !ns.equals(""))) 390 error("ns_absolute"); 391 return ns; 392 } 393 394 private Mode[] getModes(String[] modeNames) { 395 Mode[] modes = new Mode[modeNames.length]; 396 for (int i = 0; i < modes.length; i++) { 397 modes[i] = lookupCreateMode(modeNames[i]); 398 modes[i].defined = true; 399 } 400 return modes; 401 } 402 403 private String[] getInModes(Attributes attributes) { 404 String inModes = attributes.getValue("", "inModes"); 405 if (inModes == null) 406 return new String[] { DEFAULT_MODE_NAME }; 407 return StringSplitter.split(inModes); 408 } 409 410 411 void error(String key) throws SAXException { 412 hadError = true; 413 if (eh == null) 414 return; 415 eh.error(new SAXParseException(localizer.message(key), locator)); 416 } 417 418 void error(String key, String arg) throws SAXException { 419 hadError = true; 420 if (eh == null) 421 return; 422 eh.error(new SAXParseException(localizer.message(key, arg), locator)); 423 } 424 425 void error(String key, String arg, Locator locator) throws SAXException { 426 hadError = true; 427 if (eh == null) 428 return; 429 eh.error(new SAXParseException(localizer.message(key, arg), locator)); 430 } 431 432 void error(String key, String arg1, String arg2) throws SAXException { 433 hadError = true; 434 if (eh == null) 435 return; 436 eh.error(new SAXParseException(localizer.message(key, arg1, arg2), locator)); 437 } 438 439 } 440 441 SchemaImpl(boolean attributesSchema) { 442 this.attributesSchema = attributesSchema; 443 } 444 445 SchemaFuture installHandlers(XMLReader in, SchemaReceiverImpl sr) { 446 Handler h = new Handler(sr); 447 in.setContentHandler(h); 448 return h; 449 } 450 451 public Validator createValidator(PropertyMap properties) { 452 return new ValidatorImpl(startMode, properties); 453 } 454 455 private Mode lookupCreateMode(String name) { 456 Mode mode = (Mode)modeMap.get(name); 457 if (mode == null) { 458 mode = new Mode(attributesSchema ? ElementsOrAttributes.ELEMENTS : ElementsOrAttributes.NEITHER); 459 modeMap.put(name, mode); 460 } 461 return mode; 462 } 463 464 } 465 | Popular Tags |