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.collections;
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.json.*;
030import org.apache.juneau.marshall.json5.*;
031import org.apache.juneau.marshall.marshaller.*;
032import org.apache.juneau.marshall.marshaller.Json;
033import org.apache.juneau.marshall.parser.*;
034import org.apache.juneau.marshall.serializer.*;
035
036/**
037 * Java implementation of a JSON array.
038 *
039 * <p>
040 * This class is a subclass of {@link MarshalledList} that carries strict-JSON-specific surface, including
041 * strict-JSON {@link #toString()} (via {@link Json#of(Object)}), {@link JsonParser}-defaulting
042 * {@link CharSequence} / {@link Reader} constructors, and convenience methods for serializing to the
043 * various JSON dialects (JSON5, JSON Lines, canonical JSON, HJSON).
044 *
045 * <p>
046 * <b>v10.0 behavioral break:</b> {@code toString()} now produces strict (RFC 8259) JSON (was JSON5),
047 * and the {@link CharSequence} / {@link Reader} constructors now default to {@link JsonParser#DEFAULT}
048 * (was {@link Json5Parser#DEFAULT}). For the previous JSON5 behavior use {@link Json5List}.
049 *
050 * <p>
051 * Note that the use of this class is optional for generating JSON.  The serializers will accept any objects that implement the
052 * {@link Collection} interface.  But this class provides some useful additional functionality when working with JSON
053 * models constructed from Java Collections Framework objects.  For example, a constructor is provided for converting a
054 * JSON array string directly into a {@link List}.  It also contains accessor methods for to avoid common typecasting
055 * when accessing elements in a list.
056 *
057 * <h5 class='section'>Example:</h5>
058 * <p class='bjava'>
059 *    <jc>// Construct an empty List</jc>
060 *    JsonList <jv>list</jv> = JsonList.<jsm>of</jsm>();
061 *
062 *    <jc>// Construct a list of objects using various methods</jc>
063 *    <jv>list</jv> = JsonList.<jsm>of</jsm>().a(<js>"foo"</js>).a(123).a(<jk>true</jk>);
064 *    <jv>list</jv> = JsonList.<jsm>of</jsm>().a(<js>"foo"</js>, 123, <jk>true</jk>);  <jc>// Equivalent</jc>
065 *    <jv>list</jv> = JsonList.<jsm>of</jsm>(<js>"foo"</js>, 123, <jk>true</jk>);  <jc>// Equivalent</jc>
066 *
067 *    <jc>// Construct a list of integers from JSON</jc>
068 *    <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[1,2,3]"</js>);
069 *
070 *    <jc>// Construct a list of generic JsonMap objects from JSON</jc>
071 *    <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>);
072 *
073 *    <jc>// Construct JSON from JsonList</jc>
074 *    <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>);
075 *    String <jv>json</jv> = <jv>list</jv>.toString();  <jc>// Produces "[{foo:'bar'},{baz:'bing'}]"</jc>
076 *    <jv>json</jv> = <jv>list</jv>.toString(JsonSerializer.<jsf>DEFAULT</jsf>);  <jc>// Equivalent</jc>
077 *    <jv>json</jv> = JsonSerializer.<jsf>DEFAULT</jsf>.write(<jv>list</jv>);  <jc>// Equivalent</jc>
078 *
079 *    <jc>// Get one of the entries in the list as an Integer</jc>
080 *    <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[1,2,3]"</js>);
081 *    Integer <jv>integer</jv> = <jv>list</jv>.getInt(1);
082 *    <jv>integer</jv> = <jv>list</jv>.get(Integer.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
083 *
084 *    <jc>// Iterate over a list of beans using the elements() method</jc>
085 *    <jv>list</jv> = JsonList.<jsm>ofString</jsm>(<js>"[{name:'John Smith',age:45}]"</js>);
086 *    <jk>for</jk> (Person <jv>person</jv> : <jv>list</jv>.elements(Person.<jk>class</jk>) {
087 *       <jc>// Do something with p</jc>
088 *    }
089 * </p>
090 *
091 * <h5 class='section'>Notes:</h5><ul>
092 *    <li class='warn'>This class is not thread safe.
093 * </ul>
094 *
095 *
096 * @serial exclude
097 */
098@SuppressWarnings({
099   "java:S110", // Class has many fields, acceptable for collection implementation
100   "java:S1206", // Inherits equals/hashCode from LinkedList; List equality is element-based
101   "java:S2160", // equals() inherited from LinkedList; element-based equality is correct for a JSON list
102   "java:S9149" // Per-type static factories intentionally shadow the parent's.
103})
104public class JsonList extends MarshalledList {
105   private static class Unmodifiable extends JsonList {
106      private static final long serialVersionUID = 1L;
107
108      @SuppressWarnings({
109         "synthetic-access" // Access to outer class members is intentional
110      })
111      Unmodifiable(JsonList contents) {
112         if (nn(contents))
113            contents.forEach(super::add);
114      }
115
116      @Override /* Overridden from List */
117      public boolean add(Object object) {
118         throw uoroex();
119      }
120
121      @Override /* Overridden from List */
122      public void add(int location, Object object) {
123         throw uoroex();
124      }
125
126      @Override /* Overridden from List */
127      public boolean addAll(Collection<?> c) {
128         throw uoroex();
129      }
130
131      @Override /* Overridden from List */
132      public boolean addAll(int location, Collection<?> c) {
133         throw uoroex();
134      }
135
136      @Override /* Overridden from Deque */
137      public void addFirst(Object object) {
138         throw uoroex();
139      }
140
141      @Override /* Overridden from Deque */
142      public void addLast(Object object) {
143         throw uoroex();
144      }
145
146      @Override /* Overridden from List */
147      public void clear() {
148         throw uoroex();
149      }
150
151      @Override
152      public boolean isUnmodifiable() { return true; }
153
154      @Override /* Overridden from List */
155      public Iterator<Object> iterator() {
156         return readOnlyIterator(super.iterator());
157      }
158
159      @Override /* Overridden from List */
160      public ListIterator<Object> listIterator() {
161         return readOnlyListIterator(super.listIterator());
162      }
163
164      @Override /* Overridden from List */
165      public ListIterator<Object> listIterator(int location) {
166         return readOnlyListIterator(super.listIterator(location));
167      }
168
169      @Override /* Overridden from Queue */
170      public boolean offer(Object object) {
171         throw uoroex();
172      }
173
174      @Override /* Overridden from Deque */
175      public boolean offerFirst(Object object) {
176         throw uoroex();
177      }
178
179      @Override /* Overridden from Deque */
180      public boolean offerLast(Object object) {
181         throw uoroex();
182      }
183
184      @Override /* Overridden from Queue */
185      public Object poll() {
186         throw uoroex();
187      }
188
189      @Override /* Overridden from Deque */
190      public Object pollFirst() {
191         throw uoroex();
192      }
193
194      @Override /* Overridden from Deque */
195      public Object pollLast() {
196         throw uoroex();
197      }
198
199      @Override /* Overridden from Deque */
200      public Object pop() {
201         throw uoroex();
202      }
203
204      @Override /* Overridden from Deque */
205      public void push(Object object) {
206         throw uoroex();
207      }
208
209      @Override /* Overridden from Queue */
210      public Object remove() {
211         throw uoroex();
212      }
213
214      @Override /* Overridden from List */
215      public Object remove(int location) {
216         throw uoroex();
217      }
218
219      @Override /* Overridden from List */
220      public boolean remove(Object object) {
221         throw uoroex();
222      }
223
224      @Override /* Overridden from List */
225      public boolean removeAll(Collection<?> c) {
226         throw uoroex();
227      }
228
229      @Override /* Overridden from Deque */
230      public Object removeFirst() {
231         throw uoroex();
232      }
233
234      @Override /* Overridden from Deque */
235      public boolean removeFirstOccurrence(Object object) {
236         throw uoroex();
237      }
238
239      @Override /* Overridden from Collection */
240      public boolean removeIf(Predicate<? super Object> filter) {
241         throw uoroex();
242      }
243
244      @Override /* Overridden from Deque */
245      public Object removeLast() {
246         throw uoroex();
247      }
248
249      @Override /* Overridden from Deque */
250      public boolean removeLastOccurrence(Object object) {
251         throw uoroex();
252      }
253
254      @Override /* Overridden from List */
255      public void replaceAll(UnaryOperator<Object> operator) {
256         throw uoroex();
257      }
258
259      @Override /* Overridden from List */
260      public boolean retainAll(Collection<?> c) {
261         throw uoroex();
262      }
263
264      @Override /* Overridden from List */
265      public Object set(int location, Object object) {
266         throw uoroex();
267      }
268
269      @Override /* Overridden from List */
270      public void sort(Comparator<? super Object> c) {
271         throw uoroex();
272      }
273
274      @Override /* Overridden from List */
275      public List<Object> subList(int start, int end) {
276         return u(new ArrayList<>(this).subList(start, end));
277      }
278
279      private static Iterator<Object> readOnlyIterator(Iterator<Object> it) {
280         return new Iterator<>() {
281            @Override public boolean hasNext() { return it.hasNext(); }
282            @Override public Object next() { return it.next(); }
283            @Override public void remove() { throw uoroex(); }
284            @Override public void forEachRemaining(Consumer<? super Object> action) { it.forEachRemaining(action); }
285         };
286      }
287
288      private static ListIterator<Object> readOnlyListIterator(ListIterator<Object> it) {
289         return new ListIterator<>() {
290            @Override public boolean hasNext() { return it.hasNext(); }
291            @Override public Object next() { return it.next(); }
292            @Override public boolean hasPrevious() { return it.hasPrevious(); }
293            @Override public Object previous() { return it.previous(); }
294            @Override public int nextIndex() { return it.nextIndex(); }
295            @Override public int previousIndex() { return it.previousIndex(); }
296            @Override public void remove() { throw uoroex(); }
297            @Override public void set(Object e) { throw uoroex(); }
298            @Override public void add(Object e) { throw uoroex(); }
299            @Override public void forEachRemaining(Consumer<? super Object> action) { it.forEachRemaining(action); }
300         };
301      }
302   }
303
304   private static final long serialVersionUID = 1L;
305   /**
306    * An empty read-only JsonList.
307    *
308    * @serial exclude
309    */
310   @SuppressWarnings({
311      "java:S2386" // Public static final field accessed externally, cannot be protected
312   })
313   public static final JsonList EMPTY_LIST = new JsonList() {
314      private static final long serialVersionUID = 1L;
315
316      @Override /* Overridden from List */
317      public void add(int location, Object object) {
318         throw uoroex();
319      }
320
321      @Override /* Overridden from List */
322      public ListIterator<Object> listIterator(int location) {
323         return Collections.emptyList().listIterator(location);
324      }
325
326      @Override /* Overridden from List */
327      public Object remove(int location) {
328         throw uoroex();
329      }
330
331      @Override /* Overridden from List */
332      public Object set(int location, Object object) {
333         throw uoroex();
334      }
335
336      @Override /* Overridden from List */
337      public List<Object> subList(int start, int end) {
338         return Collections.emptyList().subList(start, end);
339      }
340   };
341
342   /**
343    * Construct an empty list.
344    *
345    * @return An empty list.
346    */
347   public static JsonList create() {
348      return new JsonList();
349   }
350
351   /**
352    * Construct a list initialized with the specified list.
353    *
354    * @param values
355    *    The list to copy.
356    *    <br>Can be <jk>null</jk> (returns <jk>null</jk>).
357    * @return A new list or <jk>null</jk> if the list was <jk>null</jk>.
358    */
359   public static JsonList of(Collection<?> values) {
360      return values == null ? null : new JsonList(values);
361   }
362
363   /**
364    * Construct a list initialized with the specified values.
365    *
366    * @param values The values to add to this list.
367    * @return A new list, never <jk>null</jk>.
368    */
369   public static JsonList of(Object...values) {
370      return new JsonList(values);
371   }
372
373   /**
374    * Convenience method for creating a list of array objects.
375    *
376    * @param values The initial values.
377    * @return A new list.
378    */
379   public static JsonList ofArrays(Object[]...values) {
380      var l = new JsonList();
381      Collections.addAll(l, values);
382      return l;
383   }
384
385   /**
386    * Convenience method for creating a list of collection objects.
387    *
388    * @param values The initial values.
389    * @return A new list.
390    */
391   public static JsonList ofCollections(Collection<?>...values) {
392      var l = new JsonList();
393      Collections.addAll(l, values);
394      return l;
395   }
396
397   /**
398    * Construct a list initialized with the specified JSON string.
399    *
400    * @param json
401    *    The JSON text to parse.
402    *    <br>Can be normal or simplified JSON.
403    * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>.
404    * @throws ParseException Malformed input encountered.
405    */
406   public static JsonList ofString(CharSequence json) throws ParseException {
407      return json == null ? new JsonList() : new JsonList(json);
408   }
409
410   /**
411    * Construct a list initialized with the specified reader containing JSON.
412    *
413    * @param json
414    *    The reader containing JSON text to parse.
415    *    <br>Can contain normal or simplified JSON.
416    * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>.
417    * @throws ParseException Malformed input encountered.
418    */
419   public static JsonList ofString(Reader json) throws ParseException {
420      return json == null ? new JsonList() : new JsonList(json);
421   }
422
423   /**
424    * Parses a string that can consist of either a JSON array or comma-delimited list.
425    *
426    * <p>
427    * The type of string is auto-detected.
428    *
429    * @param s The string to parse.
430    * @return A new list (empty if the input was <jk>null</jk> or empty), never <jk>null</jk>.
431    * @throws ParseException Malformed input encountered.
432    */
433   public static JsonList ofJsonOrCdl(String s) throws ParseException {
434      if (Shorts.ie(s))  // NOAI
435         return new JsonList();
436      if (! isProbablyJsonArray(s, true))
437         return new JsonList((Object[])splita(s.trim(), ','));
438      return new JsonList(s);
439   }
440
441   /**
442    * Construct a list initialized with the specified string.
443    *
444    * @param in The input being parsed.
445    * @param p
446    *    The parser to use to parse the input.
447    *    <br>If <jk>null</jk>, uses {@link JsonParser}.
448    * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>.
449    * @throws ParseException Malformed input encountered.
450    */
451   public static JsonList ofString(CharSequence in, Parser p) throws ParseException {
452      return in == null ? new JsonList() : new JsonList(in, p);
453   }
454
455   /**
456    * Construct a list initialized with the specified string.
457    *
458    * @param in
459    *    The reader containing the input being parsed.
460    *    <br>Can contain normal or simplified JSON.
461    * @param p
462    *    The parser to use to parse the input.
463    *    <br>If <jk>null</jk>, uses {@link JsonParser}.
464    * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>.
465    * @throws ParseException Malformed input encountered.
466    */
467   @SuppressWarnings({
468      "java:S1172" // Parameter reserved for future parser context support
469   })
470   public static JsonList ofString(Reader in, Parser p) throws ParseException {
471      return in == null ? new JsonList() : new JsonList(in);
472   }
473
474   /**
475    * Construct an empty list.
476    */
477   public JsonList() {}
478
479   /**
480    * Construct an empty list with the specified bean context.
481    *
482    * @param session The bean session to use for creating beans.
483    */
484   public JsonList(MarshallingSession session) {
485      super(session);
486   }
487
488   /**
489    * Construct a list initialized with the specified JSON.
490    *
491    * @param json
492    *    The JSON text to parse.
493    *    <br>Must be strict (RFC 8259) JSON; for JSON5 input use {@link Json5List} or pass
494    *    {@link Json5Parser#DEFAULT} explicitly.
495    * @throws ParseException Malformed input encountered.
496    */
497   public JsonList(CharSequence json) throws ParseException {
498      this(json, JsonParser.DEFAULT);
499   }
500
501   /**
502    * Construct a list initialized with the specified string.
503    *
504    * @param in
505    *    The input being parsed.
506    *    <br>Can be <jk>null</jk> or empty (results in an empty list; no parsing occurs).
507    * @param p
508    *    The parser to use to parse the input.
509    *    <br>If <jk>null</jk>, uses {@link JsonParser}.
510    * @throws ParseException Malformed input encountered.
511    */
512   public JsonList(CharSequence in, Parser p) throws ParseException {
513      super(in, p == null ? JsonParser.DEFAULT : p);
514   }
515
516   /**
517    * Construct a list initialized with the specified list.
518    *
519    * @param copyFrom
520    *    The list to copy.
521    *    <br>Can be <jk>null</jk> (results in an empty list).
522    */
523   public JsonList(Collection<?> copyFrom) {
524      super(copyFrom);
525   }
526
527   /**
528    * Construct a list initialized with the contents.
529    *
530    * @param entries The entries to add to this list.
531    */
532   public JsonList(Object...entries) {
533      super(entries);
534   }
535
536   /**
537    * Construct a list initialized with the specified reader containing JSON.
538    *
539    * @param json
540    *    The reader containing JSON text to parse.
541    *    <br>Must be strict (RFC 8259) JSON; for JSON5 input use {@link Json5List} or pass
542    *    {@link Json5Parser#DEFAULT} explicitly.
543    * @throws ParseException Malformed input encountered.
544    */
545   public JsonList(Reader json) throws ParseException {
546      this(json, JsonParser.DEFAULT);
547   }
548
549   /**
550    * Construct a list initialized with the specified string.
551    *
552    * @param in
553    *    The reader containing the input being parsed.
554    *    <br>Must be strict (RFC 8259) JSON.
555    * @param p
556    *    The parser to use to parse the input.
557    *    <br>If <jk>null</jk>, uses {@link JsonParser}.
558    * @throws ParseException Malformed input encountered.
559    */
560   public JsonList(Reader in, Parser p) throws ParseException {
561      super(in, p == null ? JsonParser.DEFAULT : p);
562   }
563
564   @Override /* Overridden from MarshalledList */
565   public JsonList append(Collection<?> values) {
566      super.append(values);
567      return this;
568   }
569
570   @Override /* Overridden from MarshalledList */
571   public JsonList append(Object value) {
572      super.append(value);
573      return this;
574   }
575
576   @Override /* Overridden from MarshalledList */
577   public JsonList append(Object...values) {
578      super.append(values);
579      return this;
580   }
581
582   @Override /* Overridden from MarshalledList */
583   public JsonList appendIf(boolean flag, Object value) {
584      super.appendIf(flag, value);
585      return this;
586   }
587
588   @Override /* Overridden from MarshalledList */
589   public <T> JsonList appendIf(Predicate<T> test, T value) {
590      super.appendIf(test, value);
591      return this;
592   }
593
594   @Override /* Overridden from MarshalledList */
595   public JsonList appendReverse(List<?> values) {
596      super.appendReverse(values);
597      return this;
598   }
599
600   @Override /* Overridden from MarshalledList */
601   public JsonList appendReverse(Object...values) {
602      super.appendReverse(values);
603      return this;
604   }
605
606   /**
607    * Serializes this list to a standard JSON string.
608    *
609    * @return This object as a standard JSON string.
610    */
611   public String toJson() {
612      return Json.of(this);
613   }
614
615   /**
616    * Serializes this list to a JSON5 string.
617    *
618    * <p>
619    * A synonym for {@link #toString()}.
620    *
621    * @return This object as a JSON5 string.
622    */
623   public String toJson5() {
624      return Json5.of(this);
625   }
626
627   /**
628    * Serializes this list to a JSON Lines string.
629    *
630    * @return This object as a JSON Lines string.
631    */
632   public String toJsonl() {
633      return Jsonl.of(this);
634   }
635
636   /**
637    * Serializes this list to a canonical JSON string (RFC 8785).
638    *
639    * @return This object as a canonical JSON string.
640    */
641   public String toJcs() {
642      return Jcs.of(this);
643   }
644
645   /**
646    * Serializes this list to an HJSON string.
647    *
648    * @return This object as an HJSON string.
649    */
650   public String toHjson() {
651      return Hjson.of(this);
652   }
653
654   /**
655    * Converts this object into the specified class type.
656    *
657    * <p>
658    * This method performs a round-trip serialization and deserialization to convert the list into the target type.
659    *
660    * <h5 class='section'>Notes:</h5><ul>
661    *    <li class='warn'>The current implementation uses a serialization round-trip which may be inefficient for
662    *       frequent conversions of large objects. Consider caching results or using direct object conversion where possible.
663    * </ul>
664    *
665    * @param cm The class type to convert this object to.
666    * @return A converted object.
667    */
668   public Object cast(ClassMeta<?> cm) {
669      try {
670         return Json5.to(Json5.of(this), cm);
671      } catch (ParseException | SerializeException e) {
672         throw toRex(e);
673      }
674   }
675
676   @Override /* Overridden from MarshalledList */
677   public JsonList getList(int index) {
678      return get(index, JsonList.class);
679   }
680
681   @Override /* Overridden from MarshalledList */
682   public JsonMap getMap(int index) {
683      return get(index, JsonMap.class);
684   }
685
686   @Override /* Overridden from MarshalledList */
687   public JsonList modifiable() {
688      if (isUnmodifiable())
689         return new JsonList(this);
690      return this;
691   }
692
693   @Override /* Overridden from MarshalledList */
694   public JsonList session(MarshallingSession session) {
695      super.session(session);
696      return this;
697   }
698
699   @Override /* Overridden from MarshalledList */
700   public JsonList setBeanSession(MarshallingSession value) {
701      super.setBeanSession(value);
702      return this;
703   }
704
705   @Override /* Overridden from Object */
706   public String toString() {
707      return Json.of(this);
708   }
709
710   @Override /* Overridden from MarshalledList */
711   public JsonList unmodifiable() {
712      if (this instanceof Unmodifiable this2)
713         return this2;
714      return new Unmodifiable(this);
715   }
716
717   /**
718    * Convenience method for serializing this JsonList to the specified Writer using the {@link Json#DEFAULT}
719    * marshaller.
720    *
721    * @param w The writer to send the serialized contents of this object.
722    * @return This object.
723    * @throws IOException If a problem occurred trying to write to the writer.
724    * @throws SerializeException If a problem occurred trying to convert the output.
725    */
726   public JsonList writeTo(Writer w) throws IOException, SerializeException {
727      Json.DEFAULT.write(this, w);
728      return this;
729   }
730}