1 /* Copyright 2004 The Apache Software Foundation 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package org.apache.xmlbeans.impl.inst2xsd; 16 17 import org.apache.xmlbeans; 18 import org.apache.xmlbeans.impl.common.PrefixResolver; 19 import org.apache.xmlbeans.impl.common.ValidationContext; 20 import org.apache.xmlbeans.impl.common.XmlWhitespace; 21 import org.apache.xmlbeans.impl.inst2xsd.util.Attribute; 22 import org.apache.xmlbeans.impl.inst2xsd.util.Element; 23 import org.apache.xmlbeans.impl.inst2xsd.util.TypeSystemHolder; 24 import org.apache.xmlbeans.impl.inst2xsd.util.Type; 25 import org.apache.xmlbeans.impl.util.XsTypeConverter; 26 import org.apache.xmlbeans.impl.values; 27 28 import javax.xml.namespace.QName; 29 import java.util; 30 31 /** 32 * @author Cezar Andrei ( cezar.andrei at bea.com ) 33 * Date: Jul 26, 2004 34 */ 35 public class RussianDollStrategy 36 implements XsdGenStrategy 37 { 38 static final String _xsi = "http://www.w3.org/2001/XMLSchema-instance"; 39 40 static final QName _xsiNil = new QName( _xsi, "nil", "xsi" ); 41 static final QName _xsiType = new QName( _xsi, "type", "xsi" ); 42 43 public void processDoc(XmlObject[] instances, Inst2XsdOptions options, TypeSystemHolder typeSystemHolder) 44 { 45 for (int i = 0; i < instances.length; i++) 46 { 47 XmlObject instance = instances[i]; 48 XmlCursor xc = instance.newCursor(); 49 // xc on start doc 50 51 StringBuffer comment = new StringBuffer(); 52 53 while( !xc.isStart() ) 54 { 55 xc.toNextToken(); 56 if( xc.isComment() ) 57 comment.append(xc.getTextValue()); 58 else if (xc.isEnddoc()) 59 return; 60 } 61 // xc now on the root element 62 63 Element withElem = processElement(xc, comment.toString(), options, typeSystemHolder); 64 withElem.setGlobal(true); 65 66 addGlobalElement(withElem, typeSystemHolder, options); 67 } 68 } 69 70 protected Element addGlobalElement(Element withElem, TypeSystemHolder typeSystemHolder, Inst2XsdOptions options) 71 { 72 assert withElem.isGlobal(); 73 Element intoElem = typeSystemHolder.getGlobalElement(withElem.getName()); 74 75 if (intoElem==null) 76 { 77 typeSystemHolder.addGlobalElement(withElem); 78 return withElem; 79 } 80 else 81 { 82 combineTypes(intoElem.getType(), withElem.getType(), options); 83 combineElementComments(intoElem, withElem); 84 return intoElem; 85 } 86 } 87 88 protected Element processElement(XmlCursor xc, String comment, 89 Inst2XsdOptions options, TypeSystemHolder typeSystemHolder) 90 { 91 assert xc.isStart(); 92 Element element = new Element(); 93 element.setName(xc.getName()); 94 element.setGlobal(false); 95 96 Type elemType = Type.createUnnamedType(Type.SIMPLE_TYPE_SIMPLE_CONTENT); //assume simple, set later 97 element.setType(elemType); 98 99 StringBuffer textBuff = new StringBuffer(); 100 StringBuffer commentBuff = new StringBuffer(); 101 List children = new ArrayList(); 102 List attributes = new ArrayList(); 103 104 loop: do 105 { 106 XmlCursor.TokenType tt = xc.toNextToken(); 107 switch (tt.intValue()) 108 { 109 case XmlCursor.TokenType.INT_ATTR: 110 // todo check for xsi:type 111 // ignore xsi:... attributes other than xsi:nil 112 QName attName = xc.getName(); 113 if (!_xsiNil.getNamespaceURI().equals(attName.getNamespaceURI())) 114 attributes.add(processAttribute(xc, options, element.getName().getNamespaceURI(), typeSystemHolder)); 115 else if (_xsiNil.equals(attName)) 116 element.setNillable(true); 117 118 break; 119 120 case XmlCursor.TokenType.INT_START: 121 children.add(processElement(xc, commentBuff.toString(), options, typeSystemHolder)); 122 commentBuff.delete(0, commentBuff.length()); 123 break; 124 125 case XmlCursor.TokenType.INT_TEXT: 126 textBuff.append(xc.getChars()); 127 break; 128 129 case XmlCursor.TokenType.INT_COMMENT: 130 commentBuff.append(xc.getTextValue()); 131 break; 132 133 case XmlCursor.TokenType.INT_NAMESPACE: 134 // ignore, 135 // each element and attribute will take care to define itself in the right targetNamespace 136 break; 137 138 case XmlCursor.TokenType.INT_END: 139 break loop; 140 141 case XmlCursor.TokenType.INT_PROCINST: 142 // ignore 143 break; 144 145 case XmlCursor.TokenType.INT_ENDDOC: 146 break loop; 147 148 case XmlCursor.TokenType.INT_NONE: 149 break loop; 150 151 case XmlCursor.TokenType.INT_STARTDOC: 152 throw new IllegalStateException(); 153 154 default: 155 throw new IllegalStateException("Unknown TokenType."); 156 } 157 } 158 while( true ); 159 160 String collapsedText = XmlWhitespace.collapse(textBuff.toString(), XmlWhitespace.WS_COLLAPSE); 161 162 String commnetStr = (comment == null ? 163 ( commentBuff.length() == 0 ? null : commentBuff.toString() ) : 164 ( commentBuff.length() == 0 ? comment : commentBuff.insert(0, comment).toString()) ); 165 element.setComment(commnetStr); 166 167 if (children.size()>0) 168 { 169 // complex content 170 if (collapsedText.length()>0) 171 { 172 elemType.setContentType(Type.COMPLEX_TYPE_MIXED_CONTENT); 173 } 174 else 175 { 176 elemType.setContentType(Type.COMPLEX_TYPE_COMPLEX_CONTENT); 177 } 178 processElementsInComplexType(elemType, children, element.getName().getNamespaceURI(), typeSystemHolder, options); 179 processAttributesInComplexType(elemType, attributes); 180 } 181 else 182 { 183 // simple content 184 // hack workaround for being able to call xc.getNamespaceForPrefix() 185 XmlCursor xcForNamespaces = xc.newCursor(); 186 xcForNamespaces.toParent(); 187 188 if (attributes.size()>0) 189 { 190 elemType.setContentType(Type.COMPLEX_TYPE_SIMPLE_CONTENT); 191 192 Type extendedType = Type.createNamedType( 193 processSimpleContentType(textBuff.toString(), options, xcForNamespaces), Type.SIMPLE_TYPE_SIMPLE_CONTENT); 194 elemType.setExtensionType(extendedType); 195 196 processAttributesInComplexType(elemType, attributes); 197 } 198 else 199 { 200 elemType.setContentType(Type.SIMPLE_TYPE_SIMPLE_CONTENT); 201 elemType.setName(processSimpleContentType(textBuff.toString(), options, xcForNamespaces)); 202 203 // add enumeration value 204 String enumValue = XmlString.type.getName().equals(elemType.getName()) ? textBuff.toString() : collapsedText; 205 elemType.addEnumerationValue(enumValue, xcForNamespaces); 206 } 207 208 xcForNamespaces.dispose(); // end hack 209 } 210 211 checkIfReferenceToGlobalTypeIsNeeded( element, typeSystemHolder, options); 212 213 return element; 214 } 215 216 protected void processElementsInComplexType(Type elemType, List children, String parentNamespace, 217 TypeSystemHolder typeSystemHolder, Inst2XsdOptions options) 218 { 219 Map elemNamesToElements = new HashMap(); 220 Element currentElem = null; 221 222 for (Iterator iterator = children.iterator(); iterator.hasNext();) 223 { 224 Element child = (Element) iterator.next(); 225 226 if (currentElem==null) 227 { // first element in this type 228 checkIfElementReferenceIsNeeded(child, parentNamespace, typeSystemHolder, options); 229 elemType.addElement(child); 230 elemNamesToElements.put(child.getName(), child); 231 currentElem = child; 232 continue; 233 } 234 235 if (currentElem.getName()==child.getName()) 236 { // same contiguos element 237 combineTypes(currentElem.getType(), child.getType(), options); // unify types 238 combineElementComments(currentElem, child); 239 // minOcc=0 maxOcc=unbounded 240 currentElem.setMinOccurs(0); 241 currentElem.setMaxOccurs(Element.UNBOUNDED); 242 } 243 else 244 { 245 Element sameElem = (Element)elemNamesToElements.get(child.getName()); 246 if (sameElem==null) 247 { // new element name 248 checkIfElementReferenceIsNeeded(child, parentNamespace, typeSystemHolder, options); 249 elemType.addElement(child); 250 elemNamesToElements.put(child.getName(), child); 251 } 252 else 253 { //same non contiguos 254 combineTypes(currentElem.getType(), child.getType(), options); 255 combineElementComments(currentElem, child); 256 elemType.setTopParticleForComplexOrMixedContent(Type.PARTICLE_CHOICE_UNBOUNDED); 257 } 258 currentElem = child; 259 } 260 } 261 } 262 263 protected void checkIfElementReferenceIsNeeded(Element child, String parentNamespace, 264 TypeSystemHolder typeSystemHolder, Inst2XsdOptions options) 265 { 266 if (!child.getName().getNamespaceURI().equals(parentNamespace)) 267 { 268 Element referencedElem = new Element(); 269 referencedElem.setGlobal(true); 270 referencedElem.setName(child.getName()); 271 referencedElem.setType(child.getType()); 272 273 if (child.isNillable()) 274 { 275 referencedElem.setNillable(true); 276 child.setNillable(false); 277 } 278 279 referencedElem = addGlobalElement(referencedElem, typeSystemHolder, options); 280 281 child.setRef(referencedElem); // clears child's type 282 } 283 } 284 285 protected void checkIfReferenceToGlobalTypeIsNeeded(Element elem, TypeSystemHolder typeSystemHolder, 286 Inst2XsdOptions options) 287 { 288 // RussianDollDesign doesn't define global types 289 } 290 291 protected void processAttributesInComplexType(Type elemType, List attributes) 292 { 293 assert elemType.isComplexType(); 294 for (Iterator iterator = attributes.iterator(); iterator.hasNext();) 295 { 296 Attribute att = (Attribute) iterator.next(); 297 elemType.addAttribute(att); 298 } 299 } 300 301 protected Attribute processAttribute(XmlCursor xc, Inst2XsdOptions options, String parentNamespace, 302 TypeSystemHolder typeSystemHolder) 303 { 304 assert xc.isAttr() : "xc not on attribute"; 305 Attribute attribute = new Attribute(); 306 QName attName = xc.getName(); 307 308 attribute.setName(attName); 309 310 XmlCursor parent = xc.newCursor(); 311 parent.toParent(); 312 313 Type simpleContentType = Type.createNamedType( 314 processSimpleContentType(xc.getTextValue(), options, parent), Type.SIMPLE_TYPE_SIMPLE_CONTENT); 315 316 parent.dispose(); 317 318 attribute.setType(simpleContentType); 319 320 checkIfAttributeReferenceIsNeeded(attribute, parentNamespace, typeSystemHolder); 321 322 return attribute; 323 } 324 325 protected void checkIfAttributeReferenceIsNeeded(Attribute attribute, String parentNamespace, TypeSystemHolder typeSystemHolder) 326 { 327 if (!attribute.getName().getNamespaceURI().equals("") && 328 !attribute.getName().getNamespaceURI().equals(parentNamespace)) 329 { 330 // make attribute be a reference to a top level attribute in a different targetNamespace 331 Attribute referencedAtt = new Attribute(); 332 referencedAtt.setGlobal(true); 333 referencedAtt.setName(attribute.getName()); 334 referencedAtt.setType(attribute.getType()); 335 336 typeSystemHolder.addGlobalAttribute(referencedAtt); 337 338 attribute.setRef(referencedAtt); 339 } 340 } 341 342 protected class SCTValidationContext 343 implements ValidationContext 344 { 345 protected boolean valid = true; 346 347 public boolean isValid() 348 { 349 return valid; 350 } 351 352 public void resetToValid() 353 { 354 valid = true; 355 } 356 357 public void invalid(String message) 358 { 359 valid = false; 360 } 361 362 public void invalid(String code, Object[] args) 363 { 364 valid = false; 365 } 366 } 367 368 private SCTValidationContext _validationContext = new SCTValidationContext(); 369 370 371 // List of precedence for smart simple primitive type determination 372 // byte, short, int, long, integer, float, double, decimal, 373 // boolean 374 // date, dateTime, time, gDuration, 375 // QName ?, 376 // anyUri ? - triggered only for http:// or www. constructs, 377 // list types ? 378 // string 379 protected QName processSimpleContentType(String lexicalValue, Inst2XsdOptions options, final XmlCursor xc) 380 { 381 // check options and return xsd:string or if smart is enabled, look for a better type 382 if (options.getSimpleContentTypes()==Inst2XsdOptions.SIMPLE_CONTENT_TYPES_STRING) 383 return XmlString.type.getName(); 384 385 if (options.getSimpleContentTypes()!=Inst2XsdOptions.SIMPLE_CONTENT_TYPES_SMART) 386 throw new IllegalArgumentException("Unknown value for Inst2XsdOptions.getSimpleContentTypes() :" + options.getSimpleContentTypes()); 387 388 // Inst2XsdOptions.SIMPLE_CONTENT_TYPES_SMART case 389 390 391 try 392 { 393 XsTypeConverter.lexByte(lexicalValue); 394 return XmlByte.type.getName(); 395 } 396 catch (Exception e) {} 397 398 try 399 { 400 XsTypeConverter.lexShort(lexicalValue); 401 return XmlShort.type.getName(); 402 } 403 catch (Exception e) {} 404 405 try 406 { 407 XsTypeConverter.lexInt(lexicalValue); 408 return XmlInt.type.getName(); 409 } 410 catch (Exception e) {} 411 412 try 413 { 414 XsTypeConverter.lexLong(lexicalValue); 415 return XmlLong.type.getName(); 416 } 417 catch (Exception e) {} 418 419 try 420 { 421 XsTypeConverter.lexInteger(lexicalValue); 422 return XmlInteger.type.getName(); 423 } 424 catch (Exception e) {} 425 426 try 427 { 428 XsTypeConverter.lexFloat(lexicalValue); 429 return XmlFloat.type.getName(); 430 } 431 catch (Exception e) {} 432 433 // // this not needed because it's lexical space is covered by float 434 // try 435 // { 436 // XsTypeConverter.lexDouble(lexicalValue); 437 // return XmlDouble.type.getName(); 438 // } 439 // catch (Exception e) {} 440 // 441 // try 442 // { 443 // XsTypeConverter.lexDecimal(lexicalValue); 444 // return XmlDecimal.type.getName(); 445 // } 446 // catch (Exception e) {} 447 448 XmlDateImpl.validateLexical(lexicalValue, XmlDate.type, _validationContext); 449 if (_validationContext.isValid()) 450 return XmlDate.type.getName(); 451 _validationContext.resetToValid(); 452 453 XmlDateTimeImpl.validateLexical(lexicalValue, XmlDateTime.type, _validationContext); 454 if (_validationContext.isValid()) 455 return XmlDateTime.type.getName(); 456 _validationContext.resetToValid(); 457 458 XmlTimeImpl.validateLexical(lexicalValue, XmlTime.type, _validationContext); 459 if (_validationContext.isValid()) 460 return XmlTime.type.getName(); 461 _validationContext.resetToValid(); 462 463 XmlDurationImpl.validateLexical(lexicalValue, XmlDuration.type, _validationContext); 464 if (_validationContext.isValid()) 465 return XmlDuration.type.getName(); 466 _validationContext.resetToValid(); 467 468 // check for uri 469 if (lexicalValue.startsWith("http://") || lexicalValue.startsWith("www.")) 470 { 471 XmlAnyUriImpl.validateLexical(lexicalValue, _validationContext); 472 if (_validationContext.isValid()) 473 return XmlAnyURI.type.getName(); 474 _validationContext.resetToValid(); 475 } 476 477 // check for QName 478 int idx = lexicalValue.indexOf(':'); 479 if (idx>=0 && idx==lexicalValue.lastIndexOf(':') && idx+1<lexicalValue.length()) 480 { 481 PrefixResolver prefixResolver = new PrefixResolver() 482 { 483 public String getNamespaceForPrefix(String prefix) 484 { return xc.namespaceForPrefix(prefix); } 485 }; 486 487 QName qname = XmlQNameImpl.validateLexical(lexicalValue, _validationContext, prefixResolver); 488 if (_validationContext.isValid()) 489 return XmlQName.type.getName(); 490 _validationContext.resetToValid(); 491 } 492 493 //the check for lists is probably too expensive 494 495 return XmlString.type.getName(); 496 } 497 498 499 protected void combineTypes(Type into, Type with, Inst2XsdOptions options) 500 { 501 if (into==with) 502 return; 503 504 if (into.isGlobal() && with.isGlobal() && into.getName().equals(with.getName())) 505 return; 506 507 508 if (into.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT && 509 with.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT) 510 { 511 combineSimpleTypes(into, with, options); 512 return; 513 } 514 515 if ((into.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT || 516 into.getContentType()==Type.COMPLEX_TYPE_SIMPLE_CONTENT) && 517 (with.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT || 518 with.getContentType()==Type.COMPLEX_TYPE_SIMPLE_CONTENT) ) 519 { 520 // take the extension name if it's a complex type 521 QName intoTypeName = into.isComplexType() ? into.getExtensionType().getName() : into.getName(); 522 QName withTypeName = with.isComplexType() ? with.getExtensionType().getName() : with.getName(); 523 524 //complex type simple content 525 into.setContentType(Type.COMPLEX_TYPE_SIMPLE_CONTENT); 526 527 QName moreGeneralTypeName = combineToMoreGeneralSimpleType(intoTypeName, withTypeName); 528 if (into.isComplexType()) 529 { 530 Type extendedType = Type.createNamedType(moreGeneralTypeName, Type.SIMPLE_TYPE_SIMPLE_CONTENT); 531 into.setExtensionType(extendedType); 532 } 533 else 534 into.setName(moreGeneralTypeName); 535 536 combineAttributesOfTypes(into, with); 537 return; 538 } 539 540 if (into.getContentType()==Type.COMPLEX_TYPE_COMPLEX_CONTENT && 541 with.getContentType()==Type.COMPLEX_TYPE_COMPLEX_CONTENT) 542 { 543 combineAttributesOfTypes(into, with); 544 combineElementsOfTypes(into, with, false, options); 545 return; 546 } 547 548 if (into.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT || 549 into.getContentType()==Type.COMPLEX_TYPE_SIMPLE_CONTENT || 550 with.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT || 551 with.getContentType()==Type.COMPLEX_TYPE_SIMPLE_CONTENT) 552 { 553 into.setContentType(Type.COMPLEX_TYPE_MIXED_CONTENT); 554 combineAttributesOfTypes(into, with); 555 combineElementsOfTypes(into, with, true, options); 556 return; 557 } 558 559 if ((into.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT || 560 into.getContentType()==Type.COMPLEX_TYPE_SIMPLE_CONTENT || 561 into.getContentType()==Type.COMPLEX_TYPE_COMPLEX_CONTENT || 562 into.getContentType()==Type.COMPLEX_TYPE_MIXED_CONTENT) && 563 (with.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT || 564 with.getContentType()==Type.COMPLEX_TYPE_SIMPLE_CONTENT || 565 with.getContentType()==Type.COMPLEX_TYPE_COMPLEX_CONTENT || 566 with.getContentType()==Type.COMPLEX_TYPE_MIXED_CONTENT) ) 567 { 568 into.setContentType(Type.COMPLEX_TYPE_MIXED_CONTENT); 569 combineAttributesOfTypes(into, with); 570 combineElementsOfTypes(into, with, false, options); 571 return; 572 } 573 574 throw new IllegalArgumentException("Unknown content type."); 575 } 576 577 protected void combineSimpleTypes(Type into, Type with, Inst2XsdOptions options) 578 { 579 assert (into.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT && 580 with.getContentType()==Type.SIMPLE_TYPE_SIMPLE_CONTENT) : "Invalid arguments"; 581 582 //simple type simple content 583 into.setName(combineToMoreGeneralSimpleType(into.getName(), with.getName())); 584 585 // take care of enumeration values 586 if (options.isUseEnumerations()) 587 { 588 into.addAllEnumerationsFrom(with); 589 590 if (into.getEnumerationValues().size()>options.getUseEnumerations()) 591 { 592 into.closeEnumeration(); 593 } 594 } 595 } 596 597 protected QName combineToMoreGeneralSimpleType(QName t1, QName t2) 598 { 599 if (t1.equals(t2)) 600 return t1; 601 602 if (t2.equals(XmlShort.type.getName()) && t1.equals(XmlByte.type.getName())) 603 return t2; 604 if (t1.equals(XmlShort.type.getName()) && t2.equals(XmlByte.type.getName())) 605 return t1; 606 607 if (t2.equals(XmlInt.type.getName()) && 608 (t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName())) ) 609 return t2; 610 if (t1.equals(XmlInt.type.getName()) && 611 (t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName())) ) 612 return t1; 613 614 if (t2.equals(XmlLong.type.getName()) && 615 (t1.equals(XmlInt.type.getName()) || t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName())) ) 616 return t2; 617 if (t1.equals(XmlLong.type.getName()) && 618 (t2.equals(XmlInt.type.getName()) || t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName())) ) 619 return t1; 620 621 if (t2.equals(XmlInteger.type.getName()) && 622 (t1.equals(XmlLong.type.getName()) || t1.equals(XmlInt.type.getName()) || 623 t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName())) ) 624 return t2; 625 if (t1.equals(XmlInteger.type.getName()) && 626 (t2.equals(XmlLong.type.getName()) || t2.equals(XmlInt.type.getName()) || 627 t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName())) ) 628 return t1; 629 630 if (t2.equals(XmlFloat.type.getName()) && 631 (t1.equals(XmlInteger.type.getName()) || 632 t1.equals(XmlLong.type.getName()) || t1.equals(XmlInt.type.getName()) || 633 t1.equals(XmlShort.type.getName()) || t1.equals(XmlByte.type.getName())) ) 634 return t2; 635 if (t1.equals(XmlFloat.type.getName()) && 636 (t2.equals(XmlInteger.type.getName()) || 637 t2.equals(XmlLong.type.getName()) || t2.equals(XmlInt.type.getName()) || 638 t2.equals(XmlShort.type.getName()) || t2.equals(XmlByte.type.getName())) ) 639 return t1; 640 641 //double, decimal will never get here since they don't get generated 642 643 //the rest of the combinations are not compatible, so they will combine in xsd:string 644 return XmlString.type.getName(); 645 } 646 647 protected void combineAttributesOfTypes(Type into, Type from) 648 { 649 // loop through attributes: add fromAtt if they don't exist, combine them if they exist 650 outterLoop: 651 for (int i = 0; i < from.getAttributes().size(); i++) 652 { 653 Attribute fromAtt = (Attribute)from.getAttributes().get(i); 654 for (int j = 0; j < into.getAttributes().size(); j++) 655 { 656 Attribute intoAtt = (Attribute)into.getAttributes().get(j); 657 if (intoAtt.getName().equals(fromAtt.getName())) 658 { 659 intoAtt.getType().setName( 660 combineToMoreGeneralSimpleType(intoAtt.getType().getName(), fromAtt.getType().getName())); 661 continue outterLoop; 662 } 663 } 664 // fromAtt doesn't exist in into type, will add it right now 665 into.addAttribute(fromAtt); 666 } 667 668 //optional attributes: if there are atts in into that are not in from, make them optional 669 outterLoop: 670 for (int i = 0; i < into.getAttributes().size(); i++) 671 { 672 Attribute intoAtt = (Attribute)into.getAttributes().get(i); 673 for (int j = 0; j < from.getAttributes().size(); j++) 674 { 675 Attribute fromAtt = (Attribute)from.getAttributes().get(j); 676 if (fromAtt.getName().equals(intoAtt.getName())) 677 { 678 continue; 679 } 680 } 681 // intoAtt doesn't exist in into type, will add it right now 682 intoAtt.setOptional(true); 683 } 684 } 685 686 protected void combineElementsOfTypes(Type into, Type from, boolean makeElementsOptional, Inst2XsdOptions options) 687 { 688 boolean needsUnboundedChoice = false; 689 690 if (into.getTopParticleForComplexOrMixedContent()!=Type.PARTICLE_SEQUENCE || 691 from.getTopParticleForComplexOrMixedContent()!=Type.PARTICLE_SEQUENCE) 692 needsUnboundedChoice = true; 693 694 List res = new ArrayList(); 695 696 int fromStartingIndex = 0; 697 int fromMatchedIndex = -1; 698 int intoMatchedIndex = -1; 699 700 // for each element in into 701 for (int i = 0; !needsUnboundedChoice && i < into.getElements().size(); i++) 702 { 703 // try to find one with same name in from 704 Element intoElement = (Element) into.getElements().get(i); 705 for (int j = fromStartingIndex; j < from.getElements().size(); j++) 706 { 707 Element fromElement = (Element) from.getElements().get(j); 708 if (intoElement.getName().equals(fromElement.getName())) 709 { 710 fromMatchedIndex = j; 711 break; 712 } 713 } 714 715 // if not found, it's safe to add this one to result 'res' (as optional) and continue 716 if ( fromMatchedIndex < fromStartingIndex ) 717 { 718 res.add(intoElement); 719 intoElement.setMinOccurs(0); 720 continue; 721 } 722 723 // else try out all from elemens between fromStartingIndex to fromMatchedIndex 724 // to see if they match one of the into elements 725 intoMatchingLoop: 726 for (int j2 = fromStartingIndex; j2 < fromMatchedIndex; j2++) 727 { 728 Element fromCandidate = (Element) from.getElements().get(j2); 729 730 for (int i2 = i+1; i2 < into.getElements().size(); i2++) 731 { 732 Element intoCandidate = (Element) into.getElements().get(i2); 733 if (fromCandidate.getName().equals(intoCandidate.getName())) 734 { 735 intoMatchedIndex = i2; 736 break intoMatchingLoop; 737 } 738 } 739 } 740 741 if (intoMatchedIndex<i) 742 { 743 // if none matched they are safe to be added to res as optional 744 for (int j3 = fromStartingIndex; j3 < fromMatchedIndex; j3++) 745 { 746 Element fromCandidate = (Element) from.getElements().get(j3); 747 res.add(fromCandidate); 748 fromCandidate.setMinOccurs(0); 749 } 750 // also since into[i] == from[fromMatchedIndex] add it only once 751 res.add(intoElement); 752 Element fromMatchedElement = (Element)from.getElements().get(fromMatchedIndex); 753 754 if (fromMatchedElement.getMinOccurs()<=0) 755 intoElement.setMinOccurs(0); 756 if (fromMatchedElement.getMaxOccurs()==Element.UNBOUNDED) 757 intoElement.setMaxOccurs(Element.UNBOUNDED); 758 759 combineTypes(intoElement.getType(), fromMatchedElement.getType(), options); 760 combineElementComments(intoElement, fromMatchedElement); 761 762 fromStartingIndex = fromMatchedIndex + 1; 763 continue; 764 } 765 else 766 { 767 // if matched it means into type will transform into a choice unbounded type 768 needsUnboundedChoice = true; 769 } 770 } 771 772 for (int j = fromStartingIndex; j < from.getElements().size(); j++) 773 { 774 Element remainingFromElement = (Element) from.getElements().get(j); 775 res.add(remainingFromElement); 776 remainingFromElement.setMinOccurs(0); 777 } 778 779 // if choice was detected 780 if (needsUnboundedChoice) 781 { 782 into.setTopParticleForComplexOrMixedContent(Type.PARTICLE_CHOICE_UNBOUNDED); 783 784 outterLoop: 785 for (int j = 0; j < from.getElements().size(); j++) 786 { 787 Element fromElem = (Element) from.getElements().get(j); 788 for (int i = 0; i < into.getElements().size(); i++) 789 { 790 Element intoElem = (Element)into.getElements().get(i); 791 intoElem.setMinOccurs(1); 792 intoElem.setMaxOccurs(1); 793 794 if (intoElem==fromElem) 795 continue outterLoop; 796 797 if (intoElem.getName().equals(fromElem.getName())) 798 { 799 combineTypes(intoElem.getType(), fromElem.getType(), options); 800 combineElementComments(intoElem, fromElem); 801 802 continue outterLoop; 803 } 804 } 805 806 // fromElem doesn't exist in into type, will add it right now 807 into.addElement(fromElem); 808 fromElem.setMinOccurs(1); 809 fromElem.setMaxOccurs(1); 810 } 811 return; 812 } 813 else 814 { 815 // into remains sequence but will contain the new list of elements res 816 into.setElements(res); 817 return; 818 } 819 } 820 821 protected void combineElementComments(Element into, Element with) 822 { 823 if (with.getComment()!=null && with.getComment().length()>0) 824 { 825 if (into.getComment()==null) 826 into.setComment(with.getComment()); 827 else 828 into.setComment(into.getComment() + with.getComment()); 829 } 830 } 831 }