001 package com.thaiopensource.relaxng.impl; 002 003 import org.relaxng.datatype.Datatype; 004 import org.xml.sax.ErrorHandler; 005 import org.xml.sax.Locator; 006 import org.xml.sax.SAXException; 007 import org.xml.sax.SAXParseException; 008 009 import java.util.Enumeration; 010 import java.util.Hashtable; 011 import java.util.Vector; 012 013 import com.thaiopensource.xml.util.Name; 014 015 public class IdTypeMapBuilder { 016 private boolean hadError; 017 private final ErrorHandler eh; 018 private final PatternFunction idTypeFunction = new IdTypeFunction(); 019 private final IdTypeMapImpl idTypeMap = new IdTypeMapImpl(); 020 private final Hashtable elementProcessed = new Hashtable(); 021 private final Vector possibleConflicts = new Vector(); 022 023 private void notePossibleConflict(NameClass elementNameClass, NameClass attributeNameClass, Locator loc) { 024 possibleConflicts.addElement(new PossibleConflict(elementNameClass, attributeNameClass, loc)); 025 } 026 027 private static class WrappedSAXException extends RuntimeException { 028 private final SAXException cause; 029 WrappedSAXException(SAXException cause) { 030 this.cause = cause; 031 } 032 } 033 034 private static class PossibleConflict { 035 private final NameClass elementNameClass; 036 private final NameClass attributeNameClass; 037 private final Locator locator; 038 039 private PossibleConflict(NameClass elementNameClass, NameClass attributeNameClass, Locator locator) { 040 this.elementNameClass = elementNameClass; 041 this.attributeNameClass = attributeNameClass; 042 this.locator = locator; 043 } 044 } 045 046 private static class ScopedName { 047 private final Name elementName; 048 private final Name attributeName; 049 050 private ScopedName(Name elementName, Name attributeName) { 051 this.elementName = elementName; 052 this.attributeName = attributeName; 053 } 054 055 public int hashCode() { 056 return elementName.hashCode() ^ attributeName.hashCode(); 057 } 058 059 public boolean equals(Object obj) { 060 if (!(obj instanceof ScopedName)) 061 return false; 062 ScopedName other = (ScopedName)obj; 063 return elementName.equals(other.elementName) && attributeName.equals(other.attributeName); 064 } 065 } 066 067 private static class IdTypeMapImpl implements IdTypeMap { 068 private final Hashtable table = new Hashtable(); 069 public int getIdType(Name elementName, Name attributeName) { 070 Integer n = (Integer)table.get(new ScopedName(elementName, attributeName)); 071 if (n == null) 072 return Datatype.ID_TYPE_NULL; 073 return n.intValue(); 074 } 075 private void add(Name elementName, Name attributeName, int idType) { 076 table.put(new ScopedName(elementName, attributeName), new Integer(idType)); 077 } 078 } 079 080 private class IdTypeFunction extends AbstractPatternFunction { 081 public Object caseOther(Pattern p) { 082 return new Integer(Datatype.ID_TYPE_NULL); 083 } 084 085 public Object caseData(DataPattern p) { 086 return new Integer(p.getDatatype().getIdType()); 087 } 088 089 public Object caseDataExcept(DataExceptPattern p) { 090 return new Integer(p.getDatatype().getIdType()); 091 } 092 093 public Object caseValue(ValuePattern p) { 094 return new Integer(p.getDatatype().getIdType()); 095 } 096 } 097 098 private class BuildFunction extends AbstractPatternFunction { 099 private final NameClass elementNameClass; 100 private final Locator locator; 101 private final boolean attributeIsParent; 102 103 BuildFunction(NameClass elementNameClass, Locator locator) { 104 this.elementNameClass = elementNameClass; 105 this.locator = locator; 106 this.attributeIsParent = false; 107 } 108 109 BuildFunction(NameClass elementNameClass, Locator locator, boolean attributeIsParent) { 110 this.elementNameClass = elementNameClass; 111 this.locator = locator; 112 this.attributeIsParent = attributeIsParent; 113 } 114 115 private BuildFunction down() { 116 if (!attributeIsParent) 117 return this; 118 return new BuildFunction(elementNameClass, locator, false); 119 } 120 121 public Object caseChoice(ChoicePattern p) { 122 BuildFunction f = down(); 123 p.getOperand1().apply(f); 124 p.getOperand2().apply(f); 125 return null; 126 } 127 128 public Object caseInterleave(InterleavePattern p) { 129 BuildFunction f = down(); 130 p.getOperand1().apply(f); 131 p.getOperand2().apply(f); 132 return null; 133 } 134 135 public Object caseGroup(GroupPattern p) { 136 BuildFunction f = down(); 137 p.getOperand1().apply(f); 138 p.getOperand2().apply(f); 139 return null; 140 } 141 142 public Object caseOneOrMore(OneOrMorePattern p) { 143 p.getOperand().apply(down()); 144 return null; 145 } 146 147 public Object caseElement(ElementPattern p) { 148 if (elementProcessed.get(p) != null) 149 return null; 150 elementProcessed.put(p, p); 151 p.getContent().apply(new BuildFunction(p.getNameClass(), p.getLocator())); 152 return null; 153 } 154 155 public Object caseAttribute(AttributePattern p) { 156 int idType = ((Integer)p.getContent().apply(idTypeFunction)).intValue(); 157 if (idType != Datatype.ID_TYPE_NULL) { 158 NameClass attributeNameClass = p.getNameClass(); 159 if (!(attributeNameClass instanceof SimpleNameClass)) { 160 error("id_attribute_name_class", p.getLocator()); 161 return null; 162 } 163 elementNameClass.accept(new ElementNameClassVisitor(((SimpleNameClass)attributeNameClass).getName(), 164 locator, 165 idType)); 166 } 167 else 168 notePossibleConflict(elementNameClass, p.getNameClass(), locator); 169 p.getContent().apply(new BuildFunction(null, p.getLocator(), true)); 170 return null; 171 } 172 173 private void datatype(Datatype dt) { 174 if (dt.getIdType() != Datatype.ID_TYPE_NULL && !attributeIsParent) 175 error("id_parent", locator); 176 } 177 178 public Object caseData(DataPattern p) { 179 datatype(p.getDatatype()); 180 return null; 181 } 182 183 public Object caseDataExcept(DataExceptPattern p) { 184 datatype(p.getDatatype()); 185 p.getExcept().apply(down()); 186 return null; 187 } 188 189 public Object caseValue(ValuePattern p) { 190 datatype(p.getDatatype()); 191 return null; 192 } 193 194 public Object caseList(ListPattern p) { 195 p.getOperand().apply(down()); 196 return null; 197 } 198 199 public Object caseOther(Pattern p) { 200 return null; 201 } 202 } 203 204 private class ElementNameClassVisitor implements NameClassVisitor { 205 private final Name attributeName; 206 private final Locator locator; 207 private final int idType; 208 209 ElementNameClassVisitor(Name attributeName, Locator locator, int idType) { 210 this.attributeName = attributeName; 211 this.locator = locator; 212 this.idType = idType; 213 } 214 215 public void visitChoice(NameClass nc1, NameClass nc2) { 216 nc1.accept(this); 217 nc2.accept(this); 218 } 219 220 public void visitName(Name elementName) { 221 int tem = idTypeMap.getIdType(elementName, attributeName); 222 if (tem != Datatype.ID_TYPE_NULL && tem != idType) 223 error("id_type_conflict", elementName, attributeName, locator); 224 idTypeMap.add(elementName, attributeName, idType); 225 } 226 227 public void visitNsName(String ns) { 228 visitOther(); 229 } 230 231 public void visitNsNameExcept(String ns, NameClass nc) { 232 visitOther(); 233 } 234 235 public void visitAnyName() { 236 visitOther(); 237 } 238 239 public void visitAnyNameExcept(NameClass nc) { 240 visitOther(); 241 } 242 243 public void visitNull() { 244 } 245 246 public void visitError() { 247 } 248 249 private void visitOther() { 250 error("id_element_name_class", locator); 251 } 252 } 253 254 private void error(String key, Locator locator) { 255 hadError = true; 256 if (eh != null) 257 try { 258 eh.error(new SAXParseException(SchemaBuilderImpl.localizer.message(key), locator)); 259 } 260 catch (SAXException e) { 261 throw new WrappedSAXException(e); 262 } 263 } 264 265 private void error(String key, Name arg1, Name arg2, Locator locator) { 266 hadError = true; 267 if (eh != null) 268 try { 269 eh.error(new SAXParseException(SchemaBuilderImpl.localizer.message(key, NameFormatter.format(arg1), NameFormatter.format(arg2)), 270 locator)); 271 } 272 catch (SAXException e) { 273 throw new WrappedSAXException(e); 274 } 275 } 276 277 public IdTypeMapBuilder(ErrorHandler eh, Pattern pattern) throws SAXException { 278 this.eh = eh; 279 try { 280 pattern.apply(new BuildFunction(null, null)); 281 for (Enumeration e = possibleConflicts.elements(); 282 e.hasMoreElements();) { 283 PossibleConflict pc = (PossibleConflict)e.nextElement(); 284 if (pc.elementNameClass instanceof SimpleNameClass 285 && pc.attributeNameClass instanceof SimpleNameClass) { 286 Name elementName = ((SimpleNameClass)pc.elementNameClass).getName(); 287 Name attributeName = ((SimpleNameClass)pc.attributeNameClass).getName(); 288 int idType = idTypeMap.getIdType(elementName, 289 attributeName); 290 if (idType != Datatype.ID_TYPE_NULL) 291 error("id_type_conflict", elementName, attributeName, pc.locator); 292 } 293 else { 294 for (Enumeration f = idTypeMap.table.keys(); f.hasMoreElements();) { 295 ScopedName sn = (ScopedName)f.nextElement(); 296 if (pc.elementNameClass.contains(sn.elementName) 297 && pc.attributeNameClass.contains(sn.attributeName)) { 298 error("id_type_conflict", sn.elementName, sn.attributeName, pc.locator); 299 break; 300 } 301 } 302 } 303 } 304 } 305 catch (WrappedSAXException e) { 306 throw e.cause; 307 } 308 } 309 310 public IdTypeMap getIdTypeMap() { 311 if (hadError) 312 return null; 313 return idTypeMap; 314 } 315 }