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 }