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}