~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/gojsonschema/defaults.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Author: Bodie Solomon
 
2
//         bodie@synapsegarden.net
 
3
//         github.com/binary132
 
4
//
 
5
//         2015-02-16
 
6
 
 
7
package gojsonschema
 
8
 
 
9
import (
 
10
        "errors"
 
11
        "fmt"
 
12
)
 
13
 
 
14
// InsertDefaults takes a map[string]interface{} and inserts any missing
 
15
// default values specified in the JSON-Schema document.  If the given "into"
 
16
// map is nil, it will be created.
 
17
//
 
18
// This is an initial implementation which does not support refs, etc.
 
19
// The schema must be for an object, not a bare value.
 
20
func (s *Schema) InsertDefaults(into map[string]interface{}) (m map[string]interface{}, returnErr error) {
 
21
        // Handle any panics caused by malformed schemas.
 
22
        defer schemaPanicHandler(&returnErr)
 
23
 
 
24
        properties := s.getDocProperties()
 
25
 
 
26
        // Make sure the "into" map isn't nil.
 
27
        if into == nil {
 
28
                into = make(map[string]interface{})
 
29
        }
 
30
 
 
31
        // Now insert the default values at the keys in the "into" map,
 
32
        // non-destructively.
 
33
        iterateAndInsert(into, properties)
 
34
 
 
35
        return into, nil
 
36
}
 
37
 
 
38
// schemaPanics catches panics caused by type assertions or gets on schemas
 
39
// which have somehow slipped past validation.
 
40
func schemaPanicHandler(err *error) {
 
41
        if r := recover(); r != nil {
 
42
                var msg string
 
43
                switch t := r.(type) {
 
44
                case error:
 
45
                        msg = fmt.Sprintf("schema error caused a panic: %s", t.Error())
 
46
                default:
 
47
                        msg = fmt.Sprintf("unknown panic: %#v", t)
 
48
                }
 
49
                *err = errors.New(msg)
 
50
        }
 
51
}
 
52
 
 
53
// getDocProperties retrieves the "properties" key of the standalone doc of
 
54
// the schema.
 
55
//
 
56
// Malformed schemas cause a panic.
 
57
func (s *Schema) getDocProperties() map[string]interface{} {
 
58
        f := s.pool.GetStandaloneDocument()
 
59
 
 
60
        // We need a map[string]interface because we have to check for a
 
61
        // default key.
 
62
        docMap := f.(map[string]interface{})
 
63
 
 
64
        // We need a schema with properties, since this feature does not
 
65
        // support raw value schemas.
 
66
        m := docMap["properties"]
 
67
 
 
68
        return m.(map[string]interface{})
 
69
}
 
70
 
 
71
// iterateAndInsert takes a target map and inserts any missing default values
 
72
// as specified in the properties map, according to JSON-Schema.
 
73
//
 
74
// Malformed schemas cause a panic.
 
75
func iterateAndInsert(into, properties map[string]interface{}) {
 
76
        for property, schema := range properties {
 
77
                // Iterate over each key of the schema.  Each key should have
 
78
                // a schema describing the key's properties.  If it does not,
 
79
                // ignore this key.
 
80
                typedSchema := schema.(map[string]interface{})
 
81
 
 
82
                if v, ok := into[property]; ok {
 
83
                        // If there is already a map value in the target for
 
84
                        // this key, don't overwrite it; step in.  Ignore
 
85
                        // non-map values.
 
86
                        if innerMap, ok := v.(map[string]interface{}); ok {
 
87
                                // The schema should have an inner
 
88
                                // schema since it's an object
 
89
                                schemaProperties := typedSchema["properties"]
 
90
                                typedProperties := schemaProperties.(map[string]interface{})
 
91
                                iterateAndInsert(innerMap, typedProperties)
 
92
                        }
 
93
                        continue
 
94
                }
 
95
 
 
96
                if d, ok := typedSchema["default"]; ok {
 
97
                        // Most basic case: we have a default value. Done for
 
98
                        // this key.
 
99
                        into[property] = d
 
100
                        continue
 
101
                }
 
102
 
 
103
                if p, ok := typedSchema["properties"]; ok {
 
104
                        // If we have a "properties" key, this is an object,
 
105
                        // and we need to go deeper.
 
106
                        innerSchema := p.(map[string]interface{})
 
107
                        tmpTarget := make(map[string]interface{})
 
108
                        iterateAndInsert(tmpTarget, innerSchema)
 
109
                        if len(tmpTarget) > 0 {
 
110
                                into[property] = tmpTarget
 
111
                        }
 
112
                }
 
113
        }
 
114
}