2727
2828import java .io .IOException ;
2929import java .io .InputStream ;
30+ import java .util .Map ;
31+ import java .util .Set ;
3032
3133import org .exist .xquery .BasicFunction ;
3234import org .exist .xquery .FunctionSignature ;
3335import org .exist .xquery .XPathException ;
3436import org .exist .xquery .XQueryContext ;
35- import org .exist .xquery .value .BinaryValue ;
36- import org .exist .xquery .value .FunctionParameterSequenceType ;
37- import org .exist .xquery .value .Sequence ;
38- import org .exist .xquery .value .StringValue ;
39- import org .exist .xquery .value .Type ;
37+ import org .exist .xquery .functions .map .MapType ;
38+ import org .exist .xquery .value .*;
4039import org .expath .exist .crypto .EXpathCryptoException ;
4140
4241import org .slf4j .Logger ;
4645
4746import static org .exist .xquery .FunctionDSL .*;
4847import static org .expath .exist .crypto .ExistExpathCryptoModule .*;
48+ import static org .expath .exist .crypto .ExistExpathCryptoModule .functionSignature ;
4949
5050public class HashFunction extends BasicFunction {
5151
@@ -56,12 +56,53 @@ public class HashFunction extends BasicFunction {
5656 "The data to be hashed." );
5757 private static final FunctionParameterSequenceType FS_HASH_PARAM_ALGORITHM = param ("algorithm" , Type .STRING ,
5858 "The cryptographic hashing algorithm." );
59+ private static final FunctionParameterSequenceType FS_HASH_PARAM_PROVIDER = param ("provider" , Type .STRING ,
60+ "The cryptographic hashing algorithm provider." );
5961
60- public static final FunctionSignature FS_HASH [] = functionSignatures (FS_HASH_NAME ,
61- "resulting hash value, as string." , returnsOptMany (Type .BYTE ),
62- arities (arity (FS_HASH_PARAM_DATA , FS_HASH_PARAM_ALGORITHM ),
63- arity (FS_HASH_PARAM_DATA , FS_HASH_PARAM_ALGORITHM , param ("encoding" , Type .STRING ,
64- "The encoding of the output. The legal values are \" hex\" and \" base64\" . The default value is \" base64\" ." ))));
62+ public static final FunctionSignature FS_HASH [] = functionSignatures (
63+ FS_HASH_NAME ,
64+ "resulting hash value, as string." ,
65+ returnsOptMany (Type .BYTE ),
66+ arities (
67+ arity (
68+ FS_HASH_PARAM_DATA ,
69+ FS_HASH_PARAM_ALGORITHM
70+ ),
71+ arity (
72+ FS_HASH_PARAM_DATA ,
73+ FS_HASH_PARAM_ALGORITHM ,
74+ param ("encoding" , Type .STRING , "The encoding of the output. The legal values are \" hex\" and \" base64\" . The default value is \" base64\" ." )
75+ ),
76+ arity (
77+ FS_HASH_PARAM_DATA ,
78+ FS_HASH_PARAM_ALGORITHM ,
79+ param ("encoding" , Type .STRING , "The encoding of the output. The legal values are \" hex\" and \" base64\" . The default value is \" base64\" ." ),
80+ FS_HASH_PARAM_PROVIDER
81+ )
82+ )
83+ );
84+
85+ private static final String FS_HASH_PROVIDERS_NAME = "hash-providers" ;
86+ public static final FunctionSignature FS_HASH_PROVIDERS = functionSignature (
87+ FS_HASH_PROVIDERS_NAME ,
88+ "Gets the names of all the hash providers" ,
89+ returnsOptMany (Type .STRING )
90+ );
91+
92+ private static final String FS_HASH_ALGORITHMS_NAME = "hash-algorithms" ;
93+ public static final FunctionSignature FS_HASH_ALGORITHMS [] = {
94+ functionSignature (
95+ FS_HASH_ALGORITHMS_NAME ,
96+ "Gets the names of all the hash providers" ,
97+ returnsOptMany (Type .STRING )
98+ ),
99+ functionSignature (
100+ FS_HASH_ALGORITHMS_NAME ,
101+ "Gets the names of all the hash providers" ,
102+ returns (Type .MAP ),
103+ param ("provider-name" , Type .STRING , "The name of the hash provider." )
104+ )
105+ };
65106
66107 public HashFunction (final XQueryContext context , final FunctionSignature signature ) {
67108 super (context , signature );
@@ -70,6 +111,42 @@ public HashFunction(final XQueryContext context, final FunctionSignature signatu
70111 @ Override
71112 public Sequence eval (final Sequence [] args , final Sequence contextSequence ) throws XPathException {
72113
114+ if (isCalledAs (FS_HASH_NAME )) {
115+ return hash (args );
116+
117+ } else if (isCalledAs (FS_HASH_PROVIDERS_NAME )) {
118+ final ValueSequence providers = new ValueSequence ();
119+ for (final String provider : Hash .listProviders ()) {
120+ providers .add (new StringValue (provider ));
121+ }
122+ return providers ;
123+
124+ } else if (isCalledAs (FS_HASH_ALGORITHMS_NAME )) {
125+ if (args .length == 1 ) {
126+ final String providerName = args [0 ].getStringValue ();
127+ final ValueSequence algorithmNames = new ValueSequence ();
128+ for (final String algorithmName : Hash .listAlgorithms (providerName )) {
129+ algorithmNames .add (new StringValue (algorithmName ));
130+ }
131+ return algorithmNames ;
132+
133+ } else {
134+ final MapType mapType = new MapType (this .context );
135+ for (final Map .Entry <String , Set <String >> providerAlgorithms : Hash .listAlgorithms ().entrySet ()) {
136+ final ValueSequence algorithmNames = new ValueSequence ();
137+ for (final String algorithmName : providerAlgorithms .getValue ()) {
138+ algorithmNames .add (new StringValue (algorithmName ));
139+ }
140+ mapType .add (new StringValue (providerAlgorithms .getKey ()), algorithmNames );
141+ }
142+ return mapType ;
143+ }
144+ } else {
145+ throw new XPathException (this , "Unknown function name" );
146+ }
147+ }
148+
149+ private Sequence hash (final Sequence [] args ) throws XPathException {
73150 final int inputType = args [0 ].itemAt (0 ).getType ();
74151 final String hashAlgorithm = args [1 ].getStringValue ();
75152 final String encoding ;
@@ -78,20 +155,25 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
78155 } else {
79156 encoding = args [2 ].getStringValue ().isEmpty () ? "base64" : args [2 ].getStringValue ();
80157 }
158+ String provider = null ;
159+ if (args .length == 4 ) {
160+ provider = args [3 ].getStringValue ();
161+ }
162+
81163 LOG .debug ("encoding = {}" , encoding );
82164
83165 final Sequence result ;
84166 if (inputType == Type .STRING || inputType == Type .ELEMENT || inputType == Type .DOCUMENT ) {
85167 try {
86- result = new StringValue (Hash .hashString (args [0 ].getStringValue (), hashAlgorithm , encoding ));
168+ result = new StringValue (Hash .hashString (args [0 ].getStringValue (), hashAlgorithm , provider , encoding ));
87169 } catch (CryptoException e ) {
88170 throw new EXpathCryptoException (this , e .getCryptoError ());
89171 }
90172 } else if (inputType == Type .BASE64_BINARY || inputType == Type .HEX_BINARY ) {
91173 try {
92174 final BinaryValue binaryValue = (BinaryValue ) args [0 ].itemAt (0 );
93175 try (final InputStream is = binaryValue .getInputStream ()) {
94- result = new StringValue (Hash .hashBinary (is , hashAlgorithm , encoding ));
176+ result = new StringValue (Hash .hashBinary (is , hashAlgorithm , provider , encoding ));
95177 }
96178 } catch (CryptoException e ) {
97179 throw new EXpathCryptoException (this , e .getCryptoError ());
@@ -104,4 +186,4 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
104186
105187 return result ;
106188 }
107- }
189+ }
0 commit comments