1 /* 2 * SPDX-FileCopyrightText: none 3 * SPDX-License-Identifier: CC0-1.0 4 */ 5 6 package gov.nist.secauto.metaschema.core.metapath.item; 7 8 import gov.nist.secauto.metaschema.core.metapath.impl.AbstractSequence; 9 import gov.nist.secauto.metaschema.core.metapath.impl.SequenceN; 10 import gov.nist.secauto.metaschema.core.metapath.impl.SingletonSequence; 11 import gov.nist.secauto.metaschema.core.metapath.impl.StreamSequence; 12 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; 13 import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; 14 import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException; 15 import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException; 16 import gov.nist.secauto.metaschema.core.util.CustomCollectors; 17 import gov.nist.secauto.metaschema.core.util.ObjectUtils; 18 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.List; 22 import java.util.function.Function; 23 import java.util.stream.Stream; 24 25 import edu.umd.cs.findbugs.annotations.NonNull; 26 import edu.umd.cs.findbugs.annotations.Nullable; 27 28 /** 29 * Represents an ordered collection of Metapath expression results. 30 * <p> 31 * Items is a sequence are typically ordered based on their position in the 32 * original node graph based on a depth first ordering. 33 * 34 * @param <ITEM> 35 * the Java type of the items in a sequence 36 */ 37 @SuppressWarnings("PMD.ShortMethodName") 38 public interface ISequence<ITEM extends IItem> extends List<ITEM>, ICollectionValue { 39 /** 40 * Get an empty sequence. 41 * 42 * @param <T> 43 * the item type 44 * @return the empty sequence 45 */ 46 @SuppressWarnings("null") 47 @NonNull 48 static <T extends IItem> ISequence<T> empty() { 49 return AbstractSequence.empty(); 50 } 51 52 /** 53 * Get the items in this sequence as a {@link List}. 54 * 55 * @return a list containing all the items of the sequence 56 */ 57 @Deprecated(since = "2.2.0", forRemoval = true) 58 @NonNull 59 List<ITEM> getValue(); 60 61 /** 62 * Ensure the sequence is able to be iterated over multiple times. 63 * <p> 64 * This method can be used to ensure that the sequence can be streamed or 65 * iterated over multiple times. Implementations should make sure that when this 66 * method is called, the resulting stream is supported by an underlying list. 67 * 68 * @return a sequence with the same contents, which may be the same sequence 69 */ 70 @NonNull 71 default ISequence<ITEM> reusable() { 72 return this; 73 } 74 75 /** 76 * Get a stream guaranteed to be backed by a list. 77 * <p> 78 * This call ensures that the sequence is backed by a {@link List} and not a 79 * {@link Stream}, so the underlying collection can be reused. This is done by 80 * first calling {@link #reusable()}. 81 * 82 * @return the stream 83 */ 84 @NonNull 85 default Stream<ITEM> safeStream() { 86 return ObjectUtils.notNull(reusable().stream()); 87 } 88 89 /** 90 * Get the items in this sequence as a {@link Stream}. 91 * 92 * @return a stream containing all the items of the sequence 93 */ 94 @Override 95 @NonNull 96 Stream<ITEM> stream(); 97 98 /** 99 * Retrieves the first item in a sequence. 100 * <p> 101 * If the sequence is empty, a {@code null} result is returned. If 102 * requireSingleton is {@code true} and the sequence contains more than one 103 * item, a {@link TypeMetapathException} is thrown. 104 * 105 * @param <T> 106 * the item type to return derived from the provided sequence 107 * @param items 108 * the sequence to retrieve the first item from 109 * @param requireSingleton 110 * if {@code true} then a {@link TypeMetapathException} is thrown if 111 * the sequence contains more than one item 112 * @return {@code null} if the sequence is empty, or the item otherwise 113 * @throws TypeMetapathException 114 * if the sequence contains more than one item and requireSingleton is 115 * {@code true} 116 */ 117 static <T extends IItem> T getFirstItem(@NonNull ISequence<T> items, boolean requireSingleton) { 118 return getFirstItem(items.safeStream(), requireSingleton); 119 } 120 121 /** 122 * Retrieves the first item in a stream of items. 123 * <p> 124 * If the sequence is empty, a {@code null} result is returned. If 125 * requireSingleton is {@code true} and the sequence contains more than one 126 * item, a {@link TypeMetapathException} is thrown. 127 * 128 * @param <T> 129 * the item type to return derived from the provided sequence 130 * @param items 131 * the sequence to retrieve the first item from 132 * @param requireSingleton 133 * if {@code true} then a {@link TypeMetapathException} is thrown if 134 * the sequence contains more than one item 135 * @return {@code null} if the sequence is empty, or the item otherwise 136 * @throws TypeMetapathException 137 * if the sequence contains more than one item and requireSingleton is 138 * {@code true} 139 */ 140 static <T extends IItem> T getFirstItem(@NonNull Stream<T> items, boolean requireSingleton) { 141 return items.limit(2) 142 .reduce((t, u) -> { 143 if (requireSingleton) { 144 throw new InvalidTypeMetapathException( 145 null, 146 String.format("sequence expected to contain only one item, but found multiple")); 147 } 148 return t; 149 }).orElse(null); 150 } 151 152 /** 153 * Retrieves the first item in this sequence. 154 * <p> 155 * If the sequence is empty, a {@code null} result is returned. If 156 * requireSingleton is {@code true} and the sequence contains more than one 157 * item, a {@link TypeMetapathException} is thrown. 158 * 159 * @param requireSingleton 160 * if {@code true} then a {@link TypeMetapathException} is thrown if 161 * the sequence contains more than one item 162 * @return {@code null} if the sequence is empty, or the item otherwise 163 * @throws TypeMetapathException 164 * if the sequence contains more than one item and requireSingleton is 165 * {@code true} 166 */ 167 // FIXME: 3.0: Consider changing this to use the Java Optional 168 @Nullable 169 default ITEM getFirstItem(boolean requireSingleton) { 170 return getFirstItem(this, requireSingleton); 171 } 172 173 /** 174 * An implementation of XPath 3.1 175 * <a href="https://www.w3.org/TR/xpath-functions-31/#func-data">fn:data</a> 176 * supporting <a href="https://www.w3.org/TR/xpath-31/#id-atomization">item 177 * atomization</a>. 178 * 179 * @return the atomized result 180 */ 181 @Override 182 @NonNull 183 default Stream<IAnyAtomicItem> atomize() { 184 return ObjectUtils.notNull(stream().flatMap(IItem::atomize)); 185 } 186 187 /** 188 * Get this sequence as a collection value. 189 * 190 * @return the collection value 191 */ 192 @NonNull 193 default ICollectionValue toCollectionValue() { 194 ICollectionValue retval; 195 switch (size()) { 196 case 0: 197 retval = empty(); 198 break; 199 case 1: 200 // get the singleton item 201 retval = ObjectUtils.notNull(stream().findFirst().get()); 202 break; 203 default: 204 // get this sequence of 2 or more items 205 retval = this; 206 } 207 return retval; 208 } 209 210 @Override 211 default Stream<? extends IItem> flatten() { 212 // TODO: Is a safe stream needed here? 213 return safeStream(); 214 } 215 216 /** 217 * Get this sequence. 218 * 219 * @return this sequence 220 */ 221 @Override 222 default ISequence<ITEM> toSequence() { 223 return this; 224 } 225 226 @Override 227 default ISequence<?> contentsAsSequence() { 228 return this; 229 } 230 231 /** 232 * Apply the provided {@code mapFunction} to each item in the sequence. 233 * 234 * @param <T> 235 * the Java type of the provided items 236 * @param <R> 237 * the Java type of the resulting items 238 * @param mapFunction 239 * the map function to apply to each item in the provided sequence 240 * @param seq 241 * the sequence of items to map 242 * @return a new sequence containing the mapped items 243 */ 244 static <T extends R, R extends IItem> ISequence<R> map( 245 @NonNull Function<T, R> mapFunction, 246 @NonNull ISequence<T> seq) { 247 return seq.safeStream() 248 .map(mapFunction::apply) 249 .collect(CustomCollectors.toSequence()); 250 } 251 252 /** 253 * Returns an unmodifiable sequence containing the provided {@code items}. 254 * 255 * @param <ITEM_TYPE> 256 * the type of items contained in the sequence. 257 * @param items 258 * the items to add to the sequence 259 * @return the new sequence 260 */ 261 @NonNull 262 static <ITEM_TYPE extends IItem> ISequence<ITEM_TYPE> ofCollection( // NOPMD - intentional 263 @NonNull Collection<ITEM_TYPE> items) { 264 ISequence<ITEM_TYPE> retval; 265 if (items.isEmpty()) { 266 retval = empty(); 267 } else if (items.size() == 1) { 268 retval = new SingletonSequence<>(ObjectUtils.notNull(items.iterator().next())); 269 } else { 270 retval = new SequenceN<>(items); 271 } 272 return retval; 273 } 274 275 /** 276 * Returns an unmodifiable sequence containing the provided {@code item}. 277 * <p> 278 * If the item is {@code null} and empty sequence will be created. 279 * 280 * @param <T> 281 * the type of items contained in the sequence. 282 * @param item 283 * the item to add to the sequence 284 * @return the new sequence 285 */ 286 @NonNull 287 static <T extends IItem> ISequence<T> of( // NOPMD - intentional 288 @Nullable T item) { 289 return item == null ? empty() : new SingletonSequence<>(item); 290 } 291 292 /** 293 * Returns an unmodifiable sequence containing the provided {@code items}. 294 * 295 * @param <T> 296 * the type of items contained in the sequence. 297 * @param items 298 * the items to add to the sequence 299 * @return the new sequence 300 */ 301 // TODO: remove null check on callers 302 @NonNull 303 static <T extends IItem> ISequence<T> of(@NonNull Stream<T> items) { 304 return new StreamSequence<>(items); 305 } 306 307 /** 308 * Returns an unmodifiable sequence containing zero elements. 309 * 310 * @param <T> 311 * the item type 312 * @return an empty {@code ISequence} 313 */ 314 @NonNull 315 static <T extends IItem> ISequence<T> of() { 316 return empty(); 317 } 318 319 /** 320 * Returns an unmodifiable sequence containing two items. 321 * 322 * @param <T> 323 * the {@code ISequence}'s item type 324 * @param e1 325 * the first item 326 * @param e2 327 * the second item 328 * @return an {@code ISequence} containing the specified items 329 * @throws NullPointerException 330 * if an item is {@code null} 331 */ 332 @NonNull 333 static <T extends IItem> ISequence<T> of(T e1, T e2) { 334 return new SequenceN<>(e1, e2); 335 } 336 337 /** 338 * Returns an unmodifiable sequence containing three elements. 339 * 340 * @param <T> 341 * the {@code ISequence}'s item type 342 * @param e1 343 * the first item 344 * @param e2 345 * the second item 346 * @param e3 347 * the third item 348 * @return an {@code ISequence} containing the specified items 349 * @throws NullPointerException 350 * if an item is {@code null} 351 */ 352 @NonNull 353 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3) { 354 return new SequenceN<>(e1, e2, e3); 355 } 356 357 /** 358 * Returns an unmodifiable sequence containing four items. 359 * 360 * @param <T> 361 * the {@code ISequence}'s item type 362 * @param e1 363 * the first item 364 * @param e2 365 * the second item 366 * @param e3 367 * the third item 368 * @param e4 369 * the fourth item 370 * @return an {@code ISequence} containing the specified items 371 * @throws NullPointerException 372 * if an item is {@code null} 373 */ 374 @NonNull 375 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4) { 376 return new SequenceN<>(e1, e2, e3, e4); 377 } 378 379 /** 380 * Returns an unmodifiable sequence containing five items. 381 * 382 * @param <T> 383 * the {@code ISequence}'s item type 384 * @param e1 385 * the first item 386 * @param e2 387 * the second item 388 * @param e3 389 * the third item 390 * @param e4 391 * the fourth item 392 * @param e5 393 * the fifth item 394 * @return an {@code ISequence} containing the specified items 395 * @throws NullPointerException 396 * if an item is {@code null} 397 */ 398 @NonNull 399 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5) { 400 return new SequenceN<>(e1, e2, e3, e4, e5); 401 } 402 403 /** 404 * Returns an unmodifiable sequence containing six items. 405 * 406 * @param <T> 407 * the {@code ISequence}'s item type 408 * @param e1 409 * the first item 410 * @param e2 411 * the second item 412 * @param e3 413 * the third item 414 * @param e4 415 * the fourth item 416 * @param e5 417 * the fifth item 418 * @param e6 419 * the sixth item 420 * @return an {@code ISequence} containing the specified items 421 * @throws NullPointerException 422 * if an item is {@code null} 423 */ 424 @NonNull 425 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6) { 426 return new SequenceN<>(e1, e2, e3, e4, e5, e6); 427 } 428 429 /** 430 * Returns an unmodifiable sequence containing seven items. 431 * 432 * @param <T> 433 * the {@code ISequence}'s item type 434 * @param e1 435 * the first item 436 * @param e2 437 * the second item 438 * @param e3 439 * the third item 440 * @param e4 441 * the fourth item 442 * @param e5 443 * the fifth item 444 * @param e6 445 * the sixth item 446 * @param e7 447 * the seventh item 448 * @return an {@code ISequence} containing the specified items 449 * @throws NullPointerException 450 * if an item is {@code null} 451 */ 452 @NonNull 453 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7) { 454 return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7); 455 } 456 457 /** 458 * Returns an unmodifiable sequence containing eight items. 459 * 460 * @param <T> 461 * the {@code ISequence}'s item type 462 * @param e1 463 * the first item 464 * @param e2 465 * the second item 466 * @param e3 467 * the third item 468 * @param e4 469 * the fourth item 470 * @param e5 471 * the fifth item 472 * @param e6 473 * the sixth item 474 * @param e7 475 * the seventh item 476 * @param e8 477 * the eighth item 478 * @return an {@code ISequence} containing the specified items 479 * @throws NullPointerException 480 * if an item is {@code null} 481 */ 482 @NonNull 483 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8) { 484 return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8); 485 } 486 487 /** 488 * Returns an unmodifiable sequence containing nine items. 489 * 490 * @param <T> 491 * the {@code ISequence}'s item type 492 * @param e1 493 * the first item 494 * @param e2 495 * the second item 496 * @param e3 497 * the third item 498 * @param e4 499 * the fourth item 500 * @param e5 501 * the fifth item 502 * @param e6 503 * the sixth item 504 * @param e7 505 * the seventh item 506 * @param e8 507 * the eighth item 508 * @param e9 509 * the ninth item 510 * @return an {@code ISequence} containing the specified items 511 * @throws NullPointerException 512 * if an item is {@code null} 513 */ 514 @NonNull 515 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8, T e9) { 516 return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9); 517 } 518 519 /** 520 * Returns an unmodifiable sequence containing ten items. 521 * 522 * @param <T> 523 * the {@code ISequence}'s item type 524 * @param e1 525 * the first item 526 * @param e2 527 * the second item 528 * @param e3 529 * the third item 530 * @param e4 531 * the fourth item 532 * @param e5 533 * the fifth item 534 * @param e6 535 * the sixth item 536 * @param e7 537 * the seventh item 538 * @param e8 539 * the eighth item 540 * @param e9 541 * the ninth item 542 * @param e10 543 * the tenth item 544 * @return an {@code IArrayItem} containing the specified items 545 * @throws NullPointerException 546 * if an item is {@code null} 547 */ 548 @NonNull 549 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8, T e9, T e10) { 550 return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); 551 } 552 553 /** 554 * Returns an unmodifiable sequence containing an arbitrary number of items. 555 * 556 * @param <T> 557 * the {@code ISequence}'s item type 558 * @param items 559 * the items to be contained in the list 560 * @return an {@code ISequence} containing the specified items 561 * @throws NullPointerException 562 * if an item is {@code null} or if the array is {@code null} 563 */ 564 @SafeVarargs 565 @NonNull 566 static <T extends IItem> ISequence<T> of(@NonNull T... items) { 567 return items.length == 0 ? empty() : new SequenceN<>(items); 568 } 569 570 /** 571 * Returns an unmodifiable sequence containing the items of the given 572 * Collection, in its iteration order. The given Collection must not be null, 573 * and it must not contain any null items. If the given Collection is 574 * subsequently modified, the returned array item will not reflect such 575 * modifications. 576 * 577 * @param <T> 578 * the {@code ISequence}'s item type 579 * @param collection 580 * a {@code Collection} from which items are drawn, must be non-null 581 * @return an {@code ISequence} containing the items of the given 582 * {@code Collection} 583 * @throws NullPointerException 584 * if collection is null, or if it contains any nulls 585 * @since 10 586 */ 587 @SuppressWarnings("unchecked") 588 @NonNull 589 static <T extends IItem> ISequence<T> copyOf(Collection<? extends T> collection) { 590 return collection instanceof IArrayItem 591 ? (ISequence<T>) collection 592 : collection.isEmpty() 593 ? empty() 594 : new SequenceN<>(new ArrayList<>(collection)); 595 } 596 }