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