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 }