001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.json;
014
015import org.apache.juneau.*;
016import org.apache.juneau.parser.*;
017
018/**
019 * Parses any valid JSON text into a POJO model.
020 * 
021 * <h5 class='topic'>Media types</h5>
022 * 
023 * Handles <code>Content-Type</code> types:  <code><b>application/json, text/json</b></code>
024 * 
025 * <h5 class='topic'>Description</h5>
026 * 
027 * This parser uses a state machine, which makes it very fast and efficient.  It parses JSON in about 70% of the
028 * time that it takes the built-in Java DOM parsers to parse equivalent XML.
029 * 
030 * <p>
031 * This parser handles all valid JSON syntax.
032 * In addition, when strict mode is disable, the parser also handles the following:
033 * <ul class='spaced-list'>
034 *    <li>
035 *       Javascript comments (both {@code /*} and {@code //}) are ignored.
036 *    <li>
037 *       Both single and double quoted strings.
038 *    <li>
039 *       Automatically joins concatenated strings (e.g. <code><js>"aaa"</js> + <js>'bbb'</js></code>).
040 *    <li>
041 *       Unquoted attributes.
042 * </ul>
043 * 
044 * <p>
045 * Also handles negative, decimal, hexadecimal, octal, and double numbers, including exponential notation.
046 * 
047 * <p>
048 * This parser handles the following input, and automatically returns the corresponding Java class.
049 * <ul class='spaced-list'>
050 *    <li>
051 *       JSON objects (<js>"{...}"</js>) are converted to {@link ObjectMap ObjectMaps}.
052 *       <b>Note:</b>  If a <code><xa>_type</xa>=<xs>'xxx'</xs></code> attribute is specified on the object, then an
053 *       attempt is made to convert the object to an instance of the specified Java bean class.
054 *       See the <code>beanTypeName</code> setting on the {@link PropertyStore} for more information about parsing
055 *       beans from JSON.
056 *    <li>
057 *       JSON arrays (<js>"[...]"</js>) are converted to {@link ObjectList ObjectLists}.
058 *    <li>
059 *       JSON string literals (<js>"'xyz'"</js>) are converted to {@link String Strings}.
060 *    <li>
061 *       JSON numbers (<js>"123"</js>, including octal/hexadecimal/exponential notation) are converted to
062 *       {@link Integer Integers}, {@link Long Longs}, {@link Float Floats}, or {@link Double Doubles} depending on
063 *       whether the number is decimal, and the size of the number.
064 *    <li>
065 *       JSON booleans (<js>"false"</js>) are converted to {@link Boolean Booleans}.
066 *    <li>
067 *       JSON nulls (<js>"null"</js>) are converted to <jk>null</jk>.
068 *    <li>
069 *       Input consisting of only whitespace or JSON comments are converted to <jk>null</jk>.
070 * </ul>
071 * 
072 * <p>
073 * Input can be any of the following:
074 * <ul class='spaced-list'>
075 *    <li>
076 *       <js>"{...}"</js> - Converted to a {@link ObjectMap} or an instance of a Java bean if a <xa>_type</xa>
077 *       attribute is present.
078 *    <li>
079 *       <js>"[...]"</js> - Converted to a {@link ObjectList}.
080 *    <li>
081 *       <js>"123..."</js> - Converted to a {@link Number} (either {@link Integer}, {@link Long}, {@link Float},
082 *       or {@link Double}).
083 *    <li>
084 *       <js>"true"</js>/<js>"false"</js> - Converted to a {@link Boolean}.
085 *    <li>
086 *       <js>"null"</js> - Returns <jk>null</jk>.
087 *    <li>
088 *       <js>"'xxx'"</js> - Converted to a {@link String}.
089 *    <li>
090 *       <js>"\"xxx\""</js> - Converted to a {@link String}.
091 *    <li>
092 *       <js>"'xxx' + \"yyy\""</js> - Converted to a concatenated {@link String}.
093 * </ul>
094 * 
095 * <p>
096 * TIP:  If you know you're parsing a JSON object or array, it can be easier to parse it using the
097 * {@link ObjectMap#ObjectMap(CharSequence) ObjectMap(CharSequence)} or {@link ObjectList#ObjectList(CharSequence)
098 * ObjectList(CharSequence)} constructors instead of using this class.
099 * The end result should be the same.
100 */
101public class JsonParser extends ReaderParser {
102
103   //-------------------------------------------------------------------------------------------------------------------
104   // Configurable properties
105   //-------------------------------------------------------------------------------------------------------------------
106
107   private static final String PREFIX = "JsonParser.";
108
109   /**
110    * Configuration property:  Validate end.
111    * 
112    * <h5 class='section'>Property:</h5>
113    * <ul>
114    *    <li><b>Name:</b>  <js>"JsonParser.validateEnd.b"</js>
115    *    <li><b>Data type:</b>  <code>Boolean</code>
116    *    <li><b>Default:</b>  <jk>false</jk>
117    *    <li><b>Session-overridable:</b>  <jk>true</jk>
118    *    <li><b>Methods:</b> 
119    *       <ul>
120    *          <li class='jm'>{@link JsonParserBuilder#validateEnd(boolean)}
121    *          <li class='jm'>{@link JsonParserBuilder#validateEnd()}
122    *       </ul>
123    * </ul>
124    * 
125    * <h5 class='section'>Description:</h5>
126    * <p>
127    * If <jk>true</jk>, after parsing a POJO from the input, verifies that the remaining input in 
128    * the stream consists of only comments or whitespace.
129    * 
130    * <h5 class='section'>Example:</h5>
131    * <p class='bcode'>
132    *    <jc>// Create a parser that validates that there's no garbage at the end of the input.</jc>
133    *    ReaderParser p = JsonParser.
134    *       .<jsm>create</jsm>()
135    *       .validateEnd()
136    *       .build();
137    *    
138    *    <jc>// Same, but use property.</jc>
139    *    ReaderParser p = JsonParser.
140    *       .<jsm>create</jsm>()
141    *       .set(<jsf>JSON_validateEnd</jsf>, <jk>true</jk>)
142    *       .build();
143    * 
144    *    <jc>// Should fail because input has multiple POJOs.</jc>
145    *    String in = <js>"{foo:'bar'}{baz:'qux'}"</js>;
146    *    MyBean myBean = p.parse(in, MyBean.<jk>class</jk>);
147    * </p>
148    */
149   public static final String JSON_validateEnd = PREFIX + "validateEnd.b";
150   
151   //-------------------------------------------------------------------------------------------------------------------
152   // Predefined instances
153   //-------------------------------------------------------------------------------------------------------------------
154
155   /** Default parser, all default settings.*/
156   public static final JsonParser DEFAULT = new JsonParser(PropertyStore.DEFAULT);
157
158   /** Default parser, all default settings.*/
159   public static final JsonParser DEFAULT_STRICT = new JsonParser.Strict(PropertyStore.DEFAULT);
160
161
162   //-------------------------------------------------------------------------------------------------------------------
163   // Predefined subclasses
164   //-------------------------------------------------------------------------------------------------------------------
165
166   /** Default parser, strict mode. */
167   public static class Strict extends JsonParser {
168
169      /**
170       * Constructor.
171       * 
172       * @param ps The property store containing all the settings for this object.
173       */
174      public Strict(PropertyStore ps) {
175         super(ps.builder().set(PARSER_strict, true).set(JSON_validateEnd, true).build());
176      }
177   }
178
179
180   //-------------------------------------------------------------------------------------------------------------------
181   // Instance
182   //-------------------------------------------------------------------------------------------------------------------
183
184   final boolean validateEnd;
185
186   /**
187    * Constructor.
188    * 
189    * @param ps The property store containing all the settings for this object.
190    */
191   public JsonParser(PropertyStore ps) {
192      this(ps, "application/json", "text/json");
193   }
194
195   /**
196    * Constructor.
197    * 
198    * @param ps The property store containing all the settings for this object.
199    * @param consumes The list of media types that this parser consumes (e.g. <js>"application/json"</js>).
200    */
201   public JsonParser(PropertyStore ps, String...consumes) {
202      super(ps, consumes);
203      validateEnd = getBooleanProperty(JSON_validateEnd, false);
204   }
205
206   @Override /* Context */
207   public JsonParserBuilder builder() {
208      return new JsonParserBuilder(getPropertyStore());
209   }
210
211   /**
212    * Instantiates a new clean-slate {@link JsonParserBuilder} object.
213    * 
214    * <p>
215    * This is equivalent to simply calling <code><jk>new</jk> JsonParserBuilder()</code>.
216    * 
217    * <p>
218    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 
219    * the settings of the object called on.
220    * 
221    * @return A new {@link JsonParserBuilder} object.
222    */
223   public static JsonParserBuilder create() {
224      return new JsonParserBuilder();
225   }
226
227   @Override /* Parser */
228   public ReaderParserSession createSession(ParserSessionArgs args) {
229      return new JsonParserSession(this, args);
230   }
231   
232   @Override /* Context */
233   public ObjectMap asMap() {
234      return super.asMap()
235         .append("JsonParser", new ObjectMap());
236   }
237}