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 }