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    }