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 16 package org.apache.xmlbeans.impl.validator; 17 18 import org.apache.xmlbeans.impl.common.IdentityConstraint; 19 import org.apache.xmlbeans.impl.common.QNameHelper; 20 import org.apache.xmlbeans.impl.common.ValidationContext; 21 import org.apache.xmlbeans.impl.common.ValidatorListener; 22 import org.apache.xmlbeans.impl.common.XmlWhitespace; 23 import org.apache.xmlbeans.impl.common.InvalidLexicalValueException; 24 import org.apache.xmlbeans.impl.schema.SchemaTypeVisitorImpl; 25 import org.apache.xmlbeans.impl.schema.SchemaTypeImpl; 26 import org.apache.xmlbeans.impl.values.JavaBase64HolderEx; 27 import org.apache.xmlbeans.impl.values.JavaBooleanHolder; 28 import org.apache.xmlbeans.impl.values.JavaBooleanHolderEx; 29 import org.apache.xmlbeans.impl.values.JavaDecimalHolderEx; 30 import org.apache.xmlbeans.impl.values.JavaDoubleHolderEx; 31 import org.apache.xmlbeans.impl.values.JavaFloatHolderEx; 32 import org.apache.xmlbeans.impl.values.JavaHexBinaryHolderEx; 33 import org.apache.xmlbeans.impl.values.JavaNotationHolderEx; 34 import org.apache.xmlbeans.impl.values.JavaQNameHolderEx; 35 import org.apache.xmlbeans.impl.values.JavaStringEnumerationHolderEx; 36 import org.apache.xmlbeans.impl.values.JavaUriHolderEx; 37 import org.apache.xmlbeans.impl.values.NamespaceContext; 38 import org.apache.xmlbeans.impl.values.XmlDateImpl; 39 import org.apache.xmlbeans.impl.values.XmlDurationImpl; 40 import org.apache.xmlbeans.impl.values.XmlListImpl; 41 import org.apache.xmlbeans.impl.values.XmlQNameImpl; 42 import org.apache.xmlbeans.impl.values.XmlValueOutOfRangeException; 43 import org.apache.xmlbeans.impl.util.XsTypeConverter; 44 import org.apache.xmlbeans.GDate; 45 import org.apache.xmlbeans.GDuration; 46 import org.apache.xmlbeans.QNameSet; 47 import org.apache.xmlbeans.XmlQName; 48 import org.apache.xmlbeans.SchemaAttributeModel; 49 import org.apache.xmlbeans.SchemaField; 50 import org.apache.xmlbeans.SchemaLocalAttribute; 51 import org.apache.xmlbeans.SchemaLocalElement; 52 import org.apache.xmlbeans.SchemaGlobalElement; 53 import org.apache.xmlbeans.SchemaParticle; 54 import org.apache.xmlbeans.SchemaType; 55 import org.apache.xmlbeans.SchemaTypeLoader; 56 import org.apache.xmlbeans.XmlError; 57 import org.apache.xmlbeans.XmlErrorCodes; 58 import org.apache.xmlbeans.XmlValidationError; 59 import org.apache.xmlbeans.XmlCursor; 60 import org.apache.xmlbeans.XmlObject; 61 import org.apache.xmlbeans.XmlOptions; 62 import org.apache.xmlbeans.SimpleValue; 63 import org.apache.xmlbeans.SchemaProperty; 64 import org.apache.xmlbeans.XmlString; 65 66 import java.math.BigDecimal; 67 import java.math.BigInteger; 68 import java.util.Collection; 69 import java.util.HashSet; 70 import java.util.LinkedList; 71 import java.util.List; 72 import java.util.ArrayList; 73 import java.util.Iterator; 74 import javax.xml.namespace.QName; 75 76 public final class Validator 77 implements ValidatorListener 78 { 79 public Validator ( 80 SchemaType type, SchemaField field, SchemaTypeLoader globalLoader, 81 XmlOptions options, Collection defaultErrorListener ) 82 { 83 options = XmlOptions.maskNull(options); 84 _errorListener = (Collection) options.get(XmlOptions.ERROR_LISTENER); 85 _treatLaxAsSkip = options.hasOption(XmlOptions.VALIDATE_TREAT_LAX_AS_SKIP); 86 _strict = options.hasOption(XmlOptions.VALIDATE_STRICT); 87 88 if (_errorListener == null) 89 _errorListener = defaultErrorListener; 90 91 _constraintEngine = new IdentityConstraint(_errorListener, type.isDocumentType()); 92 93 _globalTypes = globalLoader; 94 _rootType = type; 95 _rootField = field; 96 97 _vc = new ValidatorVC(); 98 } 99 100 private class ValidatorVC implements ValidationContext 101 { 102 // KHK: remove this 103 public void invalid ( String message ) 104 { 105 // TODO (dutta) Addtional Attributes for validation error have limited information 106 //at this time but will be a part of the second round of refactoring 107 108 Validator.this.emitError(_event, message, null, null, null, 109 XmlValidationError.ATTRIBUTE_TYPE_INVALID, null); 110 } 111 112 public void invalid ( String code, Object[] args ) 113 { 114 // TODO (dutta) Addtional Attributes for validation error have limited information 115 //at this time but will be a part of the second round of refactoring 116 117 Validator.this.emitError(_event, code, args, null, null, null, 118 XmlValidationError.ATTRIBUTE_TYPE_INVALID, null); 119 } 120 121 Event _event; 122 } 123 124 public boolean isValid ( ) 125 { 126 return !_invalid && _constraintEngine.isValid(); 127 } 128 129 // KHK: remove this 130 private void emitError ( Event event, String message, QName offendingQName, 131 SchemaType expectedSchemaType, List expectedQNames, 132 int errorType, SchemaType badSchemaType) 133 { 134 emitError(event, message, null, null, XmlError.SEVERITY_ERROR, null, offendingQName , expectedSchemaType, 135 expectedQNames , errorType, badSchemaType); 136 } 137 138 private void emitError ( Event event, String code, Object[] args, QName offendingQName, 139 SchemaType expectedSchemaType, List expectedQNames, 140 int errorType, SchemaType badSchemaType) 141 { 142 emitError(event, null, code, args, XmlError.SEVERITY_ERROR, null, offendingQName , expectedSchemaType, 143 expectedQNames , errorType, badSchemaType); 144 } 145 146 // KHK: remove 'message' parameter 147 private void emitError ( Event event, String message, String code, Object[] args, int severity, 148 QName fieldName, QName offendingQName, 149 SchemaType expectedSchemaType, List expectedQNames, 150 int errorType, SchemaType badSchemaType ) 151 { 152 _errorState++; 153 154 if (_suspendErrors == 0) 155 { 156 if (severity == XmlError.SEVERITY_ERROR) 157 _invalid = true; 158 159 if (_errorListener != null) 160 { 161 assert event != null; 162 XmlError error; 163 XmlCursor curs = event.getLocationAsCursor(); 164 if (curs != null) 165 { 166 // non-streaming validation uses XmlCursor 167 error = XmlValidationError.forCursorWithDetails( message, code, args, severity, 168 curs, fieldName, offendingQName, expectedSchemaType, expectedQNames, 169 errorType, badSchemaType); 170 } 171 else 172 { 173 // streaming validation uses Location 174 error = XmlValidationError.forLocationWithDetails( message, code, args, severity, 175 event.getLocation(), fieldName, offendingQName, expectedSchemaType, expectedQNames, 176 errorType, badSchemaType); 177 } 178 179 _errorListener.add(error); 180 } 181 } 182 } 183 184 private void emitFieldError ( Event event, String code, Object[] args, QName offendingQName, 185 SchemaType expectedSchemaType, List expectedQNames, 186 int errorType, SchemaType badSchemaType ) 187 { 188 emitFieldError(event, null, code, args, XmlError.SEVERITY_ERROR, offendingQName, 189 expectedSchemaType, expectedQNames, errorType, badSchemaType); 190 } 191 192 private void emitFieldError ( Event event, String message, String code, Object[] args, int severity, QName offendingQName, 193 SchemaType expectedSchemaType, List expectedQNames, 194 int errorType, SchemaType badSchemaType ) 195 { 196 QName fieldName = null; 197 if (_stateStack != null && _stateStack._field != null) 198 { 199 fieldName = _stateStack._field.getName(); 200 } 201 202 Validator.this.emitError(event, message, code, args, severity, fieldName, offendingQName, expectedSchemaType, 203 expectedQNames, errorType, badSchemaType); 204 } 205 206 // // For XmlEventListener.error 207 // 208 // public void error ( XmlError error ) 209 // { 210 // _errorState++; 211 // 212 // if (_suspendErrors == 0) 213 // { 214 // _invalid = true; 215 // 216 // if (_errorListener != null) 217 // _errorListener.add( error ); 218 // } 219 // } 220 221 public void nextEvent ( int kind, Event event ) 222 { 223 resetValues(); 224 225 if (_eatContent > 0) 226 { 227 switch ( kind ) 228 { 229 case END : _eatContent--; break; 230 case BEGIN : _eatContent++; break; 231 } 232 } 233 else 234 { 235 assert 236 kind == BEGIN || kind == ATTR || 237 kind == END || kind == TEXT || kind == ENDATTRS; 238 239 switch ( kind ) 240 { 241 case BEGIN : beginEvent( event ); break; 242 case ATTR : attrEvent( event ); break; 243 case ENDATTRS : endAttrsEvent( event ); break; 244 case TEXT : textEvent( event ); break; 245 case END : endEvent( event ); break; 246 } 247 } 248 } 249 250 private void beginEvent ( Event event ) 251 { 252 _localElement = null; 253 _wildcardElement = null; 254 State state = topState(); 255 256 SchemaType elementType = null; 257 SchemaField elementField = null; 258 259 if (state == null) 260 { 261 elementType = _rootType; 262 elementField = _rootField; 263 } 264 else 265 { 266 267 QName name = event.getName(); 268 269 assert name != null; 270 271 state._isEmpty = false; 272 273 if (state._isNil) 274 { 275 emitFieldError(event, XmlErrorCodes.ELEM_LOCALLY_VALID$NIL_WITH_CONTENT, 276 null, state._field.getName(), state._type, null, 277 XmlValidationError.NIL_ELEMENT, state._type); 278 279 _eatContent = 1; 280 return; 281 } 282 283 if (!state._isNil && state._field != null && state._field.isFixed()) 284 { 285 emitFieldError(event, XmlErrorCodes.ELEM_LOCALLY_VALID$FIXED_WITH_CONTENT, 286 new Object[] { QNameHelper.pretty(state._field.getName()) }, 287 state._field.getName(), state._type, null, 288 XmlValidationError.ELEMENT_NOT_ALLOWED, state._type); 289 } 290 291 if (!state.visit( name )) 292 { 293 findDetailedErrorBegin(event ,state , name); 294 295 _eatContent = 1; 296 297 return; 298 } 299 300 SchemaParticle currentParticle = state.currentParticle(); 301 _wildcardElement = currentParticle; 302 303 if (currentParticle.getParticleType() == SchemaParticle.WILDCARD) 304 { 305 //_wildcardElement = currentParticle; 306 QNameSet elemWildcardSet = currentParticle.getWildcardSet(); 307 308 if (!elemWildcardSet.contains( name )) 309 { 310 // Additional processing may be needed to generate more 311 // descriptive messages 312 // KHK: cvc-complex-type.2.4? cvc-particle.1.3? cvc-wildcard-namespace ? 313 emitFieldError(event, XmlErrorCodes.PARTICLE_VALID$NOT_WILDCARD_VALID, 314 new Object[] { QNameHelper.pretty(name) }, 315 name, null, null, 316 XmlValidationError.ELEMENT_NOT_ALLOWED, state._type); 317 318 _eatContent = 1; 319 320 return; 321 } 322 323 int wildcardProcess = currentParticle.getWildcardProcess(); 324 325 if (wildcardProcess == SchemaParticle.SKIP || 326 wildcardProcess == SchemaParticle.LAX && _treatLaxAsSkip) 327 { 328 _eatContent = 1; 329 return; 330 } 331 332 _localElement = _globalTypes.findElement( name ); 333 elementField = _localElement; 334 335 if (elementField == null) 336 { 337 if (wildcardProcess == SchemaParticle.STRICT) 338 { 339 // KHK: cvc-complex-type.2.4c? cvc-assess-elt.1.1.1.3.2? 340 emitFieldError( event, XmlErrorCodes.ASSESS_ELEM_SCHEMA_VALID$NOT_RESOLVED, 341 new Object[] { QNameHelper.pretty(name) }, 342 name, state._type, null, 343 XmlValidationError.ELEMENT_NOT_ALLOWED, state._type); 344 } 345 346 _eatContent = 1; 347 348 return; 349 } 350 } 351 else 352 { 353 assert currentParticle.getParticleType() == SchemaParticle.ELEMENT; 354 355 // If the current element particle name does not match the name 356 // of the event, then the current element is a substitute for 357 // the current particle. Replace the field with the global 358 // element for the replacement 359 360 if (! currentParticle.getName().equals(name)) 361 { 362 if (((SchemaLocalElement)currentParticle).blockSubstitution()) 363 { 364 emitFieldError( event, XmlErrorCodes.PARTICLE_VALID$BLOCK_SUBSTITUTION, 365 new Object[] { QNameHelper.pretty(name) }, 366 name, state._type, null, 367 XmlValidationError.ELEMENT_NOT_ALLOWED, state._type); 368 369 _eatContent = 1; 370 return; 371 } 372 373 SchemaGlobalElement newField = _globalTypes.findElement(name); 374 375 assert newField != null; 376 377 if (newField != null) 378 { 379 elementField = newField; 380 _localElement = newField; 381 } 382 } 383 else 384 { 385 elementField = (SchemaField) currentParticle; 386 } 387 } 388 389 elementType = elementField.getType(); 390 } 391 392 assert elementType != null; 393 394 // 395 // the no-type is always invalid (even if there is an xsi:type) 396 // 397 398 if (elementType.isNoType()) 399 { 400 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$NO_TYPE, 401 null, event.getName(), null, null, 402 XmlValidationError.ELEMENT_TYPE_INVALID, null); 403 404 _eatContent = 1; 405 } 406 407 // 408 // See if the element has an xsi:type on it 409 // 410 411 SchemaType xsiType = null; 412 413 String value = event.getXsiType(); 414 415 if (value != null) 416 { 417 // Turn off the listener so a public error message 418 // does not get generated, but I can see if there was 419 // an error through the error state 420 421 int originalErrorState = _errorState; 422 423 _suspendErrors++; 424 425 try 426 { 427 _vc._event = null; 428 429 xsiType = _globalTypes.findType( XmlQNameImpl.validateLexical( value, _vc, event ) ); 430 } 431 catch ( Throwable t ) 432 { 433 _errorState++; 434 } 435 finally 436 { 437 _suspendErrors--; 438 } 439 440 if (originalErrorState != _errorState) 441 { 442 // not sure how to extract this one 443 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$XSI_TYPE_INVALID_QNAME, 444 new Object[] { value }, event.getName(), xsiType, null, 445 XmlValidationError.ELEMENT_TYPE_INVALID, state._type); 446 447 _eatContent = 1; 448 449 return; 450 } 451 else if (xsiType == null) 452 { 453 // NOT SURE errorAttributes._expectedSchemaType = xsiType; 454 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$XSI_TYPE_NOT_FOUND, 455 new Object[] { value }, event.getName(), null, null, 456 XmlValidationError.ELEMENT_TYPE_INVALID, null); 457 458 _eatContent = 1; 459 460 return; 461 } 462 } 463 464 if (xsiType != null && !xsiType.equals(elementType)) 465 { 466 if (!elementType.isAssignableFrom(xsiType)) 467 { 468 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$XSI_TYPE_NOT_DERIVED, 469 new Object[] { xsiType, elementType }, event.getName(), elementType, null, 470 XmlValidationError.ELEMENT_TYPE_INVALID, (state == null ? null : state._type)); 471 472 _eatContent = 1; 473 474 return; 475 } 476 477 if (elementType.blockExtension()) 478 { 479 for ( SchemaType t = xsiType ; ! t.equals( elementType ) ; 480 t = t.getBaseType() ) 481 { 482 if (t.getDerivationType() == SchemaType.DT_EXTENSION) 483 { 484 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$XSI_TYPE_BLOCK_EXTENSION, 485 new Object[] { xsiType, elementType }, event.getName(), elementType, null, 486 XmlValidationError.ELEMENT_TYPE_INVALID, (state == null ? null : state._type)); 487 488 _eatContent = 1; 489 490 return; 491 } 492 } 493 } 494 495 if (elementType.blockRestriction()) 496 { 497 for ( SchemaType t = xsiType ; ! t.equals( elementType ) ; 498 t = t.getBaseType() ) 499 { 500 if (t.getDerivationType() == SchemaType.DT_RESTRICTION) 501 { 502 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$XSI_TYPE_BLOCK_RESTRICTION, 503 new Object[] { xsiType, elementType }, event.getName(), elementType, null, 504 XmlValidationError.ELEMENT_TYPE_INVALID, (state == null ? null : state._type)); 505 506 _eatContent = 1; 507 508 return; 509 } 510 } 511 } 512 513 if (elementField instanceof SchemaLocalElement) 514 { 515 SchemaLocalElement sle = (SchemaLocalElement)elementField; 516 _localElement = sle; 517 518 if (sle.blockExtension() || sle.blockRestriction()) 519 { 520 for ( SchemaType t = xsiType ; ! t.equals( elementType ) ; 521 t = t.getBaseType() ) 522 { 523 if ((t.getDerivationType() == SchemaType.DT_RESTRICTION && sle.blockRestriction()) || 524 (t.getDerivationType() == SchemaType.DT_EXTENSION && sle.blockExtension())) 525 { 526 //need to find a way to get the right type 527 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$XSI_TYPE_PROHIBITED_SUBST, 528 new Object[] { xsiType, QNameHelper.pretty(sle.getName()) }, 529 sle.getName(), null, null, XmlValidationError.ELEMENT_TYPE_INVALID, null); 530 531 _eatContent = 1; 532 533 return; 534 } 535 } 536 } 537 } 538 539 elementType = xsiType; 540 } 541 542 if (elementField instanceof SchemaLocalElement) 543 { 544 SchemaLocalElement sle = (SchemaLocalElement)elementField; 545 _localElement = sle; 546 547 if (sle.isAbstract()) 548 { 549 //todo (dutta) need to find a way to get the right type 550 emitError(event, XmlErrorCodes.ELEM_LOCALLY_VALID$ABSTRACT, 551 new Object[] { QNameHelper.pretty(sle.getName()) }, 552 sle.getName(), null, null, XmlValidationError.ELEMENT_TYPE_INVALID, null); 553 554 _eatContent = 1; 555 return; 556 } 557 } 558 559 if (elementType != null && elementType.isAbstract()) 560 { 561 emitError(event, XmlErrorCodes.ELEM_LOCALLY_VALID$ABSTRACT, 562 new Object[] { elementType }, 563 event.getName(), elementType, null, XmlValidationError.ELEMENT_TYPE_INVALID, (state == null ? null : state._type)); 564 565 _eatContent = 1; 566 567 return; 568 } 569 570 boolean isNil = false; 571 boolean hasNil = false; 572 573 String nilValue = event.getXsiNil(); 574 575 if (nilValue != null) 576 { 577 _vc._event = event; 578 isNil = JavaBooleanHolder.validateLexical(nilValue, _vc); 579 hasNil = true; 580 } 581 582 // note in schema spec 3.3.4, you're not even allowed to say xsi:nil="false" if you're not nillable! 583 if (hasNil && (elementField == null || !elementField.isNillable())) 584 { 585 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$NOT_NILLABLE, null, 586 elementField == null ? null : elementField.getName(), elementType, null, 587 XmlValidationError.ELEMENT_TYPE_INVALID, (state == null ? null : state._type)); 588 589 _eatContent = 1; 590 return; 591 } 592 593 if (isNil && elementField != null && elementField.isFixed()) 594 { 595 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$NIL_WITH_FIXED, null, 596 elementField == null ? null : elementField.getName(), elementType, null, 597 XmlValidationError.ELEMENT_TYPE_INVALID, (state == null ? null : state._type)); 598 } 599 600 newState( elementType, elementField, isNil ); 601 602 // Dispatch this element event to any identity constraints 603 // As well as adding any new identity constraints that exist 604 605 _constraintEngine.element( 606 event, 607 elementType, 608 elementField instanceof SchemaLocalElement 609 ? ((SchemaLocalElement) elementField).getIdentityConstraints() 610 : null ); 611 } 612 613 private void attrEvent ( Event event ) 614 { 615 QName attrName = event.getName(); 616 617 State state = topState(); 618 619 if (state._attrs == null) 620 state._attrs = new HashSet(); 621 622 if (state._attrs.contains( attrName )) 623 { 624 emitFieldError( event, XmlErrorCodes.XML_DUPLICATE_ATTRIBUTE, 625 new Object[] { QNameHelper.pretty( attrName ) }, 626 attrName, null, null, XmlValidationError.INCORRECT_ATTRIBUTE, state._type ); 627 628 return; 629 } 630 631 state._attrs.add( attrName ); 632 633 if (!state._canHaveAttrs) 634 { 635 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$NO_WILDCARD, 636 new Object[] {QNameHelper.pretty( attrName )}, attrName, null, null, 637 XmlValidationError.INCORRECT_ATTRIBUTE, state._type); 638 return; 639 } 640 641 SchemaLocalAttribute attrSchema = 642 state._attrModel == null 643 ? null 644 : state._attrModel.getAttribute( attrName ); 645 646 if (attrSchema != null) 647 { 648 _localAttribute = attrSchema; 649 650 if (attrSchema.getUse() == SchemaLocalAttribute.PROHIBITED) 651 { 652 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$PROHIBITED_ATTRIBUTE, 653 new Object[] { QNameHelper.pretty( attrName ) } , 654 attrName, null, null, XmlValidationError.INCORRECT_ATTRIBUTE, state._type ); 655 656 return; 657 } 658 659 String value = 660 validateSimpleType( 661 attrSchema.getType(), attrSchema, event, false, false ); 662 663 _constraintEngine.attr( event, attrName, attrSchema.getType(), value ); 664 665 return; 666 } 667 668 int wildcardProcess = state._attrModel.getWildcardProcess(); 669 670 _wildcardAttribute = state._attrModel; 671 672 if (wildcardProcess == SchemaAttributeModel.NONE) 673 { 674 // todo (dutta) need additional logic to determine the expectedSchemaType 675 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$NO_WILDCARD, 676 new Object[] { QNameHelper.pretty( attrName ) }, 677 attrName, null, null, XmlValidationError.INCORRECT_ATTRIBUTE, state._type); 678 679 return; 680 } 681 682 QNameSet attrWildcardSet = state._attrModel.getWildcardSet(); 683 684 if (!attrWildcardSet.contains( attrName )) 685 { 686 // todo (dutta) need additional logic to determine the expectedSchemaType 687 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$NOT_WILDCARD_VALID, 688 new Object[] { QNameHelper.pretty( attrName ) }, 689 attrName, null, null, XmlValidationError.INCORRECT_ATTRIBUTE, state._type); 690 691 return; 692 } 693 694 if (wildcardProcess == SchemaAttributeModel.SKIP || 695 wildcardProcess == SchemaAttributeModel.LAX && _treatLaxAsSkip) 696 return; 697 698 attrSchema = _globalTypes.findAttribute( attrName ); 699 _localAttribute = attrSchema; 700 701 if (attrSchema == null) 702 { 703 if (wildcardProcess == SchemaAttributeModel.LAX) 704 return; 705 706 assert wildcardProcess == SchemaAttributeModel.STRICT; 707 708 // KHK: cvc-assess-attr.1.2 ? 709 // todo (dutta) need additional logic to determine the expectedSchemaType 710 emitFieldError( event, XmlErrorCodes.ASSESS_ATTR_SCHEMA_VALID$NOT_RESOLVED, 711 new Object[] { QNameHelper.pretty( attrName ) }, 712 attrName, null, null, XmlValidationError.INCORRECT_ATTRIBUTE, state._type); 713 714 return; 715 } 716 717 String value = 718 validateSimpleType( 719 attrSchema.getType(), attrSchema, event, false, false ); 720 721 _constraintEngine.attr( event, attrName, attrSchema.getType(), value ); 722 } 723 724 private void endAttrsEvent ( Event event ) 725 { 726 State state = topState(); 727 728 if (state._attrModel != null) 729 { 730 SchemaLocalAttribute[] attrs = state._attrModel.getAttributes(); 731 732 for ( int i = 0 ; i < attrs.length ; i++ ) 733 { 734 SchemaLocalAttribute sla = attrs[ i ]; 735 736 if (state._attrs == null || 737 !state._attrs.contains( sla.getName() )) 738 { 739 if (sla.getUse() == SchemaLocalAttribute.REQUIRED) 740 { 741 // KHK: cvc-complex-type.4 742 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$MISSING_REQUIRED_ATTRIBUTE, 743 new Object[] { QNameHelper.pretty(sla.getName()) }, 744 sla.getName(), null, null, XmlValidationError.INCORRECT_ATTRIBUTE, state._type); 745 } 746 else if (sla.isDefault() || sla.isFixed()) 747 { 748 _constraintEngine.attr(event, sla.getName(), sla.getType(), sla.getDefaultText()); 749 750 // We don't need to validate attribute defaults because this is done at compiletime. 751 /* 752 String value = sla.getDefaultText(); 753 SchemaType type = sla.getType(); 754 755 if (XmlQName.type.isAssignableFrom(type)) 756 { 757 emitFieldError( 758 event, 759 "Default QName values are unsupported for attribute: " + 760 QNameHelper.pretty(sla.getName()), 761 XmlError.SEVERITY_INFO); 762 } 763 764 else 765 { 766 validateSimpleType( 767 type, sla.getDefaultText(), event ); 768 769 _constraintEngine.attr( event, type, value ); 770 } 771 */ 772 } 773 } 774 } 775 } 776 } 777 778 private void endEvent ( Event event ) 779 { 780 _localElement = null; 781 _wildcardElement = null; 782 State state = topState(); 783 784 if (!state._isNil) 785 { 786 if (!state.end()) 787 { 788 findDetailedErrorEnd(event,state); 789 } 790 791 // This end event has no text, use this fact to pass no text to 792 // handleText 793 794 if (state._isEmpty) 795 handleText( event, true, state._field ); 796 } 797 798 popState( event ); 799 800 _constraintEngine.endElement( event ); 801 } 802 803 private void textEvent ( Event event ) 804 { 805 State state = topState(); 806 807 if (state._isNil) 808 { 809 emitFieldError( event, XmlErrorCodes.ELEM_LOCALLY_VALID$NIL_WITH_CONTENT, null, 810 state._field.getName(), state._type, null, 811 XmlValidationError.NIL_ELEMENT, state._type ); 812 } 813 else 814 handleText( event, false, state._field ); 815 816 state._isEmpty = false; 817 } 818 819 820 private void handleText ( 821 Event event, boolean emptyContent, SchemaField field ) 822 { 823 State state = topState(); 824 825 if (!state._sawText) 826 { 827 if (state._hasSimpleContent) 828 { 829 String value = 830 validateSimpleType( 831 state._type, field, event, emptyContent, true ); 832 833 _constraintEngine.text( event, state._type, value, false ); 834 } 835 else if (state._canHaveMixedContent) 836 { 837 // handles cvc-elt.5.2.2.2.1, checking mixed content against fixed. 838 // if we see <mixedType>a</b>c</mixedType>, we validate against 839 // the first 'a' text and we check the content of mixedType to 840 // be empty in beginElem(). we don't care about checking against 841 // the 'c' text since there will already be an error for <b/> 842 String value = 843 validateSimpleType( 844 XmlString.type, field, event, emptyContent, true ); 845 846 _constraintEngine.text( event, XmlString.type, value, false ); 847 } 848 else if (emptyContent) 849 { 850 _constraintEngine.text( event, state._type, null, true ); 851 } 852 else 853 _constraintEngine.text( event, state._type, "", false); 854 } 855 856 if (!emptyContent && !state._canHaveMixedContent && 857 !event.textIsWhitespace() && !state._hasSimpleContent) 858 { 859 if (field instanceof SchemaLocalElement) 860 { 861 SchemaLocalElement e = (SchemaLocalElement)field; 862 863 assert state._type.getContentType() == SchemaType.EMPTY_CONTENT || 864 state._type.getContentType() == SchemaType.ELEMENT_CONTENT; 865 866 // KHK: cvc-complex-type.2.1 or .2.3 867 String errorCode = (state._type.getContentType() == SchemaType.EMPTY_CONTENT ? 868 XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$EMPTY_WITH_CONTENT : 869 XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$ELEMENT_ONLY_WITH_TEXT); 870 871 emitError(event, errorCode,new Object[] {QNameHelper.pretty(e.getName())}, 872 e.getName(), field.getType(), null, 873 XmlValidationError.ELEMENT_TYPE_INVALID, null); 874 } 875 else 876 { 877 // KHK: cvc-complex-type.2.1 or .2.3 878 // todo (dutta) offendingQName = not sure how to get this(event.getName()??); 879 emitError(event, "Can't have mixed content", event.getName(), 880 state._type, null, XmlValidationError.ELEMENT_TYPE_INVALID, null); 881 } 882 } 883 884 if (!emptyContent) 885 state._sawText = true; 886 } 887 888 private void findDetailedErrorBegin(Event event, State state, QName qName) 889 { 890 ArrayList expectedNames = new ArrayList(); 891 ArrayList optionalNames = new ArrayList(); 892 893 SchemaProperty[] eltProperties = state._type.getElementProperties(); 894 for (int ii = 0; ii < eltProperties.length; ii++) 895 { 896 //Get the element from the schema 897 SchemaProperty sProp = eltProperties[ii]; 898 899 // test if the element is valid 900 if (state.test(sProp.getName())) 901 { 902 if (0 == BigInteger.ZERO.compareTo(sProp.getMinOccurs())) 903 optionalNames.add(sProp.getName()); 904 else 905 expectedNames.add(sProp.getName()); 906 } 907 } 908 909 List names = (expectedNames.size() > 0 ? expectedNames : optionalNames); 910 911 if (names.size() > 0) 912 { 913 StringBuffer buf = new StringBuffer(); 914 for (Iterator iter = names.iterator(); iter.hasNext();) 915 { 916 QName qname = (QName) iter.next(); 917 buf.append(QNameHelper.pretty(qname)); 918 if (iter.hasNext()) 919 buf.append(" "); 920 } 921 922 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$EXPECTED_DIFFERENT_ELEMENT, 923 new Object[] { new Integer(names.size()), buf.toString(), QNameHelper.pretty(qName) }, 924 qName, null, names, XmlValidationError.INCORRECT_ELEMENT, state._type); 925 } 926 else 927 { 928 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$ELEMENT_NOT_ALLOWED, 929 new Object[] { QNameHelper.pretty(qName) }, 930 qName, null, null, XmlValidationError.INCORRECT_ELEMENT, state._type); 931 } 932 } 933 934 private void findDetailedErrorEnd(Event event, State state) 935 { 936 SchemaProperty[] eltProperties = state._type.getElementProperties(); 937 938 ArrayList expectedNames = new ArrayList(); 939 ArrayList optionalNames = new ArrayList(); 940 941 for (int ii = 0; ii < eltProperties.length; ii++) 942 { 943 //Get the element from the schema 944 SchemaProperty sProp = eltProperties[ii]; 945 946 // test if the element is valid 947 if (state.test(sProp.getName())) 948 { 949 if (0 == BigInteger.ZERO.compareTo(sProp.getMinOccurs())) 950 optionalNames.add(sProp.getName()); 951 else 952 expectedNames.add(sProp.getName()); 953 } 954 } 955 956 List names = (expectedNames.size() > 0 ? expectedNames : optionalNames); 957 958 if (names.size() > 0) 959 { 960 StringBuffer buf = new StringBuffer(); 961 for (Iterator iter = names.iterator(); iter.hasNext();) 962 { 963 QName qname = (QName) iter.next(); 964 buf.append(QNameHelper.pretty(qname)); 965 if (iter.hasNext()) 966 buf.append(" "); 967 } 968 969 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$MISSING_ELEMENT, 970 new Object[] { new Integer(names.size()), buf.toString() }, 971 null, null, names, XmlValidationError.INCORRECT_ELEMENT, state._type); 972 } 973 else 974 { 975 emitFieldError( event, XmlErrorCodes.ELEM_COMPLEX_TYPE_LOCALLY_VALID$EXPECTED_ELEMENT, 976 null, null, null, null, XmlValidationError.ELEMENT_NOT_ALLOWED, state._type); 977 } 978 } 979 980 981 private final class State 982 { 983 boolean visit ( QName name ) 984 { 985 return _canHaveElements && _visitor.visit( name ); 986 } 987 988 boolean test( QName name ) 989 { 990 return _canHaveElements && _visitor.testValid( name ); 991 } 992 993 boolean end ( ) 994 { 995 return !_canHaveElements || _visitor.visit( null ); 996 } 997 998 SchemaParticle currentParticle ( ) 999 { 1000 assert _visitor != null; 1001 return _visitor.currentParticle(); 1002 } 1003 1004 SchemaType _type; 1005 SchemaField _field; 1006 1007 boolean _canHaveAttrs; 1008 boolean _canHaveMixedContent; 1009 boolean _hasSimpleContent; 1010 1011 boolean _sawText; 1012 boolean _isEmpty; 1013 boolean _isNil; 1014 1015 SchemaTypeVisitorImpl _visitor; 1016 boolean _canHaveElements; 1017 1018 SchemaAttributeModel _attrModel; 1019 1020 HashSet _attrs; 1021 1022 State _next; 1023 } 1024 1025 private boolean derivedFromInteger( SchemaType type ) 1026 { 1027 int btc = type.getBuiltinTypeCode(); 1028 1029 while (btc == SchemaType.BTC_NOT_BUILTIN) 1030 { 1031 type = type.getBaseType(); 1032 btc = type.getBuiltinTypeCode(); 1033 } 1034 // This depends on the ordering of the constant values, which is not ideal but is easier 1035 return btc >= SchemaType.BTC_INTEGER && btc <= SchemaType.BTC_UNSIGNED_BYTE; 1036 } 1037 1038 private void newState ( SchemaType type, SchemaField field, boolean isNil ) 1039 { 1040 State state = new State(); 1041 1042 state._type = type; 1043 state._field = field; 1044 state._isEmpty = true; 1045 state._isNil = isNil; 1046 1047 if (type.isSimpleType()) 1048 { 1049 state._hasSimpleContent = true; 1050 } 1051 else 1052 { 1053 state._canHaveAttrs = true; 1054 state._attrModel = type.getAttributeModel(); 1055 1056 switch ( type.getContentType() ) 1057 { 1058 case SchemaType.EMPTY_CONTENT : 1059 break; 1060 1061 case SchemaType.SIMPLE_CONTENT : 1062 state._hasSimpleContent = true; 1063 break; 1064 1065 case SchemaType.MIXED_CONTENT : 1066 state._canHaveMixedContent = true; 1067 // Fall through 1068 1069 case SchemaType.ELEMENT_CONTENT : 1070 1071 SchemaParticle particle = type.getContentModel(); 1072 1073 state._canHaveElements = particle != null; 1074 1075 if (state._canHaveElements) 1076 state._visitor = initVisitor( particle ); 1077 1078 break; 1079 1080 default : 1081 throw new RuntimeException( "Unexpected content type" ); 1082 } 1083 } 1084 1085 pushState( state ); 1086 } 1087 1088 private void popState ( Event e ) 1089 { 1090 if (_stateStack._visitor != null) 1091 { 1092 poolVisitor( _stateStack._visitor ); 1093 _stateStack._visitor = null; 1094 } 1095 1096 _stateStack = _stateStack._next; 1097 } 1098 1099 private void pushState ( State state ) 1100 { 1101 state._next = _stateStack; 1102 _stateStack = state; 1103 } 1104 1105 private LinkedList _visitorPool = new LinkedList(); 1106 1107 private void poolVisitor( SchemaTypeVisitorImpl visitor ) 1108 { 1109 _visitorPool.add( visitor ); 1110 } 1111 1112 private SchemaTypeVisitorImpl initVisitor( SchemaParticle particle ) 1113 { 1114 if (_visitorPool.isEmpty()) 1115 return new SchemaTypeVisitorImpl( particle ); 1116 1117 SchemaTypeVisitorImpl result = 1118 (SchemaTypeVisitorImpl) _visitorPool.removeLast(); 1119 1120 result.init( particle ); 1121 1122 return result; 1123 } 1124 1125 private State topState ( ) 1126 { 1127 return _stateStack; 1128 } 1129 1130 // 1131 // Simple Type Validation 1132 // 1133 // emptyContent means that you can't use the event to get text: there is 1134 // no text, but you can use the event to do prefix resolution (in the case 1135 // where the default is a qname) 1136 // 1137 1138 private String validateSimpleType ( 1139 SchemaType type, SchemaField field, Event event, 1140 boolean emptyContent, boolean canApplyDefault ) 1141 { 1142 if (!type.isSimpleType() && 1143 type.getContentType() != SchemaType.SIMPLE_CONTENT) 1144 { 1145 assert false; 1146 // throw new RuntimeException( "Not a simple type" ); 1147 return null; // should never happen 1148 } 1149 1150 // 1151 // the no-type is always invalid 1152 // 1153 1154 if (type.isNoType()) 1155 { 1156 emitError(event, (field.isAttribute() ? XmlErrorCodes.ATTR_LOCALLY_VALID$NO_TYPE : XmlErrorCodes.ELEM_LOCALLY_VALID$NO_TYPE), 1157 null, field.getName(), type, null, XmlValidationError.ELEMENT_TYPE_INVALID, null); 1158 1159 return null; 1160 } 1161 1162 // Get the value as a string (as normalized by the white space rule 1163 // TODO - will want to optimize this later 1164 1165 String value = ""; 1166 1167 if (!emptyContent) 1168 { 1169 int wsr = type.getWhiteSpaceRule(); 1170 value = wsr == SchemaType.WS_PRESERVE ? event.getText() : event.getText( wsr ); 1171 } 1172 1173 // See if I can apply a default/fixed value 1174 1175 if (value.length() == 0 && canApplyDefault && field != null && 1176 (field.isDefault() || field.isFixed())) 1177 { 1178 if (XmlQName.type.isAssignableFrom(type)) 1179 { 1180 // TODO: will be fixed in XmlSchema 1.1 1181 emitError( event, "Default QName values are unsupported for " + 1182 QNameHelper.readable(type) + " - ignoring.", null, null, 1183 XmlError.SEVERITY_INFO, field.getName(), null, type, null, 1184 XmlValidationError.ELEMENT_TYPE_INVALID, null); 1185 1186 return null; 1187 } 1188 1189 String defaultValue = 1190 XmlWhitespace.collapse( 1191 field.getDefaultText(), type.getWhiteSpaceRule() ); 1192 1193 // BUGBUG - should validate defaultValue at compile time 1194 // KHK: cvc-elt.5.1.2 ? 1195 return 1196 validateSimpleType( type, defaultValue, event ) 1197 ? defaultValue 1198 : null; 1199 } 1200 1201 // KHK: cvc-elt.5.2.1 ? 1202 if (!validateSimpleType( type, value, event )) 1203 return null; 1204 1205 if (field != null && field.isFixed()) 1206 { 1207 // TODO - fixed value should have been cooked at compile time 1208 String fixedValue = 1209 XmlWhitespace.collapse( 1210 field.getDefaultText(), type.getWhiteSpaceRule() ); 1211 1212 if (!validateSimpleType( type, fixedValue, event )) 1213 return null; 1214 1215 XmlObject val = type.newValue( value ); 1216 XmlObject def = type.newValue( fixedValue ); 1217 1218 if (!val.valueEquals( def )) 1219 { 1220 // TODO (dutta) - make this more verbose 1221 if (field.isAttribute()) 1222 { 1223 // KHK: check for is cvc-complex-type.3.1 or cvc-au 1224 emitError(event, XmlErrorCodes.ATTR_LOCALLY_VALID$FIXED, 1225 new Object[] { value, fixedValue, QNameHelper.pretty(event.getName()) }, 1226 null, field.getType(), null, XmlValidationError.ELEMENT_TYPE_INVALID, null); 1227 } 1228 else 1229 { 1230 String errorCode = null; 1231 1232 // see rule 5 of cvc-elt: Element Locally Valid (Element) 1233 if (field.getType().getContentType() == SchemaType.MIXED_CONTENT) 1234 errorCode = XmlErrorCodes.ELEM_LOCALLY_VALID$FIXED_VALID_MIXED_CONTENT; 1235 else if (type.isSimpleType()) 1236 errorCode = XmlErrorCodes.ELEM_LOCALLY_VALID$FIXED_VALID_SIMPLE_TYPE; 1237 else 1238 assert false : "Element with fixed may not be EMPTY or ELEMENT_ONLY"; 1239 1240 emitError(event, errorCode, 1241 new Object[] { value, fixedValue }, 1242 field.getName(), field.getType(), null, XmlValidationError.ELEMENT_TYPE_INVALID, null); 1243 } 1244 1245 return null; 1246 } 1247 } 1248 1249 return value; 1250 } 1251 1252 private boolean validateSimpleType ( 1253 SchemaType type, String value, Event event ) 1254 { 1255 if (!type.isSimpleType() && 1256 type.getContentType() != SchemaType.SIMPLE_CONTENT) 1257 { 1258 assert false; 1259 throw new RuntimeException( "Not a simple type" ); 1260 } 1261 1262 int retState = _errorState; 1263 1264 switch ( type.getSimpleVariety() ) 1265 { 1266 case SchemaType.ATOMIC : validateAtomicType( type, value, event );break; 1267 case SchemaType.UNION : validateUnionType( type, value, event );break; 1268 case SchemaType.LIST : validateListType( type, value, event );break; 1269 1270 default : throw new RuntimeException( "Unexpected simple variety" ); 1271 } 1272 1273 return retState == _errorState; 1274 } 1275 1276 private void validateAtomicType ( 1277 SchemaType type, String value, Event event ) 1278 { 1279 // Now we should have only an atomic type to validate 1280 1281 assert type.getSimpleVariety() == SchemaType.ATOMIC; 1282 1283 // Record the current error state to see if any new errors are made 1284 int errorState = _errorState; 1285 _vc._event = event; 1286 1287 switch ( type.getPrimitiveType().getBuiltinTypeCode() ) 1288 { 1289 case SchemaType.BTC_ANY_SIMPLE : 1290 { 1291 // Always valid! 1292 _stringValue = value; 1293 break; 1294 } 1295 case SchemaType.BTC_STRING : 1296 { 1297 JavaStringEnumerationHolderEx.validateLexical( value, type, _vc ); 1298 _stringValue = value; 1299 break; 1300 } 1301 case SchemaType.BTC_DECIMAL : 1302 { 1303 JavaDecimalHolderEx.validateLexical( value, type, _vc ); 1304 1305 // An additional rule states that if the type is xs:integer or derived from it, 1306 // then the decimal dot is not allowed. 1307 // verify that values extending xsd:integer don't have a decimal point 1308 if ( _strict && derivedFromInteger( type ) && value.lastIndexOf('.') >= 0 ) 1309 { 1310 _vc.invalid(XmlErrorCodes.INTEGER, new Object[] { value }); 1311 } 1312 1313 if (errorState == _errorState) 1314 { 1315 _decimalValue = new BigDecimal( value ); 1316 JavaDecimalHolderEx.validateValue( _decimalValue, type, _vc ); 1317 } 1318 1319 break; 1320 } 1321 case SchemaType.BTC_BOOLEAN : 1322 { 1323 _booleanValue = JavaBooleanHolderEx.validateLexical( value, type, _vc ); 1324 break; 1325 } 1326 case SchemaType.BTC_FLOAT : 1327 { 1328 float f = 1329 JavaFloatHolderEx.validateLexical( value, type, _vc ); 1330 1331 if (errorState == _errorState) 1332 JavaFloatHolderEx.validateValue( f, type, _vc ); 1333 1334 _floatValue = f; 1335 break; 1336 } 1337 case SchemaType.BTC_DOUBLE : 1338 { 1339 double d = 1340 JavaDoubleHolderEx.validateLexical( value, type, _vc ); 1341 1342 if (errorState == _errorState) 1343 JavaDoubleHolderEx.validateValue( d, type, _vc ); 1344 1345 _doubleValue = d; 1346 break; 1347 } 1348 case SchemaType.BTC_QNAME : 1349 { 1350 QName n = 1351 JavaQNameHolderEx.validateLexical( 1352 value, type, _vc, event ); 1353 1354 if (errorState == _errorState) 1355 JavaQNameHolderEx.validateValue( n, type, _vc ); 1356 1357 _qnameValue = n; 1358 break; 1359 } 1360 case SchemaType.BTC_ANY_URI : 1361 { 1362 JavaUriHolderEx.validateLexical( value, type, _vc ); 1363 // Do strict validation 1364 if (_strict) 1365 { 1366 try 1367 { 1368 XsTypeConverter.lexAnyURI( value ); 1369 } 1370 catch (InvalidLexicalValueException ilve) 1371 { 1372 _vc.invalid(XmlErrorCodes.ANYURI, new Object[] { value }); 1373 } 1374 } 1375 _stringValue = value; 1376 break; 1377 } 1378 case SchemaType.BTC_G_MONTH : 1379 { 1380 // In the case of gMonth, there is some strict mode validation to do 1381 if (_strict && value.length() == 6 && 1382 value.charAt( 4 ) == '-' && value.charAt( 5 ) == '-') 1383 _vc.invalid(XmlErrorCodes.DATE, new Object[] { value }); 1384 // Fall through 1385 } 1386 case SchemaType.BTC_DATE_TIME : 1387 case SchemaType.BTC_TIME : 1388 case SchemaType.BTC_DATE : 1389 case SchemaType.BTC_G_YEAR_MONTH : 1390 case SchemaType.BTC_G_YEAR : 1391 case SchemaType.BTC_G_MONTH_DAY : 1392 case SchemaType.BTC_G_DAY : 1393 { 1394 GDate d = XmlDateImpl.validateLexical( value, type, _vc ); 1395 1396 if (d != null) 1397 XmlDateImpl.validateValue( d, type, _vc ); 1398 1399 _gdateValue = d; 1400 break; 1401 } 1402 case SchemaType.BTC_DURATION : 1403 { 1404 GDuration d = XmlDurationImpl.validateLexical( value, type, _vc ); 1405 1406 if (d != null) 1407 XmlDurationImpl.validateValue( d, type, _vc ); 1408 1409 _gdurationValue = d; 1410 break; 1411 } 1412 case SchemaType.BTC_BASE_64_BINARY : 1413 { 1414 byte[] v = 1415 JavaBase64HolderEx.validateLexical( value, type, _vc ); 1416 1417 if (v != null) 1418 JavaBase64HolderEx.validateValue( v, type, _vc ); 1419 1420 _byteArrayValue = v; 1421 break; 1422 } 1423 case SchemaType.BTC_HEX_BINARY : 1424 { 1425 byte[] v = 1426 JavaHexBinaryHolderEx.validateLexical( value, type, _vc ); 1427 1428 if (v != null) 1429 JavaHexBinaryHolderEx.validateValue( v, type, _vc ); 1430 1431 _byteArrayValue = v; 1432 break; 1433 } 1434 case SchemaType.BTC_NOTATION : 1435 { 1436 QName n = 1437 JavaNotationHolderEx.validateLexical( 1438 value, type, _vc, event ); 1439 1440 if (errorState == _errorState) 1441 JavaNotationHolderEx.validateValue( n, type, _vc ); 1442 1443 _qnameValue = n; 1444 break; 1445 } 1446 1447 default : 1448 throw new RuntimeException( "Unexpected primitive type code" ); 1449 } 1450 } 1451 1452 private void validateListType ( 1453 SchemaType type, String value, Event event ) 1454 { 1455 int errorState = _errorState; 1456 1457 if (!type.matchPatternFacet( value )) 1458 { 1459 emitError(event, XmlErrorCodes.DATATYPE_VALID$PATTERN_VALID, 1460 new Object[] { "list", value, QNameHelper.readable(type) }, 1461 null, type, null, XmlValidationError.LIST_INVALID, null); 1462 } 1463 1464 String[] items = XmlListImpl.split_list(value); 1465 1466 int i; 1467 XmlObject o; 1468 1469 if ((o = type.getFacet( SchemaType.FACET_LENGTH )) != null) 1470 { 1471 if ((i = ((SimpleValue)o).getIntValue()) != items.length) 1472 { 1473 //offending Qname not valid 1474 emitError(event, XmlErrorCodes.DATATYPE_LENGTH_VALID$LIST_LENGTH, 1475 new Object[] { value, new Integer(items.length), new Integer(i), QNameHelper.readable(type) }, 1476 null, type, null, XmlValidationError.LIST_INVALID, null); 1477 } 1478 } 1479 1480 if ((o = type.getFacet( SchemaType.FACET_MIN_LENGTH )) != null) 1481 { 1482 if ((i = ((SimpleValue)o).getIntValue()) > items.length) 1483 { 1484 //offending Qname not valid 1485 emitError(event, XmlErrorCodes.DATATYPE_LENGTH_VALID$LIST_LENGTH, 1486 new Object[] { value, new Integer(items.length), new Integer(i), QNameHelper.readable(type) }, 1487 null, type, null, XmlValidationError.LIST_INVALID, null); 1488 } 1489 } 1490 1491 if ((o = type.getFacet( SchemaType.FACET_MAX_LENGTH )) != null) 1492 { 1493 if ((i = ((SimpleValue)o).getIntValue()) < items.length) 1494 { 1495 //offending Qname not valid 1496 emitError(event, XmlErrorCodes.DATATYPE_LENGTH_VALID$LIST_LENGTH, 1497 new Object[] { value, new Integer(items.length), new Integer(i), QNameHelper.readable(type) }, 1498 null, type, null, XmlValidationError.LIST_INVALID, null); 1499 } 1500 } 1501 1502 SchemaType itemType = type.getListItemType(); 1503 _listValue = new ArrayList(); 1504 _listTypes = new ArrayList(); 1505 1506 for ( i = 0 ; i < items.length ; i++ ) 1507 { 1508 validateSimpleType( 1509 itemType, items[i], event ); 1510 addToList(itemType); 1511 } 1512 1513 // If no errors up to this point, then I can create an 1514 // XmlList from this value and campare it again enums. 1515 1516 if (errorState == _errorState) 1517 { 1518 if (type.getEnumerationValues() != null) 1519 { 1520 // Lists which contain QNames will need a resolver 1521 1522 NamespaceContext.push( 1523 new NamespaceContext( event ) ); 1524 1525 try 1526 { 1527 XmlObject listValue = ( (SchemaTypeImpl) type).newValidatingValue( value ); 1528 } 1529 catch (XmlValueOutOfRangeException e) 1530 { 1531 //offending Qname not valid ?? 1532 emitError(event, XmlErrorCodes.DATATYPE_ENUM_VALID, 1533 new Object[] { "list", value, QNameHelper.readable(type) }, 1534 null, type, null, XmlValidationError.LIST_INVALID, null); 1535 } 1536 finally 1537 { 1538 NamespaceContext.pop(); 1539 } 1540 } 1541 } 1542 } 1543 1544 private void validateUnionType ( 1545 SchemaType type, String value, Event event ) 1546 { 1547 // TODO - if xsi:type is specified on a union, it selects 1548 // that union member type 1549 1550 if (!type.matchPatternFacet( value )) 1551 { 1552 //offending Qname not valid ?? 1553 emitError(event, XmlErrorCodes.DATATYPE_VALID$PATTERN_VALID, 1554 new Object[] { "union", value, QNameHelper.readable(type) }, 1555 null, type, null, XmlValidationError.UNION_INVALID, null); 1556 } 1557 1558 int currentWsr = SchemaType.WS_PRESERVE; 1559 String currentValue = value; 1560 1561 SchemaType[] types = type.getUnionMemberTypes(); 1562 1563 int originalState = _errorState; 1564 1565 int i; 1566 for ( i = 0 ; i < types.length ; i++ ) 1567 { 1568 int memberWsr = types[ i ].getWhiteSpaceRule(); 1569 1570 if (memberWsr == SchemaType.WS_UNSPECIFIED) 1571 memberWsr = SchemaType.WS_PRESERVE; 1572 1573 if (memberWsr != currentWsr) 1574 { 1575 currentWsr = memberWsr; 1576 currentValue = XmlWhitespace.collapse( value, currentWsr ); 1577 } 1578 1579 int originalErrorState = _errorState; 1580 1581 _suspendErrors++; 1582 1583 try 1584 { 1585 validateSimpleType( types[ i ], currentValue, event ); 1586 } 1587 finally 1588 { 1589 _suspendErrors--; 1590 } 1591 1592 if (originalErrorState == _errorState) 1593 { 1594 _unionType = types[i]; 1595 break; 1596 } 1597 } 1598 1599 _errorState = originalState; 1600 1601 if (i >= types.length) 1602 { 1603 //offending Qname not valid ?? 1604 emitError(event, XmlErrorCodes.DATATYPE_VALID$UNION, 1605 new Object[] { value, QNameHelper.readable(type) }, 1606 null, type, null, XmlValidationError.UNION_INVALID, null); 1607 } 1608 else 1609 { 1610 XmlObject[] unionEnumvals = type.getEnumerationValues(); 1611 1612 if (unionEnumvals != null) 1613 { 1614 // Unions which contain QNames will need a resolver 1615 1616 NamespaceContext.push( new NamespaceContext( event ) ); 1617 1618 try 1619 { 1620 XmlObject unionValue = type.newValue( value ); 1621 1622 for ( i = 0 ; i < unionEnumvals.length ; i++ ) 1623 { 1624 if (unionValue.valueEquals( unionEnumvals[ i ] )) 1625 break; 1626 } 1627 1628 if (i >= unionEnumvals.length) 1629 { 1630 //offending Qname not valid ?? 1631 emitError(event, XmlErrorCodes.DATATYPE_ENUM_VALID, 1632 new Object[] { "union", value, QNameHelper.readable(type) }, 1633 null, type, null, XmlValidationError.UNION_INVALID, null); 1634 } 1635 } 1636 catch (XmlValueOutOfRangeException e) 1637 { 1638 // actually, the current union code always ends up here when invalid 1639 1640 //offending Qname not valid ?? 1641 emitError(event, XmlErrorCodes.DATATYPE_ENUM_VALID, 1642 new Object[] { "union", value, QNameHelper.readable(type) }, 1643 null, type, null, XmlValidationError.UNION_INVALID, null); 1644 } 1645 finally 1646 { 1647 NamespaceContext.pop(); 1648 } 1649 } 1650 } 1651 } 1652 1653 private void addToList(SchemaType type) 1654 { 1655 if (type.getSimpleVariety() != SchemaType.ATOMIC && 1656 type.getSimpleVariety() != SchemaType.UNION) 1657 return; 1658 1659 if (type.getUnionMemberTypes().length>0 && getUnionType()!=null) 1660 { 1661 type = getUnionType(); 1662 _unionType = null; 1663 } 1664 1665 _listTypes.add(type); 1666 1667 if (type.getPrimitiveType() == null) 1668 { 1669 // instance has an error for this value so there is no primitive type. 1670 // an error should already have been produced. 1671 _listValue.add(null); 1672 return; 1673 } 1674 1675 switch ( type.getPrimitiveType().getBuiltinTypeCode() ) 1676 { 1677 case SchemaType.BTC_ANY_SIMPLE : 1678 { 1679 _listValue.add(_stringValue); 1680 break; 1681 } 1682 case SchemaType.BTC_STRING : 1683 { 1684 _listValue.add(_stringValue); 1685 _stringValue = null; 1686 break; 1687 } 1688 case SchemaType.BTC_DECIMAL : 1689 { 1690 _listValue.add( _decimalValue ); 1691 _decimalValue = null; 1692 break; 1693 } 1694 case SchemaType.BTC_BOOLEAN : 1695 { 1696 _listValue.add(_booleanValue ? Boolean.TRUE : Boolean.FALSE); 1697 _booleanValue = false; 1698 break; 1699 } 1700 case SchemaType.BTC_FLOAT : 1701 { 1702 _listValue.add(new Float(_floatValue)); 1703 _floatValue = 0; 1704 break; 1705 } 1706 case SchemaType.BTC_DOUBLE : 1707 { 1708 _listValue.add(new Double(_doubleValue)); 1709 _doubleValue = 0; 1710 break; 1711 } 1712 case SchemaType.BTC_QNAME : 1713 { 1714 _listValue.add(_qnameValue); 1715 _qnameValue = null; 1716 break; 1717 } 1718 case SchemaType.BTC_ANY_URI : 1719 { 1720 _listTypes.add(_stringValue); 1721 break; 1722 } 1723 case SchemaType.BTC_DATE_TIME : 1724 case SchemaType.BTC_TIME : 1725 case SchemaType.BTC_DATE : 1726 case SchemaType.BTC_G_YEAR_MONTH : 1727 case SchemaType.BTC_G_YEAR : 1728 case SchemaType.BTC_G_MONTH_DAY : 1729 case SchemaType.BTC_G_DAY : 1730 case SchemaType.BTC_G_MONTH : 1731 { 1732 _listValue.add(_gdateValue); 1733 _gdateValue = null; 1734 break; 1735 } 1736 case SchemaType.BTC_DURATION : 1737 { 1738 _listValue.add(_gdurationValue); 1739 _gdurationValue = null; 1740 break; 1741 } 1742 case SchemaType.BTC_BASE_64_BINARY : 1743 { 1744 _listValue.add(_byteArrayValue); 1745 _byteArrayValue = null; 1746 break; 1747 } 1748 case SchemaType.BTC_HEX_BINARY : 1749 { 1750 _listValue.add(_byteArrayValue); 1751 _byteArrayValue = null; 1752 break; 1753 } 1754 case SchemaType.BTC_NOTATION : 1755 { 1756 _listValue.add(_qnameValue); 1757 _qnameValue = null; 1758 break; 1759 } 1760 1761 default : 1762 throw new RuntimeException( "Unexpected primitive type code" ); 1763 } 1764 } 1765 1766 // 1767 // Members of the validator class 1768 // 1769 1770 private boolean _invalid; 1771 private SchemaType _rootType; 1772 private SchemaField _rootField; 1773 private SchemaTypeLoader _globalTypes; 1774 private State _stateStack; 1775 private int _errorState; 1776 private Collection _errorListener; 1777 private boolean _treatLaxAsSkip; 1778 private boolean _strict; 1779 private ValidatorVC _vc; 1780 private int _suspendErrors; 1781 private IdentityConstraint _constraintEngine; 1782 private int _eatContent; 1783 1784 private SchemaLocalElement _localElement; 1785 private SchemaParticle _wildcardElement; 1786 private SchemaLocalAttribute _localAttribute; 1787 private SchemaAttributeModel _wildcardAttribute; 1788 private SchemaType _unionType; 1789 1790 // Strongly typed values 1791 private String _stringValue; 1792 private BigDecimal _decimalValue; 1793 private boolean _booleanValue; 1794 private float _floatValue; 1795 private double _doubleValue; 1796 private QName _qnameValue; 1797 private GDate _gdateValue; 1798 private GDuration _gdurationValue; 1799 private byte[] _byteArrayValue; 1800 private List _listValue; 1801 private List _listTypes; 1802 1803 private void resetValues() 1804 { 1805 _localAttribute = null; 1806 _wildcardAttribute = null; 1807 _stringValue = null; 1808 _decimalValue = null; 1809 _booleanValue = false; 1810 _floatValue = 0; 1811 _doubleValue = 0; 1812 _qnameValue = null; 1813 _gdateValue = null; 1814 _gdurationValue = null; 1815 _byteArrayValue = null; 1816 _listValue = null; 1817 _listTypes = null; 1818 _unionType = null; 1819 _localAttribute = null; 1820 } 1821 1822 /** 1823 * @return Returns the SchemaType of the current element. 1824 * This can be different than getCurrentElement().getType() if xsi:type attribute is used. 1825 * Null is returned if no schema type is available. 1826 * For attribute types use {@link #getCurrentAttribute()}.getType(). 1827 * Warning: the returned SchemaType can be an {@link org.apache.xmlbeans.XmlBeans#NO_TYPE}, 1828 * see {@link SchemaType#isNoType}. Or can be the parent type, for unrecognized elements 1829 * that are part of wildcards. 1830 */ 1831 public SchemaType getCurrentElementSchemaType ( ) 1832 { 1833 State state = topState(); 1834 if (state!=null) 1835 return state._type; 1836 1837 return null; 1838 } 1839 1840 /** 1841 * @return Returns the curent local element, null if one is not available, see {@link #getCurrentWildcardElement()}. 1842 */ 1843 public SchemaLocalElement getCurrentElement ( ) 1844 { 1845 if (_localElement != null) 1846 return _localElement; 1847 1848 // it means the element is to be skiped and it doesn't have a known SchemaLocalElement 1849 1850 if (_eatContent > 0) 1851 return null; 1852 1853 //try getting it from the stack (this should happen after END) 1854 1855 if (_stateStack != null && _stateStack._field instanceof SchemaLocalElement) 1856 return (SchemaLocalElement) _stateStack._field; 1857 1858 return null; 1859 } 1860 1861 /** 1862 * @return Returns the current particle, if this is a wildcard particle {@link SchemaParticle#WILDCARD} 1863 * method {@link #getCurrentElement()} might return null if wildcard's processContents is skip or lax. 1864 */ 1865 public SchemaParticle getCurrentWildcardElement() 1866 { 1867 return _wildcardElement; 1868 } 1869 1870 /** 1871 * @return Returns the curent local attribute, global attribute if the current attribute is part of an 1872 * attribute wildcard, or null if none is available. 1873 */ 1874 public SchemaLocalAttribute getCurrentAttribute() 1875 { 1876 return _localAttribute; 1877 } 1878 1879 /** 1880 * @return Returns the attribute model for attributes if available, else null is returned. 1881 */ 1882 public SchemaAttributeModel getCurrentWildcardAttribute() 1883 { 1884 return _wildcardAttribute; 1885 } 1886 1887 public String getStringValue() 1888 { 1889 return _stringValue; 1890 } 1891 1892 public BigDecimal getDecimalValue() 1893 { 1894 return _decimalValue; 1895 } 1896 1897 public boolean getBooleanValue() 1898 { 1899 return _booleanValue; 1900 } 1901 1902 public float getFloatValue() 1903 { 1904 return _floatValue; 1905 } 1906 1907 public double getDoubleValue() 1908 { 1909 return _doubleValue; 1910 } 1911 1912 public QName getQNameValue() 1913 { 1914 return _qnameValue; 1915 } 1916 1917 public GDate getGDateValue() 1918 { 1919 return _gdateValue; 1920 } 1921 1922 public GDuration getGDurationValue() 1923 { 1924 return _gdurationValue; 1925 } 1926 1927 public byte[] getByteArrayValue() 1928 { 1929 return _byteArrayValue; 1930 } 1931 1932 public List getListValue() 1933 { 1934 return _listValue; 1935 } 1936 1937 public List getListTypes() 1938 { 1939 return _listTypes; 1940 } 1941 1942 public SchemaType getUnionType() 1943 { 1944 return _unionType; 1945 } 1946 }