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

net.sf.saxon.lib.ConversionRules 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.lib;

import net.sf.saxon.expr.sort.LFUCache;
import net.sf.saxon.om.NotationSet;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.type.*;
import net.sf.saxon.value.StringToDouble11;

/**
 * This class defines a set of rules for converting between different atomic types. It handles the variations
 * that arise between different versions of the W3C specifications, for example the changes in Name syntax
 * between XML 1.0 and XML 1.1, the introduction of "+INF" as a permitted xs:double value in XSD 1.1, and so on.
 * 

It is possible to nominate a customized ConversionRules object at the level of the * {@link net.sf.saxon.Configuration}, either by instantiating this class and changing the properties, or * by subclassing.

* * @see net.sf.saxon.Configuration#setConversionRules(ConversionRules) * @since 9.3 */ public class ConversionRules { private StringToDouble stringToDouble = StringToDouble11.getInstance(); private NotationSet notationSet; // may be null private URIChecker uriChecker; private boolean allowYearZero = true; private TypeHierarchy typeHierarchy; // may be null private final LFUCache converterCache = new LFUCache<>(100, true); /** * Default conversion rules. Changed in Saxon 9.9 so these are the XSD 1.1 rules (year zero allowed in dates, * {@code -INF} allowed in {@code xs:double}). Modifying the default conversion rules is inadvisable, * but it could potentially be done in order to retain compatibility with earlier Saxon releases. */ public final static ConversionRules DEFAULT = new ConversionRules(); public ConversionRules() { } /** * Create a copy of these conversion rules. * * @return a copy of the rules. The cache of converters is NOT copied (because changes to the conversion rules would * invalidate the cache) */ public ConversionRules copy() { ConversionRules cr = new ConversionRules(); copyTo(cr); return cr; } /** * Create a copy of these conversion rules. * * @param cr a ConversionRules object which will be updated to hold a copy of the rules. * The cache of converters is NOT copied (because changes to the conversion rules would * invalidate the cache) */ public void copyTo(ConversionRules cr) { cr.stringToDouble = stringToDouble; cr.notationSet = notationSet; cr.uriChecker = uriChecker; cr.allowYearZero = allowYearZero; cr.typeHierarchy = typeHierarchy; cr.converterCache.clear(); } public void setTypeHierarchy(TypeHierarchy typeHierarchy) { this.typeHierarchy = typeHierarchy; } /** * Set the converter that will be used for converting strings to doubles and floats. * * @param converter the converter to be used. There are two converters in regular use: * they differ only in whether the lexical value "+INF" is recognized as a representation of * positive infinity. */ public void setStringToDoubleConverter(StringToDouble converter) { this.stringToDouble = converter; } /** * Get the converter that will be used for converting strings to doubles and floats. * * @return the converter to be used. There are two converters in regular use: * they differ only in whether the lexical value "+INF" is recognized as a representation of * positive infinity. */ public StringToDouble getStringToDoubleConverter() { return stringToDouble; } /** * Specify the set of notations that are accepted by xs:NOTATION and its subclasses. This is to * support the rule that for a notation to be valid, it must be declared in an xs:notation declaration * in the schema * * @param notations the set of notations that are recognized; or null, to indicate that all notation * names are accepted */ public void setNotationSet(/*@Nullable*/ NotationSet notations) { this.notationSet = notations; } /** * Ask whether a given notation is accepted by xs:NOTATION and its subclasses. This is to * support the rule that for a notation to be valid, it must be declared in an xs:notation declaration * in the schema * * @param uri the namespace URI of the notation * @param local the local part of the name of the notation * @return true if the notation is in the set of recognized notation names */ public boolean isDeclaredNotation(String uri, String local) { //noinspection SimplifiableIfStatement if (notationSet == null) { return true; // in the absence of a known configuration, treat all notations as valid } else { return notationSet.isDeclaredNotation(uri, local); } } /** * Set the class to be used for checking URI values. By default, no checking takes place. * * @param checker an object to be used for checking URIs; or null if any string is accepted as an anyURI value */ public void setURIChecker(URIChecker checker) { this.uriChecker = checker; } /** * Ask whether a string is a valid instance of xs:anyURI according to the rules * defined by the current URIChecker * * @param str the string to be checked against the rules for URIs * @return true if the string represents a valid xs:anyURI value */ public boolean isValidURI(String str) { return uriChecker == null || uriChecker.isValidURI(str); } /** * Say whether year zero is permitted in dates. By default it is not permitted when XSD 1.0 is in use, * but it is permitted when XSD 1.1 is used. * * @param allowed true if year zero is permitted */ public void setAllowYearZero(boolean allowed) { allowYearZero = allowed; } /** * Ask whether year zero is permitted in dates. By default it is not permitted when XSD 1.0 is in use, * but it is permitted when XSD 1.1 is used. * * @return true if year zero is permitted */ public boolean isAllowYearZero() { return allowYearZero; } /** * Get a Converter for a given pair of atomic types. These can be primitive types, * derived types, or user-defined types. The converter implements the casting rules. * * @param source the source type * @param target the target type * @return a Converter if conversion between the two types is possible; or null otherwise */ /*@Nullable*/ public Converter getConverter(AtomicType source, AtomicType target) { // Handle some common cases before looking in the cache if (source == target) { return StringConverter.IdentityConverter.INSTANCE; } else if (source == BuiltInAtomicType.STRING || source == BuiltInAtomicType.UNTYPED_ATOMIC) { return target.getStringConverter(this); } else if (target == BuiltInAtomicType.STRING) { return Converter.ToStringConverter.INSTANCE; } else if (target == BuiltInAtomicType.UNTYPED_ATOMIC) { return Converter.ToUntypedAtomicConverter.INSTANCE; } // For a lookup key, use the primitive type of the source type (always 10 bits) and the // fingerprint of the target type (20 bits) int key = (source.getPrimitiveType() << 20) | target.getFingerprint(); Converter converter = converterCache.get(key); if (converter == null) { converter = makeConverter(source, target); if (converter != null) { converterCache.put(key, converter); } else { return null; } } return converter; } /** * Create a converter that handles conversion from one primitive type to another. *

This method is intended for internal use only. The approved way to get a converter is using the * factory method {@link net.sf.saxon.lib.ConversionRules#getConverter(net.sf.saxon.type.AtomicType, net.sf.saxon.type.AtomicType)}}

* * @param sourceType the type of the value to be converted * @param targetType the type of the result of the conversion * @return the converter if one is available; or null if no conversion is possible */ /*@Nullable*/ private Converter makeConverter(AtomicType sourceType, AtomicType targetType) { if (sourceType == targetType) { return StringConverter.IdentityConverter.INSTANCE; } int tt = targetType.getFingerprint(); int tp = targetType.getPrimitiveType(); int st = sourceType.getPrimitiveType(); if ((st == StandardNames.XS_STRING || st == StandardNames.XS_UNTYPED_ATOMIC) && (tp == StandardNames.XS_STRING || tp == StandardNames.XS_UNTYPED_ATOMIC)) { return makeStringConverter(targetType); } if (!targetType.isPrimitiveType()) { @SuppressWarnings("RedundantCast") AtomicType primTarget = (AtomicType)targetType.getPrimitiveItemType(); if (sourceType == primTarget) { return new Converter.DownCastingConverter(targetType, this); } else if (st == StandardNames.XS_STRING || st == StandardNames.XS_UNTYPED_ATOMIC) { return makeStringConverter(targetType); } else { Converter stageOne = makeConverter(sourceType, primTarget); if (stageOne == null) { return null; } Converter stageTwo = new Converter.DownCastingConverter(targetType, this); return new Converter.TwoPhaseConverter(stageOne, stageTwo); } } if (st == tt) { // we are casting between subtypes of the same primitive type. if (typeHierarchy != null && typeHierarchy.isSubType(sourceType, targetType)) { return new Converter.UpCastingConverter(targetType); } @SuppressWarnings("RedundantCast") Converter upcast = new Converter.UpCastingConverter((AtomicType)sourceType.getPrimitiveItemType()); Converter downcast = new Converter.DownCastingConverter(targetType, this); return new Converter.TwoPhaseConverter(upcast, downcast); } switch (tt) { case StandardNames.XS_UNTYPED_ATOMIC: return Converter.ToUntypedAtomicConverter.INSTANCE; case StandardNames.XS_STRING: return Converter.ToStringConverter.INSTANCE; case StandardNames.XS_FLOAT: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return new StringConverter.StringToFloat(this); case StandardNames.XS_DOUBLE: case StandardNames.XS_DECIMAL: case StandardNames.XS_INTEGER: case StandardNames.XS_NUMERIC: return Converter.NumericToFloat.INSTANCE; case StandardNames.XS_BOOLEAN: return Converter.BooleanToFloat.INSTANCE; default: return null; } case StandardNames.XS_DOUBLE: case StandardNames.XS_NUMERIC: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return stringToDouble; case StandardNames.XS_FLOAT: case StandardNames.XS_DECIMAL: case StandardNames.XS_INTEGER: case StandardNames.XS_NUMERIC: return Converter.NumericToDouble.INSTANCE; case StandardNames.XS_BOOLEAN: return Converter.BooleanToDouble.INSTANCE; default: return null; } case StandardNames.XS_DECIMAL: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToDecimal.INSTANCE; case StandardNames.XS_FLOAT: return Converter.FloatToDecimal.INSTANCE; case StandardNames.XS_DOUBLE: return Converter.DoubleToDecimal.INSTANCE; case StandardNames.XS_INTEGER: return Converter.IntegerToDecimal.INSTANCE; case StandardNames.XS_NUMERIC: return Converter.NumericToDecimal.INSTANCE; case StandardNames.XS_BOOLEAN: return Converter.BooleanToDecimal.INSTANCE; default: return null; } case StandardNames.XS_INTEGER: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToInteger.INSTANCE; case StandardNames.XS_FLOAT: return Converter.FloatToInteger.INSTANCE; case StandardNames.XS_DOUBLE: return Converter.DoubleToInteger.INSTANCE; case StandardNames.XS_DECIMAL: return Converter.DecimalToInteger.INSTANCE; case StandardNames.XS_NUMERIC: return Converter.NumericToInteger.INSTANCE; case StandardNames.XS_BOOLEAN: return Converter.BooleanToInteger.INSTANCE; default: return null; } case StandardNames.XS_DURATION: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToDuration.INSTANCE; case StandardNames.XS_DAY_TIME_DURATION: case StandardNames.XS_YEAR_MONTH_DURATION: return new Converter.UpCastingConverter(BuiltInAtomicType.DURATION); default: return null; } case StandardNames.XS_YEAR_MONTH_DURATION: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToYearMonthDuration.INSTANCE; case StandardNames.XS_DURATION: case StandardNames.XS_DAY_TIME_DURATION: return Converter.DurationToYearMonthDuration.INSTANCE; default: return null; } case StandardNames.XS_DAY_TIME_DURATION: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToDayTimeDuration.INSTANCE; case StandardNames.XS_DURATION: case StandardNames.XS_YEAR_MONTH_DURATION: return Converter.DurationToDayTimeDuration.INSTANCE; default: return null; } case StandardNames.XS_DATE_TIME: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return new StringConverter.StringToDateTime(this); case StandardNames.XS_DATE: return Converter.DateToDateTime.INSTANCE; default: return null; } case StandardNames.XS_TIME: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToTime.INSTANCE; case StandardNames.XS_DATE_TIME: return Converter.DateTimeToTime.INSTANCE; default: return null; } case StandardNames.XS_DATE: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return new StringConverter.StringToDate(this); case StandardNames.XS_DATE_TIME: return Converter.DateTimeToDate.INSTANCE; default: return null; } case StandardNames.XS_G_YEAR_MONTH: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return new StringConverter.StringToGYearMonth(this); case StandardNames.XS_DATE: return Converter.TwoPhaseConverter.makeTwoPhaseConverter( BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME, BuiltInAtomicType.G_YEAR_MONTH, this); case StandardNames.XS_DATE_TIME: return Converter.DateTimeToGYearMonth.INSTANCE; default: return null; } case StandardNames.XS_G_YEAR: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return new StringConverter.StringToGYear(this); case StandardNames.XS_DATE: return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME, BuiltInAtomicType.G_YEAR, this); case StandardNames.XS_DATE_TIME: return Converter.DateTimeToGYear.INSTANCE; default: return null; } case StandardNames.XS_G_MONTH_DAY: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToGMonthDay.INSTANCE; case StandardNames.XS_DATE: return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME, BuiltInAtomicType.G_MONTH_DAY, this); case StandardNames.XS_DATE_TIME: return Converter.DateTimeToGMonthDay.INSTANCE; default: return null; } case StandardNames.XS_G_DAY: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToGDay.INSTANCE; case StandardNames.XS_DATE: return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME, BuiltInAtomicType.G_DAY, this); case StandardNames.XS_DATE_TIME: return Converter.DateTimeToGDay.INSTANCE; default: return null; } case StandardNames.XS_G_MONTH: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToGMonth.INSTANCE; case StandardNames.XS_DATE: return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME, BuiltInAtomicType.G_MONTH, this); case StandardNames.XS_DATE_TIME: return Converter.DateTimeToGMonth.INSTANCE; default: return null; } case StandardNames.XS_BOOLEAN: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToBoolean.INSTANCE; case StandardNames.XS_FLOAT: case StandardNames.XS_DOUBLE: case StandardNames.XS_DECIMAL: case StandardNames.XS_INTEGER: case StandardNames.XS_NUMERIC: return Converter.NumericToBoolean.INSTANCE; default: return null; } case StandardNames.XS_BASE64_BINARY: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToBase64Binary.INSTANCE; case StandardNames.XS_HEX_BINARY: return Converter.HexBinaryToBase64Binary.INSTANCE; default: return null; } case StandardNames.XS_HEX_BINARY: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return StringConverter.StringToHexBinary.INSTANCE; case StandardNames.XS_BASE64_BINARY: return Converter.Base64BinaryToHexBinary.INSTANCE; default: return null; } case StandardNames.XS_ANY_URI: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return new StringConverter.StringToAnyURI(this); default: return null; } case StandardNames.XS_QNAME: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return new StringConverter.StringToQName(this); case StandardNames.XS_NOTATION: return Converter.NotationToQName.INSTANCE; default: return null; } case StandardNames.XS_NOTATION: switch (st) { case StandardNames.XS_UNTYPED_ATOMIC: case StandardNames.XS_STRING: return new StringConverter.StringToNotation(this); case StandardNames.XS_QNAME: return Converter.QNameToNotation.INSTANCE; default: return null; } case StandardNames.XS_ANY_ATOMIC_TYPE: return StringConverter.IdentityConverter.INSTANCE; default: throw new IllegalArgumentException("Unknown primitive type " + tt); } } /** * Static factory method to get a StringConverter for a specific target type * * @param targetType the target type of the conversion * @return a StringConverter that can be used to convert strings to the target type, or to * validate strings against the target type */ /*@NotNull*/ public StringConverter makeStringConverter(/*@NotNull*/ final AtomicType targetType) { int tt = targetType.getPrimitiveType(); if (targetType.isBuiltInType()) { if (tt == StandardNames.XS_STRING) { switch (targetType.getFingerprint()) { case StandardNames.XS_STRING: return StringConverter.StringToString.INSTANCE; case StandardNames.XS_NORMALIZED_STRING: return StringConverter.StringToNormalizedString.INSTANCE; case StandardNames.XS_TOKEN: return StringConverter.StringToToken.INSTANCE; case StandardNames.XS_LANGUAGE: return StringConverter.StringToLanguage.INSTANCE; case StandardNames.XS_NAME: return StringConverter.StringToName.INSTANCE; case StandardNames.XS_NCNAME: return StringConverter.StringToNCName.TO_NCNAME; case StandardNames.XS_ID: return StringConverter.StringToNCName.TO_ID; case StandardNames.XS_IDREF: return StringConverter.StringToNCName.TO_IDREF; case StandardNames.XS_ENTITY: return StringConverter.StringToNCName.TO_ENTITY; case StandardNames.XS_NMTOKEN: return StringConverter.StringToNMTOKEN.INSTANCE; default: throw new AssertionError("Unknown built-in subtype of xs:string"); } } else if (tt == StandardNames.XS_UNTYPED_ATOMIC) { return StringConverter.StringToUntypedAtomic.INSTANCE; } else if (targetType.isPrimitiveType()) { // converter to built-in types unrelated to xs:string Converter converter = getConverter(BuiltInAtomicType.STRING, targetType); assert converter != null; return (StringConverter) converter; } else if (tt == StandardNames.XS_INTEGER) { return new StringConverter.StringToIntegerSubtype((BuiltInAtomicType) targetType); } else { switch (targetType.getFingerprint()) { case StandardNames.XS_DAY_TIME_DURATION: return StringConverter.StringToDayTimeDuration.INSTANCE; case StandardNames.XS_YEAR_MONTH_DURATION: return StringConverter.StringToYearMonthDuration.INSTANCE; case StandardNames.XS_DATE_TIME_STAMP: StringConverter first = new StringConverter.StringToDateTime(this); Converter.DownCastingConverter second = new Converter.DownCastingConverter(targetType, this); return new StringConverter.StringToNonStringDerivedType(first, second); default: throw new AssertionError("Unknown built in type " + targetType); } } } else { if (tt == StandardNames.XS_STRING) { if (targetType.getBuiltInBaseType() == BuiltInAtomicType.STRING) { // converter to user-defined subtypes of xs:string return new StringConverter.StringToStringSubtype(this, targetType); } else { // converter to user-defined subtypes of built-in subtypes of xs:string return new StringConverter.StringToDerivedStringSubtype(this, targetType); } } else { // converter to user-defined types derived from types other than xs:string @SuppressWarnings("RedundantCast") StringConverter first = ((AtomicType)targetType.getPrimitiveItemType()).getStringConverter(this); Converter.DownCastingConverter second = new Converter.DownCastingConverter(targetType, this); return new StringConverter.StringToNonStringDerivedType(first, second); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy