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.json5l;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import org.apache.juneau.commons.collections.*;
022import org.apache.juneau.marshall.json5.*;
023
024/**
025 * Parses JSON5L (JSON5 Lines) input into POJO models.
026 *
027 * <h5 class='topic'>Media types</h5>
028 * <p>
029 * Handles <c>Content-Type</c> types:  <bc>application/json5l, application/json5lines, text/json5l</bc>
030 *
031 * <h5 class='topic'>Description</h5>
032 * <p>
033 * JSON5L combines the relaxed JSON5 dialect with JSONL's newline-delimited framing.  Each non-empty,
034 * non-comment line is parsed as a complete JSON5 value (single-quoted strings, unquoted field names,
035 * trailing commas, comments, relaxed numbers).  Because JSON5 is a strict superset of JSON, this
036 * parser also reads plain JSONL input unchanged.
037 *
038 * <p>
039 * Comment handling follows JSON5 line-by-line: a line that is blank or contains only a comment is
040 * skipped (like a blank line); inline comments within a record's JSON are tolerated.  A
041 * <c>/* &#42;/</c> block comment must open and close on the same physical line — block comments
042 * cannot span record boundaries.
043 *
044 * <h5 class='section'>Example:</h5>
045 * <p class='bjava'>
046 *    <jc>// Parse JSON5L into a list of POJOs</jc>
047 *    List&lt;MyBean&gt; <jv>list</jv> = Json5lParser.<jsf>DEFAULT</jsf>.read(<jv>json5lInput</jv>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
048 * </p>
049 *
050 * <h5 class='figure'>Example input:</h5>
051 * <p class='bjson'>
052 * // header comment line
053 * {name:'Alice',age:30}
054 * {name:'Bob',age:25}  // trailing comment
055 * </p>
056 *
057 * <h5 class='section'>Notes:</h5><ul>
058 *    <li class='note'>This class is thread safe and reusable.
059 * </ul>
060 *
061 * <h5 class='section'>See Also:</h5><ul>
062 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Json5l">JSON5L Basics</a>
063 * </ul>
064 */
065@SuppressWarnings({
066   "java:S110", // Inheritance depth acceptable for Json5lParser hierarchy
067   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
068   "java:S9149" // Per-format static factories intentionally shadow the parent's.
069})
070public class Json5lParser extends Json5Parser {
071
072   /**
073    * Builder class.
074    */
075   public static class Builder extends Json5Parser.Builder {
076
077      private static final Cache<HashKey,Json5lParser> CACHE = Cache.of(HashKey.class, Json5lParser.class).build();
078
079      /**
080       * Constructor, default settings.
081       */
082      protected Builder() {
083         consumes("application/json5l,application/json5lines,text/json5l,application/jsonl,application/jsonlines,application/x-ndjson,text/jsonl")
084            .type(Json5lParser.class);
085      }
086
087      /**
088       * Copy constructor.
089       *
090       * @param copyFrom The builder to copy from.
091       *    <br>Cannot be <jk>null</jk>.
092       */
093      protected Builder(Builder copyFrom) {
094         super(reqnn("copyFrom", copyFrom));
095      }
096
097      /**
098       * Copy constructor.
099       *
100       * @param copyFrom The bean to copy from.
101       *    <br>Cannot be <jk>null</jk>.
102       */
103      protected Builder(Json5lParser copyFrom) {
104         super(reqnn("copyFrom", copyFrom));
105      }
106
107      @Override /* Overridden from Context.Builder<?> */
108      public Json5lParser build() {
109         return cache(CACHE).build(Json5lParser.class);
110      }
111
112      @Override /* Overridden from Context.Builder<?> */
113      public Builder copy() {
114         return new Builder(this);
115      }
116   }
117
118   /** Default parser, Accept=application/json5l. */
119   public static final Json5lParser DEFAULT = new Json5lParser(create());
120
121   /**
122    * Creates a new builder for this object.
123    *
124    * @return A new builder.
125    */
126   public static Builder create() {
127      return new Builder();
128   }
129
130   /**
131    * Constructor.
132    *
133    * @param builder The builder for this object.
134    */
135   public Json5lParser(Builder builder) {
136      super(builder);
137   }
138
139   @Override /* Overridden from Context */
140   public Builder copy() {
141      return new Builder(this);
142   }
143
144   @Override /* Overridden from Context */
145   public Json5lParserSession.Builder createSession() {
146      return Json5lParserSession.create(this);
147   }
148
149   @Override /* Overridden from Context */
150   public Json5lParserSession getSession() {
151      return createSession().build();
152   }
153}