1 /*
2  * Copyright (C) 2021 The Android Open Source Project
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 package androidx.constraintlayout.core.parser;
17 
18 import org.jspecify.annotations.NonNull;
19 
20 import java.util.ArrayList;
21 import java.util.Objects;
22 
23 public class CLContainer extends CLElement {
24     ArrayList<CLElement> mElements = new ArrayList<>();
25 
CLContainer(char[] content)26     public CLContainer(char[] content) {
27         super(content);
28     }
29 
30     // @TODO: add description
allocate(char[] content)31     public static CLElement allocate(char[] content) {
32         return new CLContainer(content);
33     }
34 
35     // @TODO: add description
add(CLElement element)36     public void add(CLElement element) {
37         mElements.add(element);
38         if (CLParser.sDebug) {
39             System.out.println("added element " + element + " to " + this);
40         }
41     }
42 
43     @Override
toString()44     public String toString() {
45         StringBuilder list = new StringBuilder();
46         for (CLElement element : mElements) {
47             if (list.length() > 0) {
48                 list.append("; ");
49             }
50             list.append(element);
51         }
52         return super.toString() + " = <" + list + " >";
53     }
54 
55     // @TODO: add description
size()56     public int size() {
57         return mElements.size();
58     }
59 
60     // @TODO: add description
names()61     public ArrayList<String> names() {
62         ArrayList<String> names = new ArrayList<>();
63         for (CLElement element : mElements) {
64             if (element instanceof CLKey) {
65                 CLKey key = (CLKey) element;
66                 names.add(key.content());
67             }
68         }
69         return names;
70     }
71 
72     // @TODO: add description
has(String name)73     public boolean has(String name) {
74         for (CLElement element : mElements) {
75             if (element instanceof CLKey) {
76                 CLKey key = (CLKey) element;
77                 if (key.content().equals(name)) {
78                     return true;
79                 }
80             }
81         }
82         return false;
83     }
84 
85     // @TODO: add description
put(String name, CLElement value)86     public void put(String name, CLElement value) {
87         for (CLElement element : mElements) {
88             CLKey key = (CLKey) element;
89             if (key.content().equals(name)) {
90                 key.set(value);
91                 return;
92             }
93         }
94         CLKey key = (CLKey) CLKey.allocate(name, value);
95         mElements.add(key);
96     }
97 
98     // @TODO: add description
putNumber(String name, float value)99     public void putNumber(String name, float value) {
100         put(name, new CLNumber(value));
101     }
102 
putString(String name, String value)103     public void putString(String name, String value) {
104         CLElement stringElement = new CLString(value.toCharArray());
105         stringElement.setStart(0L);
106         stringElement.setEnd(value.length() - 1);
107         put(name, stringElement);
108     }
109 
110     // @TODO: add description
remove(String name)111     public void remove(String name) {
112         ArrayList<CLElement> toRemove = new ArrayList<>();
113         for (CLElement element : mElements) {
114             CLKey key = (CLKey) element;
115             if (key.content().equals(name)) {
116                 toRemove.add(element);
117             }
118         }
119         for (CLElement element : toRemove) {
120             mElements.remove(element);
121         }
122     }
123 
clear()124     public void clear() {
125         mElements.clear();
126     }
127 
128     /////////////////////////////////////////////////////////////////////////
129     // By name
130     /////////////////////////////////////////////////////////////////////////
131 
132     // @TODO: add description
get(String name)133     public CLElement get(String name) throws CLParsingException {
134         for (CLElement element : mElements) {
135             CLKey key = (CLKey) element;
136             if (key.content().equals(name)) {
137                 return key.getValue();
138             }
139         }
140         throw new CLParsingException("no element for key <" + name + ">", this);
141     }
142 
143     // @TODO: add description
getInt(String name)144     public int getInt(String name) throws CLParsingException {
145         CLElement element = get(name);
146         if (element != null) {
147             return element.getInt();
148         }
149         throw new CLParsingException("no int found for key <" + name + ">,"
150                 + " found [" + element.getStrClass() + "] : " + element, this);
151     }
152 
153     // @TODO: add description
getFloat(String name)154     public float getFloat(String name) throws CLParsingException {
155         CLElement element = get(name);
156         if (element != null) {
157             return element.getFloat();
158         }
159         throw new CLParsingException("no float found for key <" + name + ">,"
160                 + " found [" + element.getStrClass() + "] : " + element, this);
161     }
162 
163     // @TODO: add description
getArray(String name)164     public CLArray getArray(String name) throws CLParsingException {
165         CLElement element = get(name);
166         if (element instanceof CLArray) {
167             return (CLArray) element;
168         }
169         throw new CLParsingException("no array found for key <" + name + ">,"
170                 + " found [" + element.getStrClass() + "] : " + element, this);
171     }
172 
173     // @TODO: add description
getObject(String name)174     public CLObject getObject(String name) throws CLParsingException {
175         CLElement element = get(name);
176         if (element instanceof CLObject) {
177             return (CLObject) element;
178         }
179         throw new CLParsingException("no object found for key <" + name + ">,"
180                 + " found [" + element.getStrClass() + "] : " + element, this);
181     }
182 
183     // @TODO: add description
getString(String name)184     public String getString(String name) throws CLParsingException {
185         CLElement element = get(name);
186         if (element instanceof CLString) {
187             return element.content();
188         }
189         String strClass = null;
190         if (element != null) {
191             strClass = element.getStrClass();
192         }
193         throw new CLParsingException("no string found for key <" + name + ">,"
194                 + " found [" + strClass + "] : " + element, this);
195     }
196 
197     // @TODO: add description
getBoolean(String name)198     public boolean getBoolean(String name) throws CLParsingException {
199         CLElement element = get(name);
200         if (element instanceof CLToken) {
201             return ((CLToken) element).getBoolean();
202         }
203         throw new CLParsingException("no boolean found for key <" + name + ">,"
204                 + " found [" + element.getStrClass() + "] : " + element, this);
205     }
206 
207     /////////////////////////////////////////////////////////////////////////
208     // Optional
209     /////////////////////////////////////////////////////////////////////////
210 
211     // @TODO: add description
getOrNull(String name)212     public CLElement getOrNull(String name) {
213         for (CLElement element : mElements) {
214             CLKey key = (CLKey) element;
215             if (key.content().equals(name)) {
216                 return key.getValue();
217             }
218         }
219         return null;
220     }
221 
222     // @TODO: add description
getObjectOrNull(String name)223     public CLObject getObjectOrNull(String name) {
224         CLElement element = getOrNull(name);
225         if (element instanceof CLObject) {
226             return (CLObject) element;
227         }
228         return null;
229     }
230 
231     // @TODO: add description
getArrayOrNull(String name)232     public CLArray getArrayOrNull(String name) {
233         CLElement element = getOrNull(name);
234         if (element instanceof CLArray) {
235             return (CLArray) element;
236         }
237         return null;
238     }
239 
getArrayOrCreate(String name)240     public CLArray getArrayOrCreate(String name) {
241         CLArray array = getArrayOrNull(name);
242         if (array != null) {
243             return array;
244         }
245         array = new CLArray(new char[]{});
246         put(name, array);
247         return array;
248     }
249 
250     // @TODO: add description
getStringOrNull(String name)251     public String getStringOrNull(String name) {
252         CLElement element = getOrNull(name);
253         if (element instanceof CLString) {
254             return element.content();
255         }
256         return null;
257     }
258 
259     // @TODO: add description
getFloatOrNaN(String name)260     public float getFloatOrNaN(String name) {
261         CLElement element = getOrNull(name);
262         if (element instanceof CLNumber) {
263             return element.getFloat();
264         }
265         return Float.NaN;
266     }
267 
268     /////////////////////////////////////////////////////////////////////////
269     // By index
270     /////////////////////////////////////////////////////////////////////////
271 
272     // @TODO: add description
get(int index)273     public CLElement get(int index) throws CLParsingException {
274         if (index >= 0 && index < mElements.size()) {
275             return mElements.get(index);
276         }
277         throw new CLParsingException("no element at index " + index, this);
278     }
279 
280     // @TODO: add description
getInt(int index)281     public int getInt(int index) throws CLParsingException {
282         CLElement element = get(index);
283         if (element != null) {
284             return element.getInt();
285         }
286         throw new CLParsingException("no int at index " + index, this);
287     }
288 
289     // @TODO: add description
getFloat(int index)290     public float getFloat(int index) throws CLParsingException {
291         CLElement element = get(index);
292         if (element != null) {
293             return element.getFloat();
294         }
295         throw new CLParsingException("no float at index " + index, this);
296     }
297 
298     // @TODO: add description
getArray(int index)299     public CLArray getArray(int index) throws CLParsingException {
300         CLElement element = get(index);
301         if (element instanceof CLArray) {
302             return (CLArray) element;
303         }
304         throw new CLParsingException("no array at index " + index, this);
305     }
306 
307     // @TODO: add description
getObject(int index)308     public CLObject getObject(int index) throws CLParsingException {
309         CLElement element = get(index);
310         if (element instanceof CLObject) {
311             return (CLObject) element;
312         }
313         throw new CLParsingException("no object at index " + index, this);
314     }
315 
316     // @TODO: add description
getString(int index)317     public String getString(int index) throws CLParsingException {
318         CLElement element = get(index);
319         if (element instanceof CLString) {
320             return element.content();
321         }
322         throw new CLParsingException("no string at index " + index, this);
323     }
324 
325     // @TODO: add description
getBoolean(int index)326     public boolean getBoolean(int index) throws CLParsingException {
327         CLElement element = get(index);
328         if (element instanceof CLToken) {
329             return ((CLToken) element).getBoolean();
330         }
331         throw new CLParsingException("no boolean at index " + index, this);
332     }
333 
334     /////////////////////////////////////////////////////////////////////////
335     // Optional
336     /////////////////////////////////////////////////////////////////////////
337 
338     // @TODO: add description
getOrNull(int index)339     public CLElement getOrNull(int index) {
340         if (index >= 0 && index < mElements.size()) {
341             return mElements.get(index);
342         }
343         return null;
344     }
345 
346     // @TODO: add description
getStringOrNull(int index)347     public String getStringOrNull(int index) {
348         CLElement element = getOrNull(index);
349         if (element instanceof CLString) {
350             return element.content();
351         }
352         return null;
353     }
354 
355     @Override
clone()356     public @NonNull CLContainer clone() {
357         CLContainer clone = (CLContainer) super.clone();
358         ArrayList<CLElement> clonedArray = new ArrayList<>(mElements.size());
359         for (CLElement element: mElements) {
360             CLElement elementClone = element.clone();
361             elementClone.setContainer(clone);
362             clonedArray.add(elementClone);
363         }
364         clone.mElements = clonedArray;
365         return clone;
366     }
367 
368     @Override
equals(Object obj)369     public boolean equals(Object obj) {
370         if (this == obj) {
371             return true;
372         }
373 
374         if (!(obj instanceof CLContainer)) {
375             return false;
376         }
377         return this.mElements.equals(((CLContainer) obj).mElements);
378     }
379 
380     @Override
hashCode()381     public int hashCode() {
382         return Objects.hash(mElements, super.hashCode());
383     }
384 }
385