
net.sf.saxon.lib.ConversionRules Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 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.LRUCache;
import net.sf.saxon.om.NotationSet;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.type.*;
/**
* 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 = StringToDouble.getInstance();
private NotationSet notationSet; // may be null
private URIChecker uriChecker;
private boolean allowYearZero;
private TypeHierarchy typeHierarchy; // may be null
// These two tables need to be synchronised to make the caching thread-safe
private LRUCache converterCache =
new LRUCache(100, true);
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 string the string to be checked against the rules for URIs
* @return true if the string represents a valid xs:anyURI value
*/
public boolean isValidURI(CharSequence string) {
return uriChecker == null || uriChecker.isValidURI(string);
}
/**
* 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) {
// 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(/*@NotNull*/ AtomicType sourceType, /*@NotNull*/ AtomicType targetType) {
if (sourceType == targetType) {
return StringConverter.IdentityConverter.THE_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()) {
AtomicType primTarget = 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);
}
Converter upcast = new Converter.UpCastingConverter(sourceType.getPrimitiveItemType());
Converter downcast = new Converter.DownCastingConverter(targetType, this);
return new Converter.TwoPhaseConverter(upcast, downcast);
}
switch (tt) {
case StandardNames.XS_UNTYPED_ATOMIC:
return Converter.TO_UNTYPED_ATOMIC;
case StandardNames.XS_STRING:
return Converter.TO_STRING;
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.NUMERIC_TO_FLOAT;
case StandardNames.XS_BOOLEAN:
return Converter.BOOLEAN_TO_FLOAT;
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.NUMERIC_TO_DOUBLE;
case StandardNames.XS_BOOLEAN:
return Converter.BOOLEAN_TO_DOUBLE;
default:
return null;
}
case StandardNames.XS_DECIMAL:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_DECIMAL;
case StandardNames.XS_FLOAT:
return Converter.FLOAT_TO_DECIMAL;
case StandardNames.XS_DOUBLE:
return Converter.DOUBLE_TO_DECIMAL;
case StandardNames.XS_INTEGER:
return Converter.INTEGER_TO_DECIMAL;
case StandardNames.XS_NUMERIC:
return Converter.NUMERIC_TO_DECIMAL;
case StandardNames.XS_BOOLEAN:
return Converter.BOOLEAN_TO_DECIMAL;
default:
return null;
}
case StandardNames.XS_INTEGER:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_INTEGER;
case StandardNames.XS_FLOAT:
return Converter.FLOAT_TO_INTEGER;
case StandardNames.XS_DOUBLE:
return Converter.DOUBLE_TO_INTEGER;
case StandardNames.XS_DECIMAL:
return Converter.DECIMAL_TO_INTEGER;
case StandardNames.XS_NUMERIC:
return Converter.NUMERIC_TO_INTEGER;
case StandardNames.XS_BOOLEAN:
return Converter.BOOLEAN_TO_INTEGER;
default:
return null;
}
case StandardNames.XS_DURATION:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_DURATION;
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.STRING_TO_YEAR_MONTH_DURATION;
case StandardNames.XS_DURATION:
case StandardNames.XS_DAY_TIME_DURATION:
return Converter.DURATION_TO_YEAR_MONTH_DURATION;
default:
return null;
}
case StandardNames.XS_DAY_TIME_DURATION:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_DAY_TIME_DURATION;
case StandardNames.XS_DURATION:
case StandardNames.XS_YEAR_MONTH_DURATION:
return Converter.DURATION_TO_DAY_TIME_DURATION;
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.DATE_TO_DATE_TIME;
default:
return null;
}
case StandardNames.XS_TIME:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_TIME;
case StandardNames.XS_DATE_TIME:
return Converter.DATE_TIME_TO_TIME;
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.DATE_TIME_TO_DATE;
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.DATE_TIME_TO_G_YEAR_MONTH;
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.DATE_TIME_TO_G_YEAR;
default:
return null;
}
case StandardNames.XS_G_MONTH_DAY:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_G_MONTH_DAY;
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.DATE_TIME_TO_G_MONTH_DAY;
default:
return null;
}
case StandardNames.XS_G_DAY:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_G_DAY;
case StandardNames.XS_DATE:
return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME,
BuiltInAtomicType.G_DAY, this);
case StandardNames.XS_DATE_TIME:
return Converter.DATE_TIME_TO_G_DAY;
default:
return null;
}
case StandardNames.XS_G_MONTH:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_G_MONTH;
case StandardNames.XS_DATE:
return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME,
BuiltInAtomicType.G_MONTH, this);
case StandardNames.XS_DATE_TIME:
return Converter.DATE_TIME_TO_G_MONTH;
default:
return null;
}
case StandardNames.XS_BOOLEAN:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_BOOLEAN;
case StandardNames.XS_FLOAT:
case StandardNames.XS_DOUBLE:
case StandardNames.XS_DECIMAL:
case StandardNames.XS_INTEGER:
case StandardNames.XS_NUMERIC:
return Converter.NUMERIC_TO_BOOLEAN;
default:
return null;
}
case StandardNames.XS_BASE64_BINARY:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_BASE64_BINARY;
case StandardNames.XS_HEX_BINARY:
return Converter.HEX_BINARY_TO_BASE64_BINARY;
default:
return null;
}
case StandardNames.XS_HEX_BINARY:
switch (st) {
case StandardNames.XS_UNTYPED_ATOMIC:
case StandardNames.XS_STRING:
return StringConverter.STRING_TO_HEX_BINARY;
case StandardNames.XS_BASE64_BINARY:
return Converter.BASE64_BINARY_TO_HEX_BINARY;
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.NOTATION_TO_QNAME;
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.QNAME_TO_NOTATION;
default:
return null;
}
case StandardNames.XS_ANY_ATOMIC_TYPE:
return Converter.IDENTITY_CONVERTER;
default:
throw new IllegalArgumentException("Unknown primitive type " + tt);
}
}
/**
* Get a Converter that converts from strings to a given atomic type. These can be primitive types,
* derived types, or user-defined types. The converter implements the casting rules.
*
* @param target the target type
* @return a Converter if conversion between the two types is possible; or null otherwise
*/
// public StringConverter getStringConverter(AtomicType target) {
// // Beware of infinite recursion. The method AtomicType.getStringConverter() should not call this method,
// // except by specifying a target type higher in the type heirarchy.
// StringConverter converter = target.getStringConverter(this);
// if (converter != null) {
// return converter;
// }
// int key = target.getFingerprint();
// converter = stringConverterCache.get(key);
// if (converter == null) {
// converter = makeStringConverter(target);
// stringConverterCache.put(key, converter);
// }
// return converter;
// }
/**
* 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*/
private 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.STRING_TO_STRING;
case StandardNames.XS_NORMALIZED_STRING:
return StringConverter.STRING_TO_NORMALIZED_STRING;
case StandardNames.XS_TOKEN:
return StringConverter.STRING_TO_TOKEN;
case StandardNames.XS_LANGUAGE:
return StringConverter.STRING_TO_LANGUAGE;
case StandardNames.XS_NAME:
return StringConverter.STRING_TO_NAME;
case StandardNames.XS_NCNAME:
return StringConverter.STRING_TO_NCNAME;
case StandardNames.XS_ID:
return StringConverter.STRING_TO_ID;
case StandardNames.XS_IDREF:
return StringConverter.STRING_TO_IDREF;
case StandardNames.XS_ENTITY:
return StringConverter.STRING_TO_ENTITY;
case StandardNames.XS_NMTOKEN:
return StringConverter.STRING_TO_NMTOKEN;
default:
throw new AssertionError("Unknown built-in subtype of xs:string");
}
} else if (tt == StandardNames.XS_UNTYPED_ATOMIC) {
return StringConverter.STRING_TO_UNTYPED_ATOMIC;
} 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.STRING_TO_DAY_TIME_DURATION;
case StandardNames.XS_YEAR_MONTH_DURATION:
return StringConverter.STRING_TO_YEAR_MONTH_DURATION;
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.toString());
}
}
} 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
StringConverter first = ((AtomicType) targetType.getPrimitiveItemType()).getStringConverter(this);
Converter.DownCastingConverter second = new Converter.DownCastingConverter(targetType, this);
return new StringConverter.StringToNonStringDerivedType(first, second);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy