001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.json5; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020import static org.apache.juneau.commons.utils.StringUtils.*; 021import static org.apache.juneau.commons.utils.ThrowableUtils.*; 022 023import java.io.*; 024import java.util.*; 025import java.util.function.*; 026 027import org.apache.juneau.commons.utils.*; 028import org.apache.juneau.marshall.*; 029import org.apache.juneau.marshall.collections.*; 030import org.apache.juneau.marshall.marshaller.*; 031import org.apache.juneau.marshall.parser.*; 032import org.apache.juneau.marshall.serializer.*; 033 034/** 035 * JSON5-flavored {@link MarshalledList}. 036 * 037 * <p> 038 * Carries the JSON5-specific surface that historically lived on {@link JsonList}: a JSON5 039 * {@link #toString()}, JSON5 default-parser constructors, and convenience methods for working with 040 * JSON5 array text. 041 * 042 * <p> 043 * For strict (RFC 8259) JSON behavior, use {@link JsonList}. For a neutral, marshaller-agnostic 044 * base with no language coupling, use {@link MarshalledList}. 045 * 046 * <h5 class='section'>Example:</h5> 047 * <p class='bjava'> 048 * <jc>// Construct an empty list</jc> 049 * Json5List <jv>list</jv> = Json5List.<jsm>of</jsm>(); 050 * 051 * <jc>// Construct a list of generic Json5Map objects from JSON5</jc> 052 * <jv>list</jv> = Json5List.<jsm>ofString</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); 053 * 054 * <jc>// JSON5 round-trip via toString()</jc> 055 * String <jv>s</jv> = <jv>list</jv>.toString(); <jc>// "[{foo:'bar'},{baz:'bing'}]"</jc> 056 * </p> 057 * 058 * <h5 class='section'>Notes:</h5><ul> 059 * <li class='warn'>This class is not thread safe. 060 * </ul> 061 * 062 * @serial exclude 063 */ 064@SuppressWarnings({ 065 "java:S110", // Class has many fields, acceptable for collection implementation 066 "java:S1206", // Inherits equals/hashCode from MarshalledList; List equality is element-based 067 "java:S2160", // equals() inherited from MarshalledList; element-based equality is correct 068 "java:S9149" // Per-format static factories intentionally shadow the parent's. 069}) 070public class Json5List extends MarshalledList { 071 private static class Unmodifiable extends Json5List { 072 private static final long serialVersionUID = 1L; 073 074 @SuppressWarnings({ 075 "synthetic-access" // Access to outer class members is intentional 076 }) 077 Unmodifiable(Json5List contents) { 078 if (nn(contents)) 079 contents.forEach(super::add); 080 } 081 082 @Override /* Overridden from List */ 083 public boolean add(Object object) { 084 throw uoroex(); 085 } 086 087 @Override /* Overridden from List */ 088 public void add(int location, Object object) { 089 throw uoroex(); 090 } 091 092 @Override /* Overridden from List */ 093 public boolean addAll(Collection<?> c) { 094 throw uoroex(); 095 } 096 097 @Override /* Overridden from List */ 098 public boolean addAll(int location, Collection<?> c) { 099 throw uoroex(); 100 } 101 102 @Override /* Overridden from Deque */ 103 public void addFirst(Object object) { 104 throw uoroex(); 105 } 106 107 @Override /* Overridden from Deque */ 108 public void addLast(Object object) { 109 throw uoroex(); 110 } 111 112 @Override /* Overridden from List */ 113 public void clear() { 114 throw uoroex(); 115 } 116 117 @Override 118 public boolean isUnmodifiable() { return true; } 119 120 @Override /* Overridden from List */ 121 public Iterator<Object> iterator() { 122 return readOnlyIterator(super.iterator()); 123 } 124 125 @Override /* Overridden from List */ 126 public ListIterator<Object> listIterator() { 127 return readOnlyListIterator(super.listIterator()); 128 } 129 130 @Override /* Overridden from List */ 131 public ListIterator<Object> listIterator(int location) { 132 return readOnlyListIterator(super.listIterator(location)); 133 } 134 135 @Override /* Overridden from Queue */ 136 public boolean offer(Object object) { 137 throw uoroex(); 138 } 139 140 @Override /* Overridden from Deque */ 141 public boolean offerFirst(Object object) { 142 throw uoroex(); 143 } 144 145 @Override /* Overridden from Deque */ 146 public boolean offerLast(Object object) { 147 throw uoroex(); 148 } 149 150 @Override /* Overridden from Queue */ 151 public Object poll() { 152 throw uoroex(); 153 } 154 155 @Override /* Overridden from Deque */ 156 public Object pollFirst() { 157 throw uoroex(); 158 } 159 160 @Override /* Overridden from Deque */ 161 public Object pollLast() { 162 throw uoroex(); 163 } 164 165 @Override /* Overridden from Deque */ 166 public Object pop() { 167 throw uoroex(); 168 } 169 170 @Override /* Overridden from Deque */ 171 public void push(Object object) { 172 throw uoroex(); 173 } 174 175 @Override /* Overridden from Queue */ 176 public Object remove() { 177 throw uoroex(); 178 } 179 180 @Override /* Overridden from List */ 181 public Object remove(int location) { 182 throw uoroex(); 183 } 184 185 @Override /* Overridden from List */ 186 public boolean remove(Object object) { 187 throw uoroex(); 188 } 189 190 @Override /* Overridden from List */ 191 public boolean removeAll(Collection<?> c) { 192 throw uoroex(); 193 } 194 195 @Override /* Overridden from Deque */ 196 public Object removeFirst() { 197 throw uoroex(); 198 } 199 200 @Override /* Overridden from Deque */ 201 public boolean removeFirstOccurrence(Object object) { 202 throw uoroex(); 203 } 204 205 @Override /* Overridden from Collection */ 206 public boolean removeIf(Predicate<? super Object> filter) { 207 throw uoroex(); 208 } 209 210 @Override /* Overridden from Deque */ 211 public Object removeLast() { 212 throw uoroex(); 213 } 214 215 @Override /* Overridden from Deque */ 216 public boolean removeLastOccurrence(Object object) { 217 throw uoroex(); 218 } 219 220 @Override /* Overridden from List */ 221 public void replaceAll(UnaryOperator<Object> operator) { 222 throw uoroex(); 223 } 224 225 @Override /* Overridden from List */ 226 public boolean retainAll(Collection<?> c) { 227 throw uoroex(); 228 } 229 230 @Override /* Overridden from List */ 231 public Object set(int location, Object object) { 232 throw uoroex(); 233 } 234 235 @Override /* Overridden from List */ 236 public void sort(Comparator<? super Object> c) { 237 throw uoroex(); 238 } 239 240 @Override /* Overridden from List */ 241 public List<Object> subList(int start, int end) { 242 return u(new ArrayList<>(this).subList(start, end)); 243 } 244 245 private static Iterator<Object> readOnlyIterator(Iterator<Object> it) { 246 return new Iterator<>() { 247 @Override public boolean hasNext() { return it.hasNext(); } 248 @Override public Object next() { return it.next(); } 249 @Override public void remove() { throw uoroex(); } 250 @Override public void forEachRemaining(Consumer<? super Object> action) { it.forEachRemaining(action); } 251 }; 252 } 253 254 private static ListIterator<Object> readOnlyListIterator(ListIterator<Object> it) { 255 return new ListIterator<>() { 256 @Override public boolean hasNext() { return it.hasNext(); } 257 @Override public Object next() { return it.next(); } 258 @Override public boolean hasPrevious() { return it.hasPrevious(); } 259 @Override public Object previous() { return it.previous(); } 260 @Override public int nextIndex() { return it.nextIndex(); } 261 @Override public int previousIndex() { return it.previousIndex(); } 262 @Override public void remove() { throw uoroex(); } 263 @Override public void set(Object e) { throw uoroex(); } 264 @Override public void add(Object e) { throw uoroex(); } 265 @Override public void forEachRemaining(Consumer<? super Object> action) { it.forEachRemaining(action); } 266 }; 267 } 268 } 269 270 private static final long serialVersionUID = 1L; 271 272 /** 273 * An empty read-only Json5List. 274 * 275 * @serial exclude 276 */ 277 @SuppressWarnings({ 278 "java:S2386" // Public static final field accessed externally, cannot be protected 279 }) 280 public static final Json5List EMPTY_LIST = new Json5List() { 281 private static final long serialVersionUID = 1L; 282 283 @Override /* Overridden from List */ 284 public void add(int location, Object object) { 285 throw uoroex(); 286 } 287 288 @Override /* Overridden from List */ 289 public ListIterator<Object> listIterator(int location) { 290 return Collections.emptyList().listIterator(location); 291 } 292 293 @Override /* Overridden from List */ 294 public Object remove(int location) { 295 throw uoroex(); 296 } 297 298 @Override /* Overridden from List */ 299 public Object set(int location, Object object) { 300 throw uoroex(); 301 } 302 303 @Override /* Overridden from List */ 304 public List<Object> subList(int start, int end) { 305 return Collections.emptyList().subList(start, end); 306 } 307 }; 308 309 /** 310 * Construct an empty list. 311 * 312 * @return An empty list. 313 */ 314 public static Json5List create() { 315 return new Json5List(); 316 } 317 318 /** 319 * Construct a list initialized with the specified list. 320 * 321 * @param values The list to copy. Can be <jk>null</jk> (returns <jk>null</jk>). 322 * @return A new list or <jk>null</jk> if the list was <jk>null</jk>. 323 */ 324 public static Json5List of(Collection<?> values) { 325 return values == null ? null : new Json5List(values); 326 } 327 328 /** 329 * Construct a list initialized with the specified values. 330 * 331 * @param values The values to add to this list. 332 * @return A new list, never <jk>null</jk>. 333 */ 334 public static Json5List of(Object...values) { 335 return new Json5List(values); 336 } 337 338 /** 339 * Convenience method for creating a list of array objects. 340 * 341 * @param values The initial values. 342 * @return A new list. 343 */ 344 public static Json5List ofArrays(Object[]...values) { 345 var l = new Json5List(); 346 Collections.addAll(l, values); 347 return l; 348 } 349 350 /** 351 * Convenience method for creating a list of collection objects. 352 * 353 * @param values The initial values. 354 * @return A new list. 355 */ 356 public static Json5List ofCollections(Collection<?>...values) { 357 var l = new Json5List(); 358 Collections.addAll(l, values); 359 return l; 360 } 361 362 /** 363 * Construct a list initialized with the specified JSON5 string. 364 * 365 * @param json5 The JSON5 text to parse. 366 * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>. 367 * @throws ParseException Malformed input encountered. 368 */ 369 public static Json5List ofString(CharSequence json5) throws ParseException { 370 return json5 == null ? new Json5List() : new Json5List(json5); 371 } 372 373 /** 374 * Construct a list initialized with the specified reader containing JSON5. 375 * 376 * @param json5 The reader containing JSON5 text to parse. 377 * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>. 378 * @throws ParseException Malformed input encountered. 379 */ 380 public static Json5List ofString(Reader json5) throws ParseException { 381 return json5 == null ? new Json5List() : new Json5List(json5); 382 } 383 384 /** 385 * Parses a string that can consist of either a JSON5 array or comma-delimited list. 386 * 387 * <p> 388 * The type of string is auto-detected. 389 * 390 * @param s The string to parse. 391 * @return A new list (empty if the input was <jk>null</jk> or empty), never <jk>null</jk>. 392 * @throws ParseException Malformed input encountered. 393 */ 394 public static Json5List ofJson5OrCdl(String s) throws ParseException { 395 if (Shorts.ie(s)) // NOAI 396 return new Json5List(); 397 if (! isProbablyJsonArray(s, true)) 398 return new Json5List((Object[])splita(s.trim(), ',')); 399 return new Json5List(s); 400 } 401 402 /** 403 * Construct a list initialized with the specified string. 404 * 405 * @param in The input being parsed. 406 * @param p The parser to use. If <jk>null</jk>, uses {@link Json5Parser}. 407 * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>. 408 * @throws ParseException Malformed input encountered. 409 */ 410 public static Json5List ofString(CharSequence in, Parser p) throws ParseException { 411 return in == null ? new Json5List() : new Json5List(in, p); 412 } 413 414 /** 415 * Construct a list initialized with the specified reader. 416 * 417 * @param in The reader containing the input being parsed. 418 * @param p The parser to use. If <jk>null</jk>, uses {@link Json5Parser}. 419 * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>. 420 * @throws ParseException Malformed input encountered. 421 */ 422 public static Json5List ofString(Reader in, Parser p) throws ParseException { 423 return in == null ? new Json5List() : new Json5List(in, p); 424 } 425 426 /** 427 * Construct an empty list. 428 */ 429 public Json5List() {} 430 431 /** 432 * Construct an empty list with the specified bean context. 433 * 434 * @param session The bean session to use for creating beans. 435 */ 436 public Json5List(MarshallingSession session) { 437 super(session); 438 } 439 440 /** 441 * Construct a list initialized with the specified JSON5 text. 442 * 443 * @param json5 The JSON5 text to parse. 444 * @throws ParseException Malformed input encountered. 445 */ 446 public Json5List(CharSequence json5) throws ParseException { 447 this(json5, Json5Parser.DEFAULT); 448 } 449 450 /** 451 * Construct a list initialized with the specified string. 452 * 453 * @param in The input being parsed. Can be <jk>null</jk> or empty (results in an empty list; no parsing occurs). 454 * @param p The parser to use. If <jk>null</jk>, uses {@link Json5Parser}. 455 * @throws ParseException Malformed input encountered. 456 */ 457 public Json5List(CharSequence in, Parser p) throws ParseException { 458 super(in, p == null ? Json5Parser.DEFAULT : p); 459 } 460 461 /** 462 * Construct a list initialized with the specified list. 463 * 464 * @param copyFrom The list to copy. Can be <jk>null</jk> (results in an empty list). 465 */ 466 public Json5List(Collection<?> copyFrom) { 467 super(copyFrom); 468 } 469 470 /** 471 * Construct a list initialized with the contents. 472 * 473 * @param entries The entries to add to this list. 474 */ 475 public Json5List(Object...entries) { 476 super(entries); 477 } 478 479 /** 480 * Construct a list initialized with the specified reader containing JSON5. 481 * 482 * @param json5 The reader containing JSON5 text to parse. 483 * @throws ParseException Malformed input encountered. 484 */ 485 public Json5List(Reader json5) throws ParseException { 486 this(json5, Json5Parser.DEFAULT); 487 } 488 489 /** 490 * Construct a list initialized with the specified reader and parser. 491 * 492 * @param in The reader containing the input being parsed. 493 * @param p The parser to use. If <jk>null</jk>, uses {@link Json5Parser}. 494 * @throws ParseException Malformed input encountered. 495 */ 496 public Json5List(Reader in, Parser p) throws ParseException { 497 super(in, p == null ? Json5Parser.DEFAULT : p); 498 } 499 500 @Override /* Overridden from MarshalledList */ 501 public Json5List append(Collection<?> values) { 502 super.append(values); 503 return this; 504 } 505 506 @Override /* Overridden from MarshalledList */ 507 public Json5List append(Object value) { 508 super.append(value); 509 return this; 510 } 511 512 @Override /* Overridden from MarshalledList */ 513 public Json5List append(Object...values) { 514 super.append(values); 515 return this; 516 } 517 518 @Override /* Overridden from MarshalledList */ 519 public Json5List appendIf(boolean flag, Object value) { 520 super.appendIf(flag, value); 521 return this; 522 } 523 524 @Override /* Overridden from MarshalledList */ 525 public <T> Json5List appendIf(Predicate<T> test, T value) { 526 super.appendIf(test, value); 527 return this; 528 } 529 530 @Override /* Overridden from MarshalledList */ 531 public Json5List appendReverse(List<?> values) { 532 super.appendReverse(values); 533 return this; 534 } 535 536 @Override /* Overridden from MarshalledList */ 537 public Json5List appendReverse(Object...values) { 538 super.appendReverse(values); 539 return this; 540 } 541 542 /** 543 * Converts this object into the specified class type. 544 * 545 * <p> 546 * This method performs a round-trip serialization and deserialization to convert the list into the target type. 547 * 548 * <h5 class='section'>Notes:</h5><ul> 549 * <li class='warn'>The current implementation uses a serialization round-trip which may be inefficient for 550 * frequent conversions of large objects. Consider caching results or using direct object conversion where possible. 551 * </ul> 552 * 553 * @param cm The class type to convert this object to. 554 * @return A converted object. 555 */ 556 public Object cast(ClassMeta<?> cm) { 557 try { 558 return Json5.to(Json5.of(this), cm); 559 } catch (ParseException | SerializeException e) { 560 throw toRex(e); 561 } 562 } 563 564 @Override /* Overridden from MarshalledList */ 565 public Json5List getList(int index) { 566 return get(index, Json5List.class); 567 } 568 569 @Override /* Overridden from MarshalledList */ 570 public Json5Map getMap(int index) { 571 return get(index, Json5Map.class); 572 } 573 574 @Override /* Overridden from MarshalledList */ 575 public Json5List modifiable() { 576 if (isUnmodifiable()) 577 return new Json5List(this); 578 return this; 579 } 580 581 @Override /* Overridden from MarshalledList */ 582 public Json5List session(MarshallingSession session) { 583 super.session(session); 584 return this; 585 } 586 587 @Override /* Overridden from MarshalledList */ 588 public Json5List setBeanSession(MarshallingSession value) { 589 super.setBeanSession(value); 590 return this; 591 } 592 593 /** 594 * Serializes this list to a JSON5 string. 595 * 596 * <p> 597 * A synonym for {@link #toString()}. 598 * 599 * @return This object as a JSON5 string. 600 */ 601 public String toJson5() { 602 return Json5.of(this); 603 } 604 605 @Override /* Overridden from Object */ 606 public String toString() { 607 return Json5.of(this); 608 } 609 610 @Override /* Overridden from MarshalledList */ 611 public Json5List unmodifiable() { 612 if (this instanceof Unmodifiable this2) 613 return this2; 614 return new Unmodifiable(this); 615 } 616 617 /** 618 * Convenience method for serializing this list to the specified Writer using the {@link Json5#DEFAULT} 619 * marshaller. 620 * 621 * @param w The writer to send the serialized contents of this object. 622 * @return This object. 623 * @throws IOException If a problem occurred trying to write to the writer. 624 * @throws SerializeException If a problem occurred trying to convert the output. 625 */ 626 public Json5List writeTo(Writer w) throws IOException, SerializeException { 627 Json5.DEFAULT.write(this, w); 628 return this; 629 } 630}