blob: 0186992ed9a501912a09ac7d32f2bd027849c523 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package hessian
import (
"io"
"reflect"
"strings"
"sync"
)
import (
perrors "github.com/pkg/errors"
)
// get @v go struct name
func typeof(v interface{}) string {
return reflect.TypeOf(v).String()
}
/////////////////////////////////////////
// map/object
/////////////////////////////////////////
//class-def ::= 'C' string int string* // mandatory type string, the number of fields, and the field names.
//object ::= 'O' int value* // class-def id, value list
// ::= [x60-x6f] value* // class-def id, value list
//
//Object serialization
//
//class Car {
// String color;
// String model;
//}
//
//out.writeObject(new Car("red", "corvette"));
//out.writeObject(new Car("green", "civic"));
//
//---
//
//C # object definition (#0)
// x0b example.Car # type is example.Car
// x92 # two fields
// x05 color # color field name
// x05 model # model field name
//
//O # object def (long form)
// x90 # object definition #0
// x03 red # color field value
// x08 corvette # model field value
//
//x60 # object def #0 (short form)
// x05 green # color field value
// x05 civic # model field value
//
//enum Color {
// RED,
// GREEN,
// BLUE,
//}
//
//out.writeObject(Color.RED);
//out.writeObject(Color.GREEN);
//out.writeObject(Color.BLUE);
//out.writeObject(Color.GREEN);
//
//---
//
//C # class definition #0
// x0b example.Color # type is example.Color
// x91 # one field
// x04 name # enumeration field is "name"
//
//x60 # object #0 (class def #0)
// x03 RED # RED value
//
//x60 # object #1 (class def #0)
// x90 # object definition ref #0
// x05 GREEN # GREEN value
//
//x60 # object #2 (class def #0)
// x04 BLUE # BLUE value
//
//x51 x91 # object ref #1, i.e. Color.GREEN
func (e *Encoder) encObject(v interface{}) error {
var (
i int
idx int
num int
err error
clsDef *classInfo
)
pojo, isPojo := v.(POJO)
vv := reflect.ValueOf(v)
// get none pojo JavaClassName
var nonePojoJavaName string
if !isPojo {
s, ok := loadPOJORegistry(vv.Type().String())
if !ok {
return perrors.Errorf("non-pojo obj %s has not being registered before!", typeof(v))
}
nonePojoJavaName = s.javaName
}
// check ref
if n, ok := e.checkRefMap(vv); ok {
e.buffer = encRef(e.buffer, n)
return nil
}
vv = UnpackPtr(vv)
// check nil pointer
if !vv.IsValid() {
e.buffer = EncNull(e.buffer)
return nil
}
// write object definition
idx = -1
for i = range e.classInfoList {
if isPojo && pojo.JavaClassName() == e.classInfoList[i].javaName || !isPojo && nonePojoJavaName == e.classInfoList[i].javaName {
idx = i
break
}
}
var ok bool
if idx == -1 {
idx, ok = checkPOJORegistry(typeof(v))
if !ok {
if reflect.TypeOf(v).Implements(javaEnumType) {
idx = RegisterJavaEnum(v.(POJOEnum))
} else if isPojo {
idx = RegisterPOJO(pojo)
} else {
return perrors.Errorf("non-pojo obj %s has not being registered before!", typeof(v))
}
}
_, clsDef, err = getStructDefByIndex(idx)
if err != nil {
return perrors.WithStack(err)
}
idx = len(e.classInfoList)
e.classInfoList = append(e.classInfoList, clsDef)
e.buffer = append(e.buffer, clsDef.buffer...)
}
// write object instance
if byte(idx) <= OBJECT_DIRECT_MAX {
e.buffer = encByte(e.buffer, byte(idx)+BC_OBJECT_DIRECT)
} else {
e.buffer = encByte(e.buffer, BC_OBJECT)
e.buffer = encInt32(e.buffer, int32(idx))
}
if reflect.TypeOf(v).Implements(javaEnumType) {
e.buffer = encString(e.buffer, v.(POJOEnum).String())
return nil
}
structs := []reflect.Value{vv}
for len(structs) > 0 {
vv := structs[0]
vvt := vv.Type()
num = vv.NumField()
for i = 0; i < num; i++ {
tf := vvt.Field(i)
// skip unexported anonymous field
if tf.PkgPath != "" {
continue
}
// skip ignored field
if tag, _ := tf.Tag.Lookup(tagIdentifier); tag == `-` {
continue
}
field := vv.Field(i)
if tf.Anonymous && field.Kind() == reflect.Struct {
structs = append(structs, field)
continue
}
if err = e.Encode(field.Interface()); err != nil {
fieldName := field.Type().String()
return perrors.Wrapf(err, "failed to encode field: %s, %+v", fieldName, field.Interface())
}
}
structs = structs[1:]
}
return nil
}
/////////////////////////////////////////
// Object
/////////////////////////////////////////
//class-def ::= 'C' string int string* // mandatory type string, the number of fields, and the field names.
//object ::= 'O' int value* // class-def id, value list
// ::= [x60-x6f] value* // class-def id, value list
//
//Object serialization
//
//class Car {
// String color;
// String model;
//}
//
//out.writeObject(new Car("red", "corvette"));
//out.writeObject(new Car("green", "civic"));
//
//---
//
//C # object definition (#0)
// x0b example.Car # type is example.Car
// x92 # two fields
// x05 color # color field name
// x05 model # model field name
//
//O # object def (long form)
// x90 # object definition #0
// x03 red # color field value
// x08 corvette # model field value
//
//x60 # object def #0 (short form)
// x05 green # color field value
// x05 civic # model field value
//
//
//
//
//
//enum Color {
// RED,
// GREEN,
// BLUE,
//}
//
//out.writeObject(Color.RED);
//out.writeObject(Color.GREEN);
//out.writeObject(Color.BLUE);
//out.writeObject(Color.GREEN);
//
//---
//
//C # class definition #0
// x0b example.Color # type is example.Color
// x91 # one field
// x04 name # enumeration field is "name"
//
//x60 # object #0 (class def #0)
// x03 RED # RED value
//
//x60 # object #1 (class def #0)
// x90 # object definition ref #0
// x05 GREEN # GREEN value
//
//x60 # object #2 (class def #0)
// x04 BLUE # BLUE value
//
//x51 x91 # object ref #1, i.e. Color.GREEN
func (d *Decoder) decClassDef() (interface{}, error) {
var (
err error
clsName string
fieldNum int32
fieldName string
fieldList []string
)
clsName, err = d.decString(TAG_READ)
if err != nil {
return nil, perrors.WithStack(err)
}
fieldNum, err = d.decInt32(TAG_READ)
if err != nil {
return nil, perrors.WithStack(err)
}
fieldList = make([]string, fieldNum)
for i := 0; i < int(fieldNum); i++ {
fieldName, err = d.decString(TAG_READ)
if err != nil {
return nil, perrors.Wrapf(err, "decClassDef->decString, field num:%d, index:%d", fieldNum, i)
}
fieldList[i] = fieldName
}
return &classInfo{javaName: clsName, fieldNameList: fieldList}, nil
}
type fieldInfo struct {
indexes []int
field *reflect.StructField
}
// map[rType][fieldName]indexes
var fieldIndexCache sync.Map
func findFieldWithCache(name string, typ reflect.Type) ([]int, *reflect.StructField, error) {
typCache, _ := fieldIndexCache.Load(typ)
if typCache == nil {
typCache = &sync.Map{}
fieldIndexCache.Store(typ, typCache)
}
iindexes, existCache := typCache.(*sync.Map).Load(name)
if existCache && iindexes != nil {
finfo := iindexes.(*fieldInfo)
var err error
if len(finfo.indexes) == 0 {
err = perrors.Errorf("failed to find field %s", name)
}
return finfo.indexes, finfo.field, err
}
indexes, field, err := findField(name, typ)
typCache.(*sync.Map).Store(name, &fieldInfo{indexes: indexes, field: field})
return indexes, field, err
}
// findField find structField in rType
//
// return
// indexes []int
// field reflect.StructField
// err error
func findField(name string, typ reflect.Type) ([]int, *reflect.StructField, error) {
for i := 0; i < typ.NumField(); i++ {
// matching tag first, then lowerCamelCase, SameCase, lowerCase
typField := typ.Field(i)
tagVal, hasTag := typField.Tag.Lookup(tagIdentifier)
fieldName := typField.Name
if hasTag && tagVal == name ||
fieldName == name ||
lowerCamelCase(fieldName) == name ||
strings.ToLower(fieldName) == name {
return []int{i}, &typField, nil
}
if typField.Anonymous && typField.Type.Kind() == reflect.Struct {
next, field, _ := findField(name, typField.Type)
if len(next) > 0 {
indexes := []int{i}
indexes = append(indexes, next...)
return indexes, field, nil
}
}
}
return []int{}, nil, perrors.Errorf("failed to find field %s", name)
}
func (d *Decoder) decInstance(typ reflect.Type, cls *classInfo) (interface{}, error) {
if typ.Kind() != reflect.Struct {
return nil, perrors.Errorf("wrong type expect Struct but get:%s", typ.String())
}
vRef := reflect.New(typ)
// add pointer ref so that ref the same object
d.appendRefs(vRef.Interface())
vv := vRef.Elem()
for i := 0; i < len(cls.fieldNameList); i++ {
fieldName := cls.fieldNameList[i]
index, fieldStruct, err := findFieldWithCache(fieldName, typ)
if err != nil {
d.DecodeValue()
continue
}
// skip unexported anonymous field
if fieldStruct.PkgPath != "" {
continue
}
field := vv.FieldByIndex(index)
if !field.CanSet() {
return nil, perrors.Errorf("decInstance CanSet false for field %s", fieldName)
}
// get field type from type object, not do that from value
fldTyp := UnpackPtrType(field.Type())
// unpack pointer to enable value setting
fldRawValue := UnpackPtrValue(field)
kind := fldTyp.Kind()
switch kind {
case reflect.String:
str, err := d.decString(TAG_READ)
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->ReadString: %s", fieldName)
}
fldRawValue.SetString(str)
case reflect.Int32, reflect.Int16, reflect.Int8:
num, err := d.decInt32(TAG_READ)
if err != nil {
// java enum
if fldRawValue.Type().Implements(javaEnumType) {
d.unreadByte() // Enum parsing, decInt64 above has read a byte, so you need to return a byte here
s, decErr := d.DecodeValue()
if decErr != nil {
return nil, perrors.Wrapf(decErr, "decInstance->decObject field name:%s", fieldName)
}
enumValue, _ := s.(JavaEnum)
num = int32(enumValue)
} else {
return nil, perrors.Wrapf(err, "decInstance->decInt32, field name:%s", fieldName)
}
}
fldRawValue.SetInt(int64(num))
case reflect.Uint16, reflect.Uint8:
num, err := d.decInt32(TAG_READ)
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->decInt32, field name:%s", fieldName)
}
fldRawValue.SetUint(uint64(num))
case reflect.Uint, reflect.Int, reflect.Int64:
num, err := d.decInt64(TAG_READ)
if err != nil {
if fldTyp.Implements(javaEnumType) {
d.unreadByte() // Enum parsing, decInt64 above has read a byte, so you need to return a byte here
s, decErr := d.Decode()
if decErr != nil {
return nil, perrors.Wrapf(decErr, "decInstance->decObject field name:%s", fieldName)
}
enumValue, _ := s.(JavaEnum)
num = int64(enumValue)
} else {
return nil, perrors.Wrapf(err, "decInstance->decInt64 field name:%s", fieldName)
}
}
fldRawValue.SetInt(num)
case reflect.Uint32, reflect.Uint64:
num, err := d.decInt64(TAG_READ)
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->decInt64, field name:%s", fieldName)
}
fldRawValue.SetUint(uint64(num))
case reflect.Bool:
b, err := d.Decode()
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->Decode field name:%s", fieldName)
}
v, ok := b.(bool)
if !ok {
return nil, perrors.Wrapf(err, "value convert to bool failed, field name:%s", fieldName)
}
if fldRawValue.Kind() == reflect.Ptr && fldRawValue.CanSet() {
if b != nil {
field.Set(reflect.ValueOf(&v))
}
} else if fldRawValue.Kind() != reflect.Ptr {
fldRawValue.SetBool(v)
}
case reflect.Float32, reflect.Float64:
num, err := d.decDouble(TAG_READ)
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->decDouble field name:%s", fieldName)
}
fldRawValue.SetFloat(num.(float64))
case reflect.Map:
// decode map should use the original field value for correct value setting
err := d.decMapByValue(field)
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->decMapByValue field name: %s", fieldName)
}
case reflect.Slice, reflect.Array:
m, err := d.decList(TAG_READ)
if err != nil {
if err == io.EOF {
break
}
return nil, perrors.WithStack(err)
}
// set slice separately
err = SetSlice(fldRawValue, m)
if err != nil {
return nil, err
}
case reflect.Struct:
var (
err error
s interface{}
)
fldType := UnpackPtrType(fldRawValue.Type())
if fldType.String() == "time.Time" {
s, err = d.decDate(TAG_READ)
if err != nil {
return nil, perrors.WithStack(err)
}
SetValue(fldRawValue, EnsurePackValue(s))
} else {
s, err = d.decObject(TAG_READ)
if err != nil {
return nil, perrors.WithStack(err)
}
if s != nil {
// set value which accepting pointers
SetValue(fldRawValue, EnsurePackValue(s))
}
}
case reflect.Interface:
s, err := d.DecodeValue()
if err != nil {
return nil, perrors.WithStack(err)
}
if s != nil {
if ref, ok := s.(*_refHolder); ok {
_ = unpackRefHolder(fldRawValue, fldTyp, ref)
} else {
// set value which accepting pointers
SetValue(fldRawValue, EnsurePackValue(s))
}
}
default:
return nil, perrors.Errorf("unknown struct member type: %v %v", kind, typ.Name()+"."+fieldStruct.Name)
}
} // end for
return vRef.Interface(), nil
}
func (d *Decoder) appendClsDef(cd *classInfo) {
d.classInfoList = append(d.classInfoList, cd)
}
func (d *Decoder) getStructDefByIndex(idx int) (reflect.Type, *classInfo, error) {
var (
ok bool
cls *classInfo
s *structInfo
err error
)
if len(d.classInfoList) <= idx || idx < 0 {
return nil, cls, perrors.Errorf("illegal class index @idx %d", idx)
}
cls = d.classInfoList[idx]
s, ok = getStructInfo(cls.javaName)
if !ok {
// exception
if s, ok = checkAndGetException(cls); ok {
return s.typ, cls, nil
}
if !d.isSkip {
err = perrors.Errorf("can not find go type name %s in registry", cls.javaName)
}
return nil, cls, err
}
return s.typ, cls, nil
}
func (d *Decoder) decEnum(javaName string, flag int32) (JavaEnum, error) {
var (
err error
enumName string
ok bool
info *structInfo
enumValue JavaEnum
)
enumName, err = d.decString(TAG_READ) // java enum class member is "name"
if err != nil {
return InvalidJavaEnum, perrors.Wrap(err, "decString for decJavaEnum")
}
info, ok = getStructInfo(javaName)
if !ok {
return InvalidJavaEnum, perrors.Errorf("getStructInfo(javaName:%s) = false", javaName)
}
enumValue = info.inst.(POJOEnum).EnumValue(enumName)
d.appendRefs(enumValue)
return enumValue, nil
}
// skip this object
func (d *Decoder) skip(cls *classInfo) error {
len := len(cls.fieldNameList)
if len < 1 {
return nil
}
for i := 0; i < len; i++ {
// skip class fields.
if _, err := d.DecodeValue(); err != nil {
return err
}
}
return nil
}
func (d *Decoder) decObject(flag int32) (interface{}, error) {
var (
tag byte
idx int32
err error
typ reflect.Type
cls *classInfo
)
if flag != TAG_READ {
tag = byte(flag)
} else {
tag, _ = d.ReadByte()
}
switch {
case tag == BC_NULL:
return nil, nil
case tag == BC_REF:
return d.decRef(int32(tag))
case tag == BC_OBJECT_DEF:
clsDef, decErr := d.decClassDef()
if decErr != nil {
return nil, perrors.Wrap(decErr, "decObject->decClassDef byte double")
}
cls, _ = clsDef.(*classInfo)
// add to slice
d.appendClsDef(cls)
return d.DecodeValue()
case tag == BC_OBJECT:
idx, err = d.decInt32(TAG_READ)
if err != nil {
return nil, err
}
typ, cls, err = d.getStructDefByIndex(int(idx))
if err != nil {
return nil, err
}
if typ == nil {
return nil, d.skip(cls)
}
if typ.Implements(javaEnumType) {
return d.decEnum(cls.javaName, TAG_READ)
}
if c, ok := GetSerializer(cls.javaName); ok {
return c.DecObject(d, typ, cls)
}
return d.decInstance(typ, cls)
case BC_OBJECT_DIRECT <= tag && tag <= (BC_OBJECT_DIRECT+OBJECT_DIRECT_MAX):
typ, cls, err = d.getStructDefByIndex(int(tag - BC_OBJECT_DIRECT))
if err != nil {
return nil, err
}
if typ == nil {
return nil, d.skip(cls)
}
if typ.Implements(javaEnumType) {
return d.decEnum(cls.javaName, TAG_READ)
}
if c, ok := GetSerializer(cls.javaName); ok {
return c.DecObject(d, typ, cls)
}
return d.decInstance(typ, cls)
default:
return nil, perrors.Errorf("decObject illegal object type tag:%+v", tag)
}
}