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 }