001 package com.thaiopensource.validate.nrl; 002 003 import org.xml.sax.helpers.DefaultHandler; 004 import org.xml.sax.ErrorHandler; 005 import org.xml.sax.Attributes; 006 import org.xml.sax.SAXException; 007 import org.xml.sax.ContentHandler; 008 import org.xml.sax.Locator; 009 import org.xml.sax.SAXParseException; 010 import org.xml.sax.DTDHandler; 011 import com.thaiopensource.validate.Validator; 012 import com.thaiopensource.validate.Schema; 013 import com.thaiopensource.validate.ValidateProperty; 014 import com.thaiopensource.validate.nrl.ActionSet; 015 import com.thaiopensource.validate.nrl.AttributeActionSet; 016 import com.thaiopensource.validate.nrl.FilteredAttributes; 017 import com.thaiopensource.validate.nrl.Hashset; 018 import com.thaiopensource.validate.nrl.IntSet; 019 import com.thaiopensource.validate.nrl.Mode; 020 import com.thaiopensource.validate.nrl.ModeUsage; 021 import com.thaiopensource.validate.nrl.NoResultAction; 022 import com.thaiopensource.validate.nrl.ResultAction; 023 import com.thaiopensource.validate.nrl.SectionState; 024 import com.thaiopensource.util.Localizer; 025 import com.thaiopensource.util.PropertyMap; 026 027 import java.util.Vector; 028 import java.util.Stack; 029 import java.util.Hashtable; 030 import java.util.Enumeration; 031 032 class ValidatorImpl extends DefaultHandler implements Validator { 033 private static final String BEARER_URI = ""; 034 private static final String BEARER_LOCAL_NAME = "#bearer"; 035 private static final String NO_NS = "\0"; 036 private final ErrorHandler eh; 037 private final PropertyMap properties; 038 private Locator locator; 039 private Section currentSection; 040 private PrefixMapping prefixMapping = null; 041 private final Hashtable validatorHandlerCache = new Hashtable(); 042 private final Localizer localizer = new Localizer(ValidatorImpl.class); 043 private final Hashset noResultActions = new Hashset(); 044 private final Hashtable attributeNamespaceIndexSets = new Hashtable(); 045 private final Vector activeHandlersAttributeIndexSets = new Vector(); 046 private final Hashset attributeSchemas = new Hashset(); 047 private boolean attributeNamespaceRejected; 048 private Attributes filteredAttributes; 049 private final Mode startMode; 050 051 static private class PrefixMapping { 052 final String prefix; 053 final String uri; 054 final PrefixMapping parent; 055 056 PrefixMapping(String prefix, String uri, PrefixMapping parent) { 057 this.prefix = prefix; 058 this.uri = uri; 059 this.parent = parent; 060 } 061 } 062 063 private class Section implements SectionState { 064 final Section parent; 065 /** 066 * Namespace of this section. Empty string for absent. 067 */ 068 final String ns; 069 /** 070 * Number of open elements in this section. 071 */ 072 int depth = 0; 073 /** 074 * List of the Validators rooted in this section 075 */ 076 final Vector validators = new Vector(); 077 final Vector schemas = new Vector(); 078 /** 079 * List of the ContentHandlers that want to see the elements in this section 080 */ 081 final Vector activeHandlers = new Vector(); 082 final Vector activeHandlersAttributeModeUsage = new Vector(); 083 final Vector attributeValidationModeUsages = new Vector(); 084 /** 085 * List of Programs saying what to do with child sections 086 */ 087 final Vector childPrograms = new Vector(); 088 final Stack context = new Stack(); 089 boolean contextDependent = false; 090 int attributeProcessing = Mode.ATTRIBUTE_PROCESSING_NONE; 091 092 Section(String ns, Section parent) { 093 this.ns = ns; 094 this.parent = parent; 095 } 096 097 public void addChildMode(ModeUsage modeUsage, ContentHandler handler) { 098 childPrograms.addElement(new Program(modeUsage, handler)); 099 if (modeUsage.isContextDependent()) 100 contextDependent = true; 101 } 102 103 public void addValidator(Schema schema, ModeUsage modeUsage) { 104 schemas.addElement(schema); 105 Validator validator = createValidator(schema); 106 validators.addElement(validator); 107 activeHandlers.addElement(validator.getContentHandler()); 108 activeHandlersAttributeModeUsage.addElement(modeUsage); 109 attributeProcessing = Math.max(attributeProcessing, 110 modeUsage.getAttributeProcessing()); 111 childPrograms.addElement(new Program(modeUsage, validator.getContentHandler())); 112 if (modeUsage.isContextDependent()) 113 contextDependent = true; 114 } 115 116 public void addActiveHandler(ContentHandler handler, ModeUsage attributeModeUsage) { 117 activeHandlers.addElement(handler); 118 activeHandlersAttributeModeUsage.addElement(attributeModeUsage); 119 attributeProcessing = Math.max(attributeProcessing, 120 attributeModeUsage.getAttributeProcessing()); 121 if (attributeModeUsage.isContextDependent()) 122 contextDependent = true; 123 } 124 125 public void addAttributeValidationModeUsage(ModeUsage modeUsage) { 126 int ap = modeUsage.getAttributeProcessing(); 127 if (ap != Mode.ATTRIBUTE_PROCESSING_NONE) { 128 attributeValidationModeUsages.addElement(modeUsage); 129 attributeProcessing = Math.max(ap, attributeProcessing); 130 if (modeUsage.isContextDependent()) 131 contextDependent = true; 132 } 133 } 134 135 public void reject() throws SAXException { 136 if (eh != null) 137 eh.error(new SAXParseException(localizer.message("reject_element", ns), 138 locator)); 139 } 140 141 } 142 143 static private class Program { 144 final ModeUsage modeUsage; 145 final ContentHandler handler; 146 147 Program(ModeUsage modeUsage, ContentHandler handler) { 148 this.modeUsage = modeUsage; 149 this.handler = handler; 150 } 151 } 152 153 ValidatorImpl(Mode mode, PropertyMap properties) { 154 this.properties = properties; 155 this.eh = ValidateProperty.ERROR_HANDLER.get(properties); 156 this.startMode = mode; 157 initCurrentSection(); 158 } 159 160 private void initCurrentSection() { 161 currentSection = new Section(NO_NS, null); 162 currentSection.addChildMode(new ModeUsage(startMode, startMode), null); 163 } 164 165 public void setDocumentLocator(Locator locator) { 166 this.locator = locator; 167 } 168 169 public void characters(char ch[], int start, int length) 170 throws SAXException { 171 for (int i = 0, len = currentSection.activeHandlers.size(); i < len; i++) 172 ((ContentHandler)(currentSection.activeHandlers.elementAt(i))).characters(ch, start, length); 173 174 } 175 176 public void ignorableWhitespace(char ch[], int start, int length) 177 throws SAXException { 178 for (int i = 0, len = currentSection.activeHandlers.size(); i < len; i++) 179 ((ContentHandler)(currentSection.activeHandlers.elementAt(i))).ignorableWhitespace(ch, start, length); 180 } 181 182 public void startElement(String uri, String localName, 183 String qName, Attributes attributes) 184 throws SAXException { 185 if (!uri.equals(currentSection.ns)) 186 startSection(uri); 187 currentSection.depth++; 188 if (currentSection.contextDependent) 189 currentSection.context.push(localName); 190 boolean transformAttributes = processAttributes(attributes); 191 for (int i = 0, len = currentSection.activeHandlers.size(); i < len; i++) { 192 ContentHandler handler = (ContentHandler)(currentSection.activeHandlers.elementAt(i)); 193 handler.startElement(uri, localName, qName, 194 transformAttributes 195 ? filterAttributes((IntSet)activeHandlersAttributeIndexSets.elementAt(i), 196 attributes) 197 : attributes); 198 } 199 } 200 201 private static Attributes filterAttributes(IntSet indexSet, Attributes attributes) { 202 if (indexSet.size() == attributes.getLength()) 203 return attributes; 204 return new FilteredAttributes(indexSet, attributes); 205 } 206 207 private boolean processAttributes(Attributes attributes) throws SAXException { 208 if (currentSection.attributeProcessing == Mode.ATTRIBUTE_PROCESSING_NONE 209 || attributes.getLength() == 0) 210 return false; 211 attributeNamespaceIndexSets.clear(); 212 for (int i = 0, len = attributes.getLength(); i < len; i++) { 213 String ns = attributes.getURI(i); 214 IntSet indexSet = (IntSet)attributeNamespaceIndexSets.get(ns); 215 if (indexSet == null) { 216 indexSet = new IntSet(); 217 attributeNamespaceIndexSets.put(ns, indexSet); 218 } 219 indexSet.add(i); 220 } 221 if (currentSection.attributeProcessing == Mode.ATTRIBUTE_PROCESSING_QUALIFIED 222 && attributeNamespaceIndexSets.size() == 1 223 && attributeNamespaceIndexSets.get("") != null) 224 return false; 225 Vector handlerModes = currentSection.activeHandlersAttributeModeUsage; 226 activeHandlersAttributeIndexSets.setSize(handlerModes.size()); 227 for (int i = 0, len = handlerModes.size(); i < len; i++) 228 activeHandlersAttributeIndexSets.setElementAt(new IntSet(), i); 229 boolean transform = false; 230 Vector validationModes = currentSection.attributeValidationModeUsages; 231 for (Enumeration e = attributeNamespaceIndexSets.keys(); e.hasMoreElements();) { 232 String ns = (String)e.nextElement(); 233 IntSet indexSet = (IntSet)attributeNamespaceIndexSets.get(ns); 234 attributeSchemas.clear(); 235 filteredAttributes = null; 236 attributeNamespaceRejected = false; 237 for (int i = 0, len = handlerModes.size(); i < len; i++) { 238 ModeUsage modeUsage = (ModeUsage)handlerModes.elementAt(i); 239 AttributeActionSet actions = processAttributeSection(modeUsage, ns, indexSet, attributes); 240 if (actions.getAttach()) 241 ((IntSet)activeHandlersAttributeIndexSets.get(i)).addAll(indexSet); 242 else 243 transform = true; 244 } 245 for (int i = 0, len = validationModes.size(); i < len; i++) { 246 ModeUsage modeUsage = (ModeUsage)validationModes.elementAt(i); 247 processAttributeSection(modeUsage, ns, indexSet, attributes); 248 } 249 } 250 return transform; 251 } 252 253 private AttributeActionSet processAttributeSection(ModeUsage modeUsage, 254 String ns, 255 IntSet indexSet, 256 Attributes attributes) 257 throws SAXException { 258 Mode mode = modeUsage.getMode(currentSection.context); 259 AttributeActionSet actions = mode.getAttributeActions(ns); 260 if (actions.getReject() && !attributeNamespaceRejected) { 261 attributeNamespaceRejected = true; 262 if (eh != null) 263 eh.error(new SAXParseException(localizer.message("reject_attribute", ns), 264 locator)); 265 } 266 Schema[] schemas = actions.getSchemas(); 267 for (int j = 0; j < schemas.length; j++) { 268 if (attributeSchemas.contains(schemas[j])) 269 continue; 270 attributeSchemas.add(schemas[j]); 271 if (filteredAttributes == null) 272 filteredAttributes = filterAttributes(indexSet, attributes); 273 validateAttributes(schemas[j], filteredAttributes); 274 } 275 return actions; 276 } 277 278 private void validateAttributes(Schema schema, Attributes attributes) throws SAXException { 279 Validator validator = createValidator(schema); 280 ContentHandler ch = validator.getContentHandler(); 281 initHandler(ch); 282 ch.startElement(BEARER_URI, BEARER_LOCAL_NAME, BEARER_LOCAL_NAME, attributes); 283 ch.endElement(BEARER_URI, BEARER_LOCAL_NAME, BEARER_LOCAL_NAME); 284 cleanupHandler(ch); 285 releaseValidator(schema, validator); 286 } 287 288 private void startSection(String uri) throws SAXException { 289 Section section = new Section(uri, currentSection); 290 Vector childPrograms = currentSection.childPrograms; 291 noResultActions.clear(); 292 for (int i = 0, len = childPrograms.size(); i < len; i++) { 293 Program program = (Program)childPrograms.elementAt(i); 294 ActionSet actions = program.modeUsage.getMode(currentSection.context).getElementActions(uri); 295 ResultAction resultAction = actions.getResultAction(); 296 if (resultAction != null) 297 resultAction.perform(program.handler, section); 298 NoResultAction[] nra = actions.getNoResultActions(); 299 for (int j = 0; j < nra.length; j++) { 300 NoResultAction tem = nra[j]; 301 if (!noResultActions.contains(tem)) { 302 nra[j].perform(section); 303 noResultActions.add(tem); 304 } 305 } 306 } 307 for (int i = 0, len = section.validators.size(); i < len; i++) 308 initHandler(((Validator)section.validators.elementAt(i)).getContentHandler()); 309 currentSection = section; 310 } 311 312 private void initHandler(ContentHandler ch) throws SAXException { 313 if (locator != null) 314 ch.setDocumentLocator(locator); 315 ch.startDocument(); 316 for (PrefixMapping pm = prefixMapping; pm != null; pm = pm.parent) 317 ch.startPrefixMapping(pm.prefix, pm.uri); 318 } 319 320 public void endElement(String uri, String localName, String qName) 321 throws SAXException { 322 for (int i = 0, len = currentSection.activeHandlers.size(); i < len; i++) 323 ((ContentHandler)(currentSection.activeHandlers.elementAt(i))).endElement(uri, localName, qName); 324 currentSection.depth--; 325 if (currentSection.contextDependent) 326 currentSection.context.pop(); 327 if (currentSection.depth == 0) 328 endSection(); 329 } 330 331 private void endSection() throws SAXException { 332 for (int i = 0, len = currentSection.validators.size(); i < len; i++) { 333 Validator validator = (Validator)currentSection.validators.elementAt(i); 334 cleanupHandler(validator.getContentHandler()); 335 releaseValidator((Schema)currentSection.schemas.elementAt(i), validator); 336 // endDocument() on one of the validators may throw an exception 337 // in this case we don't want to release the validator twice 338 currentSection.validators.setElementAt(null, i); 339 } 340 currentSection = currentSection.parent; 341 } 342 343 private void cleanupHandler(ContentHandler vh) throws SAXException { 344 for (PrefixMapping pm = prefixMapping; pm != null; pm = pm.parent) 345 vh.endPrefixMapping(pm.prefix); 346 vh.endDocument(); 347 } 348 349 public void endDocument() 350 throws SAXException { 351 } 352 353 public void startPrefixMapping(String prefix, String uri) 354 throws SAXException { 355 super.startPrefixMapping(prefix, uri); 356 prefixMapping = new PrefixMapping(prefix, uri, prefixMapping); 357 } 358 359 public void endPrefixMapping(String prefix) 360 throws SAXException { 361 super.endPrefixMapping(prefix); 362 prefixMapping = prefixMapping.parent; 363 } 364 365 private Validator createValidator(Schema schema) { 366 Stack stack = (Stack)validatorHandlerCache.get(schema); 367 if (stack == null) { 368 stack = new Stack(); 369 validatorHandlerCache.put(schema, stack); 370 } 371 if (stack.empty()) 372 return schema.createValidator(properties); 373 return (Validator)stack.pop(); 374 } 375 376 private void releaseValidator(Schema schema, Validator vh) { 377 if (vh == null) 378 return; 379 vh.reset(); 380 ((Stack)validatorHandlerCache.get(schema)).push(vh); 381 } 382 383 public void reset() { 384 for (; currentSection != null; currentSection = currentSection.parent) { 385 for (int i = 0, len = currentSection.validators.size(); i < len; i++) 386 releaseValidator((Schema)currentSection.schemas.elementAt(i), 387 (Validator)currentSection.validators.elementAt(i)); 388 } 389 initCurrentSection(); 390 } 391 392 public ContentHandler getContentHandler() { 393 return this; 394 } 395 396 public DTDHandler getDTDHandler() { 397 return this; 398 } 399 }