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