• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.uber.nullaway.handlers.stream;
2 /*
3  * Copyright (c) 2017 Uber Technologies, Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21  * THE SOFTWARE.
22  */
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.collect.ImmutableSet;
26 import com.google.errorprone.predicates.TypePredicate;
27 import java.util.ArrayList;
28 import java.util.List;
29 
30 /**
31  * Used to produce a new list of StreamTypeRecord models, where each model represents a class from a
32  * stream-based API such as RxJava.
33  *
34  * <p>This class should be used as:
35  *
36  * <p>[...] models = StreamModelBuilder.start() // Start the builder .addStreamType(...) // Add a
37  * type filter matching a stream type .withX(...) // Model the type methods ... .end();
38  */
39 public class StreamModelBuilder {
40 
41   private final List<StreamTypeRecord> typeRecords = new ArrayList<>();
42   private TypePredicate tp = null;
43   private ImmutableSet.Builder<String> filterMethodSigs;
44   private ImmutableSet.Builder<String> filterMethodSimpleNames;
45   private ImmutableMap.Builder<String, MaplikeMethodRecord> mapMethodSigToRecord;
46   private ImmutableMap.Builder<String, MaplikeMethodRecord> mapMethodSimpleNameToRecord;
47   private ImmutableSet.Builder<String> passthroughMethodSigs;
48   private ImmutableSet.Builder<String> passthroughMethodSimpleNames;
49 
StreamModelBuilder()50   private StreamModelBuilder() {}
51 
52   /**
53    * Get an empty StreamModelBuilder.
54    *
55    * @return An empty StreamModelBuilder.
56    */
start()57   public static StreamModelBuilder start() {
58     return new StreamModelBuilder();
59   }
60 
finalizeOpenStreamTypeRecord()61   private void finalizeOpenStreamTypeRecord() {
62     if (this.tp != null) {
63       typeRecords.add(
64           new StreamTypeRecord(
65               this.tp,
66               filterMethodSigs.build(),
67               filterMethodSimpleNames.build(),
68               mapMethodSigToRecord.build(),
69               mapMethodSimpleNameToRecord.build(),
70               passthroughMethodSigs.build(),
71               passthroughMethodSimpleNames.build()));
72     }
73   }
74 
75   /**
76    * Add a stream type to our models.
77    *
78    * @param tp A type predicate matching the class/interface of the type in our stream-based API.
79    * @return This builder (for chaining).
80    */
addStreamType(TypePredicate tp)81   public StreamModelBuilder addStreamType(TypePredicate tp) {
82     finalizeOpenStreamTypeRecord();
83     this.tp = tp;
84     this.filterMethodSigs = ImmutableSet.builder();
85     this.filterMethodSimpleNames = ImmutableSet.builder();
86     this.mapMethodSigToRecord = ImmutableMap.builder();
87     this.mapMethodSimpleNameToRecord = ImmutableMap.builder();
88     this.passthroughMethodSigs = ImmutableSet.builder();
89     this.passthroughMethodSimpleNames = ImmutableSet.builder();
90     return this;
91   }
92 
93   /**
94    * Add a filter method to the last added stream type.
95    *
96    * @param filterMethodSig The full sub-signature (everything except the receiver type) of the
97    *     filter method.
98    * @return This builder (for chaining).
99    */
withFilterMethodFromSignature(String filterMethodSig)100   public StreamModelBuilder withFilterMethodFromSignature(String filterMethodSig) {
101     this.filterMethodSigs.add(filterMethodSig);
102     return this;
103   }
104 
105   /**
106    * Add all methods of the last stream type with the given simple name as filter methods.
107    *
108    * @param methodSimpleName The method's simple name.
109    * @return This builder (for chaining).
110    */
withFilterMethodAllFromName(String methodSimpleName)111   public StreamModelBuilder withFilterMethodAllFromName(String methodSimpleName) {
112     this.filterMethodSimpleNames.add(methodSimpleName);
113     return this;
114   }
115 
116   /**
117    * Add a model for a map method to the last added stream type.
118    *
119    * @param methodSig The full sub-signature (everything except the receiver type) of the method.
120    * @param innerMethodName The name of the inner "apply" method of the callback or functional
121    *     interface that must be passed to this method.
122    * @param argsFromStream The indexes (starting at 0, not counting the receiver) of all the
123    *     arguments to this method that receive objects from the stream.
124    * @return This builder (for chaining).
125    */
withMapMethodFromSignature( String methodSig, String innerMethodName, ImmutableSet<Integer> argsFromStream)126   public StreamModelBuilder withMapMethodFromSignature(
127       String methodSig, String innerMethodName, ImmutableSet<Integer> argsFromStream) {
128     this.mapMethodSigToRecord.put(
129         methodSig, new MaplikeMethodRecord(innerMethodName, argsFromStream));
130     return this;
131   }
132 
133   /**
134    * Add all methods of the last stream type with the given simple name as map methods.
135    *
136    * @param methodSimpleName The method's simple name.
137    * @param innerMethodName The name of the inner "apply" method of the callback or functional
138    *     interface that must be passed to this method.
139    * @param argsFromStream The indexes (starting at 0, not counting the receiver) of all the
140    *     arguments to this method that receive objects from the stream. Must be the same for all
141    *     methods with this name (else use withMapMethodFromSignature).
142    * @return This builder (for chaining).
143    */
withMapMethodAllFromName( String methodSimpleName, String innerMethodName, ImmutableSet<Integer> argsFromStream)144   public StreamModelBuilder withMapMethodAllFromName(
145       String methodSimpleName, String innerMethodName, ImmutableSet<Integer> argsFromStream) {
146     this.mapMethodSimpleNameToRecord.put(
147         methodSimpleName, new MaplikeMethodRecord(innerMethodName, argsFromStream));
148     return this;
149   }
150 
151   /**
152    * Add a passthrough method to the last added stream type.
153    *
154    * <p>A passthrough method is a method that affects the stream but doesn't change the nullability
155    * information of the elements inside the stream (e.g. in o.filter(...).sync().map(...), sync() is
156    * a passthrough method if the exact same objects that are added to the stream at the end of
157    * filter are those that will be consumed by map(...).
158    *
159    * @param passthroughMethodSig The full sub-signature (everything except the receiver type) of the
160    *     method.
161    * @return This builder (for chaining).
162    */
withPassthroughMethodFromSignature(String passthroughMethodSig)163   public StreamModelBuilder withPassthroughMethodFromSignature(String passthroughMethodSig) {
164     this.passthroughMethodSigs.add(passthroughMethodSig);
165     return this;
166   }
167 
168   /**
169    * Add all methods of the last stream type with the given simple name as passthrough methods.
170    *
171    * @param methodSimpleName The method's simple name.
172    * @return This builder (for chaining).
173    */
withPassthroughMethodAllFromName(String methodSimpleName)174   public StreamModelBuilder withPassthroughMethodAllFromName(String methodSimpleName) {
175     this.passthroughMethodSimpleNames.add(methodSimpleName);
176     return this;
177   }
178 
179   /**
180    * Add a passthrough method that uses the value to the last added stream type.
181    *
182    * <p>Like a normal passthrough method, but it takes a callback which inspects but doesn't change
183    * the elements flowing through the stream.
184    *
185    * @param passthroughMethodSig The full sub-signature (everything except the receiver type) of the
186    *     method.
187    * @param innerMethodName The name of the inner method of the callback or functional interface
188    *     that must be passed to this method.
189    * @param argsFromStream The indexes (starting at 0, not counting the receiver) of all the
190    *     arguments to this method that receive objects from the stream.
191    * @return This builder (for chaining).
192    */
withUseAndPassthroughMethodFromSignature( String passthroughMethodSig, String innerMethodName, ImmutableSet<Integer> argsFromStream)193   public StreamModelBuilder withUseAndPassthroughMethodFromSignature(
194       String passthroughMethodSig, String innerMethodName, ImmutableSet<Integer> argsFromStream) {
195     this.mapMethodSigToRecord.put(
196         passthroughMethodSig, new MaplikeMethodRecord(innerMethodName, argsFromStream));
197     this.passthroughMethodSigs.add(passthroughMethodSig);
198     return this;
199   }
200 
201   /**
202    * Add all methods of the last stream type with the given simple name as use-and-passthrough
203    * methods.
204    *
205    * @param methodSimpleName The method's simple name.
206    * @param innerMethodName The name of the inner method of the callback or functional interface
207    *     that must be passed to this method.
208    * @param argsFromStream The indexes (starting at 0, not counting the receiver) of all the
209    *     arguments to this method that receive objects from the stream. Must be the same for all
210    *     methods with this name (else use withUseAndPassthroughMethodFromSignature).
211    * @return This builder (for chaining).
212    */
withUseAndPassthroughMethodAllFromName( String methodSimpleName, String innerMethodName, ImmutableSet<Integer> argsFromStream)213   public StreamModelBuilder withUseAndPassthroughMethodAllFromName(
214       String methodSimpleName, String innerMethodName, ImmutableSet<Integer> argsFromStream) {
215     this.mapMethodSimpleNameToRecord.put(
216         methodSimpleName, new MaplikeMethodRecord(innerMethodName, argsFromStream));
217     this.passthroughMethodSimpleNames.add(methodSimpleName);
218     return this;
219   }
220 
221   /**
222    * Turn the models added to this builder into a list of StreamTypeRecord objects.
223    *
224    * @return The finalized (immutable) models.
225    */
end()226   public ImmutableList<StreamTypeRecord> end() {
227     finalizeOpenStreamTypeRecord();
228     return ImmutableList.copyOf(typeRecords);
229   }
230 }
231