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