001 package com.thaiopensource.validate.mns; 002 003 import com.thaiopensource.validate.Schema; 004 import com.thaiopensource.validate.Validator; 005 import com.thaiopensource.validate.ValidateProperty; 006 import com.thaiopensource.validate.mns.ContextMap; 007 import com.thaiopensource.validate.mns.Hashset; 008 import com.thaiopensource.validate.mns.ElementsOrAttributes; 009 import com.thaiopensource.xml.util.Name; 010 import com.thaiopensource.util.Localizer; 011 import com.thaiopensource.util.PropertyMap; 012 import org.xml.sax.Attributes; 013 import org.xml.sax.ErrorHandler; 014 import org.xml.sax.Locator; 015 import org.xml.sax.SAXException; 016 import org.xml.sax.SAXParseException; 017 import org.xml.sax.ContentHandler; 018 import org.xml.sax.DTDHandler; 019 import org.xml.sax.helpers.DefaultHandler; 020 021 import java.util.Stack; 022 import java.util.Hashtable; 023 024 class ValidatorImpl extends DefaultHandler implements Validator { 025 private static final String BEARER_URI = ""; 026 private static final String BEARER_LOCAL_NAME = "#bearer"; 027 private SchemaImpl.Mode currentMode; 028 private int laxDepth = 0; 029 private final ErrorHandler eh; 030 private final PropertyMap properties; 031 private Locator locator; 032 private Subtree subtrees = null; 033 private final Hashset attributeNamespaces = new Hashset(); 034 private PrefixMapping prefixMapping = null; 035 private final Localizer localizer = new Localizer(ValidatorImpl.class); 036 private final Hashtable validatorCache = new Hashtable(); 037 038 static private class Subtree { 039 final Subtree parent; 040 final Validator validator; 041 final Schema schema; 042 final Hashset coveredNamespaces; 043 final ElementsOrAttributes prune; 044 final SchemaImpl.Mode parentMode; 045 final int parentLaxDepth; 046 final Stack context = new Stack(); 047 final ContextMap contextMap; 048 049 Subtree(Hashset coveredNamespaces, ContextMap contextMap, 050 ElementsOrAttributes prune, Validator validator, 051 Schema schema, SchemaImpl.Mode parentMode, int parentLaxDepth, Subtree parent) { 052 this.coveredNamespaces = coveredNamespaces; 053 this.contextMap = contextMap; 054 this.prune = prune; 055 this.validator = validator; 056 this.schema = schema; 057 this.parentMode = parentMode; 058 this.parentLaxDepth = parentLaxDepth; 059 this.parent = parent; 060 } 061 } 062 063 static private class PrefixMapping { 064 final String prefix; 065 final String uri; 066 final PrefixMapping parent; 067 068 PrefixMapping(String prefix, String uri, PrefixMapping parent) { 069 this.prefix = prefix; 070 this.uri = uri; 071 this.parent = parent; 072 } 073 } 074 075 ValidatorImpl(SchemaImpl.Mode mode, PropertyMap properties) { 076 this.currentMode = mode; 077 this.properties = properties; 078 this.eh = ValidateProperty.ERROR_HANDLER.get(properties); 079 } 080 081 public void setDocumentLocator(Locator locator) { 082 this.locator = locator; 083 } 084 085 public void characters(char ch[], int start, int length) 086 throws SAXException { 087 for (Subtree st = subtrees; wantsEvent(st); st = st.parent) 088 st.validator.getContentHandler().characters(ch, start, length); 089 } 090 091 public void ignorableWhitespace(char ch[], int start, int length) 092 throws SAXException { 093 for (Subtree st = subtrees; wantsEvent(st); st = st.parent) 094 st.validator.getContentHandler().ignorableWhitespace(ch, start, length); 095 } 096 097 private SchemaImpl.Mode getMode() { 098 if (subtrees != null) { 099 SchemaImpl.Mode mode = (SchemaImpl.Mode)subtrees.contextMap.get(subtrees.context); 100 if (mode != null) 101 return mode; 102 } 103 return currentMode; 104 } 105 106 public void startElement(String uri, String localName, 107 String qName, Attributes attributes) 108 throws SAXException { 109 if (namespaceCovered(uri)) 110 subtrees.context.push(new Name(uri, localName)); 111 else { 112 SchemaImpl.Mode mode = getMode(); 113 SchemaImpl.ElementAction elementAction = mode.getElementAction(uri); 114 if (elementAction == null) { 115 if (laxDepth == 0 && !mode.getLax().containsElements()) 116 error("element_undeclared_namespace", uri); 117 laxDepth++; 118 } 119 else { 120 subtrees = new Subtree(elementAction.getCoveredNamespaces(), 121 elementAction.getContextMap(), 122 elementAction.getPrune(), 123 createValidator(elementAction.getSchema()), 124 elementAction.getSchema(), 125 currentMode, 126 laxDepth, 127 subtrees); 128 subtrees.context.push(new Name(uri, localName)); 129 currentMode = elementAction.getMode(); 130 laxDepth = 0; 131 startSubtree(subtrees.validator.getContentHandler()); 132 } 133 } 134 for (Subtree st = subtrees; wantsEvent(st); st = st.parent) { 135 Attributes prunedAtts; 136 if (st.prune.containsAttributes()) 137 prunedAtts = new NamespaceFilteredAttributes(uri, true, attributes); 138 else 139 prunedAtts = attributes; 140 st.validator.getContentHandler().startElement(uri, localName, qName, prunedAtts); 141 } 142 for (int i = 0, len = attributes.getLength(); i < len; i++) { 143 String ns = attributes.getURI(i); 144 if (!ns.equals("") 145 && !ns.equals(uri) 146 && !namespaceCovered(ns) 147 && !attributeNamespaces.contains(ns)) { 148 attributeNamespaces.add(ns); 149 validateAttributes(ns, attributes); 150 } 151 } 152 attributeNamespaces.clear(); 153 } 154 155 private boolean namespaceCovered(String ns) { 156 return (laxDepth == 0 157 && subtrees != null 158 && subtrees.coveredNamespaces.contains(ns)); 159 } 160 161 private boolean wantsEvent(Subtree st) { 162 return st != null && (!st.prune.containsElements() || (laxDepth == 0 && st == subtrees)); 163 } 164 165 private void validateAttributes(String ns, Attributes attributes) throws SAXException { 166 SchemaImpl.Mode mode = getMode(); 167 Schema attributesSchema = mode.getAttributesSchema(ns); 168 if (attributesSchema == null) { 169 if (!mode.getLax().containsAttributes()) 170 error("attributes_undeclared_namespace", ns); 171 return; 172 } 173 Validator validator = createValidator(attributesSchema); 174 ContentHandler ch = validator.getContentHandler(); 175 startSubtree(ch); 176 ch.startElement(BEARER_URI, BEARER_LOCAL_NAME, BEARER_LOCAL_NAME, 177 new NamespaceFilteredAttributes(ns, false, attributes)); 178 ch.endElement(BEARER_URI, BEARER_LOCAL_NAME, BEARER_LOCAL_NAME); 179 endSubtree(ch); 180 releaseValidator(attributesSchema, validator); 181 } 182 183 private void startSubtree(ContentHandler ch) throws SAXException { 184 if (locator != null) 185 ch.setDocumentLocator(locator); 186 ch.startDocument(); 187 for (PrefixMapping pm = prefixMapping; pm != null; pm = pm.parent) 188 ch.startPrefixMapping(pm.prefix, pm.uri); 189 } 190 191 private void endSubtree(ContentHandler ch) throws SAXException { 192 for (PrefixMapping pm = prefixMapping; pm != null; pm = pm.parent) 193 ch.endPrefixMapping(pm.prefix); 194 ch.endDocument(); 195 } 196 197 public void endElement(String uri, String localName, String qName) 198 throws SAXException { 199 for (Subtree st = subtrees; wantsEvent(st); st = st.parent) 200 st.validator.getContentHandler().endElement(uri, localName, qName); 201 if (laxDepth > 0) 202 laxDepth--; 203 else if (!subtrees.context.empty()) { 204 subtrees.context.pop(); 205 if (subtrees.context.empty()) { 206 endSubtree(subtrees.validator.getContentHandler()); 207 releaseValidator(subtrees.schema, subtrees.validator); 208 currentMode = subtrees.parentMode; 209 laxDepth = subtrees.parentLaxDepth; 210 subtrees = subtrees.parent; 211 } 212 } 213 } 214 215 private Validator createValidator(Schema schema) { 216 Stack stack = (Stack)validatorCache.get(schema); 217 if (stack == null) { 218 stack = new Stack(); 219 validatorCache.put(schema, stack); 220 } 221 if (stack.empty()) 222 return schema.createValidator(properties); 223 return (Validator)stack.pop(); 224 } 225 226 private void releaseValidator(Schema schema, Validator validator) { 227 validator.reset(); 228 ((Stack)validatorCache.get(schema)).push(validator); 229 } 230 231 public void endDocument() 232 throws SAXException { 233 } 234 235 public void startPrefixMapping(String prefix, String uri) 236 throws SAXException { 237 super.startPrefixMapping(prefix, uri); 238 prefixMapping = new PrefixMapping(prefix, uri, prefixMapping); 239 } 240 241 public void endPrefixMapping(String prefix) 242 throws SAXException { 243 super.endPrefixMapping(prefix); 244 prefixMapping = prefixMapping.parent; 245 } 246 247 public void reset() { 248 subtrees = null; 249 locator = null; 250 } 251 252 public ContentHandler getContentHandler() { 253 return this; 254 } 255 256 public DTDHandler getDTDHandler() { 257 return null; 258 } 259 260 private void error(String key, String arg) throws SAXException { 261 eh.error(new SAXParseException(localizer.message(key, arg), locator)); 262 } 263 }