Types in Pathan
===============

Types
=====

Pathan has a class for each of the XMLSchema atomic types used in
XPath 2

These classes are:  simpleTypes/XS...Impl.hpp

eg. XSDecimalImpl, XSIntegerImpl public: BaseItemImpl

The XML Schema base types have their own inheritance hierarchy - see
Schema part 2.

The C++ class hierarchy attempts to match this hierarchy where
sensible, eg. XSInteger is inherited from XSDecimal. However, the
match is not absolute - our classes do not inherit off the class
XSAnySimpleTypeImpl since this would mean lugging an extra string
around with us.

There are two quite different processes which can occur on simpleTypes
and it is easy to get them confused.

C++ static casting
==================

Our sequence class Sequence contains a vector of BaseItem s. BaseItem
is a wrapper around BaseItemImpl, the base class for all the atomic
types. Often, for example in functions, we know the type of the atomic
type object contained by the BaseItem (eg. XSDecimalImpl) but C++ does
not have this information.

For example, in the execute() function of a method taking (node*) we
know we have a sequence of XSNode items. We want to call
XSNodeImpl::getDOMNode() but all we have is a BaseItem &:

BaseItem &item = getFirstParam();
XSNode node(item);
const DOMNode *domNode = node.getDOMNode();

here the XSNode() constructor achieves the static cast.

All XSNodeImpl functions are available on XSNode, this is true for all
type wrapper.

Note that we are not `XPath casting' whatever was in item to an XSNode
item, we are merely telling C++ that we in fact have an object of type
XSNode() already.

If you try constructing XSNode() around a BaseItem that does not hold
a node, it'll throw an exception. So don't :)

There is one other subtlety. The Impl constructor functions also get
wrapped, for example:

XSStringImpl::XSStringImpl(XMLCh *str)

is wrapped into

XSString::XSString(XMLCh *str)

This means:

XSString newStr(X("hello"));

will actually create a new XSStringImpl class wrapped in an XSString
wrapper.

So creation of entirely new objects is possible with the wrapper
interface.


XPath casting
=============

XPath does specify a mechanism for casting between atomic types,
eg. creating a decimal from the string representation "1.0". This is
done by constructor functions and various other things like function
call parameter processing and fallback conversions.

The allowable conversions are listed in the table in section 16 of
Function and Operators.

In our model, these casts are achieved by using the virtual methods

BaseItem BaseItemImpl::castAsXSWhatever()

or their wrapped counterparts

BaseItem BaseItem::castAsXSWhatever()

In the base class these methods throw exceptions - allowable casts are
achieved by overloading the virtual methods, eg.

BaseItem XSDecimal::castAsXSBoolean()


The only subtlety in casting is casting around the Schema atomic types
hierarchy (see diagram).

Any type may be casted to a parent type, eg. XSInteger -> XSDecimal
(this is straightforward). This is implemented directly by the C++
hierarchy - XSInteger is derived from XSDecimal and hence inherits
castAsXSDecimal(). Similarly, anything in the XSDecimal tree inherits
this function.

However, (16.5) in the spec lets you cast between ANY types derived
from the same base type, base type being float, double, decimal, gDay
etc.

For example, the cast positiveInteger->negativeInteger is
theoretically allowed. The spec states that if the value from the
first type does not conform to the facets of the second type then an
exception is thrown. This is implemented in the code by XSDecimalImpl
containing all the castAs methods for types inherited from it:

  virtual BaseItem castAsXSShort(XPath2Context &context) const;
	virtual BaseItem castAsXSByte(XPath2Context &context) const;
	virtual BaseItem castAsXSNonPositiveInteger(XPath2Context &context) const;
etc.

The definition of these functions is as follows:

BaseItem XSDecimalImpl::castAsXSNegativeInteger(XPath2Context &context) const
{
	return BaseItem(new XSNegativeIntegerImpl(asString()));
}

and relies on the fact that the Impl string constructors reject any
badly formed or out of range input. It's a tad slow but unless we move
over to a more formal facets system it is probably okay. Could do with
more tests though.

Type checking
=============

This is part of SequenceType matching (see below).

- A named atomic type matches a value if the dynamic type of the value
is the same as the named atomic type or is derived from the named
atomic type by restriction.

ie. an XSInteger will match (XSDecimal?) since it is derived from
XSDecimal by restriction.

Hence in the C++ code we need a way to check if a BaseItem is of the
correct type (or derived from the correct type).

This is achieved with:

bool BaseItem::isType(const XMLChString &URI, const XMLChString &name, XPath2Context &context) const;

and, in the actual atomic types classes themselves

virtual bool BaseItemImpl::isType(BaseItem::whichType type) const = 0;


SequenceType
============

schema/SequenceType.cpp is a rather mauled class

It can do 2 functions at the moment.

1) doing a raw SequenceType matching process, eg. for the
-instance of- operator

2) doing a SequenceType matching with extra processing for passing
parameters into functions

1) [see FunctionInstanceOf] call SequenceType::matches()

2) Use Sequence::castAs() on a sequence - this is taken care of by
XPath2Function::getParamNumber() style methods

Incidentally, fallback conversions are taken care of in
Sequence::castAs - this only works for functions (we should use
unparameterised atomize() in all other cases, as per the Formal
Semantics)

1) SequenceType uses BaseItem::isType() to check whether atomic types
conform (see above)

2) in addition to BaseItem::isType(), we use BaseItem::castAsType()
since some type conversion is required for function parameters
