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