diff options
Diffstat (limited to 'src/mongo/gotools/common/json/mongo_extjson.go')
-rw-r--r-- | src/mongo/gotools/common/json/mongo_extjson.go | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/src/mongo/gotools/common/json/mongo_extjson.go b/src/mongo/gotools/common/json/mongo_extjson.go new file mode 100644 index 00000000000..606b1368b3c --- /dev/null +++ b/src/mongo/gotools/common/json/mongo_extjson.go @@ -0,0 +1,392 @@ +package json + +import ( + "fmt" + "gopkg.in/mgo.v2/bson" + "reflect" +) + +// Represents base-64 encoded binary data +type BinData struct { + Type byte + Base64 string +} + +// Represents the number of milliseconds since the Unix epoch. +type Date int64 + +type ISODate string + +type ObjectId string + +// Represents a reference to another document. +type DBRef struct { + Collection string + Id interface{} + Database string // optional +} + +// Refers to a document in some namespace by wrapping a string containing the namespace +// and the objectId in which the _id of the document is contained +type DBPointer struct { + Namespace string + Id bson.ObjectId +} + +// Represents the literal MinKey. +type MinKey struct{} + +// Represents the literal MaxKey. +type MaxKey struct{} + +// Represents a signed 32-bit integer. +type NumberInt int32 + +// Represents a signed 64-bit integer. +type NumberLong int64 + +// Represents a signed 64-bit float. +type NumberFloat float64 + +type Decimal128 struct { + bson.Decimal128 +} + +// Represents a regular expression. +type RegExp struct { + Pattern string + Options string +} + +// Represents a timestamp value. +type Timestamp struct { + Seconds uint32 + Increment uint32 +} + +type JavaScript struct { + Code string + Scope interface{} +} + +type Float float64 + +// Represents the literal undefined. +type Undefined struct{} + +var ( + // primitive types + byteType = reflect.TypeOf(byte(0)) + stringType = reflect.TypeOf(string("")) + uint32Type = reflect.TypeOf(uint32(0)) + + // object types + binDataType = reflect.TypeOf(BinData{}) + dateType = reflect.TypeOf(Date(0)) + isoDateType = reflect.TypeOf(ISODate("")) + dbRefType = reflect.TypeOf(DBRef{}) + dbPointerType = reflect.TypeOf(DBPointer{}) + maxKeyType = reflect.TypeOf(MaxKey{}) + minKeyType = reflect.TypeOf(MinKey{}) + numberIntType = reflect.TypeOf(NumberInt(0)) + numberLongType = reflect.TypeOf(NumberLong(0)) + numberFloatType = reflect.TypeOf(NumberFloat(0)) + objectIdType = reflect.TypeOf(ObjectId("")) + regexpType = reflect.TypeOf(RegExp{}) + timestampType = reflect.TypeOf(Timestamp{}) + undefinedType = reflect.TypeOf(Undefined{}) + orderedBSONType = reflect.TypeOf(bson.D{}) + interfaceType = reflect.TypeOf((*interface{})(nil)) +) + +func (d Date) isFormatable() bool { + return int64(d) < int64(32535215999000) +} + +func stateBeginExtendedValue(s *scanner, c int) int { + switch c { + case 'u': // beginning of undefined + s.step = stateU + case 'B': // beginning of BinData or Boolean + s.step = stateB + case 'D': // beginning of Date + s.step = stateD + case 'I': // beginning of Infinity or ISODate + s.step = stateI + case 'M': // beginning of MinKey or MaxKey + s.step = stateM + case 'N': // beginning of NaN or NumberXX + s.step = stateUpperN + case 'O': // beginning of ObjectId + s.step = stateO + case 'R': // beginning of RegExp + s.step = stateR + case 'T': // beginning of Timestamp + s.step = stateUpperT + case '/': // beginning of /foo/i + s.step = stateInRegexpPattern + default: + return s.error(c, "looking for beginning of value") + } + + return scanBeginLiteral +} + +// stateB is the state after reading `B`. +func stateB(s *scanner, c int) int { + if c == 'i' { + s.step = stateBi + return scanContinue + } + if c == 'o' { + s.step = stateBo + return scanContinue + } + return s.error(c, "in literal BinData or Boolean (expecting 'i' or 'o')") +} + +// stateUpperN is the state after reading `N`. +func stateUpperN(s *scanner, c int) int { + if c == 'a' { + s.step = stateUpperNa + return scanContinue + } + if c == 'u' { + s.step = stateUpperNu + return scanContinue + } + return s.error(c, "in literal NaN or Number (expecting 'a' or 'u')") +} + +// stateM is the state after reading `M`. +func stateM(s *scanner, c int) int { + if c == 'a' { + s.step = stateUpperMa + return scanContinue + } + if c == 'i' { + s.step = stateUpperMi + return scanContinue + } + return s.error(c, "in literal MaxKey or MinKey (expecting 'a' or 'i')") +} + +// stateD is the state after reading `D`. +func stateD(s *scanner, c int) int { + switch c { + case 'a': + s.step = stateDa + case 'B': + s.step = stateDB + case 'b': + s.step = stateDb + default: + return s.error(c, "in literal Date or DBRef (expecting 'a' or 'B')") + } + return scanContinue +} + +// stateDB is the state after reading `DB`. +func stateDB(s *scanner, c int) int { + if c == 'R' { + s.step = stateDBR + return scanContinue + } + if c == 'P' { + s.step = stateDBP + return scanContinue + } + return s.error(c, "in state DB (expecting 'R or P')") +} + +// stateI is the state after reading `I`. +func stateI(s *scanner, c int) int { + switch c { + case 'n': + s.step = stateIn + case 'S': + s.step = stateIS + default: + return s.error(c, "in literal Infinity or ISO (expecting 'n' or 'S')") + } + return scanContinue +} + +// Decodes a literal stored in item into v. +func (d *decodeState) storeExtendedLiteral(item []byte, v reflect.Value, fromQuoted bool) bool { + switch c := item[0]; c { + case 'n': + d.storeNewLiteral(v, fromQuoted) + + case 'u': // undefined + switch kind := v.Kind(); kind { + case reflect.Interface: + v.Set(reflect.ValueOf(Undefined{})) + default: + d.error(fmt.Errorf("cannot store %v value into %v type", undefinedType, kind)) + } + + case 'B': // BinData or Boolean + switch item[1] { + case 'i': // BinData + d.storeBinData(v) + case 'o': // Boolean + d.storeBoolean(v) + } + case 'D': // Date, DBRef, DBPointer, Dbpointer,or Dbref + switch item[1] { + case 'a': // Date + d.storeDate(v) + case 'b': // Dbref + d.storeDBRef(v) + case 'B': // DBRef or DBPointer + switch item[2] { + case 'R': //DBRef + d.storeDBRef(v) + case 'P': //DBPointer + d.storeDBPointer(v) + } + } + case 'I': + switch item[1] { + case 'S': // ISODate + d.storeISODate(v) + } + + case 'M': // MinKey or MaxKey + switch item[1] { + case 'i': // MinKey + switch kind := v.Kind(); kind { + case reflect.Interface: + v.Set(reflect.ValueOf(MinKey{})) + default: + d.error(fmt.Errorf("cannot store %v value into %v type", minKeyType, kind)) + } + case 'a': // MaxKey + switch kind := v.Kind(); kind { + case reflect.Interface: + v.Set(reflect.ValueOf(MaxKey{})) + default: + d.error(fmt.Errorf("cannot store %v value into %v type", maxKeyType, kind)) + } + } + + case 'O': // ObjectId + d.storeObjectId(v) + + case 'N': // NumberInt or NumberLong + switch item[6] { + case 'I': // NumberInt + d.storeNumberInt(v) + case 'L': // NumberLong + d.storeNumberLong(v) + } + + case 'R': // RegExp constructor + d.storeRegexp(v) + + case 'T': // Timestamp + d.storeTimestamp(v) + + case '/': // regular expression literal + op := d.scanWhile(scanSkipSpace) + if op != scanRegexpPattern { + d.error(fmt.Errorf("expected beginning of regular expression pattern")) + } + + pattern, options, err := d.regexp() + if err != nil { + d.error(err) + } + switch kind := v.Kind(); kind { + case reflect.Interface: + v.Set(reflect.ValueOf(RegExp{pattern, options})) + default: + d.error(fmt.Errorf("cannot store %v value into %v type", regexpType, kind)) + } + + default: + return false + } + + return true +} + +// Returns a literal from the underlying byte data. +func (d *decodeState) getExtendedLiteral(item []byte) (interface{}, bool) { + switch c := item[0]; c { + case 'n': + return d.getNewLiteral(), true + + case 'u': // undefined + return Undefined{}, true + + case 'B': // BinData or Boolean + switch item[1] { + case 'i': // BinData + return d.getBinData(), true + case 'o': // Boolean + return d.getBoolean(), true + } + + case 'D': // Date, DBRef, or Dbref + switch item[1] { + case 'a': // Date + return d.getDate(), true + case 'b': // Dbref + return d.getDBRef(), true + case 'B': // DBRef or DBPoiner + switch item[2] { + case 'R': // DBRef + return d.getDBRef(), true + case 'P': // DBPointer + return d.getDBPointer(), true + } + } + + case 'M': // MinKey or MaxKey + switch item[1] { + case 'i': // MinKey + return MinKey{}, true + case 'a': // MaxKey + return MaxKey{}, true + } + + case 'O': // ObjectId + return d.getObjectId(), true + + case 'N': // NumberInt or NumberLong + switch item[6] { + case 'I': // NumberInt + return d.getNumberInt(), true + case 'L': // NumberLong + return d.getNumberLong(), true + } + + case 'R': // RegExp constructor + return d.getRegexp(), true + + case 'T': // Timestamp + return d.getTimestamp(), true + + case 'I': // ISO Date + switch item[1] { + case 'S': // ISODate + return d.getDate(), true + } + + case '/': // regular expression literal + op := d.scanWhile(scanSkipSpace) + if op != scanRegexpPattern { + d.error(fmt.Errorf("expected beginning of regular expression pattern")) + } + + pattern, options, err := d.regexp() + if err != nil { + d.error(err) + } + return RegExp{pattern, options}, true + } + + return nil, false +} |