1 7 package org.jboss.xb.binding.metadata.unmarshalling.impl; 8 9 import org.jboss.xb.binding.Util; 10 import org.jboss.xb.binding.JBossXBRuntimeException; 11 import org.jboss.xb.binding.SimpleTypeBindings; 12 import org.jboss.xb.binding.metadata.unmarshalling.DocumentBinding; 13 import org.jboss.xb.binding.metadata.unmarshalling.NamespaceBinding; 14 import org.jboss.xb.binding.metadata.unmarshalling.TopElementBinding; 15 import org.jboss.xb.binding.metadata.unmarshalling.BasicElementBinding; 16 import org.jboss.xb.binding.metadata.unmarshalling.ElementBinding; 17 import org.jboss.xb.binding.metadata.unmarshalling.AttributeBinding; 18 import org.jboss.xb.binding.metadata.unmarshalling.XmlValueBinding; 19 import org.jboss.logging.Logger; 20 import org.apache.xerces.xs.XSImplementation; 21 import org.apache.xerces.xs.XSModel; 22 import org.apache.xerces.xs.XSLoader; 23 import org.apache.xerces.xs.StringList; 24 import org.apache.xerces.xs.XSNamedMap; 25 import org.apache.xerces.xs.XSConstants; 26 import org.apache.xerces.xs.XSElementDeclaration; 27 import org.apache.xerces.xs.XSTypeDefinition; 28 import org.apache.xerces.xs.XSComplexTypeDefinition; 29 import org.apache.xerces.xs.XSParticle; 30 import org.apache.xerces.xs.XSTerm; 31 import org.apache.xerces.xs.XSModelGroup; 32 import org.apache.xerces.xs.XSObjectList; 33 import org.apache.xerces.dom3.bootstrap.DOMImplementationRegistry; 34 35 import javax.xml.namespace.QName ; 36 import java.util.Collection ; 37 import java.util.Map ; 38 import java.util.HashMap ; 39 import java.lang.reflect.Method ; 40 import java.lang.reflect.Field ; 41 import java.lang.reflect.Modifier ; 42 43 47 public class XsdBinder 48 { 49 private static final Logger log = Logger.getLogger(XsdBinder.class); 50 51 private XsdBinder() 52 { 53 } 54 55 public static DocumentBinding bindXsd(String xsdUrl) 56 { 57 return bindXsd(xsdUrl, null); 58 } 59 60 public static DocumentBinding bindXsd(String xsdUrl, DocumentBinding delegate) 61 { 62 DocumentBindingImpl localDoc; 63 if(delegate instanceof DocumentBindingImpl) 64 { 65 localDoc = (DocumentBindingImpl)delegate; 66 } 67 else 68 { 69 localDoc = new DocumentBindingImpl(delegate); 70 } 71 72 XSModel model = loadSchema(xsdUrl); 73 StringList namespaces = model.getNamespaces(); 74 for(int i = 0; i < namespaces.getLength(); ++i) 75 { 76 String namespaceUri = namespaces.item(i); 77 NamespaceBindingImpl ns = localDoc.bindNamespace(namespaceUri); 78 79 XSNamedMap components = model.getComponentsByNamespace(XSConstants.ELEMENT_DECLARATION, namespaceUri); 80 for(int j = 0; j < components.getLength(); ++j) 81 { 82 XSElementDeclaration element = (XSElementDeclaration)components.item(j); 83 bindTopElement(localDoc, ns, element); 84 } 85 } 86 87 return localDoc; 88 } 89 90 private static final void bindTopElement(DocumentBinding doc, 91 NamespaceBindingImpl ns, 92 XSElementDeclaration element) 93 { 94 TopElementBindingImpl top = new TopElementBindingImpl(ns, element); 95 ns.addTopElement(top); 96 97 bindComplexElement(element, doc, top); 98 } 99 100 private static void bindComplexElement(XSElementDeclaration elementDecl, 101 DocumentBinding doc, 102 ParentElement parentBinding) 103 { 104 XSTypeDefinition type = elementDecl.getTypeDefinition(); 105 if(type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) 106 { 107 XSComplexTypeDefinition complexType = (XSComplexTypeDefinition)type; 108 XSParticle particle = complexType.getParticle(); 109 if(particle != null) 110 { 111 bindParticle(doc, parentBinding, particle); 112 } 113 114 142 } 143 } 144 145 private static void bindParticle(DocumentBinding doc, 146 ParentElement parent, 147 XSParticle particle) 148 { 149 XSTerm term = particle.getTerm(); 150 switch(term.getType()) 151 { 152 case XSConstants.MODEL_GROUP: 153 bindModelGroup(doc, parent, (XSModelGroup)term); 154 break; 155 case XSConstants.WILDCARD: 156 break; 158 case XSConstants.ELEMENT_DECLARATION: 159 bindElement(doc, parent, (XSElementDeclaration)term); 160 break; 161 default: 162 throw new IllegalStateException ("Unexpected term type: " + term.getType()); 163 } 164 } 165 166 private static void bindElement(DocumentBinding doc, 167 ParentElement parent, 168 XSElementDeclaration elementDecl) 169 { 170 ElementBindingImpl child = new ElementBindingImpl(parent, elementDecl); 171 parent.addChild(child); 172 bindComplexElement(elementDecl, doc, child); 173 } 174 175 private static void bindModelGroup(DocumentBinding doc, 176 ParentElement parent, 177 XSModelGroup modelGroup) 178 { 179 XSObjectList particles = modelGroup.getParticles(); 180 for(int i = 0; i < particles.getLength(); ++i) 181 { 182 XSParticle particle = (XSParticle)particles.item(i); 183 bindParticle(doc, parent, particle); 184 } 185 } 186 187 189 private static XSModel loadSchema(String xsdURL) 190 { 191 XSImplementation impl = getXSImplementation(); 192 XSLoader schemaLoader = impl.createXSLoader(null); 193 XSModel model = schemaLoader.loadURI(xsdURL); 194 if(model == null) 195 { 196 throw new IllegalArgumentException ("Invalid URI for schema: " + xsdURL); 197 } 198 199 return model; 200 } 201 202 private static XSImplementation getXSImplementation() 203 { 204 System.setProperty(DOMImplementationRegistry.PROPERTY, "org.apache.xerces.dom.DOMXSImplementationSourceImpl"); 206 207 XSImplementation impl; 208 try 209 { 210 DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); 211 impl = (XSImplementation)registry.getDOMImplementation("XS-Loader"); 212 } 213 catch(Exception e) 214 { 215 log.error("Failed to create schema loader.", e); 216 throw new IllegalStateException ("Failed to create schema loader: " + e.getMessage()); 217 } 218 return impl; 219 } 220 221 223 public static final class DocumentBindingImpl 224 extends DocumentBindingFactoryImpl.AbstractDocumentBinding 225 { 226 private final Map namespaces = new HashMap (); 227 228 public DocumentBindingImpl(DocumentBinding doc) 229 { 230 super(doc); 231 } 232 233 NamespaceBindingImpl bindNamespace(String namespaceUri) 234 { 235 NamespaceBindingImpl ns = new NamespaceBindingImpl(doc, namespaceUri); 236 namespaces.put(namespaceUri, ns); 237 return ns; 238 } 239 240 protected NamespaceBinding getNamespaceLocal(String namespaceUri) 241 { 242 return (NamespaceBinding)namespaces.get(namespaceUri); 243 } 244 } 245 246 private static final class NamespaceBindingImpl 247 extends DocumentBindingFactoryImpl.AbstractNamespaceBinding 248 { 249 private final Map tops = new HashMap (); 250 251 public NamespaceBindingImpl(DocumentBinding doc, String namespaceUri) 252 { 253 super(doc, namespaceUri); 254 } 255 256 protected String getJavaPackageLocal() 257 { 258 return Util.xmlNamespaceToJavaPackage(namespaceUri); 259 } 260 261 protected TopElementBinding getTopElementLocal(String elementName) 262 { 263 return (TopElementBinding)tops.get(elementName); 264 } 265 266 void addTopElement(TopElementBindingImpl top) 267 { 268 tops.put(top.getName().getLocalPart(), top); 269 } 270 } 271 272 private static final class ElementBindingImpl 273 extends DocumentBindingFactoryImpl.AbstractElementBinding 274 implements ParentElement 275 { 276 private final XSElementDeclaration elementDecl; 277 private final Map children = new HashMap (); 278 private Class javaType; 279 private Class fieldType; 280 private Method getter; 281 private Method setter; 282 private Field field; 283 284 public ElementBindingImpl(BasicElementBinding parent, XSElementDeclaration elementDecl) 285 { 286 super(parent, new QName (elementDecl.getNamespace(), elementDecl.getName())); 287 this.elementDecl = elementDecl; 288 } 289 290 private void init() 291 { 292 DocumentBinding doc = parent.getDocument(); 293 294 XSTypeDefinition typeDef = elementDecl.getTypeDefinition(); 296 String typeBasedClsName = null; 297 if("http://www.w3.org/2001/XMLSchema".equals(typeDef.getNamespace())) 298 { 299 javaType = SimpleTypeBindings.classForType(typeDef.getName()); 300 } 301 else if(typeDef.getName() != null) 302 { 303 NamespaceBinding ns = doc.getNamespace(typeDef.getNamespace()); 304 typeBasedClsName = ns.getJavaPackage() + "." + Util.xmlNameToClassName(typeDef.getName(), true); 305 try 306 { 307 javaType = Thread.currentThread().getContextClassLoader().loadClass(typeBasedClsName); 308 } 309 catch(ClassNotFoundException e) 310 { 311 } 312 } 313 314 String elBasedClsName = Util.xmlNameToClassName(elementDecl.getName(), true); 315 if(javaType == null) 316 { 317 NamespaceBinding ns = doc.getNamespace(elementDecl.getNamespace()); 318 try 321 { 322 javaType = 323 Thread.currentThread().getContextClassLoader().loadClass(ns.getJavaPackage() + "." + elBasedClsName); 324 } 325 catch(ClassNotFoundException e1) 326 { 327 } 329 } 330 331 Class parentType = parent.getJavaType(); 332 if(Collection .class.isAssignableFrom(parentType)) 333 { 334 if(javaType == null) 335 { 336 javaType = String .class; 337 } 338 } 339 else 340 { 341 try 342 { 343 getter = parentType.getMethod("get" + elBasedClsName, null); 344 fieldType = getter.getReturnType(); 345 try 346 { 347 setter = parentType.getMethod("set" + elBasedClsName, new Class []{getter.getReturnType()}); 348 } 349 catch(NoSuchMethodException e) 350 { 351 setter = null; 353 } 354 } 355 catch(NoSuchMethodException e) 356 { 357 String fieldName = Util.xmlNameToFieldName(elementDecl.getName(), true); 358 try 359 { 360 field = parentType.getField(fieldName); 361 fieldType = field.getType(); 362 } 363 catch(NoSuchFieldException e1) 364 { 365 } 366 } 367 368 if(fieldType != null) 369 { 370 if(Modifier.isFinal(fieldType.getModifiers()) || 371 javaType == null && 372 !Modifier.isInterface(fieldType.getModifiers()) && 373 !Modifier.isAbstract(fieldType.getModifiers())) 374 { 375 javaType = fieldType; 376 } 377 else if(fieldType == Collection .class || Collection .class.isAssignableFrom(fieldType)) 378 { 379 if(javaType == null) 380 { 381 javaType = java.util.ArrayList .class; 383 } 384 } 385 else if(javaType != null) 386 { 387 if(javaType != fieldType && !fieldType.isAssignableFrom(javaType)) 388 { 389 javaType = null; 390 } 391 } 392 } 393 } 394 395 if(javaType == null) 396 { 397 throw new JBossXBRuntimeException("Failed to bind element " + 398 name + 399 " to any non-abstract Java type. Parent is " + 400 parentType + 401 ", field is " + 402 fieldType 403 + ", base=" + elBasedClsName 404 ); 405 } 406 } 407 408 protected Field getFieldLocal() 409 { 410 if(javaType == null) 411 { 412 init(); 413 } 414 return field; 415 } 416 417 protected Method getGetterLocal() 418 { 419 if(javaType == null) 420 { 421 init(); 422 } 423 return getter; 424 } 425 426 protected Method getSetterLocal() 427 { 428 if(javaType == null) 429 { 430 init(); 431 } 432 return setter; 433 } 434 435 protected Class getFieldTypeLocal() 436 { 437 if(javaType == null) 438 { 439 init(); 440 } 441 return fieldType; 442 } 443 444 protected Class getJavaTypeLocal() 445 { 446 if(javaType == null) 447 { 448 init(); 449 } 450 return javaType; 451 } 452 453 protected ElementBinding getElementLocal(QName elementName) 454 { 455 return (ElementBinding)children.get(elementName); 456 } 457 458 protected AttributeBinding getAttributeLocal(QName attributeName) 459 { 460 String fieldName = Util.xmlNameToClassName(attributeName.getLocalPart(), true); 461 fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1); 462 return new AttributeBindingImpl(attributeName, null, getJavaType(), fieldName); 463 } 464 465 protected XmlValueBinding getValueLocal() 466 { 467 throw new UnsupportedOperationException ("getValueLocal is not implemented."); 469 } 470 471 public void addChild(BasicElementBinding child) 472 { 473 children.put(child.getName(), child); 474 } 475 } 476 477 private static class TopElementBindingImpl 478 extends DocumentBindingFactoryImpl.AbstractTopElementBinding 479 implements ParentElement 480 { 481 private final XSElementDeclaration elementDecl; 482 private final Map children = new HashMap (); 483 protected Class javaType; 484 485 public TopElementBindingImpl(NamespaceBinding ns, XSElementDeclaration elementDecl) 486 { 487 super(ns, elementDecl.getName()); 488 this.elementDecl = elementDecl; 489 } 490 491 private void init() 492 { 493 DocumentBinding doc = ns.getDocument(); 494 495 XSTypeDefinition typeDef = elementDecl.getTypeDefinition(); 497 String typeBasedClsName = null; 498 if("http://www.w3.org/2001/XMLSchema".equals(typeDef.getNamespace())) 499 { 500 javaType = SimpleTypeBindings.classForType(typeDef.getName()); 501 } 502 else if(typeDef.getName() != null) 503 { 504 NamespaceBinding ns = doc.getNamespace(typeDef.getNamespace()); 505 typeBasedClsName = ns.getJavaPackage() + "." + Util.xmlNameToClassName(typeDef.getName(), true); 506 try 507 { 508 javaType = Thread.currentThread().getContextClassLoader().loadClass(typeBasedClsName); 509 } 510 catch(ClassNotFoundException e) 511 { 512 } 513 } 514 515 if(javaType == null) 516 { 517 NamespaceBinding ns = doc.getNamespace(elementDecl.getNamespace()); 520 String elBasedClsName = ns.getJavaPackage() + "." + Util.xmlNameToClassName(elementDecl.getName(), true); 521 try 522 { 523 javaType = Thread.currentThread().getContextClassLoader().loadClass(elBasedClsName); 524 } 525 catch(ClassNotFoundException e1) 526 { 527 throw new JBossXBRuntimeException("Failed to bind element " + 528 name + 529 " using XSD type (" + 530 typeBasedClsName + 531 ") and element name (" + 532 elBasedClsName + 533 "): classes not found." 534 ); 535 } 536 } 537 } 538 539 protected Class getJavaTypeLocal() 540 { 541 if(javaType == null) 542 { 543 init(); 544 } 545 return javaType; 546 } 547 548 protected ElementBinding getElementLocal(QName elementName) 549 { 550 return (ElementBinding)children.get(elementName); 551 } 552 553 protected AttributeBinding getAttributeLocal(QName attributeName) 554 { 555 String fieldName = Util.xmlNameToClassName(attributeName.getLocalPart(), true); 556 fieldName = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1); 557 return new AttributeBindingImpl(attributeName, null, getJavaType(), fieldName); 558 } 559 560 protected XmlValueBinding getValueLocal() 561 { 562 throw new UnsupportedOperationException ("getValueLocal is not implemented."); 564 } 565 566 public void addChild(BasicElementBinding child) 567 { 568 children.put(child.getName(), child); 569 } 570 } 571 572 interface ParentElement 573 extends BasicElementBinding 574 { 575 void addChild(BasicElementBinding child); 576 } 577 } 578 | Popular Tags |