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.csv; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.CollectionUtils.*; 021import static org.apache.juneau.commons.utils.ThrowableUtils.*; 022import static org.apache.juneau.commons.utils.Utils.*; 023 024import java.io.*; 025import java.lang.reflect.*; 026import java.nio.charset.*; 027import java.util.*; 028import java.util.function.*; 029 030import org.apache.juneau.*; 031import org.apache.juneau.commons.lang.*; 032import org.apache.juneau.httppart.*; 033import org.apache.juneau.serializer.*; 034import org.apache.juneau.svl.*; 035 036/** 037 * Session object that lives for the duration of a single use of {@link CsvSerializer}. 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 */ 044@SuppressWarnings("resource") 045public class CsvSerializerSession extends WriterSerializerSession { 046 /** 047 * Builder class. 048 */ 049 public static class Builder extends WriterSerializerSession.Builder { 050 051 /** 052 * Constructor 053 * 054 * @param ctx The context creating this session. 055 * <br>Cannot be <jk>null</jk>. 056 */ 057 protected Builder(CsvSerializer ctx) { 058 super(assertArgNotNull("ctx", ctx)); 059 } 060 061 @Override /* Overridden from Builder */ 062 public <T> Builder apply(Class<T> type, Consumer<T> apply) { 063 super.apply(type, apply); 064 return this; 065 } 066 067 @Override 068 public CsvSerializerSession build() { 069 return new CsvSerializerSession(this); 070 } 071 072 @Override /* Overridden from Builder */ 073 public Builder debug(Boolean value) { 074 super.debug(value); 075 return this; 076 } 077 078 @Override /* Overridden from Builder */ 079 public Builder fileCharset(Charset value) { 080 super.fileCharset(value); 081 return this; 082 } 083 084 @Override /* Overridden from Builder */ 085 public Builder javaMethod(Method value) { 086 super.javaMethod(value); 087 return this; 088 } 089 090 @Override /* Overridden from Builder */ 091 public Builder locale(Locale value) { 092 super.locale(value); 093 return this; 094 } 095 096 @Override /* Overridden from Builder */ 097 public Builder mediaType(MediaType value) { 098 super.mediaType(value); 099 return this; 100 } 101 102 @Override /* Overridden from Builder */ 103 public Builder mediaTypeDefault(MediaType value) { 104 super.mediaTypeDefault(value); 105 return this; 106 } 107 108 @Override /* Overridden from Builder */ 109 public Builder properties(Map<String,Object> value) { 110 super.properties(value); 111 return this; 112 } 113 114 @Override /* Overridden from Builder */ 115 public Builder property(String key, Object value) { 116 super.property(key, value); 117 return this; 118 } 119 120 @Override /* Overridden from Builder */ 121 public Builder resolver(VarResolverSession value) { 122 super.resolver(value); 123 return this; 124 } 125 126 @Override /* Overridden from Builder */ 127 public Builder schema(HttpPartSchema value) { 128 super.schema(value); 129 return this; 130 } 131 132 @Override /* Overridden from Builder */ 133 public Builder schemaDefault(HttpPartSchema value) { 134 super.schemaDefault(value); 135 return this; 136 } 137 138 @Override /* Overridden from Builder */ 139 public Builder streamCharset(Charset value) { 140 super.streamCharset(value); 141 return this; 142 } 143 144 @Override /* Overridden from Builder */ 145 public Builder timeZone(TimeZone value) { 146 super.timeZone(value); 147 return this; 148 } 149 150 @Override /* Overridden from Builder */ 151 public Builder timeZoneDefault(TimeZone value) { 152 super.timeZoneDefault(value); 153 return this; 154 } 155 156 @Override /* Overridden from Builder */ 157 public Builder unmodifiable() { 158 super.unmodifiable(); 159 return this; 160 } 161 162 @Override /* Overridden from Builder */ 163 public Builder uriContext(UriContext value) { 164 super.uriContext(value); 165 return this; 166 } 167 168 @Override /* Overridden from Builder */ 169 public Builder useWhitespace(Boolean value) { 170 super.useWhitespace(value); 171 return this; 172 } 173 } 174 175 /** 176 * Creates a new builder for this object. 177 * 178 * @param ctx The context creating this session. 179 * <br>Cannot be <jk>null</jk>. 180 * @return A new builder. 181 */ 182 public static Builder create(CsvSerializer ctx) { 183 return new Builder(assertArgNotNull("ctx", ctx)); 184 } 185 186 /** 187 * Constructor. 188 * 189 * @param builder The builder for this object. 190 */ 191 protected CsvSerializerSession(Builder builder) { 192 super(builder); 193 } 194 195 /** 196 * Applies any registered object swap to the specified value. 197 * 198 * <p> 199 * If a swap is registered for the value's type, the value is transformed using the swap's 200 * {@code swap()} method before being serialized. 201 * 202 * @param value The value to potentially swap. 203 * @param type The class metadata of the value's type. 204 * @return The swapped value, or the original value if no swap is registered. 205 */ 206 @SuppressWarnings({ "rawtypes" }) 207 private Object applySwap(Object value, ClassMeta<?> type) { 208 try { 209 if (value == null || type == null) 210 return value; 211 212 org.apache.juneau.swap.ObjectSwap swap = type.getSwap(this); 213 if (nn(swap)) { 214 return swap(swap, value); 215 } 216 return value; 217 } catch (SerializeException e) { 218 throw rex(e); 219 } 220 } 221 222 @SuppressWarnings({ "rawtypes", "unchecked" }) 223 @Override /* Overridden from SerializerSession */ 224 protected void doSerialize(SerializerPipe pipe, Object o) throws IOException, SerializeException { 225 226 try (var w = getCsvWriter(pipe)) { 227 var cm = getClassMetaForObject(o); 228 var l = (Collection<?>)null; 229 if (cm.isArray()) { 230 l = l((Object[])o); 231 } else if (cm.isCollection()) { 232 l = (Collection<?>)o; 233 } else { 234 l = Collections.singleton(o); 235 } 236 237 // TODO - Doesn't support DynaBeans. 238 if (ne(l)) { 239 var entryType = getClassMetaForObject(l.iterator().next()); 240 if (entryType.isBean()) { 241 var bm = entryType.getBeanMeta(); 242 var addComma = Flag.create(); 243 244 bm.getProperties().values().stream().filter(BeanPropertyMeta::canRead).forEach(x -> { 245 addComma.ifSet(() -> w.w(',')).set(); 246 w.writeEntry(x.getName()); 247 }); 248 249// bm.forEachProperty(BeanPropertyMeta::canRead, x -> { 250// addComma.ifSet(() -> w.w(',')).set(); 251// w.writeEntry(x.getName()); 252// }); 253 w.append('\n'); 254 l.forEach(x -> { 255 var addComma2 = Flag.create(); 256 BeanMap<?> bean = toBeanMap(x); 257 bm.getProperties().values().stream().filter(BeanPropertyMeta::canRead).forEach(y -> { 258 addComma2.ifSet(() -> w.w(',')).set(); 259 // Bean property values are already swapped by BeanPropertyMeta.get() via toSerializedForm() 260 var value = y.get(bean, y.getName()); 261 w.writeEntry(value); 262 }); 263 w.w('\n'); 264 }); 265 } else if (entryType.isMap()) { 266 var addComma = Flag.create(); 267 var first = (Map)l.iterator().next(); 268 first.keySet().forEach(x -> { 269 addComma.ifSet(() -> w.w(',')).set(); 270 w.writeEntry(x); 271 }); 272 w.append('\n'); 273 l.stream().forEach(x -> { 274 var addComma2 = Flag.create(); 275 var map = (Map)x; 276 map.values().forEach(y -> { 277 addComma2.ifSet(() -> w.w(',')).set(); 278 var value = applySwap(y, getClassMetaForObject(y)); 279 w.writeEntry(value); 280 }); 281 w.w('\n'); 282 }); 283 } else { 284 w.writeEntry("value"); 285 w.append('\n'); 286 l.stream().forEach(x -> { 287 var value = applySwap(x, getClassMetaForObject(x)); 288 w.writeEntry(value); 289 w.w('\n'); 290 }); 291 } 292 } 293 } 294 } 295 296 CsvWriter getCsvWriter(SerializerPipe out) { 297 var output = out.getRawOutput(); 298 if (output instanceof CsvWriter output2) 299 return output2; 300 var w = new CsvWriter(out.getWriter(), isUseWhitespace(), getMaxIndent(), getQuoteChar(), isTrimStrings(), getUriResolver()); 301 out.setWriter(w); 302 return w; 303 } 304}