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      setCurrentClass(sType);
071
072      Object o = null;
073      DataType dt = is.readDataType();
074      int length = (int)is.readLength();
075
076      if (dt != DataType.NULL) {
077         if (dt == BOOLEAN)
078            o = is.readBoolean();
079         else if (dt == INT)
080            o = is.readInt();
081         else if (dt == LONG)
082            o = is.readLong();
083         else if (dt == FLOAT)
084            o = is.readFloat();
085         else if (dt == DOUBLE)
086            o = is.readDouble();
087         else if (dt == STRING)
088            o = trim(is.readString());
089         else if (dt == BIN)
090            o = is.readBinary();
091         else if (dt == ARRAY && sType.isObject()) {
092            ObjectList ol = new ObjectList(this);
093            for (int i = 0; i < length; i++)
094               ol.add(parseAnything(object(), is, outer, pMeta));
095            o = ol;
096         } else if (dt == MAP && sType.isObject()) {
097            ObjectMap om = new ObjectMap(this);
098            for (int i = 0; i < length; i++)
099               om.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, om, pMeta));
100            o = cast(om, pMeta, eType);
101         }
102
103         if (sType.isObject()) {
104            // Do nothing.
105         } else if (sType.isBoolean() || sType.isCharSequence() || sType.isChar() || sType.isNumber()) {
106            o = convertToType(o, sType);
107         } else if (sType.isMap()) {
108            if (dt == MAP) {
109               Map m = (sType.canCreateNewInstance(outer) ? (Map)sType.newInstance(outer) : new ObjectMap(this));
110               for (int i = 0; i < length; i++) {
111                  Object key = parseAnything(sType.getKeyType(), is, outer, pMeta);
112                  ClassMeta<?> vt = sType.getValueType();
113                  Object value = parseAnything(vt, is, m, pMeta);
114                  setName(vt, value, key);
115                  m.put(key, value);
116               }
117               o = m;
118            } else {
119               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
120            }
121         } else if (builder != null || sType.canCreateNewBean(outer)) {
122            if (dt == MAP) {
123               BeanMap m = builder == null ? newBeanMap(outer, sType.getInnerClass()) : toBeanMap(builder.create(this, eType));
124               for (int i = 0; i < length; i++) {
125                  String pName = parseAnything(string(), is, m.getBean(false), null);
126                  BeanPropertyMeta bpm = m.getPropertyMeta(pName);
127                  if (bpm == null) {
128                     if (pName.equals(getBeanTypePropertyName(eType)))
129                        parseAnything(string(), is, null, null);
130                     else
131                        onUnknownProperty(pName, m);
132                  } else {
133                     ClassMeta<?> cm = bpm.getClassMeta();
134                     Object value = parseAnything(cm, is, m.getBean(false), bpm);
135                     setName(cm, value, pName);
136                     bpm.set(m, pName, value);
137                  }
138               }
139               o = builder == null ? m.getBean() : builder.build(this, m.getBean(), eType);
140            } else {
141               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
142            }
143         } else if (sType.canCreateNewInstanceFromString(outer) && dt == STRING) {
144            o = sType.newInstanceFromString(outer, o == null ? "" : o.toString());
145         } else if (sType.isCollection()) {
146            if (dt == MAP) {
147               ObjectMap m = new ObjectMap(this);
148               for (int i = 0; i < length; i++)
149                  m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
150               o = cast(m, pMeta, eType);
151            } else if (dt == ARRAY) {
152               Collection l = (
153                  sType.canCreateNewInstance(outer)
154                  ? (Collection)sType.newInstance()
155                  : new ObjectList(this)
156               );
157               for (int i = 0; i < length; i++)
158                  l.add(parseAnything(sType.getElementType(), is, l, pMeta));
159               o = l;
160            } else {
161               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
162            }
163         } else if (sType.isArray() || sType.isArgs()) {
164            if (dt == MAP) {
165               ObjectMap m = new ObjectMap(this);
166               for (int i = 0; i < length; i++)
167                  m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
168               o = cast(m, pMeta, eType);
169            } else if (dt == ARRAY) {
170               Collection l = (
171                  sType.isCollection() && sType.canCreateNewInstance(outer)
172                  ? (Collection)sType.newInstance()
173                  : new ObjectList(this)
174               );
175               for (int i = 0; i < length; i++)
176                  l.add(parseAnything(sType.isArgs() ? sType.getArg(i) : sType.getElementType(), is, l, pMeta));
177               o = toArray(sType, l);
178            } else {
179               throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
180            }
181         } else if (dt == MAP) {
182            ObjectMap m = new ObjectMap(this);
183            for (int i = 0; i < length; i++)
184               m.put((String)parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
185            if (m.containsKey(getBeanTypePropertyName(eType)))
186               o = cast(m, pMeta, eType);
187            else
188               throw new ParseException(this, "Class ''{0}'' could not be instantiated.  Reason: ''{1}''",
189                  sType.getInnerClass().getName(), sType.getNotABeanReason());
190         } else {
191            throw new ParseException(this, "Invalid data type {0} encountered for parse type {1}", dt, sType);
192         }
193      }
194
195      if (swap != null && o != null)
196         o = unswap(swap, o, eType);
197
198      if (outer != null)
199         setParent(eType, o, outer);
200
201      return (T)o;
202   }
203
204   //-----------------------------------------------------------------------------------------------------------------
205   // Other methods
206   //-----------------------------------------------------------------------------------------------------------------
207
208   @Override /* Session */
209   public ObjectMap toMap() {
210      return super.toMap()
211         .append("MsgPackParserSession", new DefaultFilteringObjectMap()
212         );
213   }
214}