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}