001package org.apache.juneau.marshall;
002// ***************************************************************************************************************************
003// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
004// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
005// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
006// * with the License.  You may obtain a copy of the License at                                                              *
007// *                                                                                                                         *
008// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
009// *                                                                                                                         *
010// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
011// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
012// * specific language governing permissions and limitations under the License.                                              *
013// ***************************************************************************************************************************
014
015import java.io.*;
016import java.lang.reflect.*;
017
018import org.apache.juneau.*;
019import org.apache.juneau.parser.*;
020import org.apache.juneau.serializer.*;
021
022/**
023 * Top-level class for a pairing of a {@link Serializer} and {@link Parser} into a single class with convenience read/write methods.
024 *
025 * <p>
026 *    The general idea is to combine a single serializer and parser inside a simplified API for reading and writing POJOs.
027 *
028 * <h5 class='figure'>Examples:</h5>
029 * <p class='bcode w800'>
030 *    <jc>// Using instance.</jc>
031 *    Marshall json = <jk>new</jk> Json();
032 *    MyPojo myPojo = json.read(string, MyPojo.<jk>class</jk>);
033 *    String string = json.write(myPojo);
034 * </p>
035 * <p class='bcode w800'>
036 * <jc>// Using DEFAULT instance.</jc>
037 *    MyPojo myPojo = Json.<jsf>DEFAULT</jsf>.read(string, MyPojo.<jk>class</jk>);
038 *    String string = Json.<jsf>DEFAULT</jsf>.write(myPojo);
039 * </p>
040 *
041 * <h5 class='section'>See Also:</h5>
042 * <ul>
043 *    <li class='link'>{@doc juneau-marshall.Marshalls}
044 * </ul>
045 */
046public abstract class Marshall {
047
048   private final Serializer s;
049   private final Parser p;
050
051   /**
052    * Constructor.
053    *
054    * @param s
055    *    The serializer to use for serializing output.
056    *    <br>Must not be <jk>null</jk>.
057    * @param p
058    *    The parser to use for parsing input.
059    *    <br>Must not be <jk>null</jk>.
060    */
061   protected Marshall(Serializer s, Parser p) {
062      this.s = s;
063      this.p = p;
064   }
065
066   /**
067    * Serializes a POJO directly to either a <code>String</code> or <code><jk>byte</jk>[]</code> depending on the serializer type.
068    *
069    * @param o The object to serialize.
070    * @return
071    *    The serialized object.
072    *    <br>Character-based serializers will return a <code>String</code>
073    *    <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code>
074    * @throws SerializeException If a problem occurred trying to convert the output.
075    */
076   public Object write(Object o) throws SerializeException {
077      return s.createSession().serialize(o);
078   }
079
080   /**
081    * Serializes a POJO to the specified output stream or writer.
082    *
083    * <p>
084    * Equivalent to calling <code>serializer.createSession().serialize(o, output);</code>
085    *
086    * @param o The object to serialize.
087    * @param output
088    *    The output object.
089    *    <br>Character-based serializers can handle the following output class types:
090    *    <ul>
091    *       <li>{@link Writer}
092    *       <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
093    *       <li>{@link File} - Output will be written as system-default encoded stream.
094    *       <li>{@link StringBuilder} - Output will be written to the specified string builder.
095    *    </ul>
096    *    <br>Stream-based serializers can handle the following output class types:
097    *    <ul>
098    *       <li>{@link OutputStream}
099    *       <li>{@link File}
100    *    </ul>
101    * @throws SerializeException If a problem occurred trying to convert the output.
102    */
103   public final void write(Object o, Object output) throws SerializeException {
104      s.createSession().serialize(o, output);
105   }
106
107   /**
108    * Convenience method for serializing an object to a String.
109    *
110    * <p>
111    * For writer-based serializers, this is identical to calling {@link Serializer#serialize(Object)}.
112    * <br>For stream-based serializers, this converts the returned byte array to a string based on
113    * the {@link OutputStreamSerializer#OSSERIALIZER_binaryFormat} setting.
114    *
115    * @param o The object to serialize.
116    * @return The output serialized to a string.
117    */
118   public final String toString(Object o) {
119      try {
120         return s.serializeToString(o);
121      } catch (Exception e) {
122         throw new RuntimeException(e);
123      }
124   }
125
126
127   /**
128    * Convenience method for calling <code>System.out.println(...)</code> on the specified object after calling {@link #toString(Object)}.
129    *
130    * @param o The object to serialize and then send to the console.
131    * @return This object (for method chaining).
132    */
133   public final Marshall println(Object o) {
134      System.out.println(toString(o));
135      return this;
136   }
137
138   /**
139    * Parses input into the specified object type.
140    *
141    * <p>
142    * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
143    *
144    * <h5 class='section'>Examples:</h5>
145    * <p class='bcode w800'>
146    *    Marshall m = Json.<jsf>DEFAULT</jsf>;
147    *
148    *    <jc>// Parse into a linked-list of strings.</jc>
149    *    List l = m.read(json, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
150    *
151    *    <jc>// Parse into a linked-list of beans.</jc>
152    *    List l = m.read(json, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
153    *
154    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
155    *    List l = m.read(json, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
156    *
157    *    <jc>// Parse into a map of string keys/values.</jc>
158    *    Map m = m.read(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
159    *
160    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
161    *    Map m = m.read(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
162    * </p>
163    *
164    * <p>
165    * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type.
166    *
167    * <p>
168    * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
169    *
170    * <p>
171    * The array can be arbitrarily long to indicate arbitrarily complex data structures.
172    *
173    * <h5 class='section'>Notes:</h5>
174    * <ul class='spaced-list'>
175    *    <li>
176    *       Use the {@link #read(Object, Class)} method instead if you don't need a parameterized map/collection.
177    * </ul>
178    *
179    * @param <T> The class type of the object to create.
180    * @param input
181    *    The input.
182    *    <br>Character-based parsers can handle the following input class types:
183    *    <ul>
184    *       <li><jk>null</jk>
185    *       <li>{@link Reader}
186    *       <li>{@link CharSequence}
187    *       <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by
188    *          {@link ReaderParser#RPARSER_inputStreamCharset} property value).
189    *       <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by
190    *          {@link ReaderParser#RPARSER_inputStreamCharset} property value).
191    *       <li>{@link File} containing system encoded text (or charset defined by
192    *          {@link ReaderParser#RPARSER_fileCharset} property value).
193    *    </ul>
194    *    <br>Stream-based parsers can handle the following input class types:
195    *    <ul>
196    *       <li><jk>null</jk>
197    *       <li>{@link InputStream}
198    *       <li><code><jk>byte</jk>[]</code>
199    *       <li>{@link File}
200    *       <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser#ISPARSER_binaryFormat} setting.
201    *    </ul>
202    * @param type
203    *    The object type to create.
204    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
205    * @param args
206    *    The type arguments of the class if it's a collection or map.
207    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
208    *    <br>Ignored if the main type is not a map or collection.
209    * @return The parsed object.
210    * @throws ParseException
211    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
212    * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections.
213    */
214   public final <T> T read(Object input, Type type, Type...args) throws ParseException {
215      return p.createSession().parse(input, type, args);
216   }
217
218   /**
219    * Same as {@link #read(Object, Type, Type...)} except optimized for a non-parameterized class.
220    *
221    * <p>
222    * This is the preferred parse method for simple types since you don't need to cast the results.
223    *
224    * <h5 class='section'>Examples:</h5>
225    * <p class='bcode w800'>
226    *    Marshall m = Json.<jsf>DEFAULT</jsf>;
227    *
228    *    <jc>// Parse into a string.</jc>
229    *    String s = m.read(json, String.<jk>class</jk>);
230    *
231    *    <jc>// Parse into a bean.</jc>
232    *    MyBean b = m.read(json, MyBean.<jk>class</jk>);
233    *
234    *    <jc>// Parse into a bean array.</jc>
235    *    MyBean[] ba = m.read(json, MyBean[].<jk>class</jk>);
236    *
237    *    <jc>// Parse into a linked-list of objects.</jc>
238    *    List l = m.read(json, LinkedList.<jk>class</jk>);
239    *
240    *    <jc>// Parse into a map of object keys/values.</jc>
241    *    Map m = m.read(json, TreeMap.<jk>class</jk>);
242    * </p>
243    *
244    * @param <T> The class type of the object being created.
245    * @param input
246    *    The input.
247    *    See {@link #read(Object, Type, Type...)} for details.
248    * @param type The object type to create.
249    * @return The parsed object.
250    * @throws ParseException
251    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
252    */
253   public final <T> T read(Object input, Class<T> type) throws ParseException {
254      return p.createSession().parse(input, type);
255   }
256}