KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > config > schema > defaults > FromSchemaDefaultValueProvider


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.config.schema.defaults;
5
6 import org.apache.xmlbeans.SchemaLocalAttribute;
7 import org.apache.xmlbeans.SchemaParticle;
8 import org.apache.xmlbeans.SchemaType;
9 import org.apache.xmlbeans.XmlException;
10 import org.apache.xmlbeans.XmlObject;
11
12 import com.tc.util.Assert;
13
14 import java.util.regex.Pattern JavaDoc;
15
16 /**
17  * A {@link DefaultValueProvider} that gets defaults from the schema itself.
18  */

19 public class FromSchemaDefaultValueProvider implements DefaultValueProvider {
20
21   public FromSchemaDefaultValueProvider() {
22     // Nothing here.
23
}
24
25   // This makes sure we don't try to interpret XPaths that have anything other than normal element delimiters in them.
26
private static final Pattern JavaDoc ALLOWED_COMPONENT_PATTERN = Pattern.compile("@?[A-Za-z0-9][A-Za-z0-9-]*");
27
28   public boolean isOptional(SchemaType baseType, String JavaDoc xpath) throws XmlException {
29     return fetchParticle(baseType, xpath).isOptional();
30   }
31
32   public boolean possibleForXPathToHaveDefault(String JavaDoc xpath) {
33     return isInterpretableXPath(xpath);
34   }
35
36   public XmlObject defaultFor(SchemaType baseType, String JavaDoc xpath) throws XmlException {
37     XmlObject out = fetchDefault(baseType, xpath);
38     if (out == null) throw new XmlException("The element at XPath '" + xpath + "' has no default specified.");
39     else return out;
40   }
41
42   public boolean hasDefault(SchemaType baseType, String JavaDoc xpath) throws XmlException {
43     return fetchDefault(baseType, xpath) != null;
44   }
45
46   private XmlObject fetchDefault(SchemaType baseType, String JavaDoc xpath) throws XmlException {
47     SchemaInfo info = fetchParticle(baseType, xpath);
48     if (!info.isDefault()) return null;
49     else return info.defaultValue();
50   }
51
52   private static class SchemaInfo {
53     private final boolean isDefault;
54     private final boolean isOptional;
55     private final XmlObject defaultValue;
56
57     public SchemaInfo(boolean isDefault, boolean isOptional, XmlObject defaultValue) {
58       Assert.eval(defaultValue == null || isDefault);
59       Assert.eval(defaultValue == null || isOptional);
60
61       this.isDefault = isDefault;
62       this.isOptional = isOptional;
63       this.defaultValue = defaultValue;
64     }
65
66     public boolean isDefault() {
67       return this.isDefault;
68     }
69
70     public boolean isOptional() {
71       return this.isOptional;
72     }
73
74     public XmlObject defaultValue() {
75       return this.defaultValue;
76     }
77   }
78
79   // This is the single most incomprehensible piece of code in the entire system. I have absolutely no idea what this
80
// XPath stuff is actually supposed to do; there is almost no documentation on it. I made this work by long
81
// trial-and-error. You're on your own here, mate.
82
private SchemaInfo fetchParticle(SchemaType baseType, String JavaDoc xpath) throws XmlException {
83     Assert.assertNotNull(baseType);
84     Assert.assertNotBlank(xpath);
85
86     if (!isInterpretableXPath(xpath)) {
87       // formatting
88
throw new XmlException(
89                              "Right now, our default-finding code doesn't support anything other than a path consisting "
90                                  + "of only simple elements. '" + xpath + "' is not such a path.");
91     }
92
93     String JavaDoc[] components = xpath.split("/");
94     SchemaParticle currentParticle = baseType.getContentModel();
95     Assert.assertNotNull(currentParticle);
96
97     boolean anyAreOptional = false;
98
99     if (components.length == 1 && components[0].startsWith("@")) {
100       // We don't yet support direct attribute grabs for defaults; this is because I can't figure out how to get
101
// XMLBeans to do them right.
102
return new SchemaInfo(false, currentParticle.getMinOccurs().intValue() == 0, null);
103     }
104
105     for (int i = 0; i < components.length; ++i) {
106       String JavaDoc component = components[i];
107       if (currentParticle.getMinOccurs().intValue() == 0) anyAreOptional = true;
108
109       int particleType = currentParticle.getParticleType();
110
111       // Attributes should've been caught on the last time through.
112
if (component.startsWith("@")) {
113         // formatting
114
throw new XmlException("Component '" + component + "' of XPath '" + xpath
115                                + "' specifies an attribute in an invalid position.");
116       }
117
118       if ((i == components.length - 2) && (components[i + 1].startsWith("@"))) {
119         String JavaDoc attributeName = components[i + 1].substring(1);
120
121         if (currentParticle.getType() == null || currentParticle.getType().getAttributeModel() == null) {
122           // formatting
123
throw new XmlException("The element purportedly containing attribute '" + attributeName + "' in XPath '"
124                                  + xpath + "' seems to have no attributes at all.");
125         }
126
127         SchemaLocalAttribute[] attributes = currentParticle.getType().getAttributeModel().getAttributes();
128         for (int j = 0; j < attributes.length; ++j) {
129           if (attributes[j].getName().getLocalPart().equals(attributeName)) { return new SchemaInfo(attributes[j]
130               .isDefault(), attributes[j].getMinOccurs().intValue() == 0, attributes[j].getDefaultValue()); }
131         }
132
133         throw new XmlException("Attribute '" + attributeName + "' of element '" + component + "' in XPath '" + xpath
134                                + "' was not found.");
135       }
136
137       if (particleType == SchemaParticle.ELEMENT) {
138         if (currentParticle.getName().getLocalPart().equals(component)) {
139           if (i == components.length - 1) break;
140           else {
141             currentParticle = currentParticle.getType().getContentModel();
142             Assert.assertNotNull(currentParticle);
143             continue;
144           }
145         } else {
146           throw new XmlException("Component '" + component + "' of XPath '" + xpath
147                                  + "' not found; we have one element only, '"
148                                  + currentParticle.getName().getLocalPart() + "'.");
149         }
150       }
151
152       checkParticleType(component, particleType, xpath);
153
154       SchemaParticle[] children = currentParticle.getParticleChildren();
155       if (children == null) {
156         // formatting
157
throw new XmlException("Component '" + component + "' of XPath '" + xpath + "' seems to have "
158                                + "no children. Stop.");
159       }
160
161       ElementReturn elementReturn = findNextElement(xpath, component, children, i == components.length - 1);
162       SchemaParticle next = elementReturn.particle();
163       anyAreOptional = anyAreOptional || elementReturn.isOptional();
164
165       if (next == null) {
166         // formatting
167
throw new XmlException("Component '" + component + "' of XPath '" + xpath
168                                + "' was not found. Please check the path " + "and try again.");
169       }
170
171       currentParticle = next;
172     }
173
174     if (currentParticle.getMinOccurs().intValue() == 0) anyAreOptional = true;
175
176     if (currentParticle.getParticleType() != SchemaParticle.ELEMENT) {
177       // formatting
178
throw new XmlException("XPath '" + xpath
179                              + "' points to a complex type or other item, not a single element. Stop.");
180     }
181
182     if (currentParticle.isDefault() && (!anyAreOptional)) {
183       // formatting
184
throw new XmlException("XPath '" + xpath + "' has a default, but is not optional. This doesn't make sense.");
185     }
186
187     return new SchemaInfo(currentParticle.isDefault(), anyAreOptional, currentParticle.getDefaultValue());
188   }
189
190   private static class ElementReturn {
191     private final SchemaParticle particle;
192     private final boolean isOptional;
193
194     public ElementReturn(SchemaParticle particle, boolean isOptional) {
195       Assert.assertNotNull(particle);
196       this.particle = particle;
197       this.isOptional = isOptional;
198     }
199
200     public SchemaParticle particle() {
201       return this.particle;
202     }
203
204     public boolean isOptional() {
205       return this.isOptional;
206     }
207   }
208
209   private ElementReturn findNextElement(String JavaDoc xpath, String JavaDoc component, SchemaParticle[] children, boolean lastOne)
210       throws XmlException {
211     SchemaParticle next = null;
212     StringBuffer JavaDoc actualChildren = new StringBuffer JavaDoc();
213     boolean optional = false;
214
215     for (int childIndex = 0; childIndex < children.length; ++childIndex) {
216       String JavaDoc thisChildName = children[childIndex].getName().getLocalPart();
217       if (childIndex > 0) actualChildren.append(", ");
218       actualChildren.append(thisChildName);
219       if (thisChildName != null && thisChildName.equals(component)) {
220         if (next != null) {
221           // formatting
222
throw new XmlException("Component '" + component + "' of XPath '" + xpath + "' has multiple children named '"
223                                  + component + "'. We don't support this. Stop.");
224         } else {
225           next = children[childIndex];
226         }
227       }
228     }
229
230     if ((!lastOne) && next.getParticleType() == SchemaParticle.ELEMENT) {
231       optional = optional || next.getMinOccurs().intValue() == 0;
232       next = next.getType().getContentModel();
233     }
234
235     if (next == null) {
236       // formatting
237
throw new XmlException("Component '" + component + "' of path '" + xpath + "' was not found. Instead, we found: "
238                              + actualChildren);
239     }
240
241     optional = optional || next.getMinOccurs().intValue() == 0;
242
243     return new ElementReturn(next, optional);
244   }
245
246   private void checkParticleType(String JavaDoc component, int particleType, String JavaDoc xpath) throws XmlException {
247     if (particleType != SchemaParticle.ALL && particleType != SchemaParticle.CHOICE
248         && particleType != SchemaParticle.SEQUENCE) {
249       // formatting
250
throw new XmlException("Component '" + component + "' of XPath '" + xpath + "' is a schema particle of type "
251                              + particleType + ", not " + SchemaParticle.ALL + " ('all'), " + SchemaParticle.CHOICE
252                              + " ('choice'), or " + SchemaParticle.SEQUENCE + " ('sequence'). Stop.");
253     }
254   }
255
256   private boolean isInterpretableXPath(String JavaDoc xpath) {
257     String JavaDoc[] components = xpath.split("/");
258
259     for (int i = 0; i < components.length; ++i) {
260       if (!ALLOWED_COMPONENT_PATTERN.matcher(components[i]).matches()) return false;
261     }
262
263     return true;
264   }
265 }
Popular Tags