1 /* 2 * Copyright (C) 2019 The Dagger 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 dagger.internal.codegen.extension; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import java.util.ArrayList; 22 import java.util.List; 23 import java.util.NoSuchElementException; 24 import java.util.Optional; 25 import java.util.stream.Collector; 26 import javax.annotation.Nullable; 27 28 /** 29 * A copy of {@link com.google.common.collect.MoreCollectors} to avoid issues with the '-android' 30 * variant of Guava. See b/68008628 31 */ 32 public final class DaggerCollectors { 33 34 private static final Collector<Object, ?, Optional<Object>> TO_OPTIONAL = 35 Collector.of( 36 ToOptionalState::new, 37 ToOptionalState::add, 38 ToOptionalState::combine, 39 ToOptionalState::getOptional, 40 Collector.Characteristics.UNORDERED); 41 42 /** 43 * A collector that converts a stream of zero or one elements to an {@code Optional}. The returned 44 * collector throws an {@code IllegalArgumentException} if the stream consists of two or more 45 * elements, and a {@code NullPointerException} if the stream consists of exactly one element, 46 * which is null. 47 */ 48 @SuppressWarnings("unchecked") toOptional()49 public static <T> Collector<T, ?, Optional<T>> toOptional() { 50 return (Collector) TO_OPTIONAL; 51 } 52 53 private static final Object NULL_PLACEHOLDER = new Object(); 54 55 private static final Collector<Object, ?, Object> ONLY_ELEMENT = 56 Collector.of( 57 ToOptionalState::new, 58 (state, o) -> state.add((o == null) ? NULL_PLACEHOLDER : o), 59 ToOptionalState::combine, 60 state -> { 61 Object result = state.getElement(); 62 return (result == NULL_PLACEHOLDER) ? null : result; 63 }, 64 Collector.Characteristics.UNORDERED); 65 66 /** 67 * A collector that takes a stream containing exactly one element and returns that element. The 68 * returned collector throws an {@code IllegalArgumentException} if the stream consists of two or 69 * more elements, and a {@code NoSuchElementException} if the stream is empty. 70 */ 71 @SuppressWarnings("unchecked") onlyElement()72 public static <T> Collector<T, ?, T> onlyElement() { 73 return (Collector) ONLY_ELEMENT; 74 } 75 76 private static final class ToOptionalState { 77 static final int MAX_EXTRAS = 4; 78 79 @Nullable Object element; 80 @Nullable List<Object> extras; 81 ToOptionalState()82 ToOptionalState() { 83 element = null; 84 extras = null; 85 } 86 multiples(boolean overflow)87 IllegalArgumentException multiples(boolean overflow) { 88 StringBuilder sb = 89 new StringBuilder().append("expected one element but was: <").append(element); 90 for (Object o : extras) { 91 sb.append(", ").append(o); 92 } 93 if (overflow) { 94 sb.append(", ..."); 95 } 96 sb.append('>'); 97 throw new IllegalArgumentException(sb.toString()); 98 } 99 add(Object o)100 void add(Object o) { 101 checkNotNull(o); 102 if (element == null) { 103 this.element = o; 104 } else if (extras == null) { 105 extras = new ArrayList<>(MAX_EXTRAS); 106 extras.add(o); 107 } else if (extras.size() < MAX_EXTRAS) { 108 extras.add(o); 109 } else { 110 throw multiples(true); 111 } 112 } 113 combine(ToOptionalState other)114 ToOptionalState combine(ToOptionalState other) { 115 if (element == null) { 116 return other; 117 } else if (other.element == null) { 118 return this; 119 } else { 120 if (extras == null) { 121 extras = new ArrayList<>(); 122 } 123 extras.add(other.element); 124 if (other.extras != null) { 125 this.extras.addAll(other.extras); 126 } 127 if (extras.size() > MAX_EXTRAS) { 128 extras.subList(MAX_EXTRAS, extras.size()).clear(); 129 throw multiples(true); 130 } 131 return this; 132 } 133 } 134 getOptional()135 Optional<Object> getOptional() { 136 if (extras == null) { 137 return Optional.ofNullable(element); 138 } else { 139 throw multiples(false); 140 } 141 } 142 getElement()143 Object getElement() { 144 if (element == null) { 145 throw new NoSuchElementException(); 146 } else if (extras == null) { 147 return element; 148 } else { 149 throw multiples(false); 150 } 151 } 152 } 153 DaggerCollectors()154 private DaggerCollectors() {} 155 } 156