Home » xmlbeans-2.5.0-src » org.apache.xmlbeans.impl » validator » [javadoc | source]

    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   }

Home » xmlbeans-2.5.0-src » org.apache.xmlbeans.impl » validator » [javadoc | source]