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.serializer;
018
019
020import java.io.*;
021
022import org.apache.juneau.marshall.*;
023import static org.apache.juneau.commons.utils.Shorts.*;
024
025/**
026 * Subclass of {@link SerializerSession} for stream-based serializers.
027 *
028 * <h5 class='topic'>Description</h5>
029 *
030 * This class is the parent class of all byte-based serializers.
031 * <br>It has 1 abstract method to implement...
032 * <ul>
033 *    <li>{@link #doWrite(SerializerPipe, Object)}
034 * </ul>
035 *
036 * <h5 class='section'>Notes:</h5><ul>
037 *    <li class='warn'>This class is not thread safe and is typically discarded after one use.
038 * </ul>
039 *
040 * <h5 class='section'>See Also:</h5><ul>
041 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
042 * </ul>
043 */
044@SuppressWarnings({
045   "java:S110", // Deep inheritance inherent to the serializer/parser session hierarchy
046   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
047   "resource" // Internal helpers return Closeables wired into pipe lifecycle; Eclipse JDT @Owning warning is by design.
048})
049public class OutputStreamSerializerSession extends SerializerSession {
050
051   /**
052    * Builder class.
053    */
054   @SuppressWarnings({
055      "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
056   })
057   public abstract static class Builder<SELF extends Builder<SELF>> extends SerializerSession.Builder<SELF> {
058
059      private final OutputStreamSerializer ctx;
060
061      /**
062       * Constructor
063       *
064       * @param ctx The context creating this session.
065       *    <br>Cannot be <jk>null</jk>.
066       */
067      protected Builder(OutputStreamSerializer ctx) {
068         super(reqnn("ctx", ctx));
069         this.ctx = ctx;
070      }
071
072      @Override
073      public OutputStreamSerializerSession build() {
074         return new OutputStreamSerializerSession(this);
075      }
076
077   }
078
079   /**
080    * Concrete default builder leaf for the non-subclassed {@code create()} path (CRTP terminal).
081    */
082   public static final class DefaultBuilder extends Builder<DefaultBuilder> {
083
084      DefaultBuilder(OutputStreamSerializer ctx) {
085         super(ctx);
086      }
087   }
088
089   /**
090    * Creates a new builder for this object.
091    *
092    * @param ctx The context creating this session.
093    *    <br>Cannot be <jk>null</jk>.
094    * @return A new builder.
095    */
096   @SuppressWarnings({
097      "java:S1452" // Builder<?> wildcard return intentional; callers use it to construct session instances polymorphically
098   })
099   public static Builder<?> create(OutputStreamSerializer ctx) {
100      return new DefaultBuilder(reqnn("ctx", ctx));
101   }
102
103   private final OutputStreamSerializer ctx;
104
105   /**
106    * Constructor.
107    *
108    * @param builder The builder for this object.
109    *    <br>Cannot be <jk>null</jk>.
110    */
111   protected OutputStreamSerializerSession(Builder<?> builder) {
112      super(builder);
113      ctx = builder.ctx;
114   }
115
116   @Override /* Overridden from SerializerSession */
117   public final boolean isWriterSerializer() { return false; }
118
119   /**
120    * Convenience method for serializing an object to a <code><jk>byte</jk></code>.
121    *
122    * @param o The object to serialize.
123    * @return The output serialized to a byte array.
124    * @throws SerializeException If a problem occurred trying to convert the output.
125    */
126   @Override /* Overridden from SerializerSession */
127   public final byte[] write(Object o) throws SerializeException {
128      var baos = new ByteArrayOutputStream();
129      try {
130         write(o, baos);
131         baos.flush();
132      } catch (IOException e) {
133         throw new SerializeException(e); // Should never happen.
134      }
135      return baos.toByteArray();
136   }
137
138   @Override /* Overridden from SerializerSession */
139   public final String writeToString(Object o) throws SerializeException {
140      // Stream-to-string is a debug / display convenience for binary serializer output (BSON / MsgPack /
141      // CBOR / Prototext / Parquet).  It always emits HEX irrespective of the configured BinaryFormat so that
142      // downstream tooling (test fixtures, log lines, REPL output) gets a stable, copy-pasteable
143      // representation of the binary frame regardless of how the surrounding context configures byte[]
144      // cell encoding.
145      return BinaryFormat.HEX.format(write(o));
146   }
147
148   @Override /* Overridden from SerializerSession */
149   protected SerializerPipe createPipe(Object output) {
150      return new SerializerPipe(output);
151   }
152
153   /**
154    * Binary output format.
155    *
156    * @see MarshallingContext.Builder#binaryFormat(BinaryFormat)
157    * @return
158    *    The format to use for the {@link #writeToString(Object)} method on stream-based serializers when converting byte arrays to strings.
159    */
160   protected final BinaryFormat getBinaryFormat() { return ctx.getMarshallingContext().getBinaryFormat(); }
161
162   /**
163    * Returns whether this binary serializer has a native byte-array wire type.
164    *
165    * <p>
166    * When {@code true}, {@code byte[]} payloads bypass the configured
167    * {@link BinaryFormat} dispatch in {@link org.apache.juneau.marshall.swaps.BinarySwap} and the variant
168    * binary swap installed by {@code MarshalledPropertyPostProcessor}, and are written as raw
169    * bytes through the serializer's native binary opcode (e.g. MsgPack {@code bin} family, CBOR
170    * byte-string major type 2, BSON binary subtype {@code 0x05}).  When {@code false}, the
171    * configured {@link BinaryFormat} fires for non-{@code NOT_SET} formats and produces a text
172    * wire form (HEX / BASE64 / etc.) that the serializer emits as a string in its native string
173    * column / literal type — used by serializers whose wire format has no native byte-array
174    * primitive (Parquet column writer, binary RDF formats).
175    *
176    * <p>
177    * Defaults to {@code true}.  Subclasses without a native byte-array wire type
178    * (e.g. {@link org.apache.juneau.marshall.parquet.ParquetSerializerSession} and the binary RDF
179    * sessions) override and return {@code false}.
180    *
181    * @return {@code true} if this serializer has a native byte-array wire type.
182    */
183   public boolean hasNativeBytes() {
184      return true;
185   }
186}