59
59
import javax .xml .transform .stream .StreamSource ;
60
60
import java .io .Reader ;
61
61
import java .io .StringReader ;
62
+ import java .math .BigDecimal ;
63
+ import java .math .RoundingMode ;
62
64
import java .net .URI ;
63
65
import java .net .URISyntaxException ;
64
66
import java .util .*;
@@ -84,28 +86,11 @@ class Options {
84
86
private static final long XXHASH64_SEED = 0x2245a28e ;
85
87
private static final XXHash64 XX_HASH_64 = XXHashFactory .fastestInstance ().hash64 ();
86
88
87
- private Map <QName , XdmValue > readParamsMap (final Optional <MapType > option , final String name ) throws XPathException {
88
-
89
- final Map <net .sf .saxon .s9api .QName , XdmValue > result = new HashMap <>();
90
-
91
- final MapType paramsMap = option .orElse (new MapType (context ));
92
- for (final IEntry <AtomicValue , Sequence > entry : paramsMap ) {
93
- final AtomicValue key = entry .key ();
94
- if (!(key instanceof QNameValue )) {
95
- throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Supplied " + name + " is not a valid xs:qname: " + entry );
96
- }
97
- if (entry .value () == null ) {
98
- throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Supplied " + name + " is not a valid xs:sequence: " + entry );
99
- }
100
- result .put (Convert .ToSaxon .of ((QNameValue ) key ), toSaxon .of (entry .value ()));
101
- }
102
- return result ;
103
- }
104
89
105
90
final Tuple2 <String , Source > xsltSource ;
106
91
final MapType stylesheetParams ;
107
92
final Map <net .sf .saxon .s9api .QName , XdmValue > staticParams ;
108
- final float xsltVersion ;
93
+ final XSLTVersion xsltVersion ;
109
94
final Optional <AnyURIValue > resolvedStylesheetBaseURI ;
110
95
final Optional <QNameValue > initialFunction ;
111
96
final Optional <ArrayType > functionParams ;
@@ -161,11 +146,11 @@ private Map<QName, XdmValue> readParamsMap(final Optional<MapType> option, final
161
146
final Optional <DecimalValue > explicitXsltVersion = Options .XSLT_VERSION .get (options );
162
147
if (explicitXsltVersion .isPresent ()) {
163
148
try {
164
- xsltVersion = explicitXsltVersion .get ().getFloat ( );
165
- } catch ( final XPathException e ) {
166
- throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Supplied xslt-version is not a valid xs:decimal: " + e . getMessage (), explicitXsltVersion .get (), e );
167
- }
168
- if ( xsltVersion != V1_0 && xsltVersion != V2_0 && xsltVersion != V3_0 ) {
149
+ xsltVersion = XSLTVersion . fromDecimal ( explicitXsltVersion .get ().getValue () );
150
+ if ( xsltVersion . equals ( V1_0 ) && xsltVersion . equals ( V2_0 ) && xsltVersion . equals ( V3_0 ) ) {
151
+ throw new XPathException (fnTransform , ErrorCodes .FOXT0001 , "Supplied xslt-version is an unknown XSLT version: " + explicitXsltVersion .get ());
152
+ }
153
+ } catch ( final Transform . PendingException pe ) {
169
154
throw new XPathException (fnTransform , ErrorCodes .FOXT0001 , "Supplied xslt-version is an unknown XSLT version: " + explicitXsltVersion .get ());
170
155
}
171
156
} else {
@@ -230,7 +215,25 @@ private Map<QName, XdmValue> readParamsMap(final Optional<MapType> option, final
230
215
vendorOptions = Options .VENDOR_OPTIONS .get (xsltVersion , options );
231
216
}
232
217
233
- private Delivery .Format getDeliveryFormat (final float xsltVersion , final MapType options ) throws XPathException {
218
+ private Map <QName , XdmValue > readParamsMap (final Optional <MapType > option , final String name ) throws XPathException {
219
+
220
+ final Map <net .sf .saxon .s9api .QName , XdmValue > result = new HashMap <>();
221
+
222
+ final MapType paramsMap = option .orElse (new MapType (context ));
223
+ for (final IEntry <AtomicValue , Sequence > entry : paramsMap ) {
224
+ final AtomicValue key = entry .key ();
225
+ if (!(key instanceof QNameValue )) {
226
+ throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Supplied " + name + " is not a valid xs:qname: " + entry );
227
+ }
228
+ if (entry .value () == null ) {
229
+ throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Supplied " + name + " is not a valid xs:sequence: " + entry );
230
+ }
231
+ result .put (Convert .ToSaxon .of ((QNameValue ) key ), toSaxon .of (entry .value ()));
232
+ }
233
+ return result ;
234
+ }
235
+
236
+ private Delivery .Format getDeliveryFormat (final XSLTVersion xsltVersion , final MapType options ) throws XPathException {
234
237
final String deliveryFormatString = Options .DELIVERY_FORMAT .get (xsltVersion , options ).orElse (new StringValue (Delivery .Format .DOCUMENT .name ())).getStringValue ().toUpperCase ();
235
238
final Delivery .Format format ;
236
239
try {
@@ -352,16 +355,16 @@ private void validateRequestedProperties(final MapType requestedProperties) thro
352
355
Type .DECIMAL ,"xslt-version" , V1_0 , V2_0 , V3_0 );
353
356
354
357
abstract static class Option <T > {
355
- public static final float V1_0 = 1.0f ;
356
- public static final float V2_0 = 2.0f ;
357
- public static final float V3_0 = 3.0f ;
358
+ public static final XSLTVersion V1_0 = new XSLTVersion ( 1 , 0 ) ;
359
+ public static final XSLTVersion V2_0 = new XSLTVersion ( 2 , 0 ) ;
360
+ public static final XSLTVersion V3_0 = new XSLTVersion ( 3 , 0 ) ;
358
361
359
362
protected final StringValue name ;
360
363
protected final Optional <T > defaultValue ;
361
- protected final float [] appliesToVersions ;
364
+ protected final XSLTVersion [] appliesToVersions ;
362
365
protected final int itemSubtype ;
363
366
364
- private Option (final int itemSubtype , final String name , final Optional <T > defaultValue , final float ... appliesToVersions ) {
367
+ private Option (final int itemSubtype , final String name , final Optional <T > defaultValue , final XSLTVersion ... appliesToVersions ) {
365
368
this .name = new StringValue (name );
366
369
this .defaultValue = defaultValue ;
367
370
this .appliesToVersions = appliesToVersions ;
@@ -370,16 +373,16 @@ private Option(final int itemSubtype, final String name, final Optional<T> defau
370
373
371
374
public abstract Optional <T > get (final MapType options ) throws XPathException ;
372
375
373
- private boolean notApplicableToVersion (final float xsltVersion ) {
374
- for (final float appliesToVersion : appliesToVersions ) {
375
- if (xsltVersion == appliesToVersion ) {
376
+ private boolean notApplicableToVersion (final XSLTVersion xsltVersion ) {
377
+ for (final XSLTVersion appliesToVersion : appliesToVersions ) {
378
+ if (xsltVersion . equals ( appliesToVersion ) ) {
376
379
return false ;
377
380
}
378
381
}
379
382
return true ;
380
383
}
381
384
382
- public Optional <T > get (final float xsltVersion , final MapType options ) throws XPathException {
385
+ public Optional <T > get (final XSLTVersion xsltVersion , final MapType options ) throws XPathException {
383
386
if (notApplicableToVersion (xsltVersion )) {
384
387
return Optional .empty ();
385
388
}
@@ -392,12 +395,12 @@ static class SequenceOption<T extends Sequence> extends Option<T> {
392
395
393
396
private final int sequenceSubtype ;
394
397
395
- public SequenceOption (final int sequenceSubtype , final int itemSubtype , final String name , final float ... appliesToVersions ) {
398
+ public SequenceOption (final int sequenceSubtype , final int itemSubtype , final String name , final XSLTVersion ... appliesToVersions ) {
396
399
super (itemSubtype , name , Optional .empty (), appliesToVersions );
397
400
this .sequenceSubtype = sequenceSubtype ;
398
401
}
399
402
400
- public SequenceOption (final int sequenceSubtype , final int itemSubtype , final String name , @ Nullable final T defaultValue , final float ... appliesToVersions ) {
403
+ public SequenceOption (final int sequenceSubtype , final int itemSubtype , final String name , @ Nullable final T defaultValue , final XSLTVersion ... appliesToVersions ) {
401
404
super (itemSubtype , name , Optional .ofNullable (defaultValue ), appliesToVersions );
402
405
this .sequenceSubtype = sequenceSubtype ;
403
406
}
@@ -425,11 +428,11 @@ public Optional<T> get(final MapType options) throws XPathException {
425
428
426
429
static class ItemOption <T extends Item > extends Option <T > {
427
430
428
- public ItemOption (final int itemSubtype , final String name , final float ... appliesToVersions ) {
431
+ public ItemOption (final int itemSubtype , final String name , final XSLTVersion ... appliesToVersions ) {
429
432
super (itemSubtype , name , Optional .empty (), appliesToVersions );
430
433
}
431
434
432
- public ItemOption (final int itemSubtype , final String name , @ Nullable final T defaultValue , final float ... appliesToVersions ) {
435
+ public ItemOption (final int itemSubtype , final String name , @ Nullable final T defaultValue , final XSLTVersion ... appliesToVersions ) {
433
436
super (itemSubtype , name , Optional .ofNullable (defaultValue ), appliesToVersions );
434
437
}
435
438
@@ -553,7 +556,7 @@ private AnyURIValue resolveURI(final AnyURIValue relative, final AnyURIValue bas
553
556
}
554
557
}
555
558
556
- private float getXsltVersion (final Source xsltStylesheet ) throws XPathException {
559
+ private XSLTVersion getXsltVersion (final Source xsltStylesheet ) throws XPathException {
557
560
558
561
if (xsltStylesheet instanceof DOMSource ) {
559
562
return domExtractXsltVersion (xsltStylesheet );
@@ -564,7 +567,7 @@ private float getXsltVersion(final Source xsltStylesheet) throws XPathException
564
567
throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Unable to extract version from XSLT, unrecognised source" );
565
568
}
566
569
567
- private float domExtractXsltVersion (final Source xsltStylesheet ) throws XPathException {
570
+ private XSLTVersion domExtractXsltVersion (final Source xsltStylesheet ) throws XPathException {
568
571
569
572
Node node = ((DOMSource ) xsltStylesheet ).getNode ();
570
573
if (node instanceof Document ) {
@@ -600,13 +603,13 @@ private float domExtractXsltVersion(final Source xsltStylesheet) throws XPathExc
600
603
}
601
604
602
605
try {
603
- return Float . parseFloat ( version );
604
- } catch (final NumberFormatException nfe ) {
605
- throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Unable to extract version from XSLT via DOM. Value: " + version + " : " + nfe .getMessage ());
606
+ return XSLTVersion . fromDecimal ( new BigDecimal ( version ) );
607
+ } catch (final Transform . PendingException pe ) {
608
+ throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Unable to extract version from XSLT via DOM. Value: " + version + " : " + pe .getMessage ());
606
609
}
607
610
}
608
611
609
- private float staxExtractXsltVersion (final Source xsltStylesheet ) throws XPathException {
612
+ private XSLTVersion staxExtractXsltVersion (final Source xsltStylesheet ) throws XPathException {
610
613
try {
611
614
final XMLInputFactory factory = XMLInputFactory .newInstance ();
612
615
// Sonartype checker needs this https://rules.sonarsource.com/java/RSPEC-2755
@@ -621,11 +624,11 @@ private float staxExtractXsltVersion(final Source xsltStylesheet) throws XPathEx
621
624
final StartElement startElement = event .asStartElement ();
622
625
if (Options .QN_XSL_STYLESHEET .equals (startElement .getName ())) {
623
626
final Attribute version = startElement .getAttributeByName (Options .QN_VERSION );
624
- return Float . parseFloat ( version .getValue ());
627
+ return XSLTVersion . fromDecimal ( new BigDecimal ( version .getValue () ));
625
628
}
626
629
}
627
630
}
628
- } catch (final XMLStreamException e ) {
631
+ } catch (final XMLStreamException | Transform . PendingException e ) {
629
632
throw new XPathException (fnTransform , ErrorCodes .FOXT0002 , "Unable to extract version from XSLT via STaX: " + e .getMessage (), Sequence .EMPTY_SEQUENCE , e );
630
633
}
631
634
@@ -658,5 +661,42 @@ static String get(org.exist.dom.QName qName) {
658
661
return SystemProperty .getProperty (qName .getNamespaceURI (), qName .getLocalPart (), retainedStaticContext );
659
662
}
660
663
}
664
+
665
+ static class XSLTVersion {
666
+ final int major ;
667
+ final int minor ;
668
+
669
+ XSLTVersion (final int major , final int minor ) {
670
+ this .major = major ;
671
+ this .minor = minor ;
672
+ }
673
+
674
+ public static XSLTVersion fromDecimal (final BigDecimal decimal ) throws Transform .PendingException {
675
+ final BigDecimal major = decimal .setScale (0 , RoundingMode .FLOOR );
676
+ final BigDecimal minor = decimal .subtract (major ).multiply (BigDecimal .TEN );
677
+ try {
678
+ return new XSLTVersion (major .intValueExact (), minor .intValueExact ());
679
+ } catch (final ArithmeticException ae ) {
680
+ throw new Transform .PendingException ("XSLT Version is not an exact X.Y value: " + decimal , ae );
681
+ }
682
+ }
683
+
684
+ @ Override
685
+ public boolean equals (Object o ) {
686
+ if (this == o ) return true ;
687
+ if (o == null || getClass () != o .getClass ()) return false ;
688
+ XSLTVersion version = (XSLTVersion ) o ;
689
+ return major == version .major && minor == version .minor ;
690
+ }
691
+
692
+ @ Override
693
+ public int hashCode () {
694
+ return Objects .hash (major , minor );
695
+ }
696
+
697
+ @ Override public String toString () {
698
+ return major + "." + minor ;
699
+ }
700
+ }
661
701
}
662
702
0 commit comments