blob: aab6b32ad9289ee7208958696aadb17e3f690815 [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.xpath.objects;
import java.util.Vector;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.dtm.XType;
import org.apache.xml.utils.IntVector;
import org.apache.xml.utils.WrappedRuntimeException;
/** This class provides an API representation for the XPath 2 Data Model's "Sequences"
* -- which are the basic representation for typed values. Only built-in types,
* types derived from built-ins, and sets thereof are returned directly by the XPath2
* DM; complex schema-types must be accessed through the document tree.
* */
public class XSequenceImpl extends XObject
implements XSequenceMutable
{
// %OPT% Four vectors is not exactly compact.
// Do we want to try to improve this?
private Vector m_values=new Vector();
protected Vector m_typeNamespaces=new Vector();
protected Vector m_typeNames=new Vector();
// We really shouldn't need xTypes, since the XObject should be cabable of
// storing the type.
// private IntVector xTypes=new IntVector();
protected int m_pos = -1;
protected boolean m_unlocked=true;
protected int m_homogtype=XType.EMPTYSEQ;
/** Create a sequence, initially empty.
* */
public XSequenceImpl()
{
setValues(new Vector());
}
/** Create a sequence, initially empty.
* */
public XSequenceImpl(boolean shouldCache)
{
if(shouldCache)
setValues(new Vector());
}
/**
* %REVIEW% If the type is in the XMLSchema or XMLSchema-datatype
* namespaces, should we also assign a primitive XType?
*
* @param value Item value to add to the iteration.
* @param typeNamespace String containing namespace URI of schema type
* @param typeNamespace String containing local name of schema type
*
* @return Sequence containing old plus new data. IT MAY BE A NEW
* OBJECT; user is responsible for always invoking this as
* <code>myseq=myseq.concat(val,ns,typename);</code>. However, IT MAY
* NOT BE A NEW OBJECT; don't assume that the old sequence will
* still be available after this operation returns.
*
* @see XSequenceMutable.append(Object value,String typeNamespace,String typeName)
* */
public XSequenceMutable concat(Object value,String typeNamespace,String typeName)
{
if(!m_unlocked)
throw new RuntimeException("Sequence is locked, hence not muteable!");
int xtype=0;
if(XType.XMLSCHEMA_DATATYPE_NAMESPACE.equals(typeNamespace)
|| XType.XMLSCHEMA_NAMESPACE.equals(typeNamespace))
xtype=XType.getTypeFromLocalName(typeName);
XObject xvalue = XObjectFactory.create(value);
getValues().addElement(xvalue);
// If it's an XType, we may save some space by not
// storing the strings and instead retrieving them
// from the manefest-constant tables.
// (Though storing them would simplify retrieval...)
// %REVIEW%
m_typeNames.addElement(xtype!=0 ? null : typeName);
m_typeNamespaces.addElement(xtype!=0 ? null : typeNamespace);
// Ignore xtype for right now. -sb
// xTypes.addElement(xtype);
if(m_homogtype==XType.EMPTYSEQ)
m_homogtype=xtype;
else if(m_homogtype!=xtype)
m_homogtype=XType.NOTHOMOGENOUS;
return this;
}
/** Add an object to the sequence, along with its
* primitive datatype as defined in XType
* @param value Item value to add to the iteration.
* @param xtype Primitive type number, as defined in XType.
*
* @return Sequence containing old plus new data. IT MAY BE A NEW
* OBJECT; user is responsible for always invoking this as
* <code>myseq=myseq.concat(val,type);</code>. However, IT MAY
* NOT BE A NEW OBJECT; don't assume that the old sequence will
* still be available after this operation returns.
*
* @see XSequenceMutable.concat(Object value,int xtype)
* */
public XSequenceMutable concat(Object value,int xtype)
{
if(!m_unlocked)
throw new RuntimeException("Sequence is locked, hence not mutable!");
XObject xvalue = XObjectFactory.create(value);
getValues().addElement(xvalue);
// Could store the strings. But given that I'm suppressing
// them for efficiency reasons when type was set by strings,
// I'll suppress them here for consistancy.
// %REVIEW%
m_typeNames.addElement(null);
m_typeNamespaces.addElement(null);
// Ignore xTypes for right now. See comment at commented out member var. -sb
// xTypes.addElement(xtype);
if(m_homogtype==XType.EMPTYSEQ)
m_homogtype=xtype;
else if(m_homogtype!=xtype)
m_homogtype=XType.NOTHOMOGENOUS;
return this;
}
/**
* @see org.apache.xpath.objects.XSequenceMutable#concat(XObject)
*/
public XSequenceMutable concat(XObject xvalue)
{
if(!m_unlocked)
throw new RuntimeException("Sequence is locked, hence not muteable!");
getValues().addElement(xvalue);
// Could store the strings. But given that I'm suppressing
// them for efficiency reasons when type was set by strings,
// I'll suppress them here for consistancy.
// %REVIEW%
m_typeNames.addElement(null);
m_typeNamespaces.addElement(null);
// Ignore xTypes for right now. See comment at commented out member var. -sb
// xTypes.addElement(xvalue.getType());
return this;
}
/** Append complete contents of another XSequence
*
* @return Sequence containing old plus new data. IT MAY BE A NEW
* OBJECT; user is responsible for always invoking this as
* <code>myseq=myseq.concat(otherseq);</code>. However, IT MAY
* NOT BE A NEW OBJECT; don't assume that the old sequence will
* still be available after this operation returns.
*
* @see XSequenceMutable.append(XSequence)
* */
public XSequenceMutable concat(XSequence other)
{
if(!m_unlocked)
throw new RuntimeException("Sequence is locked, hence not muteable!");
for(Object value=other.next();
value!=null;
value=other.next())
{
getValues().addElement(value);
int xtype=other.getType();
// Ignore xTypes for right now. See comment at commented out member var. -sb
// xTypes.addElement(xtype);
m_typeNames.addElement(xtype==0 ? null : other.getTypeLocalName());
m_typeNamespaces.addElement(xtype==0 ? null : other.getTypeNS());
if(m_homogtype==XType.EMPTYSEQ)
m_homogtype=xtype;
else if(m_homogtype!=xtype)
m_homogtype=XType.NOTHOMOGENOUS;
}
return this;
}
/** Prevent further mutation of this sequence. After this has
* been called, isMutable should return false.
* @see XSequence.isMutable()
* */
public void lock()
{
m_unlocked=false;
}
/** @return the number of members in this sequence. */
public int getLength()
{
return getValues().size();
}
/** Retrieve the value for this member of the sequence. Since values may be
* a heterogenous mix, and their type may not be known until they're examined,
* they're returned as Objects. This is storage-inefficient if the value(s)
* is/are builtins that map to Java primitives. Tough.
*
* @param index 0-based index into the sequence.
* @return the specified value
* @throws exception if index <0 or >=length
* */
public XObject next()
{
if(m_pos >= getValues().size()-1)
return null;
return (XObject)getValues().elementAt(++m_pos);
}
/**
* @see org.apache.xml.dtm.XSequence#previous()
*/
public XObject previous()
{
if(m_pos <= 0)
return null;
return (XObject)getValues().elementAt(--m_pos);
}
/** %REVIEW% Format not yet firmly settled. Primarily for debugging purposes!
* */
public String toString()
{
StringBuffer b=new StringBuffer("Sequence[");
String start="";
for(int i=0;i<getValues().size();++i)
{
XObject xvalue=(XObject)getValues().elementAt(i);
String tns,tn;
// int xtype=xTypes.elementAt(i);
int xtype=xvalue.getType();
if(xtype!=XType.ANYTYPE)
{
tns=XType.XMLSCHEMA_NAMESPACE;
tn=XType.getLocalNameFromType(xtype);
}
else
{
// %OPT% Is it faster to cast to String, or
// to call .toString()? The latter is _safer_...
// (Opinion from Sun: Depends on your JVM.)
tns=(String)m_typeNamespaces.elementAt(i);
tn=(String)m_typeNames.elementAt(i);
}
b.append(start)
.append(tns).append(':').append(tn);
if(xtype!=0)
b.append(" (").append(xtype).append(')');
b.append(" = ")
.append(xvalue);
if(xvalue!=null)
b.append(" {"+xvalue.getClass().getName()+"}");
start=", ";
}
return b.append(']').toString();
}
/**
* @see org.apache.xml.dtm.XSequence#allowDetachToRelease(boolean)
*/
public void allowDetachToRelease(boolean allowRelease)
{
}
/**
* @see org.apache.xml.dtm.XSequence#detach()
*/
public void detach()
{
// Do nothing
}
/**
* @see org.apache.xml.dtm.XSequence#getCurrent()
*/
public XObject getCurrent()
{
return (XObject)getValues().elementAt(m_pos);
}
/**
* @see org.apache.xml.dtm.XSequence#getCurrentPos()
*/
public int getCurrentPos()
{
return m_pos;
}
/**
* @see org.apache.xml.dtm.XSequence#getTypeLocalName()
*/
public String getTypeLocalName()
{
XObject xobj = getCurrent();
int xtype = xobj.getType();
if (xtype != XType.ANYTYPE)
return XType.getLocalNameFromType(xtype);
else
return (String) m_typeNames.elementAt(m_pos);
}
/**
* @see org.apache.xml.dtm.XSequence#getTypeNS()
*/
public String getTypeNS()
{
XObject xobj = getCurrent();
int xtype = xobj.getType();
if (xtype != XType.ANYTYPE)
return XType.XMLSCHEMA_NAMESPACE;
else
return (String) m_typeNamespaces.elementAt(m_pos);
}
/**
* If this iterator holds homogenous primitive types, return that type ID.
*
* @return The homogenous type, or NOTHOMOGENOUS, or EMPTYSEQ.
*/
public int getTypes()
{
return m_homogtype;
}
/**
* @see org.apache.xml.dtm.XSequence#getType()
*/
public int getType()
{
XObject xobj = getCurrent();
if(null == xobj)
return XType.SEQ; // %REVIEW% Not sure what this should return.
int xtype = xobj.getType();
return xtype;
}
/** @return false, since I don't think we're ever going to put
* nodes into an XSequenceImpl (they aren't objects, unless we
* are handling DOM nodes...)
* %REVIEW%
* */
public boolean isPureNodeSequence()
{
return false;
}
/**
* @see org.apache.xml.dtm.XSequence#isFresh()
*/
public boolean isFresh()
{
return (m_pos == -1) ? true : false;
}
/**
* @see org.apache.xml.dtm.XSequence#isMutable()
*/
public boolean isMutable()
{
return m_unlocked;
}
/** %BUG% Problem here -- to recognize inheritance we need to
* reach into the schemas. Currently that can be done by using
* the XPathContext (which we ain't got) to query the AbstractSchema
* for an XPath2Type (both of which are Xerces-specific and buried
* in XNI2DTM) and asking it... We need a higher-level API for that
* test.
*
* %REVIEW% As a stopgap, match literal type name directly.
*
* @see org.apache.xml.dtm.XSequence#isSchemaType(String, String)
*/
public boolean isSchemaType(String namespace, String localname)
{
String tns=getTypeNS();
String tn=getTypeLocalName();
return
((tns==null) ? namespace==null : tns.equals(namespace))
&&
((tn==null) ? localname==null : tn.equals(localname))
;
}
/**
* @see org.apache.xml.dtm.XSequence#isSingletonOrEmpty()
*/
public boolean isSingletonOrEmpty()
{
return getLength() <= 1;
}
/**
* @see org.apache.xml.dtm.XSequence#setCurrentPos(int)
*/
public void setCurrentPos(int i)
{
m_pos = i-1;
}
/**
* This method performs no actions.
* @see org.apache.xml.dtm.XSequence#setShouldCache(boolean)
*/
public void setShouldCache(boolean b)
{
// Ignore. We always cache.
}
/**
* @return true -- setCurrentPos() works for this object
* @see org.apache.xml.dtm.XSequence#getIsRandomAccess()
*/
public boolean getIsRandomAccess()
{
return true;
}
/**
* @see org.apache.xml.dtm.XSequence#reset()
*/
public void reset()
{
m_pos = -1;
}
/**
* @see java.lang.Object#clone()
*/
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
/**
* Return a (fresh) sequence representing this object.
* @return XSequence
*/
public XSequence xseq()
{
if(isFresh())
return this;
try
{
XSequence xseq = (XSequence)this.clone();
xseq.reset();
return xseq;
}
catch (CloneNotSupportedException e)
{
// will never happen
throw new WrappedRuntimeException(e);
}
}
/**
* Returns the values.
* @return Vector
*/
public Vector getValues()
{
return m_values;
}
/**
* Returns the values.
* @return Vector
*/
public Object object()
{
return m_values;
}
/**
* Sets the values.
* @param values The values to set
*/
public void setValues(Vector values)
{
this.m_values = values;
}
/**
* @see org.apache.xpath.objects.XSequenceMutable#getItem(int)
*/
public XObject getItem(int pos)
{
return (XObject)getValues().elementAt(pos);
}
/**
* @see org.apache.xpath.objects.XSequenceMutable#insertItemAt(XObject, int)
*/
public void insertItemAt(XObject value, int pos)
{
// TBD: Update homogtype?
getValues().insertElementAt(value, pos);
}
/**
* @see org.apache.xpath.objects.XSequenceMutable#setItem(XObject, int)
*/
public void setItem(XObject value, int pos)
{
// TBD: Update homogtype?
getValues().setElementAt(value, pos);
}
/**
* @see org.apache.xpath.objects.XObject#isSequenceProper()
*/
public boolean isSequenceProper()
{
return true;
}
}