blob: 1ad7ada9501848aaca67bbfef5a786803a7ddc0a [file] [log] [blame]
Ashley Rosede080eb2018-12-07 17:20:25 -05001/*
Ashley Rosec1a4dec2018-12-13 18:06:30 -05002 * Copyright 2019 The Android Open Source Project
Ashley Rosede080eb2018-12-07 17:20:25 -05003 *
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
17package android.processor.view.inspector;
18
19import com.squareup.javapoet.ClassName;
20
Ashley Rose171a7232018-12-11 17:32:58 -050021import java.util.Collection;
Ashley Rose0b671da2019-01-25 15:41:29 -050022import java.util.Collections;
Ashley Rose171a7232018-12-11 17:32:58 -050023import java.util.HashMap;
Ashley Rose0b671da2019-01-25 15:41:29 -050024import java.util.List;
Ashley Rose171a7232018-12-11 17:32:58 -050025import java.util.Map;
Ashley Rose89d6bce2019-03-01 19:24:50 -050026import java.util.NoSuchElementException;
Ashley Rosec1a4dec2018-12-13 18:06:30 -050027import java.util.Objects;
Ashley Rosede080eb2018-12-07 17:20:25 -050028import java.util.Optional;
29
30/**
31 * Model of an inspectable class derived from annotations.
32 *
Ashley Rose171a7232018-12-11 17:32:58 -050033 * This class does not use any {@code javax.lang.model} objects to facilitate building models for
34 * testing {@link InspectionCompanionGenerator}.
Ashley Rosede080eb2018-12-07 17:20:25 -050035 */
36public final class InspectableClassModel {
37 private final ClassName mClassName;
Ashley Rose171a7232018-12-11 17:32:58 -050038 private final Map<String, Property> mPropertyMap;
Ashley Rosede080eb2018-12-07 17:20:25 -050039 private Optional<String> mNodeName = Optional.empty();
40
41 /**
42 * @param className The name of the modeled class
43 */
44 public InspectableClassModel(ClassName className) {
45 mClassName = className;
Ashley Rose171a7232018-12-11 17:32:58 -050046 mPropertyMap = new HashMap<>();
Ashley Rosede080eb2018-12-07 17:20:25 -050047 }
48
49 public ClassName getClassName() {
50 return mClassName;
51 }
52
53 public Optional<String> getNodeName() {
54 return mNodeName;
55 }
56
57 public void setNodeName(Optional<String> nodeName) {
58 mNodeName = nodeName;
59 }
Ashley Rose171a7232018-12-11 17:32:58 -050060
61 /**
62 * Add a property to the model, replacing an existing property of the same name.
63 *
64 * @param property The property to add or replace
65 */
66 public void putProperty(Property property) {
67 mPropertyMap.put(property.getName(), property);
68 }
69
70 /**
71 * Get a property by name.
72 *
73 * @param name The name of the property
74 * @return The property or an empty optional
75 */
76 public Optional<Property> getProperty(String name) {
Ashley Rosec1a4dec2018-12-13 18:06:30 -050077 return Optional.ofNullable(mPropertyMap.get(name));
Ashley Rose171a7232018-12-11 17:32:58 -050078 }
79
80 /**
81 * Get all the properties defined on this model.
82 *
83 * @return An un-ordered collection of properties
84 */
85 public Collection<Property> getAllProperties() {
86 return mPropertyMap.values();
87 }
88
89 /**
Ashley Rose89d6bce2019-03-01 19:24:50 -050090 * Represents a way to access a property, either a getter or a field.
91 */
92 public static final class Accessor {
93 private final String mName;
94 private final Type mType;
95
96 /**
97 * Construct an accessor for a field.
98 *
99 * @param name The name of the field
100 * @return The new accessor
101 * @see Type#FIELD
102 */
103 static Accessor ofField(String name) {
104 return new Accessor(name, Type.FIELD);
105 }
106
107 /**
108 * Construct an accessor for a getter.
109 *
110 * @param name The name of the getter
111 * @return The new accessor
112 * @see Type#GETTER
113 */
114 static Accessor ofGetter(String name) {
115 return new Accessor(name, Type.GETTER);
116 }
117
118 public Accessor(String name, Type type) {
119 mName = Objects.requireNonNull(name, "Accessor name must not be null");
120 mType = Objects.requireNonNull(type, "Accessor type must not be null");
121 }
122
123 public String getName() {
124 return mName;
125 }
126
127 public Type getType() {
128 return mType;
129 }
130
131 /**
132 * Get the invocation of this accessor.
133 *
134 * Example: {@code "getValue()"} for a getter or {@code "valueField"} for a field.
135 *
136 * @return A string representing the invocation of this accessor
137 */
138 public String invocation() {
139 switch (mType) {
140 case FIELD:
141 return mName;
142 case GETTER:
143 return String.format("%s()", mName);
144 default:
145 throw new NoSuchElementException(
146 String.format("No such accessor type %s", mType));
147 }
148 }
149
150 public enum Type {
151 /**
152 * A property accessed by a public field.
153 *
154 * @see #ofField(String)
155 */
156 FIELD,
157
158 /**
159 * A property accessed by a public getter method.
160 *
161 * @see #ofGetter(String)
162 */
163 GETTER
164 }
165 }
166
167 /**
Ashley Rose171a7232018-12-11 17:32:58 -0500168 * Model an inspectable property
169 */
170 public static final class Property {
171 private final String mName;
Ashley Rose89d6bce2019-03-01 19:24:50 -0500172 private final Accessor mAccessor;
Ashley Rosec1a4dec2018-12-13 18:06:30 -0500173 private final Type mType;
Ashley Rose171a7232018-12-11 17:32:58 -0500174 private boolean mAttributeIdInferrableFromR = true;
175 private int mAttributeId = 0;
Ashley Rose0b671da2019-01-25 15:41:29 -0500176 private List<IntEnumEntry> mIntEnumEntries;
177 private List<IntFlagEntry> mIntFlagEntries;
Ashley Rose171a7232018-12-11 17:32:58 -0500178
Ashley Rose89d6bce2019-03-01 19:24:50 -0500179 public Property(String name, Accessor accessor, Type type) {
Ashley Rosec1a4dec2018-12-13 18:06:30 -0500180 mName = Objects.requireNonNull(name, "Name must not be null");
Ashley Rose89d6bce2019-03-01 19:24:50 -0500181 mAccessor = Objects.requireNonNull(accessor, "Accessor must not be null");
Ashley Rosec1a4dec2018-12-13 18:06:30 -0500182 mType = Objects.requireNonNull(type, "Type must not be null");
Ashley Rose171a7232018-12-11 17:32:58 -0500183 }
184
185 public int getAttributeId() {
186 return mAttributeId;
187 }
188
189 /**
190 * Set the attribute ID, and mark the attribute ID as non-inferrable.
191 *
192 * @param attributeId The attribute ID for this property
193 */
194 public void setAttributeId(int attributeId) {
195 mAttributeIdInferrableFromR = false;
196 mAttributeId = attributeId;
197 }
198
199 public boolean isAttributeIdInferrableFromR() {
200 return mAttributeIdInferrableFromR;
201 }
202
203 public void setAttributeIdInferrableFromR(boolean attributeIdInferrableFromR) {
204 mAttributeIdInferrableFromR = attributeIdInferrableFromR;
205 }
206
207 public String getName() {
208 return mName;
209 }
210
Ashley Rose89d6bce2019-03-01 19:24:50 -0500211 public Accessor getAccessor() {
212 return mAccessor;
Ashley Rose171a7232018-12-11 17:32:58 -0500213 }
214
Ashley Rose171a7232018-12-11 17:32:58 -0500215 public Type getType() {
216 return mType;
217 }
218
Ashley Rose0b671da2019-01-25 15:41:29 -0500219 /**
220 * Get the mapping for an {@code int} enumeration, if present.
221 *
222 * @return A list of mapping entries, empty if absent
223 */
224 public List<IntEnumEntry> getIntEnumEntries() {
225 if (mIntEnumEntries != null) {
226 return mIntEnumEntries;
227 } else {
228 return Collections.emptyList();
229 }
230 }
231
232 public void setIntEnumEntries(List<IntEnumEntry> intEnumEntries) {
233 mIntEnumEntries = intEnumEntries;
234 }
235
236 /**
237 * Get the mapping of {@code int} flags, if present.
238 *
239 * @return A list of mapping entries, empty if absent
240 */
241 public List<IntFlagEntry> getIntFlagEntries() {
242 if (mIntFlagEntries != null) {
243 return mIntFlagEntries;
244 } else {
245 return Collections.emptyList();
246 }
247 }
248
249 public void setIntFlagEntries(List<IntFlagEntry> intFlagEntries) {
250 mIntFlagEntries = intFlagEntries;
251 }
252
Ashley Rose171a7232018-12-11 17:32:58 -0500253 public enum Type {
254 /** Primitive or boxed {@code boolean} */
255 BOOLEAN,
256
257 /** Primitive or boxed {@code byte} */
258 BYTE,
259
260 /** Primitive or boxed {@code char} */
261 CHAR,
262
263 /** Primitive or boxed {@code double} */
264 DOUBLE,
265
266 /** Primitive or boxed {@code float} */
267 FLOAT,
268
269 /** Primitive or boxed {@code int} */
270 INT,
271
272 /** Primitive or boxed {@code long} */
273 LONG,
274
275 /** Primitive or boxed {@code short} */
276 SHORT,
277
278 /** Any other object */
279 OBJECT,
280
281 /**
282 * A color object or packed color {@code int} or {@code long}.
283 *
284 * @see android.graphics.Color
285 * @see android.annotation.ColorInt
286 * @see android.annotation.ColorLong
287 */
288 COLOR,
289
290 /**
291 * An {@code int} packed with a gravity specification
292 *
293 * @see android.view.Gravity
294 */
295 GRAVITY,
296
297 /**
298 * An enumeration packed into an {@code int}.
299 *
300 * @see android.view.inspector.IntEnumMapping
Ashley Rose0b671da2019-01-25 15:41:29 -0500301 * @see IntEnumEntry
Ashley Rose171a7232018-12-11 17:32:58 -0500302 */
303 INT_ENUM,
304
305 /**
306 * Non-exclusive or partially-exclusive flags packed into an {@code int}.
307 *
308 * @see android.view.inspector.IntFlagMapping
Ashley Rose0b671da2019-01-25 15:41:29 -0500309 * @see IntFlagEntry
Ashley Rose171a7232018-12-11 17:32:58 -0500310 */
Ashley Rosee8914812019-03-05 17:12:00 -0500311 INT_FLAG,
312
313 /** A resource ID */
314 RESOURCE_ID
Ashley Rose171a7232018-12-11 17:32:58 -0500315 }
316 }
Ashley Rose0b671da2019-01-25 15:41:29 -0500317
318 /**
319 * Model one entry in a int enum mapping.
320 *
321 * @see android.view.inspector.IntEnumMapping
322 */
323 public static final class IntEnumEntry {
324 private final String mName;
325 private final int mValue;
326
327 public IntEnumEntry(String name, int value) {
328 mName = Objects.requireNonNull(name, "Name must not be null");
329 mValue = value;
330 }
331
332 public String getName() {
333 return mName;
334 }
335
336 public int getValue() {
337 return mValue;
338 }
339 }
340
341 /**
342 * Model one entry in an int flag mapping.
343 *
344 * @see android.view.inspector.IntFlagMapping
345 */
346 public static final class IntFlagEntry {
347 private final String mName;
348 private final int mTarget;
349 private final int mMask;
350
351 public IntFlagEntry(String name, int target, int mask) {
352 mName = Objects.requireNonNull(name, "Name must not be null");
353 mTarget = target;
354 mMask = mask;
355 }
356
357 public IntFlagEntry(String name, int target) {
358 this(name, target, target);
359 }
360
361 /**
362 * Determine if this entry has a bitmask.
363 *
364 * @return True if the bitmask and target are different, false otherwise
365 */
366 public boolean hasMask() {
367 return mTarget != mMask;
368 }
369
370 public String getName() {
371 return mName;
372 }
373
374 public int getTarget() {
375 return mTarget;
376 }
377
378 public int getMask() {
379 return mMask;
380 }
381 }
Ashley Rosede080eb2018-12-07 17:20:25 -0500382}