1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.api.util;
18
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Map;
22 import java.util.Set;
23
24 import org.forgerock.api.models.ApiDescription;
25 import org.forgerock.api.models.ApiError;
26 import org.forgerock.api.models.Reference;
27 import org.forgerock.api.models.Resource;
28 import org.forgerock.api.models.Schema;
29 import org.forgerock.util.Reject;
30
31
32
33
34
35 public class ReferenceResolver {
36
37 private static final String DEFINITIONS_REF = "#/definitions/";
38
39 private static final String ERRORS_REF = "#/errors/";
40
41 private static final String SERVICES_REF = "#/services/";
42
43 private final ApiDescription local;
44
45 private final Map<String, ApiDescription> map;
46
47
48
49
50
51
52
53 public ReferenceResolver(final ApiDescription local) {
54 this.local = Reject.checkNotNull(local);
55 map = new HashMap<>();
56 register(local);
57 }
58
59
60
61
62
63
64
65
66 public ReferenceResolver register(final ApiDescription apiDescription) {
67 if (map.containsKey(apiDescription.getId())) {
68 throw new IllegalStateException("Already registered ID = " + apiDescription.getId());
69 }
70 map.put(apiDescription.getId(), apiDescription);
71 return this;
72 }
73
74
75
76
77
78
79
80
81 public ReferenceResolver registerAll(final ApiDescription... apiDescriptions) {
82 for (final ApiDescription item : apiDescriptions) {
83 register(item);
84 }
85 return this;
86 }
87
88
89
90
91
92
93
94 public Schema getDefinition(final Reference reference) {
95 return resolveDefinition(reference, new HashSet<String>());
96 }
97
98 private Schema resolveDefinition(final Reference reference, final Set<String> visitedRefs) {
99 final int nameStart = reference.getValue().indexOf(DEFINITIONS_REF);
100 if (nameStart != -1) {
101 final String name = reference.getValue().substring(nameStart + DEFINITIONS_REF.length());
102 if (!name.isEmpty()) {
103 if (!visitedRefs.add(reference.getValue())) {
104 throw new IllegalStateException("Reference loop detected: " + reference.getValue());
105 }
106 if (nameStart == 0) {
107
108 if (local.getDefinitions() != null) {
109 final Schema schema = local.getDefinitions().get(name);
110 if (schema != null && schema.getReference() != null) {
111
112 return resolveDefinition(schema.getReference(), visitedRefs);
113 }
114 return schema;
115 }
116 } else {
117 final String namespace = reference.getValue().substring(0, nameStart);
118 final ApiDescription apiDescription = map.get(namespace);
119 if (apiDescription != null && apiDescription.getDefinitions() != null) {
120 final Schema schema = apiDescription.getDefinitions().get(name);
121 if (schema != null && schema.getReference() != null) {
122
123 return resolveDefinition(schema.getReference(), visitedRefs);
124 }
125 return schema;
126 }
127 }
128 }
129 }
130 return null;
131 }
132
133
134
135
136
137
138
139 public ApiError getError(final Reference reference) {
140 return resolveError(reference, new HashSet<String>());
141 }
142
143 private ApiError resolveError(final Reference reference, final Set<String> visitedRefs) {
144 final int nameStart = reference.getValue().indexOf(ERRORS_REF);
145 if (nameStart != -1) {
146 final String name = reference.getValue().substring(nameStart + ERRORS_REF.length());
147 if (!name.isEmpty()) {
148 if (!visitedRefs.add(reference.getValue())) {
149 throw new IllegalStateException("Reference loop detected: " + reference.getValue());
150 }
151 if (nameStart == 0) {
152
153 if (local.getErrors() != null) {
154 final ApiError error = local.getErrors().get(name);
155 if (error != null && error.getReference() != null) {
156
157 return resolveError(error.getReference(), visitedRefs);
158 }
159 return error;
160 }
161 } else {
162 final String namespace = reference.getValue().substring(0, nameStart);
163 final ApiDescription apiDescription = map.get(namespace);
164 if (apiDescription != null && apiDescription.getErrors() != null) {
165 final ApiError error = apiDescription.getErrors().get(name);
166 if (error != null && error.getReference() != null) {
167
168 return resolveError(error.getReference(), visitedRefs);
169 }
170 return error;
171 }
172 }
173 }
174 }
175 return null;
176 }
177
178
179
180
181
182
183
184 public Resource getService(final Reference reference) {
185 return resolveService(reference, new HashSet<String>());
186 }
187
188 private Resource resolveService(final Reference reference, final Set<String> visitedRefs) {
189 final int nameStart = reference.getValue().indexOf(SERVICES_REF);
190 if (nameStart != -1) {
191 final String name = reference.getValue().substring(nameStart + SERVICES_REF.length());
192 if (!name.isEmpty()) {
193 if (!visitedRefs.add(reference.getValue())) {
194 throw new IllegalStateException("Reference loop detected: " + reference.getValue());
195 }
196 if (nameStart == 0) {
197
198 if (local.getServices() != null) {
199 final Resource service = local.getServices().get(name);
200 if (service != null && service.getReference() != null) {
201
202 return resolveService(service.getReference(), visitedRefs);
203 }
204 return service;
205 }
206 } else {
207 final String namespace = reference.getValue().substring(0, nameStart);
208 final ApiDescription apiDescription = map.get(namespace);
209 if (apiDescription != null && apiDescription.getServices() != null) {
210 final Resource service = apiDescription.getServices().get(name);
211 if (service != null && service.getReference() != null) {
212
213 return resolveService(service.getReference(), visitedRefs);
214 }
215 return service;
216 }
217 }
218 }
219 }
220 return null;
221 }
222 }