All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.s9api.XdmAtomicValue Maven / Gradle / Ivy

There is a newer version: 12.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2022 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.s9api;

import net.sf.saxon.expr.sort.AtomicMatchKey;
import net.sf.saxon.str.StringView;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.transpile.CSharpModifiers;
import net.sf.saxon.type.*;
import net.sf.saxon.value.*;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.time.*;

/**
 * The class XdmAtomicValue represents an item in an XPath 2.0 sequence that is an atomic value.
 * The value may belong to any of the 19 primitive types defined in XML Schema, or to a type
 * derived from these primitive types, or the XPath 2.0 type xs:untypedAtomic. The type may
 * be either a built-in type or a user-defined type.
 * 

An XdmAtomicValue is immutable.

*/ @CSharpModifiers(code = {"internal"}) public class XdmAtomicValue extends XdmItem { /** * Create an XdmAtomicValue that wraps a supplied AtomicValue. This * method is primarily for internal use, though it is also available to applications * that manipulate data using lower-level Saxon interfaces. * * @param value the value to be wrapped. */ public XdmAtomicValue(AtomicValue value) { super(value); } /** * Create an xs:boolean atomic value * * @param value the boolean value, true or false */ public XdmAtomicValue(boolean value) { this(BooleanValue.get(value)); } /** * Create an xs:long atomic value * * @param value the xs:integer value, as a long */ public XdmAtomicValue(long value) { this(Int64Value.makeDerived(value, BuiltInAtomicType.LONG)); } /** * Create an xs:int atomic value * * @param value the xs:integer value, as a long */ public XdmAtomicValue(int value) { this(Int64Value.makeDerived(value, BuiltInAtomicType.INT)); } /** * Create an xs:short atomic value * * @param value the xs:integer value, as a long */ public XdmAtomicValue(short value) { this(Int64Value.makeDerived(value, BuiltInAtomicType.SHORT)); } /** * Create an xs:short atomic value * * @param value the xs:integer value, as a long */ public XdmAtomicValue(byte value) { this(Int64Value.makeDerived(value, BuiltInAtomicType.BYTE)); } /** * Create an xs:decimal atomic value * * @param value the xs:decimal value, as a BigDecimal */ public XdmAtomicValue(BigDecimal value) { this(new BigDecimalValue(value)); } /** * Create an xs:double atomic value * * @param value the xs:double value, as a double */ public XdmAtomicValue(double value) { this(new DoubleValue(value)); } /** * Create an xs:float atomic value * * @param value the xs:float value, as a float */ public XdmAtomicValue(float value) { this(new FloatValue(value)); } /** * Create an xs:string atomic value * * @param value the xs:string value, as a string */ public XdmAtomicValue(String value) { this(new StringValue(value)); } /** * Create an xs:anyURI atomic value * * @param value the xs:anyURI value, as a URI */ public XdmAtomicValue(URI value) { this(new AnyURIValue((value.toString()))); } /** * Create an xs:QName atomic value * * @param value the xs:QName value, as a QName */ public XdmAtomicValue(QName value) { this(new QNameValue(value.getStructuredQName(), BuiltInAtomicType.QNAME)); } /** * Create an xs:dateTime atomic value from a {@link Instant}. The resulting value * will always have a timezone component. * * @param value the xs:dateTime value in the form of a {@link Instant} * @since 10.0 */ public XdmAtomicValue(Instant value) { this(DateTimeValue.fromJavaInstant(value)); } /** * Create an xs:dateTime atomic value from a {@link ZonedDateTime}. The resulting value * will always have a timezone component. Note that the timezone offset (e.g. -05:00) is retained, * but the civil time zone identity (e.g. America/New_York) is lost. * * @param value the xs:dateTime value in the form of a {@link ZonedDateTime} * @since 10.0 */ public XdmAtomicValue(ZonedDateTime value) { this(DateTimeValue.fromZonedDateTime(value)); } /** * Create an xs:dateTime atomic value from a {@link OffsetDateTime}. The resulting value * will always have a timezone component. The timezone offset (e.g. -05:00) is retained. * * @param value the xs:dateTime value in the form of a {@link OffsetDateTime} * @since 10.0 */ public XdmAtomicValue(OffsetDateTime value) { this(DateTimeValue.fromOffsetDateTime(value)); } /** * Create an xs:dateTime atomic value from a {@link LocalDateTime}. The resulting value * will have no timezone component. * * @param value the xs:dateTime value in the form of a {@link LocalDateTime} * @since 10.0 */ public XdmAtomicValue(LocalDateTime value) { this(DateTimeValue.fromLocalDateTime(value)); } /** * Create an xs:date atomic value from a {@link LocalDate}. The resulting value * will have no timezone component. * * @param value the xs:dateTime value in the form of a {@link LocalDate} * @since 10.0 */ public XdmAtomicValue(LocalDate value) { this(new DateValue(value)); } /** * Construct an atomic value given its lexical representation and the name of the required * built-in atomic type. *

This method cannot be used to construct values that are namespace-sensitive (QNames and Notations)

* * @param lexicalForm the value in the lexical space of the target data type. More strictly, the input * value before the actions of the whitespace facet for the target data type are applied. * @param type the required atomic type. This must either be one of the built-in * atomic types defined in XML Schema, or a user-defined type whose definition appears * in a schema that is known to the Processor. It must not be an abstract type. * @throws SaxonApiException if the type is unknown, or is not atomic, or is namespace-sensitive; * or if the value supplied in lexicalForm is not in the lexical space of the specified atomic * type. */ public XdmAtomicValue(String lexicalForm, ItemType type) throws SaxonApiException { super(fromLexicalForm(lexicalForm,type)); } private static AtomicValue fromLexicalForm(String lexicalForm, ItemType type) throws SaxonApiException { net.sf.saxon.type.ItemType it = type.getUnderlyingItemType(); if (!it.isPlainType()) { throw new SaxonApiException("Requested type is not atomic"); } if (((AtomicType) it).isAbstract()) { throw new SaxonApiException("Requested type is an abstract type"); } if (((AtomicType) it).isNamespaceSensitive()) { throw new SaxonApiException("Requested type is namespace-sensitive"); } try { StringConverter converter = ((AtomicType) it).getStringConverter(type.getConversionRules()); return converter.convertString(StringView.of(lexicalForm).tidy()).asAtomic(); } catch (ValidationException e) { throw new SaxonApiException(e); } } /** * Attempt conversion from an arbitrary Java object * @param value the value to convert from. This must either be an instance of {@link AtomicValue} * or {@link QName}, * or one of the basic Java types Boolean, Integer, Long, Short, Character, Byte, * String, Double, Float, BigDecimal, BigInteger, URI, ZonedDateTime, * LocalDateTime, or LocalDate * @return the result of the conversion if successful * @throws IllegalArgumentException if conversion from this kind of Object is not supported * @since 10.0 (extended in 10.0 to handle additional data types ZonedDateTime, LocalDateTime, LocalDate) */ public static XdmAtomicValue makeAtomicValue(Object value) { if (value instanceof AtomicValue) { return new XdmAtomicValue((AtomicValue)value); } else if (value instanceof Boolean) { return new XdmAtomicValue((Boolean)value); } else if (value instanceof Integer) { return new XdmAtomicValue((Integer) value); } else if (value instanceof Long) { return new XdmAtomicValue((Long) value); } else if (value instanceof Short) { return new XdmAtomicValue((Short) value); } else if (value instanceof Character) { return new XdmAtomicValue((Character) value); } else if (value instanceof Byte) { return new XdmAtomicValue((Byte) value); } else if (value instanceof String) { return new XdmAtomicValue((String) value); } else if (value instanceof Double) { return new XdmAtomicValue((Double) value); } else if (value instanceof Float) { return new XdmAtomicValue((Float) value); } else if (value instanceof BigDecimal) { return new XdmAtomicValue((BigDecimal) value); } else if (value instanceof BigInteger) { return new XdmAtomicValue(IntegerValue.makeIntegerValue((BigInteger) value)); } else if (value instanceof URI) { return new XdmAtomicValue((URI) value); } else if (value instanceof QName) { return new XdmAtomicValue((QName) value); } else if (value instanceof ZonedDateTime) { return new XdmAtomicValue((ZonedDateTime) value); } else if (value instanceof LocalDateTime) { return new XdmAtomicValue((LocalDateTime) value); } else if (value instanceof LocalDate) { return new XdmAtomicValue((LocalDate) value); } else if (value instanceof XdmAtomicValue) { return (XdmAtomicValue)value; } else { throw new IllegalArgumentException(value.toString()); } } /** * Get the underlying implementation object representing the value. This method allows * access to lower-level Saxon functionality, including classes and methods that offer * no guarantee of stability across releases. * * @return the underlying implementation object representing the value * @since 9.8 (previously inherited from XdmValue which returns a Sequence) */ @Override public AtomicValue getUnderlyingValue() { return (AtomicValue)super.getUnderlyingValue(); } /** * Get the result of converting the atomic value to a string. This has the same * effect as the XPath string() function. */ public String toString() { return getStringValue(); } /** * Get the primitive type of this atomic value, as a QName. The primitive types for this purpose are * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration, * and xs:untypedAtomic. For external objects, the result is xs:anyAtomicType. * * @return a QName naming the primitive type of this atomic value. This will always be an atomic type. */ /*@NotNull*/ public QName getPrimitiveTypeName() { AtomicValue value = getUnderlyingValue(); BuiltInAtomicType type = value.getPrimitiveType(); return new QName(type.getStructuredQName()); } /** * Get the type of this atomic value, as a QName. * * @return a QName naming the type of this atomic value. This will always be an atomic type. */ public QName getTypeName() { AtomicValue value = getUnderlyingValue(); AtomicType type = value.getItemType(); return new QName(type.getStructuredQName()); } /** * Get the value as a Java object of the nearest equivalent type. *

The result type is as follows:

* * * * * * * * * * * * * * * *
XPath types and corresponding Java classes
XPath type Java class
xs:string String
xs:integer java.math.BigInteger
xs:decimal java.math.BigDecimal
xs:double Double
xs:float Float
xs:boolean Boolean
xs:QName QName
xs:anyURI String
xs:dateTime ZonedDateTime or LocalDateTime (depending whether a timezone is present)
xs:date LocalDate (dropping any timezone)
xs:untypedAtomicString
Other types currently String, but this may change in the future
* * @return the value, converted to a Java object of a suitable type * @since 10.0 (added additional return types for xs:dateTime and xs:date) */ @SuppressWarnings({"AutoBoxing"}) public Object getValue() { AtomicValue av = getUnderlyingValue(); if (av instanceof StringValue) { return av.getUnicodeStringValue(); } else if (av instanceof IntegerValue) { return ((IntegerValue) av).asBigInteger(); } else if (av instanceof DoubleValue) { return ((DoubleValue) av).getDoubleValue(); } else if (av instanceof FloatValue) { return ((FloatValue) av).getFloatValue(); } else if (av instanceof BooleanValue) { return ((BooleanValue) av).getBooleanValue(); } else if (av instanceof BigDecimalValue) { return ((BigDecimalValue) av).getDecimalValue(); } else if (av instanceof DateTimeValue) { if (((DateTimeValue) av).hasTimezone()) { return ((DateTimeValue) av).toZonedDateTime(); } else { return ((DateTimeValue) av).toLocalDateTime(); } } else if (av instanceof DateValue) { return ((DateValue)av).toLocalDate(); } else if (av instanceof QNameValue) { QNameValue q = (QNameValue) av; return new QName(q.getPrefix(), q.getNamespaceURI(), q.getLocalName()); } else { return av.getUnicodeStringValue(); } } /** * Get the value converted to a boolean using the XPath casting rules * * @return the result of converting to a boolean (Note: this is not the same as the * effective boolean value). * @throws SaxonApiException if the value cannot be cast to a boolean */ public boolean getBooleanValue() throws SaxonApiException { AtomicValue av = getUnderlyingValue(); if (av instanceof BooleanValue) { return ((BooleanValue) av).getBooleanValue(); } else if (av instanceof NumericValue) { return !av.isNaN() && ((NumericValue) av).signum() != 0; } else if (av instanceof StringValue) { String s = Whitespace.trim(av.getUnicodeStringValue().tidy()).toString(); return "1".equals(s) || "true".equals(s); } else { throw new SaxonApiException("Cannot cast item to a boolean"); } } /** * Get the value converted to an integer using the XPath casting rules * * @return the result of converting to an integer * @throws SaxonApiException if the value cannot be cast to an integer */ public long getLongValue() throws SaxonApiException { AtomicValue av = getUnderlyingValue(); if (av instanceof BooleanValue) { return ((BooleanValue) av).getBooleanValue() ? 0L : 1L; } else if (av instanceof NumericValue) { try { return ((NumericValue) av).longValue(); } catch (XPathException e) { throw new SaxonApiException("Cannot cast item to an integer"); } } else if (av instanceof StringValue) { StringToDouble converter = StringToDouble.getInstance(); return (long) converter.stringToNumber(av.getUnicodeStringValue().tidy()); } else { throw new SaxonApiException("Cannot cast item to an integer"); } } /** * Get the value converted to a double using the XPath casting rules. *

If the value is a string, the XSD 1.1 rules are used, which means that the string * "+INF" is recognised.

* * @return the result of converting to a double * @throws SaxonApiException if the value cannot be cast to a double */ public double getDoubleValue() throws SaxonApiException { AtomicValue av = getUnderlyingValue(); if (av instanceof BooleanValue) { return ((BooleanValue) av).getBooleanValue() ? 0.0 : 1.0; } else if (av instanceof NumericValue) { return ((NumericValue) av).getDoubleValue(); } else if (av instanceof StringValue) { try { StringToDouble converter = StringToDouble11.getInstance(); return converter.stringToNumber(av.getUnicodeStringValue().tidy()); } catch (NumberFormatException e) { throw new SaxonApiException(e.getMessage()); } } else { throw new SaxonApiException("Cannot cast item to a double"); } } /** * Get the value converted to a decimal using the XPath casting rules * * @return the result of converting to a decimal * @throws SaxonApiException if the value cannot be cast to a double */ public BigDecimal getDecimalValue() throws SaxonApiException { AtomicValue av = getUnderlyingValue(); if (av instanceof BooleanValue) { return ((BooleanValue) av).getBooleanValue() ? BigDecimal.ZERO : BigDecimal.ONE; } else if (av instanceof NumericValue) { try { return ((NumericValue) av).getDecimalValue(); } catch (XPathException e) { throw new SaxonApiException("Cannot cast item to a decimal"); } } else if (av instanceof StringValue) { return new BigDecimal(av.getStringValue()); } else { throw new SaxonApiException("Cannot cast item to a decimal"); } } /** * For an XdmAtomicValue representing an xs:QName or xs:NOTATION value, * get the value as an instance of QName * @return the corresponding QName if the value is an xs:QName or xs:NOTATION, * or null otherwise */ public QName getQNameValue() { AtomicValue av = getUnderlyingValue(); if (av instanceof QualifiedNameValue) { return new QName(((QualifiedNameValue)av).getStructuredQName()); } else { return null; } } /** * For an XdmAtomicValue representing an xs:dateTime value including timezone, * get the value as an instance of {@link Instant} * * @return the corresponding Instant if the value is an xs:dateTime including * a timezone; otherwise null * @since 10.0 */ public Instant getInstant() { AtomicValue av = getUnderlyingValue(); if (av instanceof DateTimeValue && ((DateTimeValue)av).hasTimezone()) { return ((DateTimeValue) av).toJavaInstant(); } else { return null; } } /** * For an XdmAtomicValue representing an xs:dateTime value including timezone, * get the value as an instance of {@link ZonedDateTime} * * @return the corresponding ZonedDateTime if the value is an xs:dateTime including * a timezone; otherwise null * @since 10.0 */ public ZonedDateTime getZonedDateTime() { AtomicValue av = getUnderlyingValue(); if (av instanceof DateTimeValue && ((DateTimeValue) av).hasTimezone()) { return ((DateTimeValue) av).toZonedDateTime(); } else { return null; } } /** * For an XdmAtomicValue representing an xs:dateTime value including timezone, * get the value as an instance of {@link OffsetDateTime} * * @return the corresponding OffsetDateTime if the value is an xs:dateTime including * a timezone; otherwise null * @since 10.0 */ public OffsetDateTime getOffsetDateTime() { AtomicValue av = getUnderlyingValue(); if (av instanceof DateTimeValue && ((DateTimeValue) av).hasTimezone()) { return ((DateTimeValue) av).toOffsetDateTime(); } else { return null; } } /** * For an XdmAtomicValue representing an xs:dateTime value, * get the value as an instance of {@link LocalDateTime}. Any timezone * information in the value is discarded * * @return the corresponding LocalDateTime if the value is an xs:dateTime; otherwise null * @since 10.0 */ public LocalDateTime getLocalDateTime() { AtomicValue av = getUnderlyingValue(); if (av instanceof DateTimeValue) { return ((DateTimeValue) av).toLocalDateTime(); } else { return null; } } /** * For an XdmAtomicValue representing an xs:date value, * get the value as an instance of {@link LocalDate}. Any timezone * information in the value is discarded * * @return the corresponding LocalDate if the value is an xs:date; otherwise null * @since 10.0 */ public LocalDate getLocalDate() { AtomicValue av = getUnderlyingValue(); if (av instanceof DateValue) { return ((DateValue) av).toLocalDate(); } else { return null; } } /** * Compare values for equality. * Two atomic values are considered equal if they are equal according to the * rules of the op:is-same-key() operation, used when comparing keys in maps * @param other the value to be compared * @return true if the values are deemed equal */ public boolean equals(Object other) { if (other instanceof XdmAtomicValue) { AtomicMatchKey a = getUnderlyingValue().asMapKey(); AtomicMatchKey b = ((XdmAtomicValue)other).getUnderlyingValue().asMapKey(); return a.equals(b); } else { return false; } } /** * Get a hashcode that reflects the rules for equality matching * @return a suitable hashcode */ @Override public int hashCode() { return getUnderlyingValue().asMapKey().hashCode(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy