blob: d9f3cc05a361c3bf33cb23a9fb964a3990f39fd9 [file] [log] [blame]
// Copyright 2016-2019 Alex Stocks, Wongoo, Yincheng Fang
//
// Licensed 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"
)
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 POJO) error {
var (
ok bool
i int
idx int
num int
err error
clsDef classInfo
)
vv := reflect.ValueOf(v)
// 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 v.JavaClassName() == e.classInfoList[i].javaName {
idx = i
break
}
}
if idx == -1 {
idx, ok = checkPOJORegistry(typeof(v))
if !ok {
if reflect.TypeOf(v).Implements(javaEnumType) {
idx = RegisterJavaEnum(v.(POJOEnum))
} else {
idx = RegisterPOJO(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
}
num = vv.NumField()
for i = 0; i < num; i++ {
// skip unexported anonymous field
if vv.Type().Field(i).PkgPath != "" {
continue
}
field := vv.Field(i)
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())
}
}
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
}
func findField(name string, typ reflect.Type) (int, error) {
for i := 0; i < typ.NumField(); i++ {
// matching tag first, then lowerCamelCase, SameCase, lowerCase
if val, has := typ.Field(i).Tag.Lookup(tagIdentifier); has && strings.Compare(val, name) == 0 {
return i, nil
}
fieldName := typ.Field(i).Name
switch {
case strings.Compare(lowerCamelCase(fieldName), name) == 0:
return i, nil
case strings.Compare(fieldName, name) == 0:
return i, nil
case strings.Compare(strings.ToLower(fieldName), name) == 0:
return i, nil
}
}
return 0, 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, err := findField(fieldName, typ)
if err != nil {
return nil, perrors.Errorf("can not find field %s", fieldName)
}
// skip unexported anonymous field
if vv.Type().Field(index).PkgPath != "" {
continue
}
field := vv.Field(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, err := d.DecodeValue()
if err != nil {
return nil, perrors.Wrapf(err, "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, err := d.Decode()
if err != nil {
return nil, perrors.Wrapf(err, "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)
}
fldRawValue.SetBool(b.(bool))
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, reflect.Interface:
var (
err error
s interface{}
)
if fldRawValue.Type().String() == "time.Time" {
s, err = d.decDate(TAG_READ)
if err != nil {
return nil, perrors.WithStack(err)
}
fldRawValue.Set(reflect.ValueOf(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))
}
}
default:
return nil, perrors.Errorf("unknown struct member type: %v %v", kind, typ.Name()+"."+typ.Field(index).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
)
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 {
return nil, cls, perrors.Errorf("can not find go type name %s in registry", cls.javaName)
}
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
}
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, err := d.decClassDef()
if err != nil {
return nil, perrors.Wrap(err, "decObject->decClassDef byte double")
}
cls, _ = clsDef.(classInfo)
//add to slice
d.appendClsDef(cls)
if c, ok := GetSerializer(cls.javaName); ok {
return c.DecObject(d)
}
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.Implements(javaEnumType) {
return d.decEnum(cls.javaName, TAG_READ)
}
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.Implements(javaEnumType) {
return d.decEnum(cls.javaName, TAG_READ)
}
return d.decInstance(typ, cls)
default:
return nil, perrors.Errorf("decObject illegal object type tag:%+v", tag)
}
}