1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.forgerock.api.jackson;
17
18 import static org.forgerock.api.jackson.JacksonUtils.OBJECT_MAPPER;
19
20 import com.fasterxml.jackson.databind.JavaType;
21 import com.fasterxml.jackson.module.jsonSchema.jakarta.JsonSchema;
22 import com.fasterxml.jackson.module.jsonSchema.jakarta.types.ArraySchema;
23 import jakarta.validation.ValidationException;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import org.forgerock.api.enums.ReadPolicy;
30 import org.forgerock.api.enums.WritePolicy;
31 import org.forgerock.json.JsonValue;
32
33
34
35
36 public class CrestArraySchema extends ArraySchema implements CrestReadWritePoliciesSchema, OrderedFieldSchema,
37 ValidatableSchema, WithExampleSchema<List<Object>> {
38 private static final JavaType EXAMPLE_VALUE_TYPE = OBJECT_MAPPER.getTypeFactory()
39 .constructParametrizedType(ArrayList.class, List.class, Object.class);
40
41 private WritePolicy writePolicy;
42 private ReadPolicy readPolicy;
43 private Boolean errorOnWritePolicyFailure;
44 private Boolean returnOnDemand;
45 private Integer propertyOrder;
46 private List<Object> example;
47
48 @Override
49 public WritePolicy getWritePolicy() {
50 return writePolicy;
51 }
52
53 @Override
54 public void setWritePolicy(WritePolicy policy) {
55 this.writePolicy = policy;
56 }
57
58 @Override
59 public ReadPolicy getReadPolicy() {
60 return readPolicy;
61 }
62
63 @Override
64 public void setReadPolicy(ReadPolicy readPolicy) {
65 this.readPolicy = readPolicy;
66 }
67
68 @Override
69 public Boolean getErrorOnWritePolicyFailure() {
70 return errorOnWritePolicyFailure;
71 }
72
73 @Override
74 public void setErrorOnWritePolicyFailure(Boolean errorOnWritePolicyFailure) {
75 this.errorOnWritePolicyFailure = errorOnWritePolicyFailure;
76 }
77
78 @Override
79 public Boolean getReturnOnDemand() {
80 return returnOnDemand;
81 }
82
83 @Override
84 public void setReturnOnDemand(Boolean returnOnDemand) {
85 this.returnOnDemand = returnOnDemand;
86 }
87
88 @Override
89 public Integer getPropertyOrder() {
90 return propertyOrder;
91 }
92
93 @Override
94 public void setPropertyOrder(Integer order) {
95 this.propertyOrder = order;
96 }
97
98 @Override
99 public void validate(JsonValue object) throws ValidationException {
100 if (!object.isCollection()) {
101 throw new ValidationException("Array expected, but got: " + object.getObject());
102 }
103 if (maxItems != null && object.size() > maxItems) {
104 throw new ValidationException("Array has too many items. Maximum permitted: " + maxItems);
105 }
106 if (minItems != null && object.size() < minItems) {
107 throw new ValidationException("Array has too few items. Minimum permitted: " + minItems);
108 }
109 if (items.isSingleItems()) {
110 if (items.asSingleItems().getSchema() instanceof ValidatableSchema) {
111 ValidatableSchema itemSchema = (ValidatableSchema) items.asSingleItems().getSchema();
112 for (JsonValue item : object) {
113 itemSchema.validate(item);
114 }
115 }
116 } else {
117 Iterator<JsonValue> arrayItems = object.iterator();
118 for (JsonSchema itemSchema : items.asArrayItems().getJsonSchemas()) {
119 if (!arrayItems.hasNext()) {
120 throw new ValidationException("Not enough items. Expecting " + itemSchema);
121 }
122 if (itemSchema instanceof ValidatableSchema) {
123 ((ValidatableSchema) itemSchema).validate(arrayItems.next());
124 }
125 }
126 }
127 }
128
129 @Override
130 public List<Object> getExample() {
131 List<Object> example = this.example;
132 if (example == null) {
133 example = new ArrayList<>();
134 boolean foundExample = items.isSingleItems()
135 ? applySingleSchemaExample(example)
136 : applyMultipleSchemasExamples(example);
137 if (!foundExample) {
138 example = null;
139 }
140 }
141 return example;
142 }
143
144 private boolean applySingleSchemaExample(List<Object> example) {
145 boolean foundExample = false;
146 if (items.asSingleItems().getSchema() instanceof WithExampleSchema) {
147 Object itemsExample = ((WithExampleSchema) items.asSingleItems().getSchema()).getExample();
148 if (itemsExample != null) {
149 int count = minItems != null && minItems > 1 ? minItems : 1;
150 foundExample = true;
151 for (int i = 0; i < count; i++) {
152 example.add(itemsExample);
153 }
154 }
155 }
156 return foundExample;
157 }
158
159 private boolean applyMultipleSchemasExamples(List<Object> example) {
160 boolean foundExample = false;
161 for (JsonSchema schema : items.asArrayItems().getJsonSchemas()) {
162 if (schema instanceof WithExampleSchema) {
163 Object propertyExample = ((WithExampleSchema) schema).getExample();
164 if (propertyExample != null) {
165 foundExample = true;
166 example.add(propertyExample);
167 } else {
168 example.add(new HashMap<>());
169 }
170 }
171 }
172 return foundExample;
173 }
174
175 @Override
176 public void setExample(String example) throws IOException {
177 this.example = OBJECT_MAPPER.readValue(example, EXAMPLE_VALUE_TYPE);
178 }
179 }