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.marshaller;
018
019import org.apache.juneau.marshall.prototext.*;
020import java.io.*;
021import java.lang.reflect.*;
022import org.apache.juneau.marshall.parser.*;
023import org.apache.juneau.marshall.serializer.*;
024import org.apache.juneau.marshall.stream.*;
025
026/**
027 * A pairing of a {@link PrototextSerializer} and {@link PrototextParser} into a single class with convenience to/of methods.
028 *
029 * <p>
030 *    The general idea is to combine a single serializer and parser inside a simplified API for serializing and parsing POJOs.
031 *
032 * <h5 class='figure'>Examples:</h5>
033 * <p class='bjava'>
034 *    <jc>// Using instance.</jc>
035 *    Prototext <jv>proto</jv> = <jk>new</jk> Prototext();
036 *    MyPojo <jv>myPojo</jv> = <jv>proto</jv>.read(<jv>string</jv>, MyPojo.<jk>class</jk>);
037 *    String <jv>string</jv> = <jv>proto</jv>.write(<jv>myPojo</jv>);
038 * </p>
039 * <p class='bjava'>
040 * <jc>// Using DEFAULT instance.</jc>
041 *    MyPojo <jv>myPojo</jv> = Prototext.<jsf>DEFAULT</jsf>.read(<jv>string</jv>, MyPojo.<jk>class</jk>);
042 *    String <jv>string</jv> = Prototext.<jsf>DEFAULT</jsf>.write(<jv>myPojo</jv>);
043 * </p>
044 *
045 * <h5 class='figure'>Example output (Map of name/age):</h5>
046 * <p class='bcode'>
047 *    name: "Alice"
048 *    age: 30
049 * </p>
050 *
051 * <h5 class='figure'>Complex (nested object + array):</h5>
052 * <p class='bcode'>
053 *    name: "Alice"
054 *    age: 30
055 *    address {
056 *      street: "123 Main St"
057 *      city: "Boston"
058 *      state: "MA"
059 *    }
060 *    tags: ["a", "b", "c"]
061 * </p>
062 *
063 * <p class='bjava'>
064 * <jc>// Using static shortcuts.</jc>
065 *    MyPojo <jv>myPojo</jv> = Prototext.<jsm>to</jsm>(<jv>string</jv>, MyPojo.<jk>class</jk>);
066 *    String <jv>string</jv> = Prototext.<jsm>of</jsm>(<jv>myPojo</jv>);
067 * </p>
068 *
069 * <h5 class='section'>See Also:</h5><ul>
070 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshallers">Marshallers</a>
071 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Prototext">Protobuf Text Format Basics</a>
072 * </ul>
073 */
074@SuppressWarnings({
075   "resource" // Cursor shortcut methods return Closeables owned by the caller; Eclipse JDT @Owning warning is by design.
076})
077public class Prototext extends CharMarshaller {
078
079   /** Default marshaller instance. */
080   public static final Prototext DEFAULT = new Prototext();
081
082   /**
083    * Serializes a POJO to a <c>String</c> using the {@link #DEFAULT} marshaller.
084    *
085    * <p>
086    * A shortcut for calling <c><jsf>DEFAULT</jsf>.write(<jv>object</jv>)</c>.
087    *
088    * @param object The object to serialize.
089    * @return The serialized object.
090    * @throws SerializeException If a problem occurred trying to convert the output.
091    */
092   public static String of(Object object) throws SerializeException {
093      return DEFAULT.write(object);
094   }
095
096   /**
097    * Parses an input into the specified object type using the {@link #DEFAULT} marshaller.
098    *
099    * <p>
100    * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>)</c>.
101    *
102    * @param <T> The class type of the object being created.
103    * @param input The input.
104    * @param type The object type to create.
105    * @return The parsed object.
106    * @throws ParseException Malformed input encountered.
107    */
108   public static <T> T to(String input, Class<T> type) throws ParseException {
109      return DEFAULT.read(input, type);
110   }
111
112   /**
113    * Parses an input into the specified parameterized object type using the {@link #DEFAULT} marshaller.
114    *
115    * <p>
116    * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>, <jv>args</jv>)</c>.
117    *
118    * @param <T> The class type of the object to create.
119    * @param input The input.
120    * @param type The object type to create.
121    * @param args The type arguments of the class if it's a collection or map.
122    * @return The parsed object.
123    * @throws ParseException Malformed input encountered.
124    */
125   public static <T> T to(String input, Type type, Type... args) throws ParseException {
126      return DEFAULT.read(input, type, args);
127   }
128
129   /**
130    * Parses the contents of a {@link Reader} into the specified object type using the {@link #DEFAULT} marshaller.
131    *
132    * <p>
133    * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>)</c> that catches any
134    * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link ParseException}, so the
135    * caller is not burdened with a checked exception.
136    *
137    * @param <T> The class type of the object being created.
138    * @param input The input reader.
139    * @param type The object type to create.
140    * @return The parsed object.
141    * @throws ParseException Malformed input encountered or an I/O error occurred on the underlying stream.
142    */
143   public static <T> T to(Reader input, Class<T> type) throws ParseException {
144      try {
145         return DEFAULT.read(input, type);
146      } catch (IOException e) {
147         throw new ParseException(e);
148      }
149   }
150
151   /**
152    * Parses the contents of a {@link Reader} into the specified parameterized object type using the {@link #DEFAULT} marshaller.
153    *
154    * <p>
155    * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, <jv>type</jv>, <jv>args</jv>)</c> that catches any
156    * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link ParseException}, so the
157    * caller is not burdened with a checked exception.
158    *
159    * @param <T> The class type of the object to create.
160    * @param input The input reader.
161    * @param type The object type to create.
162    * @param args The type arguments of the class if it's a collection or map.
163    * @return The parsed object.
164    * @throws ParseException Malformed input encountered or an I/O error occurred on the underlying stream.
165    */
166   public static <T> T to(Reader input, Type type, Type... args) throws ParseException {
167      try {
168         return DEFAULT.read(input, type, args);
169      } catch (IOException e) {
170         throw new ParseException(e);
171      }
172   }
173
174   /**
175    * Serializes a POJO to the specified {@link Writer} using the {@link #DEFAULT} marshaller.
176    *
177    * <p>
178    * A shortcut for calling <c><jsf>DEFAULT</jsf>.write(<jv>object</jv>, <jv>output</jv>)</c> that catches any
179    * {@link IOException} from the underlying stream and rethrows it as an unchecked {@link SerializeException}, so the
180    * caller is not burdened with a checked exception.
181    *
182    * @param object The object to serialize.
183    * @param output The writer to serialize to.
184    * @throws SerializeException If a problem occurred trying to convert the output or an I/O error occurred on the underlying stream.
185    */
186   public static void of(Object object, Writer output) throws SerializeException {
187      try {
188         DEFAULT.write(object, output);
189      } catch (IOException e) {
190         throw new SerializeException(e);
191      }
192   }
193
194   /**
195    * Opens a low-level {@link RecordReader} cursor over the specified input using the {@link #DEFAULT} marshaller.
196    *
197    * <p>
198    * A shortcut for calling <c><jsf>DEFAULT</jsf>.readRecords(<jv>input</jv>)</c>.
199    *
200    * @param input The input.
201    * @return A new {@link RecordReader}.
202    * @throws IOException If a problem occurred opening the underlying input.
203    */
204   public static RecordReader toRecords(Object input) throws IOException {
205      return DEFAULT.readRecords(input);
206   }
207
208   /**
209    * Opens a low-level {@link RecordWriter} cursor over the specified output using the {@link #DEFAULT} marshaller.
210    *
211    * <p>
212    * A shortcut for calling <c><jsf>DEFAULT</jsf>.writeRecords(<jv>output</jv>)</c>.
213    *
214    * @param output The output.
215    * @return A new {@link RecordWriter}.
216    * @throws IOException If a problem occurred opening the underlying output.
217    */
218   public static RecordWriter ofRecords(Object output) throws IOException {
219      return DEFAULT.writeRecords(output);
220   }
221
222   /** Creates using default serializer and parser. */
223   public Prototext() {
224      this(PrototextSerializer.DEFAULT, PrototextParser.DEFAULT);
225   }
226
227   /**
228    * Creates with custom serializer and parser.
229    *
230    * @param s The serializer.
231    * @param p The parser.
232    */
233   public Prototext(PrototextSerializer s, PrototextParser p) {
234      super(s, p);
235   }
236}