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
019
020import java.io.*;
021
022import org.apache.juneau.marshall.json.*;
023import org.apache.juneau.marshall.jsonl.*;
024import org.apache.juneau.marshall.serializer.*;
025import org.apache.juneau.marshall.stream.*;
026import static org.apache.juneau.commons.utils.Shorts.*;
027
028/**
029 * Session object that lives for the duration of a single use of {@link Json5lSerializer}.
030 *
031 * <p>
032 * Extends {@link JsonlSerializerSession} to inherit JSONL's line-delimited framing.  When the
033 * owning {@link Json5lSerializer} has {@link Json5lSerializer.Builder#json5Sugar() json5Sugar}
034 * enabled, the per-line output switches to JSON5 sugar (single-quoted strings, unquoted field names
035 * where safe) on both the databind and token-streaming paths; otherwise the inherited strict
036 * RFC-8259 output is used unchanged.
037 *
038 * <h5 class='section'>Notes:</h5><ul>
039 *    <li class='warn'>This class is not thread safe and is typically discarded after one use.
040 * </ul>
041 *
042 * <h5 class='section'>See Also:</h5><ul>
043 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Json5l">JSON5L Basics</a>
044 * </ul>
045 */
046@SuppressWarnings({
047   "java:S110", // Inheritance depth acceptable
048   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
049   "resource" // Resource management handled externally
050})
051public class Json5lSerializerSession extends JsonlSerializerSession {
052
053   private static final char SUGAR_QUOTE = '\'';
054
055   /**
056    * Builder class.
057    */
058   public static class Builder extends JsonlSerializerSession.Builder {
059
060      final Json5lSerializer ctx;
061
062      /**
063       * Constructor
064       *
065       * @param ctx The context creating this session.
066       *    <br>Cannot be <jk>null</jk>.
067       */
068      protected Builder(Json5lSerializer ctx) {
069         super(reqnn("ctx", ctx));
070         this.ctx = ctx;
071      }
072
073      @Override
074      public Json5lSerializerSession build() {
075         return new Json5lSerializerSession(this);
076      }
077   }
078
079   /**
080    * Creates a new builder for this object.
081    *
082    * @param ctx The context creating this session.
083    *    <br>Cannot be <jk>null</jk>.
084    * @return A new builder.
085    */
086   public static Builder create(Json5lSerializer ctx) {
087      return new Builder(reqnn("ctx", ctx));
088   }
089
090   private final boolean json5Sugar;
091
092   /**
093    * Constructor.
094    *
095    * @param builder The builder for this object.
096    */
097   protected Json5lSerializerSession(Builder builder) {
098      super(builder);
099      json5Sugar = builder.ctx.isJson5Sugar();
100   }
101
102   @Override /* Overridden from JsonSerializerSession */
103   protected JsonWriter<?> getJsonWriter(SerializerPipe out) {
104      if (! json5Sugar)
105         return super.getJsonWriter(out);
106      var output = out.getRawOutput();
107      if (output instanceof JsonWriter<?> output2)
108         return output2;
109      var w = JsonWriter.create(out.getWriter(), isUseWhitespace(), getMaxIndent(), isEscapeSolidus(), SUGAR_QUOTE, true, isTrimStrings(), getUriResolver());
110      out.setWriter(w);
111      return w;
112   }
113
114   @Override /* Overridden from JsonlSerializerSession */
115   public TokenWriter writeTokens(Object output) throws IOException {
116      if (! json5Sugar)
117         return super.writeTokens(output);
118      var walk = new PojoWalker.Options(
119         isKeepNullProperties(),
120         isTrimEmptyMaps(),
121         isTrimEmptyCollections(),
122         isSortMaps(),
123         isSortCollections(),
124         isTrimStrings(),
125         getMarshallingContext());
126      var settings = new JsonTokenWriter.Settings(
127         false /* useWhitespace — JSON5L is one-record-per-line, no pretty-print */,
128         getMaxIndent(),
129         SUGAR_QUOTE,
130         isEscapeSolidus(),
131         isTrimStrings(),
132         true /* simpleAttrs — JSON5 sugar: unquoted field names where safe */,
133         walk,
134         false /* disableObject */);
135      return JsonlTokenWriter.forOutput(output, settings);
136   }
137}