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.msgpack.DataType.*;
016
017import java.io.IOException;
018import java.util.*;
019
020import org.apache.juneau.*;
021import org.apache.juneau.parser.*;
022import org.apache.juneau.transform.*;
023
024/**
025 * Session object that lives for the duration of a single use of {@link MsgPackParser}.
026 *
027 * <p>
028 * This class is NOT thread safe.
029 * It is typically discarded after one-time use although it can be reused against multiple inputs.
030 */
031@SuppressWarnings({ "rawtypes", "unchecked" })
032public final class MsgPackParserSession extends InputStreamParserSession {
033
034   /**
035    * Create a new session using properties specified in the context.
036    *
037    * @param ctx
038    *    The context creating this session object.
039    *    The context contains all the configuration settings for this object.
040    * @param args
041    *    Runtime session arguments.
042    */
043   protected MsgPackParserSession(MsgPackParser ctx, ParserSessionArgs args) {
044      super(ctx, args);
045   }
046
047   @Override /* ParserSession */
048   protected <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws IOException, ParseException, ExecutableException {
049      try (MsgPackInputStream is = new MsgPackInputStream(pipe)) {
050         return parseAnything(type, is, getOuter(), null);
051      }
052   }
053
054   /*
055    * Workhorse method.
056    */
057   private <T> T parseAnything(ClassMeta<?> eType, MsgPackInputStream is, Object outer, BeanPropertyMeta pMeta) throws IOException, ParseException, ExecutableException {
058
059      if (eType == null)
060         eType = object();
061      PojoSwap<T,Object> swap = (PojoSwap<T,Object>)eType.getPojoSwap(this);
062      BuilderSwap<T,Object> builder = (BuilderSwap<T,Object>)eType.getBuilderSwap(this);
063      ClassMeta<?> sType = null;
064      if (builder != null)
065         sType = builder.getBuilderClassMeta(this);
066      else if (swap != null)
067         sType = swap.getSwapClassMeta(this);
068      else
069         sType = eType;
070
071      if (sType.isOptional()) 
072         return (T)Optional.ofNullable(parseAnything(eType.getElementType(), is, outer, pMeta));
073
074      setCurrentClass(sType);
075
076      Object o = null;
077      DataType dt = is.readDataType();
078      int length = (int)is.readLength();
079
080      if (dt != DataType.NULL) {
081         if (dt == BOOLEAN)
082            o = is.readBoolean();
083         else if (dt == INT)
084            o = is.readInt();
085         else if (dt == LONG)
086            o = is.readLong();
087         else if (dt == FLOAT)
088            o = is.readFloat();
089         else if (dt == DOUBLE)
090            o = is.readDouble();
091         else if (dt == STRING)
092            o = trim(is.readString());
093         else if (dt == BIN)
094            o = is.readBinary();
095         else if (dt == ARRAY && sType.isObject()) {
096            ObjectList ol = new ObjectList(this);
097            for (int i = 0; i < length; i++)
098               ol.add(parseAnything(object(), is, outer, pMeta));
099            o = ol;
100         } else if (dt == MAP && sType.isObject()) {
101            ObjectMap om = new ObjectMap(this);
102            for (int i = 0; i < length; i++)
103               om.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, om, pMeta));
104            o = cast(om, pMeta, eType);
105         }
106
107         if (sType.isObject()) {
108            // Do nothing.
109         } else if (sType.isBoolean() || sType.isCharSequence() || sType.isChar() || sType.isNumber()) {
110            o = convertToType(o, sType);
111         } else if (sType.isMap()) {
112            if (dt == MAP) {
113               Map m = (sType.canCreateNewInstance(outer) ? (Map)sType.newInstance(outer) : new ObjectMap(this));
114               for (int i = 0; i < length; i++) {
115                  Object key = parseAnything(sType.getKeyType(), is, outer, pMeta);
116                  ClassMeta<?> vt = sType.getValueType();
117                  Object value = parseAnything(vt, is, m, pMeta);
118                  setName(vt, value, key);
119                  m.put(key, value);
120               }
121               o = m;
122            } else {
123               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
124            }
125         } else if (builder != null || sType.canCreateNewBean(outer)) {
126            if (dt == MAP) {
127               BeanMap m = builder == null ? newBeanMap(outer, sType.getInnerClass()) : toBeanMap(builder.create(this, eType));
128               for (int i = 0; i < length; i++) {
129                  String pName = parseAnything(string(), is, m.getBean(false), null);
130                  BeanPropertyMeta bpm = m.getPropertyMeta(pName);
131                  if (bpm == null) {
132                     if (pName.equals(getBeanTypePropertyName(eType)))
133                        parseAnything(string(), is, null, null);
134                     else
135                        onUnknownProperty(pName, m);
136                  } else {
137                     ClassMeta<?> cm = bpm.getClassMeta();
138                     Object value = parseAnything(cm, is, m.getBean(false), bpm);
139                     setName(cm, value, pName);
140                     bpm.set(m, pName, value);
141                  }
142               }
143               o = builder == null ? m.getBean() : builder.build(this, m.getBean(), eType);
144            } else {
145               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
146            }
147         } else if (sType.canCreateNewInstanceFromString(outer) && dt == STRING) {
148            o = sType.newInstanceFromString(outer, o == null ? "" : o.toString());
149         } else if (sType.isCollection()) {
150            if (dt == MAP) {
151               ObjectMap m = new ObjectMap(this);
152               for (int i = 0; i < length; i++)
153                  m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
154               o = cast(m, pMeta, eType);
155            } else if (dt == ARRAY) {
156               Collection l = (
157                  sType.canCreateNewInstance(outer)
158                  ? (Collection)sType.newInstance()
159                  : new ObjectList(this)
160               );
161               for (int i = 0; i < length; i++)
162                  l.add(parseAnything(sType.getElementType(), is, l, pMeta));
163               o = l;
164            } else {
165               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
166            }
167         } else if (sType.isArray() || sType.isArgs()) {
168            if (dt == MAP) {
169               ObjectMap m = new ObjectMap(this);
170               for (int i = 0; i < length; i++)
171                  m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
172               o = cast(m, pMeta, eType);
173            } else if (dt == ARRAY) {
174               Collection l = (
175                  sType.isCollection() && sType.canCreateNewInstance(outer)
176                  ? (Collection)sType.newInstance()
177                  : new ObjectList(this)
178               );
179               for (int i = 0; i < length; i++)
180                  l.add(parseAnything(sType.isArgs() ? sType.getArg(i) : sType.getElementType(), is, l, pMeta));
181               o = toArray(sType, l);
182            } else {
183               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
184            }
185         } else if (dt == MAP) {
186            ObjectMap m = new ObjectMap(this);
187            for (int i = 0; i < length; i++)
188               m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
189            if (m.containsKey(getBeanTypePropertyName(eType)))
190               o = cast(m, pMeta, eType);
191            else
192               throw new ParseException(this, "Class ''{0}'' could not be instantiated.  Reason: ''{1}''",
193                  sType.getInnerClass().getName(), sType.getNotABeanReason());
194         } else {
195            throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
196         }
197      }
198
199      if (swap != null && o != null)
200         o = unswap(swap, o, eType);
201
202      if (outer != null)
203         setParent(eType, o, outer);
204
205      return (T)o;
206   }
207
208   //-----------------------------------------------------------------------------------------------------------------
209   // Other methods
210   //-----------------------------------------------------------------------------------------------------------------
211
212   @Override /* Session */
213   public ObjectMap toMap() {
214      return super.toMap()
215         .append("MsgPackParserSession", new DefaultFilteringObjectMap()
216         );
217   }
218}