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