1// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// author           xeipuuv
16// author-github    https://github.com/xeipuuv
17// author-mail      xeipuuv@gmail.com
18//
19// repository-name  gojsonschema
20// repository-desc  An implementation of JSON Schema, based on IETF's draft v4 - Go language.
21//
22// description      Defines the structure of a sub-subSchema.
23//                  A sub-subSchema can contain other sub-schemas.
24//
25// created          27-02-2013
26
27package gojsonschema
28
29import (
30	"errors"
31	"regexp"
32	"strings"
33
34	"github.com/xeipuuv/gojsonreference"
35)
36
37const (
38	KEY_SCHEMA                = "$subSchema"
39	KEY_ID                    = "$id"
40	KEY_REF                   = "$ref"
41	KEY_TITLE                 = "title"
42	KEY_DESCRIPTION           = "description"
43	KEY_TYPE                  = "type"
44	KEY_ITEMS                 = "items"
45	KEY_ADDITIONAL_ITEMS      = "additionalItems"
46	KEY_PROPERTIES            = "properties"
47	KEY_PATTERN_PROPERTIES    = "patternProperties"
48	KEY_ADDITIONAL_PROPERTIES = "additionalProperties"
49	KEY_DEFINITIONS           = "definitions"
50	KEY_MULTIPLE_OF           = "multipleOf"
51	KEY_MINIMUM               = "minimum"
52	KEY_MAXIMUM               = "maximum"
53	KEY_EXCLUSIVE_MINIMUM     = "exclusiveMinimum"
54	KEY_EXCLUSIVE_MAXIMUM     = "exclusiveMaximum"
55	KEY_MIN_LENGTH            = "minLength"
56	KEY_MAX_LENGTH            = "maxLength"
57	KEY_PATTERN               = "pattern"
58	KEY_FORMAT                = "format"
59	KEY_MIN_PROPERTIES        = "minProperties"
60	KEY_MAX_PROPERTIES        = "maxProperties"
61	KEY_DEPENDENCIES          = "dependencies"
62	KEY_REQUIRED              = "required"
63	KEY_MIN_ITEMS             = "minItems"
64	KEY_MAX_ITEMS             = "maxItems"
65	KEY_UNIQUE_ITEMS          = "uniqueItems"
66	KEY_ENUM                  = "enum"
67	KEY_ONE_OF                = "oneOf"
68	KEY_ANY_OF                = "anyOf"
69	KEY_ALL_OF                = "allOf"
70	KEY_NOT                   = "not"
71)
72
73type subSchema struct {
74
75	// basic subSchema meta properties
76	id          *string
77	title       *string
78	description *string
79
80	property string
81
82	// Types associated with the subSchema
83	types jsonSchemaType
84
85	// Reference url
86	ref *gojsonreference.JsonReference
87	// Schema referenced
88	refSchema *subSchema
89	// Json reference
90	subSchema *gojsonreference.JsonReference
91
92	// hierarchy
93	parent                      *subSchema
94	definitions                 map[string]*subSchema
95	definitionsChildren         []*subSchema
96	itemsChildren               []*subSchema
97	itemsChildrenIsSingleSchema bool
98	propertiesChildren          []*subSchema
99
100	// validation : number / integer
101	multipleOf       *float64
102	maximum          *float64
103	exclusiveMaximum bool
104	minimum          *float64
105	exclusiveMinimum bool
106
107	// validation : string
108	minLength *int
109	maxLength *int
110	pattern   *regexp.Regexp
111	format    string
112
113	// validation : object
114	minProperties *int
115	maxProperties *int
116	required      []string
117
118	dependencies         map[string]interface{}
119	additionalProperties interface{}
120	patternProperties    map[string]*subSchema
121
122	// validation : array
123	minItems    *int
124	maxItems    *int
125	uniqueItems bool
126
127	additionalItems interface{}
128
129	// validation : all
130	enum []string
131
132	// validation : subSchema
133	oneOf []*subSchema
134	anyOf []*subSchema
135	allOf []*subSchema
136	not   *subSchema
137}
138
139func (s *subSchema) AddEnum(i interface{}) error {
140
141	is, err := marshalToJsonString(i)
142	if err != nil {
143		return err
144	}
145
146	if isStringInSlice(s.enum, *is) {
147		return errors.New(formatErrorDescription(
148			Locale.KeyItemsMustBeUnique(),
149			ErrorDetails{"key": KEY_ENUM},
150		))
151	}
152
153	s.enum = append(s.enum, *is)
154
155	return nil
156}
157
158func (s *subSchema) ContainsEnum(i interface{}) (bool, error) {
159
160	is, err := marshalToJsonString(i)
161	if err != nil {
162		return false, err
163	}
164
165	return isStringInSlice(s.enum, *is), nil
166}
167
168func (s *subSchema) AddOneOf(subSchema *subSchema) {
169	s.oneOf = append(s.oneOf, subSchema)
170}
171
172func (s *subSchema) AddAllOf(subSchema *subSchema) {
173	s.allOf = append(s.allOf, subSchema)
174}
175
176func (s *subSchema) AddAnyOf(subSchema *subSchema) {
177	s.anyOf = append(s.anyOf, subSchema)
178}
179
180func (s *subSchema) SetNot(subSchema *subSchema) {
181	s.not = subSchema
182}
183
184func (s *subSchema) AddRequired(value string) error {
185
186	if isStringInSlice(s.required, value) {
187		return errors.New(formatErrorDescription(
188			Locale.KeyItemsMustBeUnique(),
189			ErrorDetails{"key": KEY_REQUIRED},
190		))
191	}
192
193	s.required = append(s.required, value)
194
195	return nil
196}
197
198func (s *subSchema) AddDefinitionChild(child *subSchema) {
199	s.definitionsChildren = append(s.definitionsChildren, child)
200}
201
202func (s *subSchema) AddItemsChild(child *subSchema) {
203	s.itemsChildren = append(s.itemsChildren, child)
204}
205
206func (s *subSchema) AddPropertiesChild(child *subSchema) {
207	s.propertiesChildren = append(s.propertiesChildren, child)
208}
209
210func (s *subSchema) PatternPropertiesString() string {
211
212	if s.patternProperties == nil || len(s.patternProperties) == 0 {
213		return STRING_UNDEFINED // should never happen
214	}
215
216	patternPropertiesKeySlice := []string{}
217	for pk := range s.patternProperties {
218		patternPropertiesKeySlice = append(patternPropertiesKeySlice, `"`+pk+`"`)
219	}
220
221	if len(patternPropertiesKeySlice) == 1 {
222		return patternPropertiesKeySlice[0]
223	}
224
225	return "[" + strings.Join(patternPropertiesKeySlice, ",") + "]"
226
227}
228