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 }