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.safeStream(), 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 @Override 186 default Stream<? extends IItem> flatten() { 187 // TODO: Is a safe stream needed here? 188 return safeStream(); 189 } 190 191 /** 192 * A {@link Collector} implementation to generates a sequence from a stream of 193 * Metapath items. 194 * 195 * @param <ITEM_TYPE> 196 * the Java type of the items 197 * @return a collector that will generate a sequence 198 */ 199 @NonNull 200 static <ITEM_TYPE extends IItem> Collector<ITEM_TYPE, ?, ISequence<ITEM_TYPE>> toSequence() { 201 return new Collector<ITEM_TYPE, List<ITEM_TYPE>, ISequence<ITEM_TYPE>>() { 202 203 @Override 204 public Supplier<List<ITEM_TYPE>> supplier() { 205 return ArrayList::new; 206 } 207 208 @Override 209 public BiConsumer<List<ITEM_TYPE>, ITEM_TYPE> accumulator() { 210 return List::add; 211 } 212 213 @Override 214 public BinaryOperator<List<ITEM_TYPE>> combiner() { 215 return (list1, list2) -> { 216 list1.addAll(list2); 217 return list1; 218 }; 219 } 220 221 @Override 222 public Function<List<ITEM_TYPE>, ISequence<ITEM_TYPE>> finisher() { 223 return list -> ofCollection(ObjectUtils.notNull(list)); 224 } 225 226 @Override 227 public Set<Characteristics> characteristics() { 228 return Collections.emptySet(); 229 } 230 }; 231 } 232 233 @Override 234 default ISequence<ITEM> asSequence() { 235 return this; 236 } 237 238 /** 239 * Apply the provided {@code mapFunction} to each item in the sequence. 240 * 241 * @param <T> 242 * the Java type of the provided items 243 * @param <R> 244 * the Java type of the resulting items 245 * @param mapFunction 246 * the map function to apply to each item in the provided sequence 247 * @param seq 248 * the sequence of items to map 249 * @return a new sequence containing the mapped items 250 */ 251 static <T extends R, R extends IItem> ISequence<R> map( 252 @NonNull Function<T, R> mapFunction, 253 @NonNull ISequence<T> seq) { 254 return seq.safeStream() 255 .map(mapFunction::apply) 256 .collect(toSequence()); 257 } 258 259 /** 260 * Returns an unmodifiable sequence containing the provided {@code items}. 261 * 262 * @param <ITEM_TYPE> 263 * the type of items contained in the sequence. 264 * @param items 265 * the items to add to the sequence 266 * @return the new sequence 267 */ 268 @NonNull 269 static <ITEM_TYPE extends IItem> ISequence<ITEM_TYPE> ofCollection( // NOPMD - intentional 270 @NonNull List<ITEM_TYPE> items) { 271 ISequence<ITEM_TYPE> retval; 272 if (items.isEmpty()) { 273 retval = empty(); 274 } else if (items.size() == 1) { 275 retval = new SingletonSequence<>(ObjectUtils.notNull(items.iterator().next())); 276 } else { 277 retval = new SequenceN<>(items); 278 } 279 return retval; 280 } 281 282 /** 283 * Returns an unmodifiable sequence containing the provided {@code item}. 284 * <p> 285 * If the item is {@code null} and empty sequence will be created. 286 * 287 * @param <T> 288 * the type of items contained in the sequence. 289 * @param item 290 * the item to add to the sequence 291 * @return the new sequence 292 */ 293 @NonNull 294 static <T extends IItem> ISequence<T> of( // NOPMD - intentional 295 @Nullable T item) { 296 return item == null ? empty() : new SingletonSequence<>(item); 297 } 298 299 /** 300 * Returns an unmodifiable sequence containing the provided {@code items}. 301 * 302 * @param <T> 303 * the type of items contained in the sequence. 304 * @param items 305 * the items to add to the sequence 306 * @return the new sequence 307 */ 308 // TODO: remove null check on callers 309 @NonNull 310 static <T extends IItem> ISequence<T> of(@NonNull Stream<T> items) { 311 return new StreamSequence<>(items); 312 } 313 314 /** 315 * Returns an unmodifiable sequence containing zero elements. 316 * 317 * @param <T> 318 * the item type 319 * @return an empty {@code ISequence} 320 */ 321 @NonNull 322 static <T extends IItem> ISequence<T> of() { 323 return empty(); 324 } 325 326 /** 327 * Returns an unmodifiable sequence containing two items. 328 * 329 * @param <T> 330 * the {@code ISequence}'s item type 331 * @param e1 332 * the first item 333 * @param e2 334 * the second 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) { 341 return new SequenceN<>(e1, e2); 342 } 343 344 /** 345 * Returns an unmodifiable sequence containing three elements. 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 * @return an {@code ISequence} containing the specified items 356 * @throws NullPointerException 357 * if an item is {@code null} 358 */ 359 @NonNull 360 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3) { 361 return new SequenceN<>(e1, e2, e3); 362 } 363 364 /** 365 * Returns an unmodifiable sequence containing four items. 366 * 367 * @param <T> 368 * the {@code ISequence}'s item type 369 * @param e1 370 * the first item 371 * @param e2 372 * the second item 373 * @param e3 374 * the third item 375 * @param e4 376 * the fourth item 377 * @return an {@code ISequence} containing the specified items 378 * @throws NullPointerException 379 * if an item is {@code null} 380 */ 381 @NonNull 382 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4) { 383 return new SequenceN<>(e1, e2, e3, e4); 384 } 385 386 /** 387 * Returns an unmodifiable sequence containing five items. 388 * 389 * @param <T> 390 * the {@code ISequence}'s item type 391 * @param e1 392 * the first item 393 * @param e2 394 * the second item 395 * @param e3 396 * the third item 397 * @param e4 398 * the fourth item 399 * @param e5 400 * the fifth item 401 * @return an {@code ISequence} containing the specified items 402 * @throws NullPointerException 403 * if an item is {@code null} 404 */ 405 @NonNull 406 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5) { 407 return new SequenceN<>(e1, e2, e3, e4, e5); 408 } 409 410 /** 411 * Returns an unmodifiable sequence containing six items. 412 * 413 * @param <T> 414 * the {@code ISequence}'s item type 415 * @param e1 416 * the first item 417 * @param e2 418 * the second item 419 * @param e3 420 * the third item 421 * @param e4 422 * the fourth item 423 * @param e5 424 * the fifth item 425 * @param e6 426 * the sixth item 427 * @return an {@code ISequence} containing the specified items 428 * @throws NullPointerException 429 * if an item is {@code null} 430 */ 431 @NonNull 432 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6) { 433 return new SequenceN<>(e1, e2, e3, e4, e5, e6); 434 } 435 436 /** 437 * Returns an unmodifiable sequence containing seven items. 438 * 439 * @param <T> 440 * the {@code ISequence}'s item type 441 * @param e1 442 * the first item 443 * @param e2 444 * the second item 445 * @param e3 446 * the third item 447 * @param e4 448 * the fourth item 449 * @param e5 450 * the fifth item 451 * @param e6 452 * the sixth item 453 * @param e7 454 * the seventh item 455 * @return an {@code ISequence} containing the specified items 456 * @throws NullPointerException 457 * if an item is {@code null} 458 */ 459 @NonNull 460 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7) { 461 return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7); 462 } 463 464 /** 465 * Returns an unmodifiable sequence containing eight items. 466 * 467 * @param <T> 468 * the {@code ISequence}'s item type 469 * @param e1 470 * the first item 471 * @param e2 472 * the second item 473 * @param e3 474 * the third item 475 * @param e4 476 * the fourth item 477 * @param e5 478 * the fifth item 479 * @param e6 480 * the sixth item 481 * @param e7 482 * the seventh item 483 * @param e8 484 * the eighth item 485 * @return an {@code ISequence} containing the specified items 486 * @throws NullPointerException 487 * if an item is {@code null} 488 */ 489 @NonNull 490 static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8) { 491 return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8); 492 } 493 494 /** 495 * Returns an unmodifiable sequence containing nine items. 496 * 497 * @param <T> 498 * the {@code ISequence}'s item type 499 * @param e1 500 * the first item 501 * @param e2 502 * the second item 503 * @param e3 504 * the third item 505 * @param e4 506 * the fourth item 507 * @param e5 508 * the fifth item 509 * @param e6 510 * the sixth item 511 * @param e7 512 * the seventh item 513 * @param e8 514 * the eighth item 515 * @param e9 516 * the ninth item 517 * @return an {@code ISequence} containing the specified items 518 * @throws NullPointerException 519 * if an item is {@code null} 520 */ 521 @NonNull 522 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) { 523 return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9); 524 } 525 526 /** 527 * Returns an unmodifiable sequence containing ten items. 528 * 529 * @param <T> 530 * the {@code ISequence}'s item type 531 * @param e1 532 * the first item 533 * @param e2 534 * the second item 535 * @param e3 536 * the third item 537 * @param e4 538 * the fourth item 539 * @param e5 540 * the fifth item 541 * @param e6 542 * the sixth item 543 * @param e7 544 * the seventh item 545 * @param e8 546 * the eighth item 547 * @param e9 548 * the ninth item 549 * @param e10 550 * the tenth item 551 * @return an {@code IArrayItem} containing the specified items 552 * @throws NullPointerException 553 * if an item is {@code null} 554 */ 555 @NonNull 556 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) { 557 return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); 558 } 559 560 /** 561 * Returns an unmodifiable sequence containing an arbitrary number of items. 562 * 563 * @param <T> 564 * the {@code ISequence}'s item type 565 * @param items 566 * the items to be contained in the list 567 * @return an {@code ISequence} containing the specified items 568 * @throws NullPointerException 569 * if an item is {@code null} or if the array is {@code null} 570 */ 571 @SafeVarargs 572 @NonNull 573 static <T extends IItem> ISequence<T> of(@NonNull T... items) { 574 return items.length == 0 ? empty() : new SequenceN<>(items); 575 } 576 577 /** 578 * Returns an unmodifiable sequence containing the items of the given 579 * Collection, in its iteration order. The given Collection must not be null, 580 * and it must not contain any null items. If the given Collection is 581 * subsequently modified, the returned array item will not reflect such 582 * modifications. 583 * 584 * @param <T> 585 * the {@code ISequence}'s item type 586 * @param collection 587 * a {@code Collection} from which items are drawn, must be non-null 588 * @return an {@code ISequence} containing the items of the given 589 * {@code Collection} 590 * @throws NullPointerException 591 * if collection is null, or if it contains any nulls 592 * @since 10 593 */ 594 @SuppressWarnings("unchecked") 595 @NonNull 596 static <T extends IItem> ISequence<T> copyOf(Collection<? extends T> collection) { 597 return collection instanceof IArrayItem 598 ? (ISequence<T>) collection 599 : collection.isEmpty() 600 ? empty() 601 : new SequenceN<>(new ArrayList<>(collection)); 602 } 603 }