Coverage Summary for Class: MoreCollectors (com.google.common.collect)

Class Method, % Line, %
MoreCollectors 0% (0/6) 0% (0/10)
MoreCollectors$ToOptionalState 0% (0/6) 0% (0/42)
Total 0% (0/12) 0% (0/52)


1 /* 2  * Copyright (C) 2016 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); 5  * you may not use this file except in compliance with the License. 6  * You may obtain a copy of the License at 7  * 8  * http://www.apache.org/licenses/LICENSE-2.0 9  * 10  * Unless required by applicable law or agreed to in writing, software 11  * distributed under the License is distributed on an "AS IS" BASIS, 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13  * See the License for the specific language governing permissions and 14  * limitations under the License. 15  */ 16  17 package com.google.common.collect; 18  19 import static com.google.common.base.Preconditions.checkNotNull; 20  21 import com.google.common.annotations.GwtCompatible; 22 import java.util.ArrayList; 23 import java.util.List; 24 import java.util.NoSuchElementException; 25 import java.util.Optional; 26 import java.util.stream.Collector; 27 import org.checkerframework.checker.nullness.qual.Nullable; 28  29 /** 30  * Collectors not present in {@code java.util.stream.Collectors} that are not otherwise associated 31  * with a {@code com.google.common} type. 32  * 33  * @author Louis Wasserman 34  * @since 21.0 35  */ 36 @GwtCompatible 37 public final class MoreCollectors { 38  39  /* 40  * TODO(lowasser): figure out if we can convert this to a concurrent AtomicReference-based 41  * collector without breaking j2cl? 42  */ 43  private static final Collector<Object, ?, Optional<Object>> TO_OPTIONAL = 44  Collector.of( 45  ToOptionalState::new, 46  ToOptionalState::add, 47  ToOptionalState::combine, 48  ToOptionalState::getOptional, 49  Collector.Characteristics.UNORDERED); 50  51  /** 52  * A collector that converts a stream of zero or one elements to an {@code Optional}. The returned 53  * collector throws an {@code IllegalArgumentException} if the stream consists of two or more 54  * elements, and a {@code NullPointerException} if the stream consists of exactly one element, 55  * which is null. 56  */ 57  @SuppressWarnings("unchecked") 58  public static <T> Collector<T, ?, Optional<T>> toOptional() { 59  return (Collector) TO_OPTIONAL; 60  } 61  62  private static final Object NULL_PLACEHOLDER = new Object(); 63  64  private static final Collector<Object, ?, Object> ONLY_ELEMENT = 65  Collector.of( 66  ToOptionalState::new, 67  (state, o) -> state.add((o == null) ? NULL_PLACEHOLDER : o), 68  ToOptionalState::combine, 69  state -> { 70  Object result = state.getElement(); 71  return (result == NULL_PLACEHOLDER) ? null : result; 72  }, 73  Collector.Characteristics.UNORDERED); 74  75  /** 76  * A collector that takes a stream containing exactly one element and returns that element. The 77  * returned collector throws an {@code IllegalArgumentException} if the stream consists of two or 78  * more elements, and a {@code NoSuchElementException} if the stream is empty. 79  */ 80  @SuppressWarnings("unchecked") 81  public static <T> Collector<T, ?, T> onlyElement() { 82  return (Collector) ONLY_ELEMENT; 83  } 84  85  /** 86  * This atrocity is here to let us report several of the elements in the stream if there were more 87  * than one, not just two. 88  */ 89  private static final class ToOptionalState { 90  static final int MAX_EXTRAS = 4; 91  92  @Nullable Object element; 93  @Nullable List<Object> extras; 94  95  ToOptionalState() { 96  element = null; 97  extras = null; 98  } 99  100  IllegalArgumentException multiples(boolean overflow) { 101  StringBuilder sb = 102  new StringBuilder().append("expected one element but was: <").append(element); 103  for (Object o : extras) { 104  sb.append(", ").append(o); 105  } 106  if (overflow) { 107  sb.append(", ..."); 108  } 109  sb.append('>'); 110  throw new IllegalArgumentException(sb.toString()); 111  } 112  113  void add(Object o) { 114  checkNotNull(o); 115  if (element == null) { 116  this.element = o; 117  } else if (extras == null) { 118  extras = new ArrayList<>(MAX_EXTRAS); 119  extras.add(o); 120  } else if (extras.size() < MAX_EXTRAS) { 121  extras.add(o); 122  } else { 123  throw multiples(true); 124  } 125  } 126  127  ToOptionalState combine(ToOptionalState other) { 128  if (element == null) { 129  return other; 130  } else if (other.element == null) { 131  return this; 132  } else { 133  if (extras == null) { 134  extras = new ArrayList<>(); 135  } 136  extras.add(other.element); 137  if (other.extras != null) { 138  this.extras.addAll(other.extras); 139  } 140  if (extras.size() > MAX_EXTRAS) { 141  extras.subList(MAX_EXTRAS, extras.size()).clear(); 142  throw multiples(true); 143  } 144  return this; 145  } 146  } 147  148  Optional<Object> getOptional() { 149  if (extras == null) { 150  return Optional.ofNullable(element); 151  } else { 152  throw multiples(false); 153  } 154  } 155  156  Object getElement() { 157  if (element == null) { 158  throw new NoSuchElementException(); 159  } else if (extras == null) { 160  return element; 161  } else { 162  throw multiples(false); 163  } 164  } 165  } 166  167  private MoreCollectors() {} 168 }