| /* 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 org.apache.xmlbeans.*; |
| import org.apache.xmlbeans.impl.common.QNameHelper; |
| import org.apache.xmlbeans.impl.common.ValidationContext; |
| import org.apache.xmlbeans.impl.schema.SchemaTypeImpl; |
| |
| import javax.xml.namespace.QName; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.List; |
| |
| |
| /** |
| * 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 final 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 (SchemaType member : members) { |
| // 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) member).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 ignored) { |
| } 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 && ((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(); |
| } |
| |
| /** |
| * 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) { |
| for (SchemaType member : members) { |
| // candidates must be a logical match for the desired typecode |
| if (logical_overlap(member, typecode)) { |
| XmlAnySimpleType newval; |
| |
| try { |
| newval = ((SchemaTypeImpl) member).newValue(val, validate); |
| } catch (XmlValueOutOfRangeException ignored) { |
| // doesn't match this type even though logical categories |
| // line up (probably because of restriciton); try the next type. |
| continue; |
| } catch (Exception e) { |
| assert (false) : "Unexpected " + e; |
| continue; |
| } |
| |
| /* 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.getStringValue(); |
| 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, v); |
| } |
| |
| protected void set_byte(byte v) { |
| set_primitive(JAVA_NUMBER, v); |
| } |
| |
| protected void set_short(short v) { |
| set_primitive(JAVA_NUMBER, v); |
| } |
| |
| protected void set_int(int v) { |
| set_primitive(JAVA_NUMBER, v); |
| } |
| |
| protected void set_long(long v) { |
| set_primitive(JAVA_NUMBER, v); |
| } |
| |
| protected void set_float(float v) { |
| set_primitive(JAVA_NUMBER, v); |
| } |
| |
| protected void set_double(double v) { |
| set_primitive(JAVA_NUMBER, 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 (XmlObject val : vals) { |
| if (val.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); |
| } |
| |
| |
| } |
| |