001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.msgpack;
014
015import static org.apache.juneau.internal.CollectionUtils.*;
016import static org.apache.juneau.msgpack.DataType.*;
017
018import java.io.IOException;
019import java.lang.reflect.*;
020import java.util.*;
021import java.util.function.*;
022
023import org.apache.juneau.*;
024import org.apache.juneau.collections.*;
025import org.apache.juneau.httppart.*;
026import org.apache.juneau.internal.*;
027import org.apache.juneau.parser.*;
028import org.apache.juneau.swap.*;
029
030/**
031 * Session object that lives for the duration of a single use of {@link MsgPackParser}.
032 *
033 * <h5 class='section'>Notes:</h5><ul>
034 *    <li class='warn'>This class is not thread safe and is typically discarded after one use.
035 * </ul>
036 *
037 * <h5 class='section'>See Also:</h5><ul>
038 *    <li class='link'><a class="doclink" href="../../../../index.html#jm.MsgPackDetails">MessagePack Details</a>
039
040 * </ul>
041 */
042@SuppressWarnings({ "rawtypes", "unchecked" })
043public final class MsgPackParserSession extends InputStreamParserSession {
044
045   //-------------------------------------------------------------------------------------------------------------------
046   // Static
047   //-------------------------------------------------------------------------------------------------------------------
048
049   /**
050    * Creates a new builder for this object.
051    *
052    * @param ctx The context creating this session.
053    * @return A new builder.
054    */
055   public static Builder create(MsgPackParser ctx) {
056      return new Builder(ctx);
057   }
058
059   //-------------------------------------------------------------------------------------------------------------------
060   // Builder
061   //-------------------------------------------------------------------------------------------------------------------
062
063   /**
064    * Builder class.
065    */
066   @FluentSetters
067   public static class Builder extends InputStreamParserSession.Builder {
068
069      MsgPackParser ctx;
070
071      /**
072       * Constructor
073       *
074       * @param ctx The context creating this session.
075       */
076      protected Builder(MsgPackParser ctx) {
077         super(ctx);
078         this.ctx = ctx;
079      }
080
081      @Override
082      public MsgPackParserSession build() {
083         return new MsgPackParserSession(this);
084      }
085
086      // <FluentSetters>
087
088      @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */
089      public <T> Builder apply(Class<T> type, Consumer<T> apply) {
090         super.apply(type, apply);
091         return this;
092      }
093
094      @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */
095      public Builder debug(Boolean value) {
096         super.debug(value);
097         return this;
098      }
099
100      @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */
101      public Builder properties(Map<String,Object> value) {
102         super.properties(value);
103         return this;
104      }
105
106      @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */
107      public Builder property(String key, Object value) {
108         super.property(key, value);
109         return this;
110      }
111
112      @Override /* GENERATED - org.apache.juneau.ContextSession.Builder */
113      public Builder unmodifiable() {
114         super.unmodifiable();
115         return this;
116      }
117
118      @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */
119      public Builder locale(Locale value) {
120         super.locale(value);
121         return this;
122      }
123
124      @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */
125      public Builder localeDefault(Locale value) {
126         super.localeDefault(value);
127         return this;
128      }
129
130      @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */
131      public Builder mediaType(MediaType value) {
132         super.mediaType(value);
133         return this;
134      }
135
136      @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */
137      public Builder mediaTypeDefault(MediaType value) {
138         super.mediaTypeDefault(value);
139         return this;
140      }
141
142      @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */
143      public Builder timeZone(TimeZone value) {
144         super.timeZone(value);
145         return this;
146      }
147
148      @Override /* GENERATED - org.apache.juneau.BeanSession.Builder */
149      public Builder timeZoneDefault(TimeZone value) {
150         super.timeZoneDefault(value);
151         return this;
152      }
153
154      @Override /* GENERATED - org.apache.juneau.parser.ParserSession.Builder */
155      public Builder javaMethod(Method value) {
156         super.javaMethod(value);
157         return this;
158      }
159
160      @Override /* GENERATED - org.apache.juneau.parser.ParserSession.Builder */
161      public Builder outer(Object value) {
162         super.outer(value);
163         return this;
164      }
165
166      @Override /* GENERATED - org.apache.juneau.parser.ParserSession.Builder */
167      public Builder schema(HttpPartSchema value) {
168         super.schema(value);
169         return this;
170      }
171
172      @Override /* GENERATED - org.apache.juneau.parser.ParserSession.Builder */
173      public Builder schemaDefault(HttpPartSchema value) {
174         super.schemaDefault(value);
175         return this;
176      }
177
178      // </FluentSetters>
179   }
180
181   //-------------------------------------------------------------------------------------------------------------------
182   // Instance
183   //-------------------------------------------------------------------------------------------------------------------
184
185   /**
186    * Constructor.
187    *
188    * @param builder The builder for this object.
189    */
190   protected MsgPackParserSession(Builder builder) {
191      super(builder);
192   }
193
194   @Override /* ParserSession */
195   protected <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws IOException, ParseException, ExecutableException {
196      try (MsgPackInputStream is = new MsgPackInputStream(pipe)) {
197         return parseAnything(type, is, getOuter(), null);
198      }
199   }
200
201   /*
202    * Workhorse method.
203    */
204   private <T> T parseAnything(ClassMeta<?> eType, MsgPackInputStream is, Object outer, BeanPropertyMeta pMeta) throws IOException, ParseException, ExecutableException {
205
206      if (eType == null)
207         eType = object();
208      ObjectSwap<T,Object> swap = (ObjectSwap<T,Object>)eType.getSwap(this);
209      BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
210      ClassMeta<?> sType = null;
211      if (builder != null)
212         sType = builder.getBuilderClassMeta(this);
213      else if (swap != null)
214         sType = swap.getSwapClassMeta(this);
215      else
216         sType = eType;
217
218      if (sType.isOptional())
219         return (T)optional(parseAnything(eType.getElementType(), is, outer, pMeta));
220
221      setCurrentClass(sType);
222
223      Object o = null;
224      DataType dt = is.readDataType();
225      int length = (int)is.readLength();
226
227      if (dt != DataType.NULL) {
228         if (dt == BOOLEAN)
229            o = is.readBoolean();
230         else if (dt == INT)
231            o = is.readInt();
232         else if (dt == LONG)
233            o = is.readLong();
234         else if (dt == FLOAT)
235            o = is.readFloat();
236         else if (dt == DOUBLE)
237            o = is.readDouble();
238         else if (dt == STRING)
239            o = trim(is.readString());
240         else if (dt == BIN)
241            o = is.readBinary();
242         else if (dt == ARRAY && sType.isObject()) {
243            JsonList jl = new JsonList(this);
244            for (int i = 0; i < length; i++)
245               jl.add(parseAnything(object(), is, outer, pMeta));
246            o = jl;
247         } else if (dt == MAP && sType.isObject()) {
248            JsonMap jm = new JsonMap(this);
249            for (int i = 0; i < length; i++)
250               jm.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, jm, pMeta));
251            o = cast(jm, pMeta, eType);
252         }
253
254         if (sType.isObject()) {
255            // Do nothing.
256         } else if (sType.isBoolean() || sType.isCharSequence() || sType.isChar() || sType.isNumber() || sType.isByteArray()) {
257            o = convertToType(o, sType);
258         } else if (sType.isMap()) {
259            if (dt == MAP) {
260               Map m = (sType.canCreateNewInstance(outer) ? (Map)sType.newInstance(outer) : newGenericMap(sType));
261               for (int i = 0; i < length; i++) {
262                  Object key = parseAnything(sType.getKeyType(), is, outer, pMeta);
263                  ClassMeta<?> vt = sType.getValueType();
264                  Object value = parseAnything(vt, is, m, pMeta);
265                  setName(vt, value, key);
266                  m.put(key, value);
267               }
268               o = m;
269            } else {
270               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
271            }
272         } else if (builder != null || sType.canCreateNewBean(outer)) {
273            if (dt == MAP) {
274               BeanMap m = builder == null ? newBeanMap(outer, sType.getInnerClass()) : toBeanMap(builder.create(this, eType));
275               for (int i = 0; i < length; i++) {
276                  String pName = parseAnything(string(), is, m.getBean(false), null);
277                  BeanPropertyMeta bpm = m.getPropertyMeta(pName);
278                  if (bpm == null) {
279                     if (pName.equals(getBeanTypePropertyName(eType)))
280                        parseAnything(string(), is, null, null);
281                     else
282                        onUnknownProperty(pName, m, parseAnything(string(), is, null, null));
283                  } else {
284                     ClassMeta<?> cm = bpm.getClassMeta();
285                     Object value = parseAnything(cm, is, m.getBean(false), bpm);
286                     setName(cm, value, pName);
287                     try {
288                        bpm.set(m, pName, value);
289                     } catch (BeanRuntimeException e) {
290                        onBeanSetterException(pMeta, e);
291                        throw e;
292                     }
293                  }
294               }
295               o = builder == null ? m.getBean() : builder.build(this, m.getBean(), eType);
296            } else {
297               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
298            }
299         } else if (sType.canCreateNewInstanceFromString(outer) && dt == STRING) {
300            o = sType.newInstanceFromString(outer, o == null ? "" : o.toString());
301         } else if (sType.isCollection()) {
302            if (dt == MAP) {
303               JsonMap m = new JsonMap(this);
304               for (int i = 0; i < length; i++)
305                  m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
306               o = cast(m, pMeta, eType);
307            } else if (dt == ARRAY) {
308               Collection l = (
309                  sType.canCreateNewInstance(outer)
310                  ? (Collection)sType.newInstance()
311                  : new JsonList(this)
312               );
313               for (int i = 0; i < length; i++)
314                  l.add(parseAnything(sType.getElementType(), is, l, pMeta));
315               o = l;
316            } else {
317               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
318            }
319         } else if (sType.isArray() || sType.isArgs()) {
320            if (dt == MAP) {
321               JsonMap m = new JsonMap(this);
322               for (int i = 0; i < length; i++)
323                  m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
324               o = cast(m, pMeta, eType);
325            } else if (dt == ARRAY) {
326               Collection l = (
327                  sType.isCollection() && sType.canCreateNewInstance(outer)
328                  ? (Collection)sType.newInstance()
329                  : new JsonList(this)
330               );
331               for (int i = 0; i < length; i++)
332                  l.add(parseAnything(sType.isArgs() ? sType.getArg(i) : sType.getElementType(), is, l, pMeta));
333               o = toArray(sType, l);
334            } else {
335               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
336            }
337         } else if (dt == MAP) {
338            JsonMap m = new JsonMap(this);
339            for (int i = 0; i < length; i++)
340               m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
341            if (m.containsKey(getBeanTypePropertyName(eType)))
342               o = cast(m, pMeta, eType);
343            else if (sType.getProxyInvocationHandler() != null)
344               o = newBeanMap(outer, sType.getInnerClass()).load(m).getBean();
345            else
346               throw new ParseException(this, "Class ''{0}'' could not be instantiated.  Reason: ''{1}''",
347                  sType.getInnerClass().getName(), sType.getNotABeanReason());
348         } else {
349            throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
350         }
351      }
352
353      if (swap != null && o != null)
354         o = unswap(swap, o, eType);
355
356      if (outer != null)
357         setParent(eType, o, outer);
358
359      return (T)o;
360   }
361}