1 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 ; 15 16 19 public class FromSchemaDefaultValueProvider implements DefaultValueProvider { 20 21 public FromSchemaDefaultValueProvider() { 22 } 24 25 private static final Pattern ALLOWED_COMPONENT_PATTERN = Pattern.compile("@?[A-Za-z0-9][A-Za-z0-9-]*"); 27 28 public boolean isOptional(SchemaType baseType, String xpath) throws XmlException { 29 return fetchParticle(baseType, xpath).isOptional(); 30 } 31 32 public boolean possibleForXPathToHaveDefault(String xpath) { 33 return isInterpretableXPath(xpath); 34 } 35 36 public XmlObject defaultFor(SchemaType baseType, String 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 xpath) throws XmlException { 43 return fetchDefault(baseType, xpath) != null; 44 } 45 46 private XmlObject fetchDefault(SchemaType baseType, String 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 private SchemaInfo fetchParticle(SchemaType baseType, String xpath) throws XmlException { 83 Assert.assertNotNull(baseType); 84 Assert.assertNotBlank(xpath); 85 86 if (!isInterpretableXPath(xpath)) { 87 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 [] 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 return new SchemaInfo(false, currentParticle.getMinOccurs().intValue() == 0, null); 103 } 104 105 for (int i = 0; i < components.length; ++i) { 106 String component = components[i]; 107 if (currentParticle.getMinOccurs().intValue() == 0) anyAreOptional = true; 108 109 int particleType = currentParticle.getParticleType(); 110 111 if (component.startsWith("@")) { 113 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 attributeName = components[i + 1].substring(1); 120 121 if (currentParticle.getType() == null || currentParticle.getType().getAttributeModel() == null) { 122 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 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 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 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 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 xpath, String component, SchemaParticle[] children, boolean lastOne) 210 throws XmlException { 211 SchemaParticle next = null; 212 StringBuffer actualChildren = new StringBuffer (); 213 boolean optional = false; 214 215 for (int childIndex = 0; childIndex < children.length; ++childIndex) { 216 String 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 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 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 component, int particleType, String xpath) throws XmlException { 247 if (particleType != SchemaParticle.ALL && particleType != SchemaParticle.CHOICE 248 && particleType != SchemaParticle.SEQUENCE) { 249 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 xpath) { 257 String [] 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 |