blob: 17228155eac8f7a7ad1e23b91e005f8838de5d7b [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache
* XMLBeans", nor may "Apache" appear in their name, without prior
* written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000-2003 BEA Systems
* Inc., <http://www.bea.com/>. For more information on the Apache Software
* Foundation, please see <http://www.apache.org/>.
*/
package org.apache.xmlbeans.impl.schema;
import org.apache.xmlbeans.impl.values.TypeStoreVisitor;
import org.apache.xmlbeans.impl.values.TypeStore;
import javax.xml.namespace.QName;
import org.apache.xmlbeans.SchemaParticle;
import org.apache.xmlbeans.SchemaLocalElement;
import org.apache.xmlbeans.SchemaField;
import java.util.Arrays;
/**
* This state machine validates element order based on a deterministic
* content model.
*/
public class SchemaTypeVisitorImpl implements TypeStoreVisitor
{
/**
* Construct it by passing the root of the content model.
*/
public SchemaTypeVisitorImpl(SchemaParticle part)
{
init(part);
}
public SchemaTypeVisitorImpl()
{
}
public void init(SchemaParticle part)
{
if (_stack == null)
{
_stack = expand(null);
}
if (_rollback == null)
{
_rollback = expand(null);
}
_stackSize = 0;
_rollbackSize = 0;
if (part != null)
{
push(part);
_rollbackIndex = 1;
}
}
public VisitorState[] expand(VisitorState[] orig)
{
int newsize = (orig == null ? 4 : orig.length * 2);
VisitorState[] result = new VisitorState[newsize];
if (orig != null)
System.arraycopy(orig, 0, result, 0, orig.length);
for (int i = (orig == null ? 0 : orig.length); i < newsize; i++)
{
result[i] = new VisitorState();
}
return result;
}
private VisitorState[] _stack;
private VisitorState[] _rollback;
int _stackSize;
int _rollbackSize;
private boolean _isValid;
private SchemaParticle _matchedParticle;
private VisitorState _top;
private int _rollbackIndex;
private static class VisitorState
{
public void copy(VisitorState orig)
{
_curPart = orig._curPart;
_curCount = orig._curCount;
_curMin = orig._curMin;
_curMax = orig._curMax;
_processedChildCount = orig._processedChildCount;
_childCount = orig._childCount;
_seen = orig._seen;
}
public void init(SchemaParticle part)
{
_curPart = part;
_curMin = part.getIntMinOccurs();
_curMax = part.getIntMaxOccurs();
_curCount = 0;
_processedChildCount = 0;
_childCount = part.countOfParticleChild();
_seen = part.getParticleType() == SchemaParticle.ALL ?
new boolean[_childCount] : null;
}
SchemaParticle _curPart;
int _curCount;
int _curMax;
int _curMin;
int _processedChildCount;
int _childCount;
boolean[] _seen;
}
VisitorState topRef()
{
return _stack[_stackSize - 1];
}
void saveCopy(VisitorState ref)
{
if (_rollback.length == _rollbackSize)
_rollback = expand(_rollback);
_rollback[_rollbackSize].copy(ref);
_rollbackSize += 1;
}
void addParticle(SchemaParticle part)
{
if (_stack.length == _stackSize)
_stack = expand(_stack);
_stack[_stackSize].init(part);
_stackSize += 1;
}
/**
* Precondition:
* stack: (R1 R2 R3 R4)
* top: null or discardable due to pop in progress
* rollback: (R8 R7 R6 R5) (empty at the start of "visit")
*
* Postcondition:
* stack: (R1 R2 R3)
* top: D4 = mutable copy of R4
* rollback (R8 R7 R6 R5 R4)
*/
boolean prepare()
{
if (_rollbackIndex == 0)
{
_top = null;
return false;
}
_top = topRef();
saveCopy(_top);
_rollbackIndex = _stackSize - 1;
return true;
}
/**
* Precondition:
* stack: (R1 R2 R3 R4 D5 D6)
* top: D7
* rollback: (R8 R7 R6 R5)
*
* Postcondition:
* stack: (R1 R2 R3 R4 D5 D6 D7)
* top: D8 = new state based on part
* rollback (R8 R7 R6 R5)
*/
void push(SchemaParticle part)
{
addParticle(part);
_top = topRef();
}
/**
* Precondition:
* stack: (R1 R2 R3 R4 D5 D6)
* top: D7 is discardable
* rollback: (R8 R7 R6 R5)
*
* Postcondition:
* stack: (R1 R2 R3 R4 D5)
* top: D6 (see prepare in case it's an R state)
* rollback (R8 R7 R6 R5)
*/
boolean pop()
{
_stackSize -= 1;
if (_stackSize <= _rollbackIndex)
return prepare();
_top = topRef();
return true;
}
/**
* Precondition:
* stack: (R1 R2 R3 R4 D5 D6)
* top: D7
* rollback: (R8 R7 R6 R5)
*
* Postcondition:
* stack: (R1 R2 R3 R4 D5 D6 D7) -> and rename to D's to R's
* top: null
* rollback ()
*/
void commit()
{
_top = null;
_rollbackIndex = _stackSize;
_rollbackSize = 0;
}
/**
* Precondition:
* stack: (R1 R2 R3 R4 D5 D6)
* top: D7 is discardable
* rollback: (R8 R7 R6 R5)
*
* Postcondition:
* stack: (R1 R2 R3 R4 R5 R6 R7 R8)
* top: null
* rollback ()
*/
void rollback()
{
while (_rollbackSize > 0)
{
_rollbackSize -= 1;
VisitorState temp = _stack[_rollbackIndex];
_stack[_rollbackIndex] = _rollback[_rollbackSize];
_rollback[_rollbackSize] = temp;
_rollbackIndex += 1;
}
_stackSize = _rollbackIndex;
_top = null;
}
/**
* When no valid next state can be found, then "notValid"
* is returned. It rolls back any changes to the state
* machine stack, sets to false the valid bit, and clears
* the matched element state before returning false.
*/
boolean notValid()
{
_isValid = false;
_matchedParticle = null;
rollback();
return false;
}
/**
* When a valid state transition has been done, then "ok"
* is returned. It commits the changed state machine state,
* stores the matched element state, and returns true.
*/
boolean ok(SchemaParticle part)
{
_matchedParticle = part;
commit();
return true;
}
/*== VISITOR IMPLEMENTATION ==*/
/**
* Traverses a deterministic content model, checking for
* validity at any given point.
*
* Call visit(null) once at the end if you're checking for
* complete validity of the sequence of elements.
*/
public boolean visit(QName eltName)
{
if (!prepare())
return notValid();
traversing: for (;;)
{
while (_top._curCount >= _top._curMax)
{
if (!pop())
break traversing;
}
minmax: switch (_top._curPart.getParticleType())
{
default:
assert(false);
case SchemaParticle.WILDCARD:
if (!_top._curPart.canStartWithElement(eltName))
{
if (_top._curCount < _top._curMin)
return notValid();
break minmax;
}
_top._curCount++;
return ok(_top._curPart);
case SchemaParticle.ELEMENT:
if (!_top._curPart.canStartWithElement(eltName))
{
if (_top._curCount < _top._curMin)
return notValid();
break minmax;
}
_top._curCount++;
return ok(_top._curPart);
case SchemaParticle.SEQUENCE:
for (int i = _top._processedChildCount; i < _top._childCount; i++)
{
SchemaParticle candidate = _top._curPart.getParticleChild(i);
if (candidate.canStartWithElement(eltName))
{
_top._processedChildCount = i + 1;
push(candidate);
continue traversing;
}
if (!candidate.isSkippable())
{
if (_top._processedChildCount != 0 || _top._curCount < _top._curMin)
return notValid();
break minmax;
}
}
_top._curCount++;
_top._processedChildCount = 0;
continue traversing;
case SchemaParticle.CHOICE:
for (int i = 0; i < _top._childCount; i++)
{
SchemaParticle candidate = _top._curPart.getParticleChild(i);
if (candidate.canStartWithElement(eltName))
{
_top._curCount++;
push(candidate);
continue traversing;
}
}
if (_top._curCount < _top._curMin && !_top._curPart.isSkippable())
return notValid();
break minmax;
case SchemaParticle.ALL:
int skipped = _top._processedChildCount;
allscan: for (int i = 0; i < _top._childCount; i++)
{
if (_top._seen[i])
continue allscan;
SchemaParticle candidate = _top._curPart.getParticleChild(i);
if (candidate.canStartWithElement(eltName))
{
_top._processedChildCount++;
_top._seen[i] = true;
push(candidate);
continue traversing;
}
else if (candidate.isSkippable())
{
skipped += 1;
}
}
if (skipped < _top._childCount)
{
if (_top._curCount < _top._curMin)
return notValid();
break minmax;
}
_top._curCount++;
_top._processedChildCount = 0;
Arrays.fill(_top._seen, false);
continue traversing;
}
// somebody called "break minmax", so pop out of loop
if (!pop())
break traversing;
}
// we've completed the outermost loop
if (eltName == null)
return ok(null);
// this means we have extra elements at the end
return notValid();
}
/**
* Constructs elementflags
*/
public int get_elementflags()
{
if (currentParticle() == null || currentParticle().getParticleType() != SchemaParticle.ELEMENT)
return 0;
SchemaLocalElement elt = (SchemaLocalElement)currentParticle();
return (elt.isNillable() ? TypeStore.NILLABLE : 0) |
(elt.isDefault() ? TypeStore.HASDEFAULT : 0) |
(elt.isFixed() ? TypeStore.FIXED : 0);
}
/**
* Returns default text
*/
public String get_default_text()
{
if (currentParticle() == null || currentParticle().getParticleType() != SchemaParticle.ELEMENT)
return null;
return ((SchemaLocalElement)currentParticle()).getDefaultText();
}
/**
* Returns the schema field for this field.
*/
public SchemaField get_schema_field() {
if (currentParticle() instanceof SchemaField)
return (SchemaField)currentParticle();
return null;
}
/**
* Returns the current schema element
*/
public SchemaParticle currentParticle()
{
return _matchedParticle;
}
/**
* Returns true if the entire content up to now is valid.
*/
public boolean isAllValid()
{
return _isValid;
}
}