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