001    package com.thaiopensource.relaxng.impl;
002    
003    import com.thaiopensource.validate.Validator;
004    import com.thaiopensource.xml.util.StringSplitter;
005    import com.thaiopensource.xml.util.Name;
006    import org.relaxng.datatype.Datatype;
007    import org.xml.sax.Attributes;
008    import org.xml.sax.ErrorHandler;
009    import org.xml.sax.Locator;
010    import org.xml.sax.SAXException;
011    import org.xml.sax.SAXParseException;
012    import org.xml.sax.ContentHandler;
013    import org.xml.sax.DTDHandler;
014    import org.xml.sax.helpers.LocatorImpl;
015    
016    import java.util.Enumeration;
017    import java.util.Hashtable;
018    import java.util.Vector;
019    
020    public class IdSoundnessChecker implements Validator, ContentHandler {
021      private final IdTypeMap idTypeMap;
022      private final ErrorHandler eh;
023      private Locator locator;
024      private final Hashtable table = new Hashtable();
025    
026      private static class Entry {
027        Locator idLoc;
028        Vector idrefLocs;
029        boolean hadId;
030      }
031    
032      public IdSoundnessChecker(IdTypeMap idTypeMap, ErrorHandler eh) {
033        this.idTypeMap = idTypeMap;
034        this.eh = eh;
035      }
036    
037      public void reset() {
038        table.clear();
039        locator = null;
040      }
041    
042      public ContentHandler getContentHandler() {
043        return this;
044      }
045    
046      public DTDHandler getDTDHandler() {
047        return null;
048      }
049    
050      public void setDocumentLocator(Locator locator) {
051        this.locator = locator;
052      }
053    
054      public void startDocument() throws SAXException {
055      }
056    
057      void setComplete() {
058      }
059    
060      public void endDocument() throws SAXException {
061        for (Enumeration e = table.keys(); e.hasMoreElements();) {
062          String token = (String)e.nextElement();
063          Entry entry = (Entry)table.get(token);
064          if (!entry.hadId) {
065            for (Enumeration f = entry.idrefLocs.elements(); f.hasMoreElements();)
066              error("missing_id", token, (Locator)f.nextElement());
067          }
068        }
069        setComplete();
070      }
071    
072      public void startPrefixMapping(String s, String s1) throws SAXException {
073      }
074    
075      public void endPrefixMapping(String s) throws SAXException {
076      }
077    
078      public void startElement(String namespaceUri, String localName, String qName, Attributes attributes)
079              throws SAXException {
080        Name elementName = new Name(namespaceUri, localName);
081        int len = attributes.getLength();
082        for (int i = 0; i < len; i++) {
083          Name attributeName = new Name(attributes.getURI(i), attributes.getLocalName(i));
084          int idType = idTypeMap.getIdType(elementName, attributeName);
085          if (idType != Datatype.ID_TYPE_NULL) {
086            String[] tokens = StringSplitter.split(attributes.getValue(i));
087            switch (idType) {
088            case Datatype.ID_TYPE_ID:
089              if (tokens.length == 1)
090                id(tokens[0]);
091              else if (tokens.length == 0)
092                error("id_no_tokens");
093              else
094                error("id_multiple_tokens");
095              break;
096            case Datatype.ID_TYPE_IDREF:
097              if (tokens.length == 1)
098                idref(tokens[0]);
099              else if (tokens.length == 0)
100                error("idref_no_tokens");
101              else
102                error("idref_multiple_tokens");
103              break;
104            case Datatype.ID_TYPE_IDREFS:
105              if (tokens.length > 0) {
106                for (int j = 0; j < tokens.length; j++)
107                  idref(tokens[j]);
108              }
109              else
110                error("idrefs_no_tokens");
111              break;
112            }
113          }
114        }
115      }
116    
117      private void id(String token) throws SAXException {
118        Entry entry = (Entry)table.get(token);
119        if (entry == null) {
120          entry = new Entry();
121          table.put(token, entry);
122        }
123        else if (entry.hadId) {
124          error("duplicate_id", token);
125          error("first_id", token, entry.idLoc);
126          return;
127        }
128        entry.idLoc = new LocatorImpl(locator);
129        entry.hadId = true;
130      }
131    
132      private void idref(String token) {
133        Entry entry = (Entry)table.get(token);
134        if (entry == null) {
135          entry = new Entry();
136          table.put(token, entry);
137        }
138        if (entry.hadId)
139          return;
140        if (entry.idrefLocs == null)
141          entry.idrefLocs = new Vector();
142        entry.idrefLocs.addElement(new LocatorImpl(locator));
143      }
144    
145      public void endElement(String s, String s1, String s2) throws SAXException {
146      }
147    
148      public void characters(char[] chars, int i, int i1) throws SAXException {
149      }
150    
151      public void ignorableWhitespace(char[] chars, int i, int i1) throws SAXException {
152      }
153    
154      public void processingInstruction(String s, String s1) throws SAXException {
155      }
156    
157      public void skippedEntity(String s) throws SAXException {
158      }
159    
160      public void notationDecl(String name,
161                               String publicId,
162                               String systemId)
163              throws SAXException {
164      }
165    
166      public void unparsedEntityDecl(String name,
167                                     String publicId,
168                                     String systemId,
169                                     String notationName)
170              throws SAXException {
171      }
172    
173      private void error(String key) throws SAXException {
174        eh.error(new SAXParseException(SchemaBuilderImpl.localizer.message(key), locator));
175      }
176    
177      private void error(String key, String arg) throws SAXException {
178        eh.error(new SAXParseException(SchemaBuilderImpl.localizer.message(key, arg), locator));
179      }
180    
181      private void error(String key, String arg, Locator loc) throws SAXException {
182        eh.error(new SAXParseException(SchemaBuilderImpl.localizer.message(key, arg),
183                                       loc));
184      }
185    }