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}