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.parser;
018
019import static org.apache.juneau.commons.utils.IoUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021import static org.apache.juneau.commons.utils.SystemUtils.*;
022
023import java.nio.charset.*;
024
025import org.apache.juneau.commons.collections.*;
026import org.apache.juneau.marshall.*;
027
028/**
029 * Subclass of {@link Parser} for character-based parsers.
030 *
031 * <h5 class='topic'>Description</h5>
032 * <p>
033 * This class is typically the parent class of all character-based parsers.
034 * It has 1 abstract method to implement on the session object...
035 * <ul>
036 *    <li>{@link ParserSession#doRead(ParserPipe, ClassMeta)}
037 * </ul>
038 *
039 * <h5 class='section'>Notes:</h5><ul>
040 *    <li class='note'>This class is thread safe and reusable.
041 * </ul>
042 *
043 * <h5 class='section'>See Also:</h5><ul>
044 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
045 * </ul>
046 */
047@SuppressWarnings({
048   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
049   "java:S9149" // Per-format static factories intentionally shadow the parent's.
050})
051public class ReaderParser extends Parser {
052
053   /**
054    * Builder class.
055    */
056   @SuppressWarnings({
057      "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
058   })
059   public abstract static class Builder<SELF extends Builder<SELF>> extends Parser.Builder<SELF> {
060
061      private Charset streamCharset;
062
063      /**
064       * Constructor, default settings.
065       */
066      protected Builder() {
067         streamCharset = env("ReaderParser.streamCharset", UTF8);
068      }
069
070      /**
071       * Copy constructor.
072       *
073       * @param copyFrom The builder to copy from.
074       *    <br>Cannot be <jk>null</jk>.
075       */
076      protected Builder(Builder<?> copyFrom) {
077         super(reqnn("copyFrom", copyFrom));
078         streamCharset = copyFrom.streamCharset;
079      }
080
081      /**
082       * Copy constructor.
083       *
084       * @param copyFrom The bean to copy from.
085       *    <br>Cannot be <jk>null</jk>.
086       */
087      protected Builder(ReaderParser copyFrom) {
088         super(reqnn("copyFrom", copyFrom));
089         streamCharset = copyFrom.streamCharset;
090      }
091
092      @Override /* Overridden from Context.Builder<?> */
093      public ReaderParser build() {
094         return build(ReaderParser.class);
095      }
096
097      @Override /* Overridden from Context.Builder<?> */
098      public abstract SELF copy();
099
100      @Override /* Overridden from Context.Builder<?> */
101      public HashKey hashKey() {
102         // @formatter:off
103         return HashKey.of(
104            super.hashKey(),
105            streamCharset
106         );
107         // @formatter:on
108      }
109
110      /**
111       * Input stream charset.
112       *
113       * <p>
114       * The character set to use for converting <c>InputStreams</c> and byte arrays to readers.
115       *
116       * <p>
117       * Used when passing in input streams and byte arrays to {@link Parser#read(Object, Class)}.
118       *
119       * <h5 class='section'>Example:</h5>
120       * <p class='bjava'>
121       *    <jc>// Create a parser that reads UTF-8 files.</jc>
122       *    ReaderParser <jv>parser</jv> = JsonParser
123       *       .<jsm>create</jsm>()
124       *       .streamCharset(Charset.<jsm>forName</jsm>(<js>"UTF-8"</js>))
125       *       .build();
126       *
127       *    <jc>// Use it to read a UTF-8 encoded input stream.</jc>
128       *    MyBean <jv>myBean</jv> = <jv>parser</jv>.read(<jk>new</jk> FileInputStream(<js>"MyBean.txt"</js>), MyBean.<jk>class</jk>);
129       * </p>
130       *
131       * @param value
132       *    The new value for this property.
133       *    <br>The default value is <js>"UTF-8"</js>.
134       *    <br>Can be <jk>null</jk> (defaults to UTF-8).
135       * @return This object.
136       */
137      public SELF streamCharset(Charset value) {
138         streamCharset = value;
139         return self();
140      }
141
142
143   }
144
145   /**
146    * Concrete default builder leaf for the non-subclassed {@link ReaderParser#create()} / {@link ReaderParser#copy()} path.
147    */
148   public static final class DefaultBuilder extends Builder<DefaultBuilder> {
149
150      DefaultBuilder() {}
151
152      DefaultBuilder(ReaderParser copyFrom) {
153         super(copyFrom);
154      }
155
156      DefaultBuilder(Builder<?> copyFrom) {
157         super(copyFrom);
158      }
159
160      @Override /* Overridden from Context.Builder<?> */
161      public DefaultBuilder copy() {
162         return new DefaultBuilder(this);
163      }
164   }
165
166   /**
167    * Creates a new builder for this object.
168    *
169    * @return A new builder.
170    */
171   @SuppressWarnings({
172      "java:S1452" // Builder<?> wildcard return intentional; callers chain via fluent API without needing the concrete type
173   })
174   public static Builder<?> create() {
175      return new DefaultBuilder();
176   }
177
178   private final Charset streamCharset;
179
180   /**
181    * Constructor.
182    *
183    * @param builder The builder for this object.
184    *    <br>Cannot be <jk>null</jk>.
185    */
186   protected ReaderParser(Builder<?> builder) {
187      super(builder);
188      streamCharset = builder.streamCharset;
189   }
190
191   @Override /* Overridden from Context */
192   public ReaderParserSession.Builder<?> createSession() {
193      return ReaderParserSession.create(this);
194   }
195
196   @Override /* Overridden from Context */
197   public ReaderParserSession getSession() { return createSession().build(); }
198
199   @Override /* Overridden from Parser */
200   public final boolean isReaderParser() { return true; }
201
202   /**
203    * Input stream charset.
204    *
205    * @see Builder#streamCharset(Charset)
206    * @return
207    *    The character set to use for converting <c>InputStreams</c> and byte arrays to readers.
208    */
209   protected final Charset getStreamCharset() { return streamCharset; }
210
211   @Override /* Overridden from Parser */
212   protected FluentMap<String,Object> properties() {
213      return super.properties()
214         .a("streamCharset", streamCharset);
215   }
216}