1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 package org.apache.openoffice.ooxml.schema.parser; 23 24 import java.io.File; 25 import java.io.FileInputStream; 26 import java.util.HashMap; 27 import java.util.Map; 28 import java.util.Vector; 29 30 import javax.xml.stream.XMLInputFactory; 31 import javax.xml.stream.XMLStreamException; 32 import javax.xml.stream.XMLStreamReader; 33 34 import org.apache.openoffice.ooxml.schema.model.attribute.Attribute; 35 import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup; 36 import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference; 37 import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference; 38 import org.apache.openoffice.ooxml.schema.model.base.INode; 39 import org.apache.openoffice.ooxml.schema.model.base.Location; 40 import org.apache.openoffice.ooxml.schema.model.base.Node; 41 import org.apache.openoffice.ooxml.schema.model.base.QualifiedName; 42 import org.apache.openoffice.ooxml.schema.model.complex.All; 43 import org.apache.openoffice.ooxml.schema.model.complex.Any; 44 import org.apache.openoffice.ooxml.schema.model.complex.Choice; 45 import org.apache.openoffice.ooxml.schema.model.complex.ComplexContent; 46 import org.apache.openoffice.ooxml.schema.model.complex.ComplexType; 47 import org.apache.openoffice.ooxml.schema.model.complex.Element; 48 import org.apache.openoffice.ooxml.schema.model.complex.ElementReference; 49 import org.apache.openoffice.ooxml.schema.model.complex.Extension; 50 import org.apache.openoffice.ooxml.schema.model.complex.Group; 51 import org.apache.openoffice.ooxml.schema.model.complex.GroupReference; 52 import org.apache.openoffice.ooxml.schema.model.complex.OccurrenceIndicator; 53 import org.apache.openoffice.ooxml.schema.model.complex.Sequence; 54 import org.apache.openoffice.ooxml.schema.model.schema.Schema; 55 import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase; 56 import org.apache.openoffice.ooxml.schema.model.simple.List; 57 import org.apache.openoffice.ooxml.schema.model.simple.Restriction; 58 import org.apache.openoffice.ooxml.schema.model.simple.SimpleContent; 59 import org.apache.openoffice.ooxml.schema.model.simple.SimpleType; 60 import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference; 61 import org.apache.openoffice.ooxml.schema.model.simple.Union; 62 63 64 /** Parser for single schema file. 65 * Imports and includes of other schema files are stored and can be retrieved 66 * by calling GetImportedSchemas(). 67 * 68 * Typical usage: 69 * 1) Create SchemaParser for top-level schema file. 70 * 2) Call Parse(). 71 * 3) Repeat the same recursively for all imported schemas (as returned by 72 * GetImportedSchemas()). 73 * 74 * All top level types (complex types, simple types, elements, etc.) are 75 * stored in the given Schema object. 76 */ 77 public class SchemaParser 78 { SchemaParser( final File aSchemaFile, final Schema aSchema, final SchemaBase aSchemaBase)79 public SchemaParser ( 80 final File aSchemaFile, 81 final Schema aSchema, 82 final SchemaBase aSchemaBase) 83 { 84 maSchema = aSchema; 85 maSchemaBase = aSchemaBase; 86 maReader = GetStreamReader(aSchemaFile); 87 msBasename = aSchemaFile.getName(); 88 maDirectory = aSchemaFile.getParentFile(); 89 maLocalNamespaceMap = new HashMap<>(); 90 maImportedSchemas = new Vector<>(); 91 maLastLocation = null; 92 } 93 94 95 96 97 /** Parse the schema file. 98 * @return 99 * Return false if there is any error. 100 * @throws XMLStreamException 101 */ Parse()102 public void Parse () 103 throws XMLStreamException 104 { 105 if (maReader == null) 106 return; 107 108 while (maReader.hasNext()) 109 { 110 final int nCode = maReader.next(); 111 switch (nCode) 112 { 113 case XMLStreamReader.START_ELEMENT: 114 if (maReader.getLocalName().equals("schema")) 115 { 116 ProcessSchemaTag(); 117 ParseSchema(); 118 119 maLastLocation = maReader.getLocation(); 120 } 121 else 122 { 123 throw CreateErrorException("expecting top level element to be 'schema'"); 124 } 125 break; 126 127 case XMLStreamReader.END_DOCUMENT: 128 return; 129 130 default: 131 throw CreateErrorException("unexpected XML event %d", nCode); 132 } 133 } 134 } 135 136 137 138 GetImportedSchemaFilenames()139 public Iterable<File> GetImportedSchemaFilenames () 140 { 141 return maImportedSchemas; 142 } 143 144 145 146 GetLineCount()147 public int GetLineCount () 148 { 149 if (maLastLocation != null) 150 return maLastLocation.getLineNumber(); 151 else 152 return 0; 153 } 154 155 156 157 GetByteCount()158 public int GetByteCount () 159 { 160 if (maLastLocation != null) 161 return maLastLocation.getCharacterOffset(); 162 else 163 return 0; 164 } 165 166 167 168 169 /** Process the namespace definitions in the outer 'schema' element. 170 */ ProcessSchemaTag()171 private void ProcessSchemaTag () 172 { 173 GetOptionalAttributeValue("id", null); 174 meAttributeFormDefault = FormDefault.valueOf( 175 GetOptionalAttributeValue("attributeFormDefault", "unqualified")); 176 meElementFormDefault = FormDefault.valueOf( 177 GetOptionalAttributeValue("elementFormDefault", "unqualified")); 178 GetOptionalAttributeValue("blockDefault", null);//=(#all|list of (extension|restriction|substitution)) 179 GetOptionalAttributeValue("finalDefault", null);//=(#all|list of (extension|restriction|list|union)) 180 msTargetNamespace = GetOptionalAttributeValue("targetNamespace", null); 181 GetOptionalAttributeValue("version", null); 182 183 for (int nIndex=0; nIndex<maReader.getNamespaceCount(); ++nIndex) 184 { 185 final String sPrefix = maReader.getNamespacePrefix(nIndex); 186 final String sURI = maReader.getNamespaceURI(nIndex); 187 maLocalNamespaceMap.put(sPrefix, sURI); 188 maSchemaBase.Namespaces.ProvideNamespace(sURI, sPrefix); 189 } 190 191 maLocalNamespaceMap.put(null, msTargetNamespace); 192 maSchemaBase.Namespaces.ProvideNamespace(msTargetNamespace, null); 193 } 194 195 196 197 ParseSchema()198 private void ParseSchema () 199 throws XMLStreamException 200 { 201 while (true) 202 { 203 switch (Next()) 204 { 205 case XMLStreamReader.START_ELEMENT: 206 ProcessTopLevelStartElement(); 207 break; 208 209 case XMLStreamReader.END_ELEMENT: 210 return; 211 212 default: 213 throw CreateErrorException("unexpected event (expteced START_ELEMENT): %d", maReader.getEventType()); 214 } 215 } 216 } 217 218 219 220 ProcessTopLevelStartElement()221 private void ProcessTopLevelStartElement () 222 throws XMLStreamException 223 { 224 assert(GetAttributeValue("minOccurs") == null); 225 assert(GetAttributeValue("maxOccurs") == null); 226 227 switch (maReader.getLocalName()) 228 { 229 case "attribute": 230 maSchemaBase.Attributes.Add(ParseAttribute()); 231 break; 232 233 case "attributeGroup": 234 maSchemaBase.AttributeGroups.Add(ParseAttributeGroup()); 235 break; 236 237 case "complexType": 238 maSchemaBase.ComplexTypes.Add(ParseComplexType()); 239 break; 240 241 case "element": 242 final Element aElement = ParseElement(null); 243 if (maSchema != null) 244 maSchema.TopLevelElements.Add(aElement); 245 maSchemaBase.TopLevelElements.Add(aElement); 246 break; 247 248 case "group": 249 maSchemaBase.Groups.Add(ParseGroup(null)); 250 break; 251 252 case "import": 253 ParseImport(); 254 break; 255 256 case "include": 257 ParseInclude(); 258 break; 259 260 case "simpleType": 261 maSchemaBase.SimpleTypes.Add(ParseSimpleType(null)); 262 break; 263 264 default: 265 throw CreateErrorException("unexpected top level element %s", maReader.getLocalName()); 266 } 267 } 268 269 270 271 ProcessStartElement(final Node aParent)272 private void ProcessStartElement (final Node aParent) 273 throws XMLStreamException 274 { 275 final String sMinimumOccurrence = GetOptionalAttributeValue("minOccurs", "1"); 276 final String sMaximumOccurrence = GetOptionalAttributeValue("maxOccurs", "1"); 277 278 final Node aLocalParent; 279 if ( ! (sMinimumOccurrence.equals("1") && sMaximumOccurrence.equals("1"))) 280 { 281 // Occurrence does not only have default values (min=max=1). 282 // Have to create an intermediate node for the occurrence indicator. 283 final OccurrenceIndicator aIndicator = new OccurrenceIndicator( 284 aParent, 285 sMinimumOccurrence, 286 sMaximumOccurrence, 287 GetLocation()); 288 aParent.AddChild(aIndicator); 289 aLocalParent = aIndicator; 290 } 291 else 292 aLocalParent = aParent; 293 294 switch (maReader.getLocalName()) 295 { 296 case "all": 297 aLocalParent.AddChild(ParseAll(aLocalParent)); 298 break; 299 300 case "any": 301 aLocalParent.AddChild(ParseAny(aLocalParent)); 302 break; 303 304 case "attribute": 305 aLocalParent.AddAttribute(ParseAttributeOrReference()); 306 break; 307 308 case "attributeGroup": 309 aLocalParent.AddAttribute(ParseAttributeGroupOrReference()); 310 break; 311 312 case "choice": 313 aLocalParent.AddChild(ParseChoice(aLocalParent)); 314 break; 315 316 case "complexContent": 317 aLocalParent.AddChild(ParseComplexContent(aLocalParent)); 318 break; 319 320 case "element": 321 aLocalParent.AddChild(ParseElementOrReference(aLocalParent)); 322 break; 323 324 case "extension": 325 aLocalParent.AddChild(ParseExtension(aLocalParent)); 326 break; 327 328 case "group": 329 aLocalParent.AddChild(ParseGroupOrReference(aLocalParent)); 330 break; 331 332 case "list": 333 aLocalParent.AddChild(ParseList(aLocalParent)); 334 break; 335 336 case "sequence": 337 aLocalParent.AddChild(ParseSequence(aLocalParent)); 338 break; 339 340 case "simpleContent": 341 aLocalParent.AddChild(ParseSimpleContent(aLocalParent)); 342 break; 343 344 default: 345 throw CreateErrorException("unsupported content type %s", maReader.getLocalName()); 346 } 347 } 348 349 350 351 IgnoreAnnotation()352 private void IgnoreAnnotation () 353 throws XMLStreamException 354 { 355 IgnoreContent(); 356 } 357 358 359 360 IgnoreContent()361 private void IgnoreContent () 362 throws XMLStreamException 363 { 364 while(true) 365 { 366 switch(maReader.next()) 367 { 368 case XMLStreamReader.START_ELEMENT: 369 IgnoreContent(); 370 break; 371 372 case XMLStreamReader.END_ELEMENT: 373 return; 374 375 case XMLStreamReader.COMMENT: 376 case XMLStreamReader.CHARACTERS: 377 break; 378 379 default: 380 throw CreateErrorException( 381 "unexpected XML event %d while ignoring content", 382 maReader.getEventType()); 383 } 384 } 385 } 386 387 388 389 ParseAll(final Node aParent)390 private All ParseAll (final Node aParent) 391 throws XMLStreamException 392 { 393 assert(HasOnlyAttributes("minOccurs", "maxOccurs")); 394 395 final All aAll = new All(aParent, GetLocation()); 396 ParseContent(aAll); 397 return aAll; 398 } 399 400 401 402 ParseAny(final Node aParent)403 private Any ParseAny (final Node aParent) 404 throws XMLStreamException 405 { 406 assert(HasOnlyAttributes("minOccurs", "maxOccurs", "namespace", "processContents")); 407 408 final Any aAny = new Any( 409 aParent, 410 GetLocation(), 411 GetOptionalAttributeValue("processContents", "strict"), 412 GetOptionalAttributeValue("namespace", "##any")); 413 ExpectEndElement("ParseAny"); 414 return aAny; 415 } 416 417 418 419 ParseAttributeGroup()420 private AttributeGroup ParseAttributeGroup () 421 throws XMLStreamException 422 { 423 assert(HasOnlyAttributes("name")); 424 425 final AttributeGroup aGroup = new AttributeGroup(GetOptionalQualifiedName("name"), GetLocation()); 426 427 while (true) 428 { 429 switch (Next()) 430 { 431 case XMLStreamReader.START_ELEMENT: 432 if ( ! maReader.getLocalName().equals("attribute")) 433 throw CreateErrorException( 434 "attributeGroup expects element 'attribute' but got %s", 435 maReader.getLocalName()); 436 else 437 aGroup.AddAttribute(ParseAttributeOrReference()); 438 break; 439 440 case XMLStreamReader.END_ELEMENT: 441 return aGroup; 442 443 default: 444 throw CreateErrorException( 445 "unexpected event when parsing attributeGroup: %d", 446 maReader.getEventType()); 447 } 448 } 449 } 450 451 452 453 ParseAttributeGroupOrReference()454 private INode ParseAttributeGroupOrReference () 455 throws XMLStreamException 456 { 457 assert(HasOnlyAttributes("ref")); 458 459 final INode aGroup; 460 if (GetAttributeValue("schemaLocation") != null) 461 { 462 aGroup = ParseAttributeGroup(); 463 } 464 else 465 { 466 aGroup = new AttributeGroupReference( 467 CreateQualifiedName( 468 GetAttributeValue("ref")), 469 GetLocation()); 470 ExpectEndElement("attribute group or reference"); 471 } 472 return aGroup; 473 } 474 475 476 477 ParseAttributeOrReference()478 private INode ParseAttributeOrReference () 479 throws XMLStreamException 480 { 481 final INode aAttribute; 482 if (GetAttributeValue("name") != null) 483 { 484 aAttribute = ParseAttribute(); 485 } 486 else 487 { 488 assert(HasOnlyAttributes("default", "fixed", "ref", "use")); 489 490 aAttribute = new AttributeReference( 491 GetQualifiedName("ref"), 492 GetOptionalAttributeValue("use", "optional"), 493 GetOptionalAttributeValue("default", null), 494 GetOptionalAttributeValue("fixed", null), 495 meAttributeFormDefault, 496 GetLocation()); 497 ExpectEndElement("attribute reference"); 498 } 499 return aAttribute; 500 } 501 502 503 504 ParseAttribute()505 private Attribute ParseAttribute () 506 throws XMLStreamException 507 { 508 assert(HasOnlyAttributes("default", "fixed", "name", "type", "use")); 509 510 final Attribute aAttribute = new Attribute( 511 GetQualifiedName("name"), 512 GetQualifiedName("type"), 513 GetOptionalAttributeValue("use", "optional"), 514 GetOptionalAttributeValue("default", null), 515 GetOptionalAttributeValue("fixed", null), 516 meAttributeFormDefault, 517 GetLocation()); 518 ExpectEndElement("attribute"); 519 520 return aAttribute; 521 } 522 523 524 ParseChoice(final Node aParent)525 private Choice ParseChoice (final Node aParent) 526 throws XMLStreamException 527 { 528 assert(HasOnlyAttributes("minOccurs", "maxOccurs")); 529 530 final Choice aChoice = new Choice(aParent, GetLocation()); 531 ParseContent(aChoice); 532 return aChoice; 533 } 534 535 536 537 ParseComplexContent(final Node aParent)538 private ComplexContent ParseComplexContent (final Node aParent) 539 throws XMLStreamException 540 { 541 assert(HasOnlyAttributes("minOccurs", "maxOccurs")); 542 543 final ComplexContent aNode = new ComplexContent(aParent, GetLocation()); 544 ParseContent(aNode); 545 return aNode; 546 } 547 548 549 550 ParseComplexType()551 private ComplexType ParseComplexType () 552 throws XMLStreamException 553 { 554 assert(HasOnlyAttributes("mixed", "name")); 555 556 final ComplexType aComplexType = new ComplexType( 557 null, 558 GetQualifiedName("name"), 559 GetLocation()); 560 561 ParseContent(aComplexType); 562 563 return aComplexType; 564 } 565 566 567 568 ParseElement(final Node aParent)569 private Element ParseElement (final Node aParent) 570 throws XMLStreamException 571 { 572 assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "type")); 573 574 final Element aElement = new Element( 575 aParent, 576 GetQualifiedName("name"), 577 GetQualifiedName("type"), 578 GetLocation()); 579 580 ExpectEndElement("element"); 581 582 return aElement; 583 } 584 585 586 587 ParseElementOrReference(final Node aParent)588 private Element ParseElementOrReference (final Node aParent) 589 throws XMLStreamException 590 { 591 assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "ref", "type")); 592 593 final Element aElement; 594 final String sName = GetOptionalAttributeValue("name", null); 595 if (sName != null) 596 { 597 aElement = ParseElement(aParent); 598 } 599 else 600 { 601 final String sElementReference = GetOptionalAttributeValue("ref", null); 602 if (sElementReference != null) 603 { 604 ExpectEndElement("element reference"); 605 aElement = new ElementReference( 606 aParent, 607 CreateQualifiedName(sElementReference), 608 GetLocation()); 609 } 610 else 611 { 612 throw CreateErrorException("element has no name and no ref"); 613 } 614 } 615 return aElement; 616 } 617 618 619 620 ParseExtension(final Node aParent)621 private Extension ParseExtension (final Node aParent) 622 throws XMLStreamException 623 { 624 assert(HasOnlyAttributes("base", "minOccurs", "maxOccurs")); 625 626 final Extension aNode = new Extension( 627 aParent, 628 CreateQualifiedName(GetAttributeValue("base")), 629 GetLocation()); 630 ParseContent(aNode); 631 return aNode; 632 } 633 634 635 636 ParseGroup(final Node aParent)637 private Group ParseGroup (final Node aParent) 638 throws XMLStreamException 639 { 640 assert(HasOnlyAttributes("name")); 641 642 final Group aGroup = new Group( 643 aParent, 644 GetQualifiedName("name"), 645 GetLocation()); 646 ParseContent(aGroup); 647 return aGroup; 648 } 649 650 651 652 ParseGroupOrReference(final Node aParent)653 private Node ParseGroupOrReference (final Node aParent) 654 throws XMLStreamException 655 { 656 assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "ref")); 657 658 final Node aGroup; 659 final String sName = GetOptionalAttributeValue("name", null); 660 if (sName != null) 661 { 662 aGroup = ParseGroup(aParent); 663 } 664 else 665 { 666 final String sGroupReference = GetOptionalAttributeValue("ref", null); 667 if (sGroupReference != null) 668 { 669 ExpectEndElement("group reference"); 670 aGroup = new GroupReference( 671 aParent, 672 CreateQualifiedName(sGroupReference), 673 GetLocation()); 674 } 675 else 676 { 677 throw CreateErrorException("group has no name and no ref"); 678 } 679 } 680 return aGroup; 681 } 682 683 684 685 ParseRestriction(final Node aParent)686 private Restriction ParseRestriction (final Node aParent) 687 throws XMLStreamException 688 { 689 assert(HasOnlyAttributes("base", "value")); 690 691 final String sBaseType = GetAttributeValue("base"); 692 final Restriction aRestriction = new Restriction( 693 aParent, 694 CreateQualifiedName(sBaseType), 695 GetLocation()); 696 697 while (true) 698 { 699 switch (Next()) 700 { 701 case XMLStreamReader.START_ELEMENT: 702 final String sValue = GetAttributeValue("value"); 703 switch (maReader.getLocalName()) 704 { 705 case "enumeration": 706 aRestriction.AddEnumeration(sValue); 707 break; 708 709 case "minInclusive": 710 aRestriction.SetMinInclusive(sValue); 711 break; 712 713 case "minExclusive": 714 aRestriction.SetMinExclusive(sValue); 715 break; 716 717 case "maxInclusive": 718 aRestriction.SetMaxInclusive(sValue); 719 break; 720 721 case "maxExclusive": 722 aRestriction.SetMaxExclusive(sValue); 723 break; 724 725 case "length": 726 aRestriction.SetLength(sValue); 727 break; 728 729 case "minLength": 730 aRestriction.SetMinLength(sValue); 731 break; 732 733 case "maxLength": 734 aRestriction.SetMaxLength(sValue); 735 break; 736 737 case "pattern": 738 aRestriction.SetPattern(sValue); 739 break; 740 741 default: 742 throw CreateErrorException("unsupported restriction type "+maReader.getLocalName()); 743 } 744 ExpectEndElement("restriction"); 745 break; 746 747 case XMLStreamReader.END_ELEMENT: 748 return aRestriction; 749 750 default: 751 throw CreateErrorException("unexpected XML event while parsing restrictions: %d", maReader.getEventType()); 752 } 753 } 754 } 755 756 757 758 ParseList(final Node aParent)759 private List ParseList (final Node aParent) 760 throws XMLStreamException 761 { 762 assert(HasOnlyAttributes("itemType")); 763 764 final List aList = new List( 765 aParent, 766 GetQualifiedName("itemType"), 767 GetLocation()); 768 ExpectEndElement("list"); 769 return aList; 770 } 771 772 773 774 ParseSequence(final Node aParent)775 private Sequence ParseSequence (final Node aParent) 776 throws XMLStreamException 777 { 778 assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name")); 779 780 final Sequence aSequence = new Sequence( 781 aParent, 782 GetOptionalQualifiedName("name"), 783 GetLocation()); 784 ParseContent(aSequence); 785 return aSequence; 786 } 787 788 789 790 ParseSimpleContent(final Node aParent)791 private SimpleContent ParseSimpleContent (final Node aParent) 792 throws XMLStreamException 793 { 794 assert(HasOnlyAttributes("minOccurs", "maxOccurs")); 795 796 final SimpleContent aSimpleContent = new SimpleContent( 797 aParent, 798 GetLocation()); 799 ParseContent(aSimpleContent); 800 return aSimpleContent; 801 } 802 803 804 805 ParseSimpleType(final Node aParent)806 private SimpleType ParseSimpleType (final Node aParent) 807 throws XMLStreamException 808 { 809 assert(HasOnlyAttributes("final", "name")); 810 811 final SimpleType aType = new SimpleType( 812 aParent, 813 GetQualifiedName("name"), 814 GetLocation()); 815 final String sFinalValue = GetOptionalAttributeValue("final", null); 816 if (sFinalValue != null) 817 aType.SetFinal(sFinalValue.split("\\s+")); 818 819 while (true) 820 { 821 switch (Next()) 822 { 823 case XMLStreamReader.START_ELEMENT: 824 switch(maReader.getLocalName()) 825 { 826 case "list": 827 aType.AddChild(ParseList(aType)); 828 break; 829 830 case "restriction": 831 aType.AddChild(ParseRestriction(aType)); 832 break; 833 834 case "union": 835 aType.AddChild(ParseUnion(aType)); 836 break; 837 838 default: 839 throw CreateErrorException("unsupported simple type part %s", maReader.getLocalName()); 840 } 841 break; 842 843 case XMLStreamReader.END_ELEMENT: 844 return aType; 845 846 default: 847 throw CreateErrorException("unexpected XML event in ParseSimpleType: %s", maReader.getEventType()); 848 } 849 } 850 } 851 852 853 854 ParseUnion(final Node aParent)855 private Union ParseUnion (final Node aParent) 856 throws XMLStreamException 857 { 858 assert(HasOnlyAttributes("memberTypes")); 859 860 final Union aUnion = new Union( 861 aParent, 862 GetLocation()); 863 final String[] aMemberTypes = GetAttributeValue("memberTypes").split("\\s+"); 864 for (int nIndex=0; nIndex<aMemberTypes.length; ++nIndex) 865 aUnion.AddChild( 866 new SimpleTypeReference( 867 aUnion, 868 CreateQualifiedName(aMemberTypes[nIndex]), 869 GetLocation())); 870 871 ParseContent(aUnion); 872 873 return aUnion; 874 } 875 876 ParseContent(final Node aParent)877 private void ParseContent (final Node aParent) 878 throws XMLStreamException 879 { 880 while(true) 881 { 882 switch(Next()) 883 { 884 case XMLStreamReader.START_ELEMENT: 885 ProcessStartElement(aParent); 886 break; 887 888 case XMLStreamReader.END_ELEMENT: 889 return; 890 891 default: 892 throw CreateErrorException( 893 "unexpected XML event %d while parsing content of %s", 894 maReader.getEventType(), 895 aParent.toString()); 896 } 897 } 898 } 899 900 901 902 ParseImport()903 private void ParseImport () 904 throws XMLStreamException 905 { 906 assert(HasOnlyAttributes("id", "namespace", "schemaLocation")); 907 908 final String sFilename = GetOptionalAttributeValue("schemaLocation", null); 909 if (sFilename == null) 910 { 911 final String sNamespaceName = GetAttributeValue("namespace"); 912 if (sNamespaceName.equals(XmlNamespace.URI)) 913 { 914 XmlNamespace.Apply(maSchemaBase); 915 maLocalNamespaceMap.put(XmlNamespace.Prefix, XmlNamespace.URI); 916 } 917 else 918 throw CreateErrorException("invalid import"); 919 } 920 else 921 { 922 maImportedSchemas.add(new File(maDirectory, sFilename)); 923 } 924 925 ExpectEndElement("import"); 926 } 927 928 929 930 ParseInclude()931 private void ParseInclude () 932 throws XMLStreamException 933 { 934 assert(HasOnlyAttributes("id", "schemaLocation")); 935 936 final String sFilename = GetOptionalAttributeValue("schemaLocation", null); 937 if (sFilename == null) 938 { 939 throw CreateErrorException("invalid include"); 940 } 941 else 942 { 943 maImportedSchemas.add(new File(maDirectory, sFilename)); 944 } 945 946 ExpectEndElement("include"); 947 } 948 949 950 951 ExpectEndElement(final String sCaller)952 private void ExpectEndElement (final String sCaller) 953 throws XMLStreamException 954 { 955 final int nNextEvent = Next(); 956 if (nNextEvent != XMLStreamReader.END_ELEMENT) 957 throw CreateErrorException("expected END_ELEMENT of %s but got %s", 958 sCaller, 959 nNextEvent); 960 } 961 962 963 964 965 /** Return the next relevant token from the XML stream. 966 * Ignores comments. 967 * @return 968 * Returns the event code of the next relevant XML event or END_DOCUMENT when the end of the file has been reached. 969 */ Next()970 private int Next () 971 throws XMLStreamException 972 { 973 while (maReader.hasNext()) 974 { 975 switch (maReader.next()) 976 { 977 case XMLStreamReader.COMMENT: 978 // Ignore comments. 979 break; 980 981 case XMLStreamReader.CHARACTERS: 982 // Ignore whitespace. 983 if (maReader.getText().matches("^\\s+$")) 984 break; 985 else 986 { 987 // Character events are not expected in schema files 988 // and therefore not supported. 989 // Alternatively, they could easily be ignored. 990 throw CreateErrorException("unexpected CHARACTERS event with text [%s]", maReader.getText()); 991 } 992 993 case XMLStreamReader.START_ELEMENT: 994 switch (maReader.getLocalName()) 995 { 996 case "annotation": 997 IgnoreAnnotation(); 998 break; 999 case "unique": 1000 // Not supported. 1001 IgnoreContent(); 1002 break; 1003 default: 1004 return XMLStreamReader.START_ELEMENT; 1005 } 1006 break; 1007 1008 default: 1009 return maReader.getEventType(); 1010 } 1011 } 1012 return XMLStreamReader.END_DOCUMENT; 1013 } 1014 1015 1016 1017 GetAttributeValue(final String sAttributeLocalName)1018 private String GetAttributeValue (final String sAttributeLocalName) 1019 { 1020 return maReader.getAttributeValue(null, sAttributeLocalName); 1021 } 1022 1023 1024 1025 GetOptionalAttributeValue( final String sAttributeLocalName, final String sDefaultValue)1026 private String GetOptionalAttributeValue ( 1027 final String sAttributeLocalName, 1028 final String sDefaultValue) 1029 { 1030 final String sValue = maReader.getAttributeValue(null, sAttributeLocalName); 1031 if (sValue == null) 1032 return sDefaultValue; 1033 else 1034 return sValue; 1035 } 1036 1037 1038 1039 1040 /** Read the specified attribute and return its value as QualifiedName object. 1041 */ GetQualifiedName(final String sAttributeLocalName)1042 private QualifiedName GetQualifiedName (final String sAttributeLocalName) 1043 { 1044 final String sName = maReader.getAttributeValue(null, sAttributeLocalName); 1045 if (sName == null) 1046 throw CreateErrorException( 1047 "did not find a qualified name as value of attribute '%s' at %s:%s", 1048 sAttributeLocalName, 1049 msBasename, 1050 maReader.getLocation()); 1051 else 1052 return CreateQualifiedName(sName); 1053 } 1054 1055 1056 1057 GetOptionalQualifiedName(final String sAttributeLocalName)1058 private QualifiedName GetOptionalQualifiedName (final String sAttributeLocalName) 1059 { 1060 final String sName = maReader.getAttributeValue(null, sAttributeLocalName); 1061 if (sName == null) 1062 return null; 1063 else 1064 return CreateQualifiedName(sName); 1065 } 1066 1067 1068 1069 1070 /** Create a QualifiedName object from the given string. 1071 * @param sName 1072 * May or may not contain a namespace prefix (separated from the name 1073 * by a colon). 1074 */ CreateQualifiedName(final String sName)1075 private QualifiedName CreateQualifiedName (final String sName) 1076 { 1077 final String[] aParts = sName.split(":"); 1078 final String sNamespaceURL; 1079 final String sLocalPart; 1080 switch (aParts.length) 1081 { 1082 case 1: 1083 // sName only consists of a local part. 1084 // Use the target namespace as namespace. 1085 sNamespaceURL = msTargetNamespace; 1086 sLocalPart = aParts[0]; 1087 break; 1088 1089 case 2: 1090 // sName is of the form prefix:local. 1091 sNamespaceURL = maLocalNamespaceMap.get(aParts[0]); 1092 sLocalPart = aParts[1]; 1093 break; 1094 1095 default: 1096 throw new RuntimeException( 1097 "the string '"+sName+"' can not be transformed into a qualified name"); 1098 } 1099 if (sNamespaceURL == null) 1100 throw CreateErrorException("can not determine namespace for '%s'", sName); 1101 // Transform the local namespace prefix into a global namespace prefix 1102 // (different schema files can use different prefixes for the same 1103 // namespace URI). 1104 final String sGlobalNamespacePrefix = maSchemaBase.Namespaces.GetNamespacePrefix(sNamespaceURL); 1105 return new QualifiedName( 1106 sNamespaceURL, 1107 sGlobalNamespacePrefix, 1108 sLocalPart); 1109 } 1110 1111 1112 1113 1114 /** Create an XMLStreamReader for the given file. 1115 * Returns null when there is an error. 1116 */ GetStreamReader(final File aSchemaFile)1117 private static XMLStreamReader GetStreamReader (final File aSchemaFile) 1118 { 1119 final XMLInputFactory aFactory = (XMLInputFactory)XMLInputFactory.newInstance(); 1120 aFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); 1121 aFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); 1122 aFactory.setProperty(XMLInputFactory.IS_COALESCING, false); 1123 1124 try 1125 { 1126 return aFactory.createXMLStreamReader( 1127 aSchemaFile.getName(), 1128 new FileInputStream(aSchemaFile)); 1129 } 1130 catch (Exception aException) 1131 { 1132 aException.printStackTrace(); 1133 return null; 1134 } 1135 } 1136 1137 1138 1139 CreateErrorException( final String sFormat, final Object ... aArgumentList)1140 private RuntimeException CreateErrorException ( 1141 final String sFormat, 1142 final Object ... aArgumentList) 1143 { 1144 return new RuntimeException(String.format(sFormat, aArgumentList) 1145 + String.format(" in %s at L%dC%dP%d", 1146 msBasename, 1147 maReader.getLocation().getLineNumber(), 1148 maReader.getLocation().getColumnNumber(), 1149 maReader.getLocation().getCharacterOffset())); 1150 } 1151 1152 1153 1154 1155 /** This predicate is only used for debugging to assert 1156 * that no unsupported attributes are present. 1157 * If there where then the parser has to be extended. 1158 */ HasOnlyAttributes(final String ... aAttributeNameList)1159 private boolean HasOnlyAttributes (final String ... aAttributeNameList) 1160 { 1161 for (int nIndex=0,nCount=maReader.getAttributeCount(); nIndex<nCount; ++nIndex) 1162 { 1163 final String sAttributeName = maReader.getAttributeLocalName(nIndex); 1164 boolean bFound = false; 1165 for (final String sName : aAttributeNameList) 1166 if (sAttributeName.equals(sName)) 1167 { 1168 bFound = true; 1169 break; 1170 } 1171 if ( ! bFound) 1172 { 1173 // Attribute name was not found in the given list. 1174 System.err.printf("attribute '%s' is not handled\n", sAttributeName); 1175 return false; 1176 } 1177 } 1178 return true; 1179 } 1180 1181 1182 1183 GetLocation()1184 private Location GetLocation () 1185 { 1186 final javax.xml.stream.Location aLocation = maReader.getLocation(); 1187 return new Location( 1188 msBasename, 1189 aLocation.getLineNumber(), 1190 aLocation.getColumnNumber(), 1191 aLocation.getCharacterOffset()); 1192 } 1193 1194 1195 1196 1197 private final Schema maSchema; 1198 private final SchemaBase maSchemaBase; 1199 private final XMLStreamReader maReader; 1200 private final String msBasename; 1201 private final File maDirectory; 1202 private String msTargetNamespace; 1203 /// Map of namespace prefix to URI, local to the currently read schema file. 1204 private final Map<String,String> maLocalNamespaceMap; 1205 /// The names of other schema files referenced by import elements. 1206 private final Vector<File> maImportedSchemas; 1207 /// Some values for tracking the number of read bytes and lines. 1208 private javax.xml.stream.Location maLastLocation; 1209 private FormDefault meAttributeFormDefault; 1210 private FormDefault meElementFormDefault; 1211 } 1212