org.apache.xmlbeans.impl.values.XmlUnionImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-xmlbeans Show documentation
Show all versions of commons-xmlbeans Show documentation
The Apache Commons Codec package contains simple encoder and decoders for
various formats such as Base64 and Hexadecimal. In addition to these
widely used encoders and decoders, the codec package also maintains a
collection of phonetic encoding utilities.
The newest version!
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.values;
import java.util.Date;
import java.util.List;
import java.util.Calendar;
import java.math.BigInteger;
import java.math.BigDecimal;
import org.apache.xmlbeans.GDate;
import org.apache.xmlbeans.GDuration;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.XmlAnySimpleType;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlErrorCodes;
import org.apache.xmlbeans.StringEnumAbstractBase;
import org.apache.xmlbeans.GDateSpecification;
import org.apache.xmlbeans.GDurationSpecification;
import org.apache.xmlbeans.SimpleValue;
import org.apache.xmlbeans.impl.schema.SchemaTypeImpl;
import org.apache.xmlbeans.impl.common.ValidationContext;
import org.apache.xmlbeans.impl.common.QNameHelper;
import javax.xml.namespace.QName;
/**
* This class implements simple union types.
*
*/
public class XmlUnionImpl extends XmlObjectBase implements XmlAnySimpleType
{
public XmlUnionImpl(SchemaType type, boolean complex)
{ _schemaType = type; initComplexType(complex, false); }
public SchemaType schemaType()
{ return _schemaType; }
public SchemaType instanceType()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).instanceType(); }
private SchemaType _schemaType;
private XmlAnySimpleType _value; // underlying value
private String _textvalue = ""; // textual value
// SIMPLE VALUE ACCESSORS BELOW -------------------------------------------
// gets raw text value
protected String compute_text(NamespaceManager nsm)
{ return _textvalue; }
protected boolean is_defaultable_ws(String v) {
try {
XmlAnySimpleType savedValue = _value;
set_text(v);
// restore the saved value
_value = savedValue;
return false;
}
catch (XmlValueOutOfRangeException e) {
return true;
}
}
protected void set_text(String s)
{
// first check against any patterns...
if (!_schemaType.matchPatternFacet(s) && _validateOnSet())
throw new XmlValueOutOfRangeException(XmlErrorCodes.DATATYPE_VALID$PATTERN_VALID,
new Object[] { "string", s, QNameHelper.readable(_schemaType) });
// save state for rollback
String original = _textvalue;
_textvalue = s;
// iterate through the types, trying to create a type
SchemaType[] members = _schemaType.getUnionConstituentTypes();
assert(members != null);
boolean pushed = false;
// boolean wasstrict = set_strict(true); // tell types to complain ferverently about errors
if (has_store())
{
NamespaceContext.push(new NamespaceContext(get_store()));
pushed = true;
}
try
{
for (boolean validate = true; validate || !_validateOnSet(); validate = false)
{
for (int i = 0; i < members.length; i++)
{
// From the point of view of the following call, "this" is a generic
// XmlAnySimpleType implementation, for which only getText can be called.
// (Note that "this" is not wrapped in the proxy object.)
try
{
XmlAnySimpleType newval = ((SchemaTypeImpl)members[i]).newValue(s, validate);
// now we need to check against (enuemration) restrictions
if (!check(newval, _schemaType))
continue;
// found one that works!
_value = newval;
return;
}
catch (XmlValueOutOfRangeException e)
{
continue;
}
catch (Exception e)
{
throw new RuntimeException("Troublesome union exception caused by unexpected " + e, e);
// assert(false) : "Unexpected " + e;
// continue;
}
}
if (!validate)
break;
}
}
finally
{
if (pushed)
NamespaceContext.pop();
// set_strict(wasstrict);
}
// doesn't match any of the members; rollback and throw
_textvalue = original;
throw new XmlValueOutOfRangeException(XmlErrorCodes.DATATYPE_VALID$UNION,
new Object[] { s, QNameHelper.readable(_schemaType) });
}
protected void set_nil()
{
_value = null;
_textvalue = null;
}
protected int get_wscanon_rule() { return SchemaType.WS_PRESERVE; }
// numerics
public float getFloatValue()
{ check_dated(); return _value == null ? 0.0f : ((SimpleValue)_value).getFloatValue(); }
public double getDoubleValue()
{ check_dated(); return _value == null ? 0.0 : ((SimpleValue)_value).getDoubleValue(); }
public BigDecimal getBigDecimalValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getBigDecimalValue(); }
public BigInteger getBigIntegerValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getBigIntegerValue(); }
public byte getByteValue()
{ check_dated(); return _value == null ? 0 : ((SimpleValue)_value).getByteValue(); }
public short getShortValue()
{ check_dated(); return _value == null ? 0 : ((SimpleValue)_value).getShortValue(); }
public int getIntValue()
{ check_dated(); return _value == null ? 0 : ((SimpleValue)_value).getIntValue(); }
public long getLongValue()
{ check_dated(); return _value == null ? 0 : ((SimpleValue)_value).getLongValue(); }
// various
public byte[] getByteArrayValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getByteArrayValue(); }
public boolean getBooleanValue()
{ check_dated(); return _value == null ? false : ((SimpleValue)_value).getBooleanValue(); }
public Calendar getCalendarValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getCalendarValue(); }
public Date getDateValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getDateValue(); }
public GDate getGDateValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getGDateValue(); }
public GDuration getGDurationValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getGDurationValue(); }
public QName getQNameValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getQNameValue(); }
public List getListValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getListValue(); }
public List xgetListValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).xgetListValue(); }
public StringEnumAbstractBase getEnumValue()
{ check_dated(); return _value == null ? null : ((SimpleValue)_value).getEnumValue(); }
public String getStringValue()
{ check_dated(); return _value == null ? null : _value.getStringValue(); }
/**
* Returns true if the space of canonical lexical forms
* of the first (source) type overlaps with the full lexical space
* of the second (target) type. Both types must be primitives.
*/
static boolean lexical_overlap(int source, int target)
{
// types are the same
if (source == target)
return true;
// one of the types has the full lexical space
if (source == SchemaType.BTC_ANY_SIMPLE ||
target == SchemaType.BTC_ANY_SIMPLE ||
source == SchemaType.BTC_STRING ||
target == SchemaType.BTC_STRING ||
source == SchemaType.BTC_ANY_URI ||
target == SchemaType.BTC_ANY_URI)
return true;
switch (source)
{
case SchemaType.BTC_BOOLEAN: switch(target)
{
case SchemaType.BTC_QNAME: // "true" is valid NcName and therefore QName
case SchemaType.BTC_NOTATION: // "true" is valid NCName
return true;
default:
return false;
}
case SchemaType.BTC_BASE_64_BINARY: switch(target)
{
case SchemaType.BTC_BOOLEAN: // "0" is valid boolean
case SchemaType.BTC_HEX_BINARY: // "0" is valid hex
case SchemaType.BTC_QNAME: // "a" is valid NcName and therefore QName
case SchemaType.BTC_NOTATION: // "a" is valid NcName
case SchemaType.BTC_FLOAT: // "0" is valid float
case SchemaType.BTC_DOUBLE: // "0" is valid double
case SchemaType.BTC_DECIMAL: // "0" is valid decimal
case SchemaType.BTC_DURATION: // "P1Y2M3DT10H30M" is both b64 and duration
case SchemaType.BTC_G_YEAR: // "1999" is valid year
return true;
default:
return false; // "-" and ":" cannot come from b64
}
case SchemaType.BTC_HEX_BINARY: switch(target)
{
case SchemaType.BTC_BOOLEAN: // "0" is valid boolean
case SchemaType.BTC_BASE_64_BINARY: // "0" is valid b64
case SchemaType.BTC_QNAME: // "A" is valid NcName and therefore QName
case SchemaType.BTC_NOTATION: // "A" is valid NcName
case SchemaType.BTC_FLOAT: // "0" is valid float
case SchemaType.BTC_DOUBLE: // "0" is valid double
case SchemaType.BTC_DECIMAL: // "0" is valid decimal
case SchemaType.BTC_G_YEAR: // "1999" is valid year
return true;
default:
return false; // "-" and ":" cannot come from b64
}
case SchemaType.BTC_QNAME:
case SchemaType.BTC_NOTATION: switch(target)
{
case SchemaType.BTC_BOOLEAN: // "true" is valid boolean
case SchemaType.BTC_BASE_64_BINARY: // "a" is valid b64
case SchemaType.BTC_HEX_BINARY: // "a" is valid hex
case SchemaType.BTC_QNAME: // "A" is valid NcName and therefore QName
case SchemaType.BTC_NOTATION: // "A" is valid NcName and therefore QName
case SchemaType.BTC_DURATION: // "P1Y2M3DT10H30M" is both NcName and duration
return true;
default:
return false;
}
case SchemaType.BTC_FLOAT:
case SchemaType.BTC_DOUBLE:
case SchemaType.BTC_DECIMAL:
case SchemaType.BTC_G_YEAR: switch(target)
{
case SchemaType.BTC_BASE_64_BINARY: // "0" is valid b64
case SchemaType.BTC_HEX_BINARY: // "0" is valid hex
case SchemaType.BTC_FLOAT: // "0" is valid float
case SchemaType.BTC_DOUBLE: // "0" is valid double
case SchemaType.BTC_DECIMAL: // "0" is valid decimal
case SchemaType.BTC_G_YEAR: // "1999" is valid year
return true;
default:
return false;
}
case SchemaType.BTC_DURATION: switch(target)
{
case SchemaType.BTC_QNAME:
case SchemaType.BTC_NOTATION:
case SchemaType.BTC_BASE_64_BINARY:
return true;
default:
return false;
}
case SchemaType.BTC_DATE_TIME:
case SchemaType.BTC_TIME:
case SchemaType.BTC_DATE:
case SchemaType.BTC_G_YEAR_MONTH:
case SchemaType.BTC_G_MONTH_DAY:
case SchemaType.BTC_G_DAY:
case SchemaType.BTC_G_MONTH:
default:
return false;
}
}
/**
* True if the given schema type's logical type is a match for
* the given category of java concepts.
*/
private static final int JAVA_NUMBER = SchemaType.BTC_LAST_BUILTIN + 1;
private static final int JAVA_DATE = SchemaType.BTC_LAST_BUILTIN + 2;
private static final int JAVA_CALENDAR = SchemaType.BTC_LAST_BUILTIN + 3;
private static final int JAVA_BYTEARRAY = SchemaType.BTC_LAST_BUILTIN + 4;
private static final int JAVA_LIST = SchemaType.BTC_LAST_BUILTIN + 5;
private static boolean logical_overlap(SchemaType type, int javacode)
{
// non-union types because it's being applied on irreducible union members!
assert(type.getSimpleVariety() != SchemaType.UNION);
if (javacode <= SchemaType.BTC_LAST_BUILTIN)
{
if (type.getSimpleVariety() != SchemaType.ATOMIC)
return false;
return (type.getPrimitiveType().getBuiltinTypeCode() == javacode);
}
switch (javacode)
{
case JAVA_NUMBER:
{
if (type.getSimpleVariety() != SchemaType.ATOMIC)
return false;
switch (type.getPrimitiveType().getBuiltinTypeCode())
{
case SchemaType.BTC_FLOAT:
case SchemaType.BTC_DOUBLE:
case SchemaType.BTC_DECIMAL:
case SchemaType.BTC_G_YEAR:
case SchemaType.BTC_G_MONTH:
case SchemaType.BTC_G_DAY:
return true;
}
return false;
}
case JAVA_DATE:
{
if (type.getSimpleVariety() != SchemaType.ATOMIC)
return false;
switch (type.getPrimitiveType().getBuiltinTypeCode())
{
case SchemaType.BTC_DATE_TIME:
case SchemaType.BTC_DATE:
return true;
}
return false;
}
case JAVA_CALENDAR:
{
if (type.getSimpleVariety() != SchemaType.ATOMIC)
return false;
switch (type.getPrimitiveType().getBuiltinTypeCode())
{
case SchemaType.BTC_DATE_TIME:
case SchemaType.BTC_DATE:
case SchemaType.BTC_TIME:
case SchemaType.BTC_G_YEAR_MONTH:
case SchemaType.BTC_G_MONTH_DAY:
case SchemaType.BTC_G_YEAR:
case SchemaType.BTC_G_MONTH:
case SchemaType.BTC_G_DAY:
return true;
}
return false;
}
case JAVA_BYTEARRAY:
{
if (type.getSimpleVariety() != SchemaType.ATOMIC)
return false;
switch (type.getPrimitiveType().getBuiltinTypeCode())
{
case SchemaType.BTC_BASE_64_BINARY:
case SchemaType.BTC_HEX_BINARY:
return true;
}
return false;
}
case JAVA_LIST:
{
return (type.getSimpleVariety() == SchemaType.LIST);
}
}
assert(false) : "missing case";
return false;
}
/**
* Grabs a chained value of type st, creating and attaching
* one if not present.
*/
private void set_primitive(int typecode, Object val)
{
SchemaType[] members = _schemaType.getUnionConstituentTypes();
assert(members != null);
boolean pushed = false;
if (has_store())
{
NamespaceContext.push(new NamespaceContext(get_store()));
pushed = true;
}
try
{
for (boolean validate = true; validate || !_validateOnSet(); validate = false)
{
outer: for (int i = 0; i < members.length; i++)
{
// candidates must be a logical match for the desired typecode
if (logical_overlap(members[i], typecode))
{
XmlAnySimpleType newval;
try
{
newval = ((SchemaTypeImpl) members[i]).newValue(val, validate);
}
catch (XmlValueOutOfRangeException e)
{
// doesn't match this type even though logical categories
// line up (probably because of restriciton); try the next type.
continue outer;
}
catch (Exception e)
{
assert(false) : "Unexpected " + e;
continue outer;
}
/* TODO: rethink this - disabling for now.
// OK, now we've got a newval... We have to verify
// that lexically it doesn't overlap with previous types
String newvaltext = null;
inner: for (int j = 0; j < i; j++)
{
if (members[j].getSimpleVariety() == SchemaType.LIST ||
lexical_overlap(members[j].getPrimitiveType().getBuiltinTypeCode(),
newval.schemaType().getPrimitiveType().getBuiltinTypeCode()))
{
// there is a preceding type that may lexically overlap with ours...
// if it lexically contains the string representation of our new
// proposed value, then it's impossible for the union to have our
// logical value (because it would have been masked) and throw an
// error.
if (newvaltext == null)
newvaltext = newval.stringValue();
try
{
// discard return value
members[i].newValue(newvaltext);
// oh bad, we succeeded. Our instance lexically looks like a
// previous type, and this isn't a valid value. Keep on hunting.
continue outer;
}
catch (XmlValueOutOfRangeException e)
{
// this is good: this error means that our value doesn't look like
// the other type.
continue inner;
}
}
}
*/
// No lexical masking: we're OK
_value = newval;
_textvalue = _value.stringValue();
return;
}
}
if (!validate)
break;
}
}
finally
{
if (pushed)
NamespaceContext.pop();
}
// doesn't match any of the members; throw
throw new XmlValueOutOfRangeException(XmlErrorCodes.DATATYPE_VALID$UNION,
new Object[] { val.toString(), QNameHelper.readable(_schemaType) });
}
// here are the setters
protected void set_boolean(boolean v)
{ set_primitive(SchemaType.BTC_BOOLEAN, new Boolean(v)); }
protected void set_byte(byte v)
{ set_primitive(JAVA_NUMBER, new Byte(v)); }
protected void set_short(short v)
{ set_primitive(JAVA_NUMBER, new Short(v)); }
protected void set_int(int v)
{ set_primitive(JAVA_NUMBER, new Integer(v)); }
protected void set_long(long v)
{ set_primitive(JAVA_NUMBER, new Long(v)); }
protected void set_float(float v)
{ set_primitive(JAVA_NUMBER, new Float(v)); }
protected void set_double(double v)
{ set_primitive(JAVA_NUMBER, new Double(v)); }
protected void set_ByteArray(byte[] b)
{ set_primitive(JAVA_BYTEARRAY, b); }
protected void set_hex(byte[] b)
{ set_primitive(JAVA_BYTEARRAY, b); }
protected void set_b64(byte[] b)
{ set_primitive(JAVA_BYTEARRAY, b); }
protected void set_BigInteger(BigInteger v)
{ set_primitive(JAVA_NUMBER, v); }
protected void set_BigDecimal(BigDecimal v)
{ set_primitive(JAVA_NUMBER, v); }
protected void set_QName(QName v)
{ set_primitive(SchemaType.BTC_QNAME, v); }
protected void set_Calendar(Calendar c)
{ set_primitive(JAVA_CALENDAR, c); }
protected void set_Date(Date d)
{ set_primitive(JAVA_DATE, d); }
protected void set_GDate(GDateSpecification d)
{
int btc = d.getBuiltinTypeCode();
if (btc <= 0)
throw new XmlValueOutOfRangeException();
set_primitive(btc, d);
}
protected void set_GDuration(GDurationSpecification d)
{ set_primitive(SchemaType.BTC_DURATION, d); }
protected void set_enum(StringEnumAbstractBase e)
{ set_primitive(SchemaType.BTC_STRING, e); }
protected void set_list(List v)
{ set_primitive(JAVA_LIST, v); }
protected void set_xmlfloat(XmlObject v)
{ set_primitive(SchemaType.BTC_FLOAT, v); }
protected void set_xmldouble(XmlObject v)
{ set_primitive(SchemaType.BTC_DOUBLE, v); }
protected void set_xmldecimal(XmlObject v)
{ set_primitive(SchemaType.BTC_DECIMAL, v); }
protected void set_xmlduration(XmlObject v)
{ set_primitive(SchemaType.BTC_DURATION, v); }
protected void set_xmldatetime(XmlObject v)
{ set_primitive(SchemaType.BTC_DATE_TIME, v); }
protected void set_xmltime(XmlObject v)
{ set_primitive(SchemaType.BTC_TIME, v); }
protected void set_xmldate(XmlObject v)
{ set_primitive(SchemaType.BTC_DATE, v); }
protected void set_xmlgyearmonth(XmlObject v)
{ set_primitive(SchemaType.BTC_G_YEAR_MONTH, v); }
protected void set_xmlgyear(XmlObject v)
{ set_primitive(SchemaType.BTC_G_YEAR, v); }
protected void set_xmlgmonthday(XmlObject v)
{ set_primitive(SchemaType.BTC_G_MONTH_DAY, v); }
protected void set_xmlgday(XmlObject v)
{ set_primitive(SchemaType.BTC_G_DAY, v); }
protected void set_xmlgmonth(XmlObject v)
{ set_primitive(SchemaType.BTC_G_MONTH, v); }
private static boolean check(XmlObject v, SchemaType sType)
{
XmlObject[] vals = sType.getEnumerationValues();
if (vals != null)
{
for (int i = 0; i < vals.length; i++)
{
if (vals[i].valueEquals(v))
return true;
}
return false;
}
return true;
}
protected boolean equal_to(XmlObject xmlobj)
{
return _value.valueEquals(xmlobj);
}
protected int value_hash_code()
{
return _value.hashCode();
}
protected void validate_simpleval(String lexical, ValidationContext ctx)
{
try
{
check_dated();
}
catch (Exception e)
{
ctx.invalid(XmlErrorCodes.UNION, new Object[] { "'" + lexical + "' does not match any of the member types for " + QNameHelper.readable(schemaType()) });
return;
}
if (_value == null)
{
ctx.invalid(XmlErrorCodes.UNION, new Object[] { "'" + lexical + "' does not match any of the member types for " + QNameHelper.readable(schemaType()) });
return;
}
((XmlObjectBase)_value).validate_simpleval(lexical, ctx);
}
}