blob: 9f59f5f18f642c881dd109006a27e5b7956063cf [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 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 "Xalan" 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",
* 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) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xml.dtm.ref.xni2dtm;
import org.apache.xerces.impl.dv.InvalidDatatypeValueException;
import org.apache.xerces.impl.dv.ValidatedInfo;
import org.apache.xerces.impl.dv.xs.XSSimpleTypeDecl;
import org.apache.xerces.impl.validation.ValidationState;
import org.apache.xerces.impl.xs.XSTypeDecl;
import org.apache.xerces.impl.xs.psvi.XSSimpleTypeDefinition;
import org.apache.xerces.impl.xs.psvi.XSTypeDefinition;
import org.apache.xerces.xni.NamespaceContext;
import org.apache.xerces.xni.psvi.ItemPSVI;
import org.apache.xml.dtm.DTMSequence;
/** The full XNI ItemPSVI is far too heavy-weight for
* our needs. But their XSTypeDecl isn't quite heavy enough; it gives
* us the actual member type, but that may be anonymous... so to get
* what XPath2 considers the proper typename, we need to examine
* additional fields as well. This class is an attempt to compromise by
* resolving the typename and storing that alongside the member type.
*
* A more efficient solution undoubtedly exists. But since XNI's PSVI
* APIs are still in flux, and since I'm just trying to get an initial
* demo running, this will suffice for now.
* %REVIEW% periodically!
* */
public class XPath2Type
{
public XSTypeDefinition m_xniType;
public String m_namespace;
public String m_localName;
/** Constructor for our internal type representation
* We will extract the low-level XSTypeDecl for the Member Type,
* and determine the proper namespace and localname. Other data
* can (hopefully) be GC'd after we're done. This is still NOT
* a lightweight beast.
*
* @param psvi XNI Post-Schema-Validation Infoset annotation.
* @param isAttr True iff we're defining type for an attribute.
* */
public XPath2Type(ItemPSVI psvi, boolean isAttr)
{
// First get the member type.
m_xniType= (psvi==null) ? null : psvi.getTypeDefinition();
// Now resolve the typename.
// There are some quibbles about algorithm; see comments on
// the resolve methods.
heavyResolveTypeName(psvi,isAttr);
}
/** Constructor for Xerces workaround
* (Xerces isn't reminding us of type at the time it provides
* validity, so XNI2DTM is passing that data around via a stack.
* This is a kluge and I hope to convince Xerces to fix it.)
*
* @param psvi XNI Post-Schema-Validation Infoset annotation.
* @param isAttr True iff we're defining type for an attribute.
* */
public XPath2Type(ItemPSVI psvi, XSSimpleTypeDefinition member, XSTypeDefinition type, boolean isAttr)
{
// First get the member type.
m_xniType= (psvi==null) ? null : type;
// Now resolve the typename.
// There are some quibbles about algorithm; see comments on
// the resolve methods.
heavyResolveTypeName(psvi,member,type,isAttr);
}
/** Internal constructor to support getItemType and
* AbstractSchema.lookUpType
* @param psvi XNI Post-Schema-Validation Infoset annotation.
* @param isAttr True iff we're defining type for an attribute.
* */
public XPath2Type(XSTypeDefinition itemType, String namespace, String localName)
{
m_xniType=itemType;
m_namespace=namespace;
m_localName=localName;
}
/** Identity needs to be defined so we can do sparse storage.
* %REVIEW% I'm not sure all three fields need to be checked, but...
* */
public boolean equals(XPath2Type other)
{
// Could cache hashCode()'s result and use that to accelerate
// doesn't-equal testing at the cost of some storage and
// slowing down does-equal tests. Not convinced it's useful here.
// %REVIEW%
return (m_xniType==other.m_xniType ||
m_xniType!=null && m_xniType.equals(other.m_xniType)) &&
// These two won't be null
m_localName.equals(other.m_localName) &&
m_namespace.equals(other.m_namespace);
}
/** Identity needs to be defined so we can do sparse storage.
* %REVIEW% I'm not sure all three fields need to be checked, but...
* */
public int hashCode()
{
// Could cache hashCode(). See discussion in equals().
// %REVIEW%
return m_namespace.hashCode()+m_localName.hashCode()+
(m_xniType==null ? 0 : m_xniType.hashCode());
}
public String getTargetNamespace() {return m_namespace;}
public String getTypeName() {return m_localName;}
public boolean derivedFrom(String namespace,String localname)
{
if(m_xniType instanceof XSTypeDecl)
return ((XSTypeDecl)m_xniType).derivedFrom(namespace,localname);
else
{
// Fallback in case no PSVI info was passed in: exact match
// (it's got to be the correct xsi:any*Type).
return(m_localName.equals(localname) && m_namespace.equals(namespace));
}
}
/** Broken out into a subroutine so I can use it for debugging purposes.
* This logic is adapted from the Xerces SimpleTypeUsage.validateString() example.
*
* %REVIEW% May be more efficient to fold it back in.
*
* NOTE: Typed value depends on context information -- for example, a
* QName must be interpreted w/r/t a specific Namespace context. It's
* the caller's responisibility to pass down that information, since only
* the caller knows which context this operation is being performed in.
*
* %REVIEW% Currently NS context comes down as a SAX accessor. Should we be
* able to take a DTM node directly as context?
*
* @param textValue Text content to be interpreted
* @param nsctxt SAX namespace context, which will be proxied as XNI.
* @return DTM_XSequence containing one or more Java values, as appropriate
* to the Built-In Type we have inherited from -- or null if no such
* mapping exists (eg, if actualType was complex)
* */
public DTMSequence typedValue(String textvalue, org.xml.sax.helpers.NamespaceSupport nsctxt)
{
Object value;
DTM_XSequence seq=null;
if(m_xniType instanceof XSSimpleTypeDefinition)
{
//create an instance of 'ValidatedInfo' to get back information
//(like actual value, normalizedValue etc..)after content is validated.
ValidatedInfo validatedInfo = new ValidatedInfo(); // %REVIEW% Can we reuse???
//get proper validation context. This is very important, since
// it's where we get data for resolving prefixes (in QNames),
// known IDs (for IDREFs and ID conflicts), entities, etc.
//Validation context passed is generally different while
// validating content and creating simple type (applyFacets)
ValidationState validationState = new ValidationState();
if(nsctxt!=null)
validationState.setNamespaceSupport(new XPath2NamespaceSupport(nsctxt));
// This may need to be bound to additional data using:
//validationState.setSymbolTable(....);
//validationState.setFacetChecking(...);
//validationState.setExtraChecking(...);
// Validate and parse the string
try{
((XSSimpleTypeDecl)m_xniType).validate(textvalue, validationState, validatedInfo);
} catch(InvalidDatatypeValueException ex){
// %REVIEW% Won't happen in prototype, where I'm only revalidating
// existing data. _WILL_ happen in production, when we start generating
// new typed data. Needs to be handled better at that time...
System.err.println(ex.getMessage());
ex.printStackTrace(System.err);
}
//now 'validatedInfo' object contains information
// for number types (decimal, double, float, and types derived from them),
// Object return is BigDecimal, Double, Float respectively.
// Boolean is handled similarly.
// Some types (string and derived) just return the string itself.
value = validatedInfo.actualValue;
//The normalized value of a string type
// (Should we check for stings and return this instead?)
String normalizedValue = validatedInfo.normalizedValue ;
// If the type is a union type, then the member type which
// actually validated the string value will be:
// XSSimpleType memberType = validatedInfo.memberType ;
// %REVIEW% I presume this handles lists by returning arrays...?
seq=new DTM_XSequence(value,this);
}
return seq==null ? DTMSequence.EMPTY : seq;
}
/** @return individual element type, if this is a list-of; else null.
* %REVIEW% -- else self?
* */
public XPath2Type getItemType()
{
if(m_xniType instanceof XSSimpleTypeDefinition)
{
XSTypeDefinition itemType=((XSSimpleTypeDefinition)m_xniType).getItemType();
if(itemType!=null)
return new XPath2Type(
itemType,
m_xniType.getNamespace(),
m_xniType.getName());
}
return null;
}
/** Implementation of the XPath2 type-name resolution algorithm
* (data model 3.5).
*
* Code donated by Sandy Gao, using the new
* Heavy-Weight PSVI interfaces.
* Fractionated to deal with validity and type info not being
* simultaneously available in current Xerces.
*
* @param psvi the psvi information for the current node
* @param attr false for element (fallback is xs:anyType)
* true for attribute (fallback is xs:anySimpleType)
* */
protected void heavyResolveTypeName(ItemPSVI psvi, XSSimpleTypeDefinition member, XSTypeDefinition type, boolean attr)
{
/* Not compatable with old light-weight schema APIs
*/
// check whether the node is valid
if (psvi == null ||
psvi.getValidity() != ItemPSVI.VALIDITY_VALID) {
// if the node is not valid, then return xs:anyType
m_namespace = "http://www.w3.org/2001/XMLSchema";
m_localName = attr ? "anySimpleType" : "anyType";
return;
}
// try provided member type definition, and return its name
if (member != null) {
m_namespace = member.getNamespace();
m_localName = member.getName();
return;
}
// try provided type definition, and return its name
if (type != null) {
m_namespace = type.getNamespace();
m_localName = type.getName();
return;
}
// Member type definitions promised to be available;
// can't proceed to check names independently
// all failed, return xs:anyType
m_namespace = "http://www.w3.org/2001/XMLSchema";
m_localName = attr ? "anySimpleType" : "anyType";
return;
// throw new java.lang.UnsupportedOperationException("Xerces Heavyweight PSVI not yet avaialble"); /**/
}
/** Implementation of the XPath2 type-name resolution algorithm
* (data model 3.5).
*
* Code donated by Sandy Gao, using the new
* Heavy-Weight PSVI interfaces
* This one works *ONLY* if type and validity are all known in the same
* PSVI, which is not the case in current Xerces.
*
* @param psvi the psvi information for the current node
* @param attr false for element (fallback is xs:anyType)
* true for attribute (fallback is xs:anySimpleType)
* */
protected void heavyResolveTypeName(ItemPSVI psvi, boolean attr)
{
heavyResolveTypeName(psvi,psvi.getMemberTypeDefinition(),
psvi.getTypeDefinition(), attr);
}
/** Modification of the XPath2 type-name resolution algorithm
* to reflect Sandy Gao's concerns about the official version
* not tolerating processors that implement the "lightweight" version
* of PSVI.
*
* Code donated by Sandy Gao, using the new
* Heavy-Weight PSVI interfaces
*
* @param psvi the psvi information for the current node
* @param attr false for element (fallback is xs:anyType)
* true for attribute (fallback is xs:anySimpleType)
* */
protected void proposedHeavyResolveTypeName(ItemPSVI psvi, boolean attr)
{
/* Not compatable with old light-weight schema APIs
*/
// NAME OF THIS CONSTANT IS IN FLUX
int VALID=ItemPSVI.VALIDITY_VALID;
//int VALID=ItemPSVI.VALID_VALIDITY;
// check whether the node is valid
if (psvi == null ||
psvi.getValidity() != VALID) {
// if the node is not valid, then return xs:anyType
m_namespace = "http://www.w3.org/2001/XMLSchema";
m_localName = attr ? "anySimpleType" : "anyType";
return;
}
// try to get the member type definition, and return its name
XSSimpleTypeDefinition member = psvi.getMemberTypeDefinition();
if (member != null && member.getName() != null) {
m_namespace = member.getNamespace();
m_localName = member.getName();
return;
}
// try to get the type definition, and return its name
XSTypeDefinition type = psvi.getTypeDefinition();
if (type != null && type.getName() != null) {
m_namespace = type.getNamespace();
m_localName = type.getName();
return;
}
// Member type definitions promised to be available;
// can't proceed to check names independently
// all failed, return xs:anyType
m_namespace = "http://www.w3.org/2001/XMLSchema";
m_localName = attr ? "anySimpleType" : "anyType";
return;
// throw new java.lang.UnsupportedOperationException("Xerces Heavyweight PSVI not yet avaialble"); /**/
}
/** Attempt to write a simplified version of the type resolution
* algorithm (data model 3.5) using only the Light-Weight PSVI
* interfaces previously supported in Xerces (which I believe will be
* phased out when heavyweight come into play).
*
* Based on code donated by Sandy Gao.
*
* @param psvi the psvi information for the current node
* @param ret a String array with size 2
* index 0 is used to return the namespace name;
* index 1 is used to return the local name.
* @param attr false for element (fallback is xs:anyType)
* true for attribute (fallback is xs:anySimpleType)
* */
protected void lightResolveTypeName(ItemPSVI psvi, boolean attr)
{
/* Not compatable with new heavy-weight schema APIs
//
int VALID=ItemPSVI.VALID_VALIDITY;
// check whether the node is valid
if (psvi == null ||
psvi.getValidity() != VALID) {
// if the node is not valid, then return xs:anyType
m_namespace = "http://www.w3.org/2001/XMLSchema";
m_localName = attr ? "anySimpleType" : "anyType";
return;
}
// Member type definitions promised NOT to be available;
// proceed to check names independently
// Need the second test, apparently
if (!psvi.isMemberTypeAnonymous() && null!=psvi.getMemberTypeName() ) {
m_namespace = psvi.getMemberTypeNamespace();
m_localName = psvi.getMemberTypeName();
return;
}
// Need the second test, apparently
if (!psvi.isTypeAnonymous() && null!=psvi.getTypeName()) {
m_namespace = psvi.getTypeNamespace();
m_localName = psvi.getTypeName();
return;
}
// all failed, return xs:anyType
m_namespace = "http://www.w3.org/2001/XMLSchema";
m_localName = attr ? "anySimpleType" : "anyType";
return;
*/ throw new java.lang.UnsupportedOperationException("Xerces Lightweight PSVI phased out"); /**/
}
//---------------------------------------------------------------------
/** Bridge from Xalan/SAX namespace context to XNI namespace context
*/
class XPath2NamespaceSupport
extends org.apache.xerces.util.NamespaceSupport
{
org.xml.sax.helpers.NamespaceSupport saxNamespaceSupport;
/** Default constructor, not useful in this wrapper. */
public XPath2NamespaceSupport() {
throw new UnsupportedOperationException("Wrong c'tor call");
} // <init>()
/**
* Constructs a namespace context object and initializes it with
* the prefixes declared in the specified context. Not useful in this
* wrapper.
*/
public XPath2NamespaceSupport(org.apache.xerces.xni.NamespaceContext context) {
throw new UnsupportedOperationException("Wrong c'tor call");
} // <init>(xni.NamespaceContext)
/**
* Constructs a namespace context object and initializes it with
* a SAX NamespaceSupport object. Use this c'tor.
* */
public XPath2NamespaceSupport(org.xml.sax.helpers.NamespaceSupport saxNS) {
saxNamespaceSupport=saxNS;
} // <init>(sax.helpers.NamespaceSupport)
//
// Public methods
//
// context management
/**
* Reset this Namespace support object for reuse.
*
* <p>It is necessary to invoke this method before reusing the
* Namespace support object for a new session.</p>
*/
public void reset(org.apache.xerces.util.SymbolTable symbolTable) {
// no-op
} // reset(SymbolTable)
/**
* Start a new Namespace context.
* <p>
* Normally, you should push a new context at the beginning
* of each XML element: the new context will automatically inherit
* the declarations of its parent context, but it will also keep
* track of which declarations were made within this context.
* <p>
* The Namespace support object always starts with a base context
* already in force: in this context, only the "xml" prefix is
* declared.
*
* @see #popContext
*/
public void pushContext() {
// I don't know if this will ever be used during validation...
saxNamespaceSupport.pushContext();
} // pushContext()
/**
* Revert to the previous Namespace context.
* <p>
* Normally, you should pop the context at the end of each
* XML element. After popping the context, all Namespace prefix
* mappings that were previously in force are restored.
* <p>
* You must not attempt to declare additional Namespace
* prefixes after popping a context, unless you push another
* context first.
*
* @see #pushContext
*/
public void popContext() {
// I don't know if this will ever be used during validation...
saxNamespaceSupport.popContext();
} // popContext()
// operations within a context.
/**
* Declare a Namespace prefix.
* <p>
* This method declares a prefix in the current Namespace
* context; the prefix will remain in force until this context
* is popped, unless it is shadowed in a descendant context.
* <p>
* To declare a default Namespace, use the empty string. The
* prefix must not be "xml" or "xmlns".
* <p>
* Note that you must <em>not</em> declare a prefix after
* you've pushed and popped another Namespace.
*
* @param prefix The prefix to declare, or null for the empty
* string.
* @param uri The Namespace URI to associate with the prefix.
*
* @return true if the prefix was legal, false otherwise
*
* @see #getURI
* @see #getDeclaredPrefixAt
*/
public boolean declarePrefix(String prefix, String uri) {
// I don't know if this will ever be used during validation...
return saxNamespaceSupport.declarePrefix(prefix,uri);
} // declarePrefix(String,String):boolean
/**
* Look up a prefix and get the currently-mapped Namespace URI.
* <p>
* This method looks up the prefix in the current context.
* Use the empty string ("") for the default Namespace.
*
* @param prefix The prefix to look up.
*
* @return The associated Namespace URI, or null if the prefix
* is undeclared in this context.
*
* @see #getDeclaredPrefixAt
*/
public String getURI(String prefix) {
return saxNamespaceSupport.getURI(prefix);
} // getURI(String):String
/**
* Look up a namespace URI and get one of the mapped prefix.
* <p>
* This method looks up the namespace URI in the current context.
*
* @param uri The namespace URI to look up.
*
* @return one of the associated prefixes, or null if the uri
* does not map to any prefix.
*
* @see #getPrefix
*/
public String getPrefix(String uri) {
return saxNamespaceSupport.getPrefix(uri);
} // getURI(String):String
/**
* Return a count of all prefixes currently declared, including
* the default prefix if bound.
*/
public int getDeclaredPrefixCount() {
java.util.Enumeration e=saxNamespaceSupport.getDeclaredPrefixes();
int count=0;
while(e.hasMoreElements())
{
++count;
e.nextElement();
}
return count;
} // getDeclaredPrefixCount():int
/**
* Returns the prefix at the specified index in the current context.
* %REVIEW% Massively inefficient in this implementation.
*/
public String getDeclaredPrefixAt(int index) {
java.util.Enumeration e=saxNamespaceSupport.getDeclaredPrefixes();
String result=null;
for(; index>=0;--index)
{
if(e.hasMoreElements())
result=(String)e.nextElement();
else
return null;
}
return result;
} // getDeclaredPrefixAt(int):String
/**
* Returns the parent namespace context or null if there is no
* parent context. The total depth of the namespace contexts
* matches the element depth in the document.
* <p>
* <strong>Note:</strong> This method <em>may</em> return the same
* NamespaceContext object reference. The caller is responsible for
* saving the declared prefix mappings before calling this method.
*/
public NamespaceContext getParentContext() {
// I suppose I could construct a new XPath2NamespaceSupport
// wrapped around the SAX parent context. I'd rather not
// unless I must.
throw new UnsupportedOperationException("Not supported; involves further crossing XNI/SAX boundaries");
} // getParentContext():NamespaceContext
} // class XPath2NamespaceSupport
} // XPath2Type