Skip to content

Commit 5b28a26

Browse files
committed
Updated entity model specification, mapping, objects, and tests.
1 parent 0f44e20 commit 5b28a26

20 files changed

+2337
-1140
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package io.zentity.model;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
6+
import java.io.IOException;
7+
import java.util.*;
8+
9+
public class Attribute {
10+
11+
public static final Set<String> VALID_TYPES = new HashSet<>(
12+
Arrays.asList("string", "long", "integer", "short", "byte", "double", "float", "boolean")
13+
);
14+
15+
private final String name;
16+
private String type = "string";
17+
18+
public Attribute(String name, JsonNode json) throws ValidationException {
19+
validateName(name);
20+
this.name = name;
21+
this.deserialize(json);
22+
}
23+
24+
public Attribute(String name, String json) throws ValidationException, IOException {
25+
validateName(name);
26+
this.name = name;
27+
this.deserialize(json);
28+
}
29+
30+
public String name() {
31+
return this.name;
32+
}
33+
34+
public String type() {
35+
return this.type;
36+
}
37+
38+
public void type(JsonNode value) throws ValidationException {
39+
validateType(value);
40+
this.type = value.textValue();
41+
}
42+
43+
private void validateName(String value) throws ValidationException {
44+
if (value.matches("^\\s*$"))
45+
throw new ValidationException("'attributes' has an attribute with empty name.");
46+
}
47+
48+
private void validateType(JsonNode value) throws ValidationException {
49+
if (!value.isTextual())
50+
throw new ValidationException("'attributes." + this.name + ".type' must be a string.");
51+
if (!VALID_TYPES.contains(value.textValue()))
52+
throw new ValidationException("'attributes." + this.name + ".type' does not support the value '" + value.textValue() + "'.");
53+
}
54+
55+
private void validateObject(JsonNode object) throws ValidationException {
56+
if (!object.isObject())
57+
throw new ValidationException("'attributes." + this.name + "' must be an object.");
58+
}
59+
60+
/**
61+
* Deserialize, validate, and hold the state of an attribute object of an entity model.
62+
* Expected structure of the json variable:
63+
* <pre>
64+
* {
65+
* "type": ATTRIBUTE_TYPE
66+
* }
67+
* </pre>
68+
*
69+
* @param json Attribute object of an entity model.
70+
* @throws ValidationException
71+
*/
72+
public void deserialize(JsonNode json) throws ValidationException {
73+
validateObject(json);
74+
75+
// Validate and hold the state of fields.
76+
Iterator<Map.Entry<String, JsonNode>> fields = json.fields();
77+
while (fields.hasNext()) {
78+
Map.Entry<String, JsonNode> field = fields.next();
79+
String name = field.getKey();
80+
JsonNode value = field.getValue();
81+
switch (name) {
82+
case "type":
83+
this.type(value);
84+
break;
85+
default:
86+
throw new ValidationException("'attributes." + this.name + "." + name + "' is not a recognized field.");
87+
}
88+
}
89+
}
90+
91+
public void deserialize(String json) throws ValidationException, IOException {
92+
deserialize(new ObjectMapper().readTree(json));
93+
}
94+
95+
}
+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package io.zentity.model;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
6+
import java.io.IOException;
7+
import java.util.*;
8+
9+
public class Index {
10+
11+
public static final Set<String> REQUIRED_FIELDS = new HashSet<>(
12+
Arrays.asList("fields")
13+
);
14+
15+
private final String name;
16+
private HashMap<String, IndexField> fields;
17+
private HashMap<String, HashMap<String, IndexField>> attributeIndexFieldsMap = new HashMap<>();
18+
19+
public Index(String name, JsonNode json) throws ValidationException {
20+
validateName(name);
21+
this.name = name;
22+
this.deserialize(json);
23+
}
24+
25+
public Index(String name, String json) throws ValidationException, IOException {
26+
validateName(name);
27+
this.name = name;
28+
this.deserialize(json);
29+
}
30+
31+
public String name() {
32+
return this.name;
33+
}
34+
35+
public HashMap<String, HashMap<String, IndexField>> attributeIndexFieldsMap() {
36+
return this.attributeIndexFieldsMap;
37+
}
38+
39+
public HashMap<String, IndexField> fields() {
40+
return this.fields;
41+
}
42+
43+
public void fields(JsonNode value) throws ValidationException {
44+
validateFields(value);
45+
HashMap<String, IndexField> fields = new HashMap<>();
46+
Iterator<Map.Entry<String, JsonNode>> children = value.fields();
47+
while (children.hasNext()) {
48+
Map.Entry<String, JsonNode> child = children.next();
49+
String fieldName = child.getKey();
50+
JsonNode fieldObject = child.getValue();
51+
validateField(fieldName, fieldObject);
52+
fields.put(fieldName, new IndexField(this.name, fieldName, fieldObject));
53+
}
54+
this.fields = fields;
55+
this.rebuildAttributeIndexFieldsMap();
56+
}
57+
58+
private void validateName(String value) throws ValidationException {
59+
if (value.matches("^\\s*$"))
60+
throw new ValidationException("'indices' has an index with an empty name.");
61+
}
62+
63+
private void validateField(String fieldName, JsonNode fieldObject) throws ValidationException {
64+
if (fieldName.equals(""))
65+
throw new ValidationException("'indices." + this.name + ".fields' has a field with an empty name.");
66+
if (!fieldObject.isObject())
67+
throw new ValidationException("'indices." + this.name + ".fields." + fieldName + "' must be an object.");
68+
if (fieldObject.size() == 0)
69+
throw new ValidationException("'indices." + this.name + ".fields." + fieldName + "' must not be empty.");
70+
}
71+
72+
private void validateFields(JsonNode value) throws ValidationException {
73+
if (!value.isObject())
74+
throw new ValidationException("'indices." + this.name + ".fields' must be an object.");
75+
if (value.size() == 0)
76+
throw new ValidationException("'indices." + this.name + ".fields' must not be empty.");
77+
}
78+
79+
private void validateObject(JsonNode object) throws ValidationException {
80+
if (!object.isObject())
81+
throw new ValidationException("'indices." + this.name + "' must be an object.");
82+
if (object.size() == 0)
83+
throw new ValidationException("'indices." + this.name + "' is empty.");
84+
}
85+
86+
/**
87+
* Create a reverse index of attribute names to index fields for faster lookup of index fields by attributes
88+
* during a resolution job.
89+
*/
90+
private void rebuildAttributeIndexFieldsMap() {
91+
this.attributeIndexFieldsMap = new HashMap<>();
92+
for (String indexFieldName : this.fields().keySet()) {
93+
String attributeName = this.fields().get(indexFieldName).attribute();
94+
if (!this.attributeIndexFieldsMap.containsKey(attributeName))
95+
this.attributeIndexFieldsMap.put(attributeName, new HashMap<>());
96+
if (!this.attributeIndexFieldsMap.get(attributeName).containsKey(indexFieldName))
97+
this.attributeIndexFieldsMap.get(attributeName).put(indexFieldName, this.fields.get(indexFieldName));
98+
}
99+
}
100+
101+
/**
102+
* Deserialize, validate, and hold the state of an index object of an entity model.
103+
* Expected structure of the json variable:
104+
* <pre>
105+
* {
106+
* "fields": {
107+
* INDEX_FIELD_NAME: INDEX_FIELD_OBJECT
108+
* ...
109+
* }
110+
* }
111+
* </pre>
112+
*
113+
* @param json Index object of an entity model.
114+
* @throws ValidationException
115+
*/
116+
public void deserialize(JsonNode json) throws ValidationException {
117+
validateObject(json);
118+
119+
// Validate the existence of required fields.
120+
for (String field : REQUIRED_FIELDS)
121+
if (!json.has(field))
122+
throw new ValidationException("'indices." + this.name + "' is missing required field '" + field + "'.");
123+
124+
// Validate and hold the state of fields.
125+
Iterator<Map.Entry<String, JsonNode>> fields = json.fields();
126+
while (fields.hasNext()) {
127+
Map.Entry<String, JsonNode> field = fields.next();
128+
String name = field.getKey();
129+
JsonNode value = field.getValue();
130+
switch (name) {
131+
case "fields":
132+
this.fields(value);
133+
break;
134+
default:
135+
throw new ValidationException("'indices." + this.name + "." + name + "' is not a recognized field.");
136+
}
137+
}
138+
}
139+
140+
public void deserialize(String json) throws ValidationException, IOException {
141+
deserialize(new ObjectMapper().readTree(json));
142+
}
143+
144+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package io.zentity.model;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
6+
import java.io.IOException;
7+
import java.util.*;
8+
9+
public class IndexField {
10+
11+
public static final Set<String> REQUIRED_FIELDS = new HashSet<>(
12+
Arrays.asList("attribute")
13+
);
14+
15+
private final String index;
16+
private final String name;
17+
private String attribute;
18+
private String matcher = null;
19+
20+
public IndexField(String index, String name, JsonNode json) throws ValidationException {
21+
validateName(name);
22+
this.index = index;
23+
this.name = name;
24+
this.deserialize(json);
25+
}
26+
27+
public IndexField(String index, String name, String json) throws ValidationException, IOException {
28+
validateName(name);
29+
this.index = index;
30+
this.name = name;
31+
this.deserialize(json);
32+
}
33+
34+
public String index() {
35+
return this.index;
36+
}
37+
38+
public String name() {
39+
return this.name;
40+
}
41+
42+
public String attribute() {
43+
return this.attribute;
44+
}
45+
46+
public void attribute(JsonNode value) throws ValidationException {
47+
validateAttribute(value);
48+
this.attribute = value.textValue();
49+
}
50+
51+
public String matcher() {
52+
return this.matcher;
53+
}
54+
55+
public void matcher(JsonNode value) throws ValidationException {
56+
validateMatcher(value);
57+
this.matcher = value.isTextual() ? value.textValue() : null;
58+
}
59+
60+
private void validateName(String value) throws ValidationException {
61+
if (value.matches("^\\s*$"))
62+
throw new ValidationException("'indices." + this.index + "' has a field with an empty name.");
63+
}
64+
65+
private void validateAttribute(JsonNode value) throws ValidationException {
66+
if (!value.isTextual())
67+
throw new ValidationException("'indices." + this.index + "." + this.name + ".attribute' must be a string.");
68+
if (value.textValue().matches("^\\s*$"))
69+
throw new ValidationException("'indices." + this.index + "." + this.name + ".attribute' must not be empty.");
70+
}
71+
72+
private void validateMatcher(JsonNode value) throws ValidationException {
73+
if (!value.isTextual() && !value.isNull())
74+
throw new ValidationException("'indices." + this.index + "." + this.name + ".matcher' must be a string or null.");
75+
if (value.textValue().matches("^\\s*$"))
76+
throw new ValidationException("'indices." + this.index + "." + this.name + ".matcher' must not be empty.");
77+
}
78+
79+
private void validateObject(JsonNode object) throws ValidationException {
80+
if (!object.isObject())
81+
throw new ValidationException("'indices." + this.index + "." + this.name + "' must be an object.");
82+
if (object.size() == 0)
83+
throw new ValidationException("'indices." + this.index + "." + this.name + "' is empty.");
84+
}
85+
86+
/**
87+
* Deserialize, validate, and hold the state of a field object of an index field object of an entity model.
88+
* Expected structure of the json variable:
89+
* <pre>
90+
* {
91+
* "attribute": ATTRIBUTE_NAME,
92+
* "matcher": MATCHER_NAME
93+
* }
94+
* </pre>
95+
*
96+
* @param json Index object of an entity model.
97+
* @throws ValidationException
98+
*/
99+
public void deserialize(JsonNode json) throws ValidationException {
100+
validateObject(json);
101+
102+
// Validate the existence of required fields.
103+
for (String field : REQUIRED_FIELDS)
104+
if (!json.has(field))
105+
throw new ValidationException("'indices." + this.index + "." + this.name + "' is missing required field '" + field + "'.");
106+
107+
// Validate and hold the state of fields.
108+
Iterator<Map.Entry<String, JsonNode>> fields = json.fields();
109+
while (fields.hasNext()) {
110+
Map.Entry<String, JsonNode> field = fields.next();
111+
String name = field.getKey();
112+
JsonNode value = field.getValue();
113+
switch (name) {
114+
case "attribute":
115+
this.attribute(value);
116+
break;
117+
case "matcher":
118+
this.matcher(value);
119+
break;
120+
default:
121+
throw new ValidationException("'indices." + this.index + "." + this.name + "." + name + "' is not a recognized field.");
122+
}
123+
}
124+
}
125+
126+
public void deserialize(String json) throws ValidationException, IOException {
127+
deserialize(new ObjectMapper().readTree(json));
128+
}
129+
130+
}

0 commit comments

Comments
 (0)