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 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.io.*; 022import java.nio.charset.*; 023 024import org.apache.juneau.commons.collections.*; 025import org.apache.juneau.commons.io.*; 026 027/** 028 * Subclass of {@link SerializerSession} for character-based serializers. 029 * 030 * <h5 class='topic'>Description</h5> 031 * 032 * This class is typically the parent class of all character-based serializers. 033 * <br>It has 1 abstract method to implement... 034 * <ul class='spaced-list'> 035 * <li> 036 * {@link #doWrite(SerializerPipe, Object)} 037 * </ul> 038 * 039 * <h5 class='section'>Notes:</h5><ul> 040 * <li class='warn'>This class is not thread safe and is typically discarded after one use. 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:S110", // Deep inheritance inherent to the serializer/parser session hierarchy 049 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 050 "resource" // Internal helpers return Closeables wired into pipe lifecycle; Eclipse JDT @Owning warning is by design. 051}) 052public class WriterSerializerSession extends SerializerSession { 053 054 /** 055 * Builder class. 056 */ 057 @SuppressWarnings({ 058 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 059 }) 060 public abstract static class Builder<SELF extends Builder<SELF>> extends SerializerSession.Builder<SELF> { 061 062 private boolean useWhitespace; 063 private Charset streamCharset; 064 private int maxIndent; 065 private char quoteChar; 066 067 /** 068 * Constructor 069 * 070 * @param ctx The context creating this session. 071 * <br>Cannot be <jk>null</jk>. 072 */ 073 protected Builder(WriterSerializer ctx) { 074 super(reqnn("ctx", ctx)); 075 streamCharset = ctx.getStreamCharset(); 076 useWhitespace = ctx.useWhitespace; 077 maxIndent = ctx.getMaxIndent(); 078 quoteChar = ctx.getQuoteChar(); 079 } 080 081 @Override 082 public WriterSerializerSession build() { 083 return new WriterSerializerSession(this); 084 } 085 086 /** 087 * Maximum indentation. 088 * 089 * @param value The new value for this property. 090 * @return This object. 091 */ 092 public SELF maxIndent(int value) { 093 maxIndent = value; 094 return self(); 095 } 096 097 @Override /* Overridden from Builder */ 098 public SELF property(String key, Object value) { 099 if (key == null) { 100 super.property(key, value); 101 return self(); 102 } 103 switch (key) { 104 case "streamCharset", "WriterSerializerSession.streamCharset": 105 return streamCharset(cvt(value, Charset.class)); 106 case "useWhitespace", "WriterSerializerSession.useWhitespace": 107 return useWhitespace(cvt(value, Boolean.class)); 108 case "maxIndent", "WriterSerializerSession.maxIndent": 109 return maxIndent(cvt(value, Integer.class)); 110 case "quoteChar", "WriterSerializerSession.quoteChar": 111 return quoteChar(cvt(value, Character.class)); 112 default: 113 super.property(key, value); 114 return self(); 115 } 116 } 117 118 /** 119 * Quote character. 120 * 121 * @param value The new value for this property. 122 * @return This object. 123 */ 124 public SELF quoteChar(char value) { 125 quoteChar = value; 126 return self(); 127 } 128 129 /** 130 * Output stream charset. 131 * 132 * <p> 133 * The character set to use when writing to OutputStreams. 134 * 135 * <p> 136 * Used when passing in output streams and byte arrays to {@link WriterSerializer#write(Object, Object)}. 137 * 138 * <p> 139 * If not specified, defaults to UTF-8. 140 * 141 * @param value 142 * The new property value. 143 * <br>Can be <jk>null</jk> (value will not be set, defaults to UTF-8). 144 * @return This object. 145 */ 146 public SELF streamCharset(Charset value) { 147 if (nn(value)) 148 streamCharset = value; 149 return self(); 150 } 151 152 /** 153 * Use whitespace. 154 * 155 * <p> 156 * If true, whitespace is added to the output to improve readability. 157 * 158 * @param value 159 * The new property value. 160 * <br>Can be <jk>null</jk> (value will not be set, existing value from context will be kept). 161 * @return This object. 162 */ 163 public SELF useWhitespace(Boolean value) { 164 if (nn(value)) 165 useWhitespace = value; 166 return self(); 167 } 168 } 169 170 /** 171 * Concrete default builder leaf for the non-subclassed {@code create()} path (CRTP terminal). 172 */ 173 public static final class DefaultBuilder extends Builder<DefaultBuilder> { 174 175 DefaultBuilder(WriterSerializer ctx) { 176 super(ctx); 177 } 178 } 179 180 /** 181 * Creates a new builder for this object. 182 * 183 * @param ctx The context creating this session. 184 * <br>Cannot be <jk>null</jk>. 185 * @return A new builder. 186 */ 187 @SuppressWarnings({ 188 "java:S1452" // Builder<?> wildcard return intentional; callers use it to construct session instances polymorphically 189 }) 190 public static Builder<?> create(WriterSerializer ctx) { 191 return new DefaultBuilder(reqnn("ctx", ctx)); 192 } 193 194 private final boolean useWhitespace; 195 private final Charset streamCharset; 196 private final int maxIndent; 197 private final char quoteChar; 198 199 /** 200 * Constructor. 201 * 202 * @param builder The builder for this object. 203 * <br>Cannot be <jk>null</jk>. 204 */ 205 protected WriterSerializerSession(Builder<?> builder) { 206 super(builder); 207 streamCharset = builder.streamCharset; 208 useWhitespace = builder.useWhitespace; 209 maxIndent = builder.maxIndent; 210 quoteChar = builder.quoteChar; 211 } 212 213 /** 214 * Returns the stream charset defined on this session. 215 * 216 * @return the stream charset defined on this session. 217 */ 218 public Charset getStreamCharset() { return streamCharset; } 219 220 @Override /* Overridden from SerializerSession */ 221 public final boolean isWriterSerializer() { return true; } 222 223 /** 224 * Convenience method for serializing an object to a <c>String</c>. 225 * 226 * @param o The object to serialize. 227 * @return The output serialized to a string. 228 * @throws SerializeException If a problem occurred trying to convert the output. 229 */ 230 @Override /* Overridden from SerializerSession */ 231 public final String write(Object o) throws SerializeException { 232 var w = new StringBuilderWriter(); 233 try { 234 write(o, w); 235 } catch (IOException e) { 236 throw new SerializeException(e); // Shouldn't happen. 237 } 238 return w.toString(); 239 } 240 241 @Override /* Overridden from SerializerSession */ 242 public final String writeToString(Object o) throws SerializeException { 243 return write(o); 244 } 245 246 @Override /* Overridden from SerializerSession */ 247 protected SerializerPipe createPipe(Object output) { 248 return new SerializerPipe(output, streamCharset); 249 } 250 251 /** 252 * Maximum indentation. 253 * 254 * @see WriterSerializer.Builder#maxIndent(int) 255 * @return 256 * The maximum indentation level in the serialized document. 257 */ 258 protected final int getMaxIndent() { return maxIndent; } 259 260 /** 261 * Quote character. 262 * 263 * @see WriterSerializer.Builder#quoteChar(char) 264 * @return 265 * The character used for quoting attributes and values. 266 */ 267 protected char getQuoteChar() { return quoteChar; } 268 269 /** 270 * Use whitespace. 271 * 272 * @see WriterSerializer.Builder#useWhitespace() 273 * @return 274 * <jk>true</jk> if whitespace is added to the output to improve readability. 275 */ 276 protected final boolean isUseWhitespace() { return useWhitespace; } 277 278 @Override /* Overridden from SerializerSession */ 279 protected FluentMap<String,Object> properties() { 280 return super.properties() 281 .a("streamCharset", streamCharset) 282 .a("useWhitespace", useWhitespace) 283 .a("maxIndent", maxIndent) 284 .a("quoteChar", quoteChar); 285 } 286}