| // Copyright (c) 2016 ~ 2019, Alex Stocks. |
| // |
| // 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 ( |
| "fmt" |
| "reflect" |
| "strings" |
| "sync" |
| ) |
| |
| import ( |
| jerrors "github.com/juju/errors" |
| ) |
| |
| const ( |
| InvalidJavaEnum JavaEnum = -1 |
| ) |
| |
| // !!! Pls attention that Every field name should be upper case. |
| // Otherwise the app may panic. |
| type POJO interface { |
| JavaClassName() string // got a go struct's Java Class package name which should be a POJO class. |
| } |
| |
| type POJOEnum interface { |
| POJO |
| String() string |
| EnumValue(string) JavaEnum |
| } |
| |
| type JavaEnum int32 |
| |
| type JavaEnumClass struct { |
| name string |
| } |
| |
| type classInfo struct { |
| javaName string |
| fieldNameList []string |
| buffer []byte // encoded buffer |
| } |
| |
| type structInfo struct { |
| typ reflect.Type |
| goName string |
| javaName string |
| index int // classInfoList index |
| inst interface{} |
| } |
| |
| type POJORegistry struct { |
| sync.RWMutex |
| classInfoList []classInfo // {class name, field name list...} list |
| j2g map[string]string // java class name --> go struct name |
| registry map[string]structInfo // go class name --> go struct info |
| } |
| |
| var ( |
| pojoRegistry = POJORegistry{ |
| j2g: make(map[string]string), |
| registry: make(map[string]structInfo), |
| } |
| pojoType = reflect.TypeOf((*POJO)(nil)).Elem() |
| javaEnumType = reflect.TypeOf((*POJOEnum)(nil)).Elem() |
| ) |
| |
| // 解析struct |
| func showPOJORegistry() { |
| pojoRegistry.Lock() |
| for k, v := range pojoRegistry.registry { |
| fmt.Println("-->> show Registered types <<----") |
| fmt.Println(k, v) |
| } |
| pojoRegistry.Unlock() |
| } |
| |
| // Register a POJO instance. The return value is -1 if @o has been registered. |
| func RegisterPOJO(o POJO) int { |
| // # definition for an object (compact map) |
| // class-def ::= 'C' string int string* |
| var ( |
| ok bool |
| b []byte |
| i int |
| n int |
| f string |
| l []string |
| t structInfo |
| c classInfo |
| v reflect.Value |
| ) |
| |
| pojoRegistry.Lock() |
| defer pojoRegistry.Unlock() |
| if _, ok = pojoRegistry.registry[o.JavaClassName()]; !ok { |
| v = reflect.ValueOf(o) |
| switch v.Kind() { |
| case reflect.Struct: |
| t.typ = v.Type() |
| case reflect.Ptr: |
| t.typ = v.Elem().Type() |
| default: |
| t.typ = reflect.TypeOf(o) |
| } |
| t.goName = t.typ.String() |
| t.javaName = o.JavaClassName() |
| t.inst = o |
| pojoRegistry.j2g[t.javaName] = t.goName |
| |
| b = b[:0] |
| b = encByte(b, BC_OBJECT_DEF) |
| b = encString(b, t.javaName) |
| l = l[:0] |
| n = t.typ.NumField() |
| b = encInt32(b, int32(n)) |
| for i = 0; i < n; i++ { |
| f = strings.ToLower(t.typ.Field(i).Name) |
| l = append(l, f) |
| b = encString(b, f) |
| } |
| |
| c = classInfo{javaName: t.javaName, fieldNameList: l} |
| c.buffer = append(c.buffer, b[:]...) |
| t.index = len(pojoRegistry.classInfoList) |
| pojoRegistry.classInfoList = append(pojoRegistry.classInfoList, c) |
| pojoRegistry.registry[t.goName] = t |
| i = t.index |
| } else { |
| i = -1 |
| } |
| |
| return i |
| } |
| |
| // Register a value type JavaEnum variable. |
| func RegisterJavaEnum(o POJOEnum) int { |
| var ( |
| ok bool |
| b []byte |
| i int |
| n int |
| f string |
| l []string |
| t structInfo |
| c classInfo |
| v reflect.Value |
| ) |
| |
| pojoRegistry.Lock() |
| defer pojoRegistry.Unlock() |
| if _, ok = pojoRegistry.registry[o.JavaClassName()]; !ok { |
| v = reflect.ValueOf(o) |
| switch v.Kind() { |
| case reflect.Struct: |
| t.typ = v.Type() |
| case reflect.Ptr: |
| t.typ = v.Elem().Type() |
| default: |
| t.typ = reflect.TypeOf(o) |
| } |
| t.goName = t.typ.String() |
| t.javaName = o.JavaClassName() |
| t.inst = o |
| pojoRegistry.j2g[t.javaName] = t.goName |
| |
| b = b[:0] |
| b = encByte(b, BC_OBJECT_DEF) |
| b = encString(b, t.javaName) |
| l = l[:0] |
| n = 1 |
| b = encInt32(b, int32(n)) |
| f = strings.ToLower("name") |
| l = append(l, f) |
| b = encString(b, f) |
| |
| c = classInfo{javaName: t.javaName, fieldNameList: l} |
| c.buffer = append(c.buffer, b[:]...) |
| t.index = len(pojoRegistry.classInfoList) |
| pojoRegistry.classInfoList = append(pojoRegistry.classInfoList, c) |
| pojoRegistry.registry[t.goName] = t |
| i = t.index |
| } else { |
| i = -1 |
| } |
| |
| return i |
| } |
| |
| // check if go struct name @goName has been registered or not. |
| func checkPOJORegistry(goName string) (int, bool) { |
| var ( |
| ok bool |
| s structInfo |
| ) |
| pojoRegistry.RLock() |
| s, ok = pojoRegistry.registry[goName] |
| pojoRegistry.RUnlock() |
| |
| return s.index, ok |
| } |
| |
| // @typeName is class's java name |
| func getStructInfo(javaName string) (structInfo, bool) { |
| var ( |
| ok bool |
| g string |
| s structInfo |
| ) |
| |
| pojoRegistry.RLock() |
| g, ok = pojoRegistry.j2g[javaName] |
| if ok { |
| s, ok = pojoRegistry.registry[g] |
| } |
| pojoRegistry.RUnlock() |
| |
| return s, ok |
| } |
| |
| func getStructDefByIndex(idx int) (reflect.Type, classInfo, error) { |
| var ( |
| ok bool |
| clsName string |
| cls classInfo |
| s structInfo |
| ) |
| |
| pojoRegistry.RLock() |
| defer pojoRegistry.RUnlock() |
| |
| if len(pojoRegistry.classInfoList) <= idx || idx < 0 { |
| return nil, cls, jerrors.Errorf("illegal class index @idx %d", idx) |
| } |
| cls = pojoRegistry.classInfoList[idx] |
| clsName = pojoRegistry.j2g[cls.javaName] |
| s, ok = pojoRegistry.registry[clsName] |
| if !ok { |
| return nil, cls, jerrors.Errorf("can not find go type name %s in registry", clsName) |
| } |
| |
| return s.typ, cls, nil |
| } |
| |
| // Create a new instance by its struct name is @goName. |
| // the return value is nil if @o has been registered. |
| func createInstance(goName string) interface{} { |
| var ( |
| ok bool |
| s structInfo |
| ) |
| |
| pojoRegistry.RLock() |
| s, ok = pojoRegistry.registry[goName] |
| pojoRegistry.RUnlock() |
| if !ok { |
| return nil |
| } |
| |
| return reflect.New(s.typ).Interface() |
| } |