diff options
Diffstat (limited to 'vendor/github.com/pquerna/ffjson/inception/decoder.go')
-rw-r--r-- | vendor/github.com/pquerna/ffjson/inception/decoder.go | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/vendor/github.com/pquerna/ffjson/inception/decoder.go b/vendor/github.com/pquerna/ffjson/inception/decoder.go new file mode 100644 index 000000000..908347a32 --- /dev/null +++ b/vendor/github.com/pquerna/ffjson/inception/decoder.go @@ -0,0 +1,323 @@ +/** + * Copyright 2014 Paul Querna + * + * 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 ffjsoninception + +import ( + "fmt" + "reflect" + "strings" + + "github.com/pquerna/ffjson/shared" +) + +var validValues []string = []string{ + "FFTok_left_brace", + "FFTok_left_bracket", + "FFTok_integer", + "FFTok_double", + "FFTok_string", + "FFTok_bool", + "FFTok_null", +} + +func CreateUnmarshalJSON(ic *Inception, si *StructInfo) error { + out := "" + ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true + if len(si.Fields) > 0 { + ic.OutputImports[`"bytes"`] = true + } + ic.OutputImports[`"fmt"`] = true + + out += tplStr(decodeTpl["header"], header{ + IC: ic, + SI: si, + }) + + out += tplStr(decodeTpl["ujFunc"], ujFunc{ + SI: si, + IC: ic, + ValidValues: validValues, + ResetFields: ic.ResetFields, + }) + + ic.OutputFuncs = append(ic.OutputFuncs, out) + + return nil +} + +func handleField(ic *Inception, name string, typ reflect.Type, ptr bool, quoted bool) string { + return handleFieldAddr(ic, name, false, typ, ptr, quoted) +} + +func handleFieldAddr(ic *Inception, name string, takeAddr bool, typ reflect.Type, ptr bool, quoted bool) string { + out := fmt.Sprintf("/* handler: %s type=%v kind=%v quoted=%t*/\n", name, typ, typ.Kind(), quoted) + + umlx := typ.Implements(unmarshalFasterType) || typeInInception(ic, typ, shared.MustDecoder) + umlx = umlx || reflect.PtrTo(typ).Implements(unmarshalFasterType) + + umlstd := typ.Implements(unmarshalerType) || reflect.PtrTo(typ).Implements(unmarshalerType) + + out += tplStr(decodeTpl["handleUnmarshaler"], handleUnmarshaler{ + IC: ic, + Name: name, + Typ: typ, + Ptr: reflect.Ptr, + TakeAddr: takeAddr || ptr, + UnmarshalJSONFFLexer: umlx, + Unmarshaler: umlstd, + }) + + if umlx || umlstd { + return out + } + + // TODO(pquerna): generic handling of token type mismatching struct type + switch typ.Kind() { + case reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64: + + allowed := buildTokens(quoted, "FFTok_string", "FFTok_integer", "FFTok_null") + out += getAllowTokens(typ.Name(), allowed...) + + out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseInt") + + case reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64: + + allowed := buildTokens(quoted, "FFTok_string", "FFTok_integer", "FFTok_null") + out += getAllowTokens(typ.Name(), allowed...) + + out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseUint") + + case reflect.Float32, + reflect.Float64: + + allowed := buildTokens(quoted, "FFTok_string", "FFTok_double", "FFTok_integer", "FFTok_null") + out += getAllowTokens(typ.Name(), allowed...) + + out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseFloat") + + case reflect.Bool: + ic.OutputImports[`"bytes"`] = true + ic.OutputImports[`"errors"`] = true + + allowed := buildTokens(quoted, "FFTok_string", "FFTok_bool", "FFTok_null") + out += getAllowTokens(typ.Name(), allowed...) + + out += tplStr(decodeTpl["handleBool"], handleBool{ + Name: name, + Typ: typ, + TakeAddr: takeAddr || ptr, + }) + + case reflect.Ptr: + out += tplStr(decodeTpl["handlePtr"], handlePtr{ + IC: ic, + Name: name, + Typ: typ, + Quoted: quoted, + }) + + case reflect.Array, + reflect.Slice: + out += getArrayHandler(ic, name, typ, ptr) + + case reflect.String: + // Is it a json.Number? + if typ.PkgPath() == "encoding/json" && typ.Name() == "Number" { + // Fall back to json package to rely on the valid number check. + // See: https://github.com/golang/go/blob/f05c3aa24d815cd3869153750c9875e35fc48a6e/src/encoding/json/decode.go#L897 + ic.OutputImports[`"encoding/json"`] = true + out += tplStr(decodeTpl["handleFallback"], handleFallback{ + Name: name, + Typ: typ, + Kind: typ.Kind(), + }) + } else { + out += tplStr(decodeTpl["handleString"], handleString{ + IC: ic, + Name: name, + Typ: typ, + TakeAddr: takeAddr || ptr, + Quoted: quoted, + }) + } + case reflect.Interface: + ic.OutputImports[`"encoding/json"`] = true + out += tplStr(decodeTpl["handleFallback"], handleFallback{ + Name: name, + Typ: typ, + Kind: typ.Kind(), + }) + case reflect.Map: + out += tplStr(decodeTpl["handleObject"], handleObject{ + IC: ic, + Name: name, + Typ: typ, + Ptr: reflect.Ptr, + TakeAddr: takeAddr || ptr, + }) + default: + ic.OutputImports[`"encoding/json"`] = true + out += tplStr(decodeTpl["handleFallback"], handleFallback{ + Name: name, + Typ: typ, + Kind: typ.Kind(), + }) + } + + return out +} + +func getArrayHandler(ic *Inception, name string, typ reflect.Type, ptr bool) string { + if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { + ic.OutputImports[`"encoding/base64"`] = true + useReflectToSet := false + if typ.Elem().Name() != "byte" { + ic.OutputImports[`"reflect"`] = true + useReflectToSet = true + } + + return tplStr(decodeTpl["handleByteSlice"], handleArray{ + IC: ic, + Name: name, + Typ: typ, + Ptr: reflect.Ptr, + UseReflectToSet: useReflectToSet, + }) + } + + if typ.Elem().Kind() == reflect.Struct && typ.Elem().Name() != "" { + goto sliceOrArray + } + + if (typ.Elem().Kind() == reflect.Struct || typ.Elem().Kind() == reflect.Map) || + typ.Elem().Kind() == reflect.Array || typ.Elem().Kind() == reflect.Slice && + typ.Elem().Name() == "" { + ic.OutputImports[`"encoding/json"`] = true + + return tplStr(decodeTpl["handleFallback"], handleFallback{ + Name: name, + Typ: typ, + Kind: typ.Kind(), + }) + } + +sliceOrArray: + + if typ.Kind() == reflect.Array { + return tplStr(decodeTpl["handleArray"], handleArray{ + IC: ic, + Name: name, + Typ: typ, + IsPtr: ptr, + Ptr: reflect.Ptr, + }) + } + + return tplStr(decodeTpl["handleSlice"], handleArray{ + IC: ic, + Name: name, + Typ: typ, + IsPtr: ptr, + Ptr: reflect.Ptr, + }) +} + +func getAllowTokens(name string, tokens ...string) string { + return tplStr(decodeTpl["allowTokens"], allowTokens{ + Name: name, + Tokens: tokens, + }) +} + +func getNumberHandler(ic *Inception, name string, takeAddr bool, typ reflect.Type, parsefunc string) string { + return tplStr(decodeTpl["handlerNumeric"], handlerNumeric{ + IC: ic, + Name: name, + ParseFunc: parsefunc, + TakeAddr: takeAddr, + Typ: typ, + }) +} + +func getNumberSize(typ reflect.Type) string { + return fmt.Sprintf("%d", typ.Bits()) +} + +func getType(ic *Inception, name string, typ reflect.Type) string { + s := typ.Name() + + if typ.PkgPath() != "" && typ.PkgPath() != ic.PackagePath { + path := removeVendor(typ.PkgPath()) + ic.OutputImports[`"`+path+`"`] = true + s = typ.String() + } + + if s == "" { + return typ.String() + } + + return s +} + +// removeVendor removes everything before and including a '/vendor/' +// substring in the package path. +// This is needed becuase that full path can't be used in the +// import statement. +func removeVendor(path string) string { + i := strings.Index(path, "/vendor/") + if i == -1 { + return path + } + return path[i+8:] +} + +func buildTokens(containsOptional bool, optional string, required ...string) []string { + if containsOptional { + return append(required, optional) + } + + return required +} + +func unquoteField(quoted bool) string { + // The outer quote of a string is already stripped out by + // the lexer. We need to check if the inner string is also + // quoted. If so, we will decode it as json string. If decoding + // fails, we will use the original string + if quoted { + return ` + unquoted, ok := fflib.UnquoteBytes(outBuf) + if ok { + outBuf = unquoted + } + ` + } + return "" +} + +func getTmpVarFor(name string) string { + return "tmp" + strings.Replace(strings.Title(name), ".", "", -1) +} |