001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.metapath.function; 028 029import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 030import gov.nist.secauto.metaschema.core.metapath.ISequence; 031import gov.nist.secauto.metaschema.core.metapath.MetapathException; 032import gov.nist.secauto.metaschema.core.metapath.item.IItem; 033import gov.nist.secauto.metaschema.core.util.ObjectUtils; 034 035import java.net.URI; 036import java.util.ArrayList; 037import java.util.EnumSet; 038import java.util.LinkedList; 039import java.util.List; 040import java.util.Objects; 041import java.util.Set; 042 043import javax.xml.namespace.QName; 044 045import edu.umd.cs.findbugs.annotations.NonNull; 046 047public interface IFunction { 048 enum FunctionProperty { 049 /** 050 * Indicates that the function will produce identical results for the same 051 * arguments (see XPath 3.1 <a href= 052 * "https://www.w3.org/TR/xpath-functions-31/#dt-deterministic">deterministic</a>). 053 * If not assigned to a function definition, a function call with the same 054 * arguments is not guaranteed to produce the same results in the same order for 055 * subsequent calls within the same execution context. 056 */ 057 DETERMINISTIC, 058 /** 059 * Indicates that the result of the function depends on property values within 060 * the static or dynamic context and the provided arguments (see XPath 3.1 061 * <a href= 062 * "https://www.w3.org/TR/xpath-functions-31/#dt-context-dependent">context-dependent</a>). 063 * If not assigned to a function definition, a call will not be affected by the 064 * property values within the static or dynamic context and will not have any 065 * arguments. 066 */ 067 CONTEXT_DEPENDENT, 068 /** 069 * Indicates that the result of the function depends on the current focus (see 070 * XPath 3.1 <a href= 071 * "https://www.w3.org/TR/xpath-functions-31/#dt-focus-independent">focus-dependent</a>). 072 * If not assigned to a function definition, a call will not be affected by the 073 * current focus. 074 */ 075 FOCUS_DEPENDENT, 076 /** 077 * The function allows the last argument to be repeated any number of times. 078 */ 079 UNBOUNDED_ARITY; 080 } 081 082 /** 083 * Retrieve the name of the function. 084 * 085 * @return the function's name 086 */ 087 @NonNull 088 String getName(); 089 090 /** 091 * Retrieve the namespace of the function. 092 * 093 * @return the function's namespace 094 */ 095 @NonNull 096 String getNamespace(); 097 098 /** 099 * Retrieve the namespace qualified name of the function. 100 * 101 * @return the namespace qualified name 102 */ 103 @NonNull 104 default QName getQName() { 105 return new QName(getNamespace(), getName()); 106 } 107 108 /** 109 * Retrieve the set of assigned function properties. 110 * 111 * @return the set of properties or an empty set 112 */ 113 @NonNull 114 Set<FunctionProperty> getProperties(); 115 116 /** 117 * Retrieve the list of function arguments. 118 * 119 * @return the function arguments or an empty list if there are none 120 */ 121 @NonNull 122 List<IArgument> getArguments(); 123 124 /** 125 * Determine the number of arguments the function has. 126 * 127 * @return the number of function arguments 128 */ 129 int arity(); 130 131 /** 132 * Determines if the result of the function call will produce identical results 133 * when provided the same implicit or explicit arguments. 134 * 135 * @return {@code true} if function is deterministic or {@code false} otherwise 136 * @see FunctionProperty#DETERMINISTIC 137 */ 138 default boolean isDeterministic() { 139 return getProperties().contains(FunctionProperty.DETERMINISTIC); 140 } 141 142 /** 143 * Determines if the result of the function call depends on property values 144 * within the static or dynamic context and the provided arguments. 145 * 146 * @return {@code true} if function is context dependent or {@code false} 147 * otherwise 148 * @see FunctionProperty#CONTEXT_DEPENDENT 149 */ 150 default boolean isContextDepenent() { 151 return getProperties().contains(FunctionProperty.CONTEXT_DEPENDENT); 152 } 153 154 /** 155 * Determines if the result of the function call depends on the current focus. 156 * 157 * @return {@code true} if function is focus dependent or {@code false} 158 * otherwise 159 * @see FunctionProperty#FOCUS_DEPENDENT 160 */ 161 default boolean isFocusDepenent() { 162 return getProperties().contains(FunctionProperty.FOCUS_DEPENDENT); 163 } 164 165 /** 166 * Determines if the final argument can be repeated. 167 * 168 * @return {@code true} if the final argument can be repeated or {@code false} 169 * otherwise 170 * @see FunctionProperty#UNBOUNDED_ARITY 171 */ 172 default boolean isArityUnbounded() { 173 return getProperties().contains(FunctionProperty.UNBOUNDED_ARITY); 174 } 175 176 /** 177 * Retrieve the function result sequence type. 178 * 179 * @return the function result sequence type 180 */ 181 @NonNull 182 ISequenceType getResult(); 183 184 // /** 185 // * Determines by static analysis if the function supports the expression 186 // arguments provided. 187 // * 188 // * @param arguments 189 // * the expression arguments to evaluate 190 // * @return {@code true} if the arguments are supported or {@code false} 191 // otherwise 192 // */ 193 // boolean isSupported(List<IExpression<?>> arguments); 194 195 @NonNull 196 ISequence<?> execute( 197 @NonNull List<ISequence<?>> arguments, 198 @NonNull DynamicContext dynamicContext, 199 @NonNull ISequence<?> focus) throws MetapathException; 200 201 /** 202 * Get the signature of the function as a string. 203 * 204 * @return the signature 205 */ 206 String toSignature(); 207 208 @NonNull 209 static Builder builder() { 210 return new Builder(); 211 } 212 213 @SuppressWarnings("PMD.LooseCoupling") 214 class Builder { 215 private String name; 216 private String namespace; 217 @SuppressWarnings("null") 218 @NonNull 219 private final EnumSet<FunctionProperty> properties = EnumSet.noneOf(FunctionProperty.class); 220 @NonNull 221 private final List<IArgument> arguments = new LinkedList<>(); 222 private Class<? extends IItem> returnType = IItem.class; 223 private Occurrence returnOccurrence = Occurrence.ONE; 224 private IFunctionExecutor functionHandler; 225 226 @NonNull 227 public Builder name(@NonNull String name) { 228 Objects.requireNonNull(name, "name"); 229 if (name.isBlank()) { 230 throw new IllegalArgumentException("the name must be non-blank"); 231 } 232 this.name = name.trim(); 233 return this; 234 } 235 236 @NonNull 237 public Builder namespace(@NonNull URI uri) { 238 return namespace(ObjectUtils.notNull(uri.toASCIIString())); 239 } 240 241 @NonNull 242 public Builder namespace(@NonNull String name) { 243 Objects.requireNonNull(name, "name"); 244 if (name.isBlank()) { 245 throw new IllegalArgumentException("the name must be non-blank"); 246 } 247 this.namespace = name.trim(); 248 return this; 249 } 250 251 @NonNull 252 public Builder deterministic() { 253 properties.add(FunctionProperty.DETERMINISTIC); 254 return this; 255 } 256 257 @NonNull 258 public Builder nonDeterministic() { 259 properties.remove(FunctionProperty.DETERMINISTIC); 260 return this; 261 } 262 263 @NonNull 264 public Builder contextDependent() { 265 properties.add(FunctionProperty.CONTEXT_DEPENDENT); 266 return this; 267 } 268 269 @NonNull 270 public Builder contextIndependent() { 271 properties.remove(FunctionProperty.CONTEXT_DEPENDENT); 272 return this; 273 } 274 275 @NonNull 276 public Builder focusDependent() { 277 properties.add(FunctionProperty.FOCUS_DEPENDENT); 278 return this; 279 } 280 281 @NonNull 282 public Builder focusIndependent() { 283 properties.remove(FunctionProperty.FOCUS_DEPENDENT); 284 return this; 285 } 286 287 @NonNull 288 public Builder allowUnboundedArity(boolean allow) { 289 if (allow) { 290 properties.add(FunctionProperty.UNBOUNDED_ARITY); 291 } else { 292 properties.remove(FunctionProperty.UNBOUNDED_ARITY); 293 } 294 return this; 295 } 296 297 @NonNull 298 public Builder returnType(@NonNull Class<? extends IItem> type) { 299 Objects.requireNonNull(type, "type"); 300 this.returnType = type; 301 return this; 302 } 303 304 @NonNull 305 public Builder returnZeroOrOne() { 306 return returnOccurrence(Occurrence.ZERO_OR_ONE); 307 } 308 309 @NonNull 310 public Builder returnOne() { 311 return returnOccurrence(Occurrence.ONE); 312 } 313 314 @NonNull 315 public Builder returnZeroOrMore() { 316 return returnOccurrence(Occurrence.ZERO_OR_MORE); 317 } 318 319 @NonNull 320 public Builder returnOneOrMore() { 321 return returnOccurrence(Occurrence.ONE_OR_MORE); 322 } 323 324 @NonNull 325 public Builder returnOccurrence(@NonNull Occurrence occurrence) { 326 Objects.requireNonNull(occurrence, "occurrence"); 327 this.returnOccurrence = occurrence; 328 return this; 329 } 330 331 @NonNull 332 public Builder argument(@NonNull IArgument.Builder builder) { 333 return argument(builder.build()); 334 } 335 336 @NonNull 337 public Builder argument(@NonNull IArgument argument) { 338 Objects.requireNonNull(argument, "argument"); 339 this.arguments.add(argument); 340 return this; 341 } 342 343 @NonNull 344 public Builder functionHandler(@NonNull IFunctionExecutor handler) { 345 Objects.requireNonNull(handler, "handler"); 346 this.functionHandler = handler; 347 return this; 348 } 349 350 @NonNull 351 public IFunction build() { 352 ISequenceType sequenceType; 353 if (returnType == null) { 354 sequenceType = ISequenceType.EMPTY; 355 } else { 356 sequenceType = new SequenceTypeImpl( 357 returnType, 358 ObjectUtils.requireNonNull(returnOccurrence, "the return occurrence must not be null")); 359 } 360 361 if (properties.contains(FunctionProperty.UNBOUNDED_ARITY) && arguments.isEmpty()) { 362 throw new IllegalStateException("to allow unbounded arity, at least one argument must be provided"); 363 } 364 365 return new DefaultFunction( 366 ObjectUtils.requireNonNull(name, "the name must not be null"), 367 ObjectUtils.requireNonNull(namespace, "the namespace must not be null"), 368 properties, 369 new ArrayList<>(arguments), 370 sequenceType, 371 ObjectUtils.requireNonNull(functionHandler, "the function handler must not be null")); 372 } 373 } 374}