| /* |
| * 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 ( |
| "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) |
| // get none pojo JavaClassName |
| var nonePojoJavaName string |
| if !isPojo { |
| s, ok := loadPOJORegistry(v) |
| if !ok { |
| return perrors.Errorf("non-pojo obj %s has not being registered before!", typeof(v)) |
| } |
| nonePojoJavaName = s.javaName |
| } |
| |
| 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 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(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 |
| } |
| |
| // EncodeMapClass encode a map as object, which MUST contains a key _class and its value is the target class name. |
| func (e *Encoder) EncodeMapClass(m map[string]interface{}) error { |
| clsName, ok := m[ClassKey] |
| if !ok { |
| return perrors.New("no _class key map") |
| } |
| |
| className, ok := clsName.(string) |
| if !ok { |
| return perrors.Errorf("expect string class name, but get %v", reflect.TypeOf(clsName)) |
| } |
| |
| return e.EncodeMapAsClass(className, m) |
| } |
| |
| // EncodeMapAsClass encode a map as object of given class name. |
| func (e *Encoder) EncodeMapAsClass(className string, m map[string]interface{}) error { |
| idx := e.classIndex(className) |
| |
| if idx == -1 { |
| var clsDef *ClassInfo |
| s, ok := getStructInfo(className) |
| if ok { |
| clsDef = pojoRegistry.classInfoList[s.index] |
| } else { |
| var err error |
| clsDef, err = buildMapClassDef(className, m) |
| if err != nil { |
| return err |
| } |
| } |
| idx = len(e.classInfoList) |
| e.classInfoList = append(e.classInfoList, clsDef) |
| e.buffer = append(e.buffer, clsDef.buffer...) |
| } |
| |
| return e.encodeMapAsIndexedClass(idx, m) |
| } |
| |
| // EncodeMapAsObject encode a map as the given class defined object. |
| // Sometimes a class may not being registered in hessian, but it can be decoded from serialized data, |
| // and the ClassInfo can be found in Decoder by calling Decoder.FindClassInfo. |
| func (e *Encoder) EncodeMapAsObject(clsDef *ClassInfo, m map[string]interface{}) error { |
| idx := e.classIndex(clsDef.javaName) |
| if idx == -1 { |
| idx = len(e.classInfoList) |
| e.classInfoList = append(e.classInfoList, clsDef) |
| if len(clsDef.buffer) == 0 { |
| clsDef.initDefBuffer() |
| } |
| e.buffer = append(e.buffer, clsDef.buffer...) |
| } |
| return e.encodeMapAsIndexedClass(idx, m) |
| } |
| |
| // encodeMapAsIndexedClass encode a map as the defined class at the given index in the encoder class list. |
| func (e *Encoder) encodeMapAsIndexedClass(idx int, m map[string]interface{}) error { |
| // 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)) |
| } |
| |
| cls := e.classInfoList[idx] |
| var err error |
| for i := 0; i < len(cls.fieldNameList); i++ { |
| fieldName := cls.fieldNameList[i] |
| if err = e.Encode(m[fieldName]); err != nil { |
| return perrors.Wrapf(err, "failed to encode field: %s, %+v", fieldName, m[fieldName]) |
| } |
| } |
| 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 err = d.decToDest(field); err != nil { |
| return nil, perrors.Wrapf(err, "decInstance->DecodeValue: %s", fieldName) |
| } |
| |
| } // 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 && d.Strict { |
| 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) (interface{}, 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) |
| enumVal := PackPtr(reflect.ValueOf(enumValue).Convert(info.typ)).Interface() |
| d.appendRefs(enumVal) |
| return enumVal, nil |
| } |
| |
| // skip this object |
| func (d *Decoder) skip(cls *ClassInfo) error { |
| fieldLen := len(cls.fieldNameList) |
| if fieldLen < 1 { |
| return nil |
| } |
| |
| for i := 0; i < fieldLen; 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 { |
| if d.isSkip { |
| return nil, d.skip(cls) |
| } |
| return d.decClassToMap(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 { |
| if d.isSkip { |
| return nil, d.skip(cls) |
| } |
| return d.decClassToMap(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) |
| } |
| } |
| |
| func (d *Decoder) decClassToMap(cls *ClassInfo) (interface{}, error) { |
| vMap := make(map[string]interface{}, len(cls.fieldNameList)) |
| vMap[ClassKey] = cls.javaName |
| |
| d.appendRefs(vMap) |
| |
| for i := 0; i < len(cls.fieldNameList); i++ { |
| fieldName := cls.fieldNameList[i] |
| |
| fieldValue, decErr := d.DecodeValue() |
| if decErr != nil { |
| return nil, perrors.Wrapf(decErr, "decClassToMap -> decode field name:%s", fieldName) |
| } |
| vMap[fieldName] = EnsureRawAny(fieldValue) |
| } |
| |
| return vMap, nil |
| } |