RequirementsBuilder.java
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2015 ForgeRock AS.
*/
package org.forgerock.selfservice.core.util;
import static org.forgerock.json.JsonValue.json;
import static org.forgerock.json.JsonValue.object;
import static org.forgerock.selfservice.core.ServiceUtils.emptyJson;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.forgerock.json.JsonValue;
import org.forgerock.util.Reject;
/**
* Helper class to assist with the building of requirements.
*
* @since 0.1.0
*/
public final class RequirementsBuilder {
private final static String JSON_SCHEMA = "http://json-schema.org/draft-04/schema#";
private enum BuilderType { JSON_SCHEMA, OBJECT, ARRAY, ONE_OF, EMPTY_OBJECT }
private final JsonValue jsonValue;
private final List<String> requiredProperties;
private final Map<String, Object> properties;
private final Map<String, Object> definitions;
private final BuilderType builderType;
private RequirementsBuilder(BuilderType type, String description) {
requiredProperties = new ArrayList<>();
properties = new HashMap<>();
definitions = new HashMap<>();
jsonValue = json(object());
builderType = type;
switch (builderType) {
case JSON_SCHEMA:
Reject.ifNull(description);
jsonValue.add("$schema", JSON_SCHEMA);
case OBJECT:
jsonValue
.add("description", description)
.add("type", "object")
.add("required", requiredProperties)
.add("properties", properties)
.add("definitions", definitions);
break;
case ARRAY:
jsonValue.add("type", "array");
break;
case ONE_OF:
jsonValue.add("type", "object");
break;
case EMPTY_OBJECT:
break;
default:
throw new IllegalArgumentException("Unknown type " + builderType);
}
}
/**
* Add a required property; default type is string.
*
* @param name
* property name
* @param description
* property description
*
* @return this builder
*/
public RequirementsBuilder addRequireProperty(String name, String description) {
addRequireProperty(name, "string", description);
return this;
}
/**
* Add a required property.
*
* @param name
* property name
* @param type
* property type
* @param description
* property description
*
* @return this builder
*/
public RequirementsBuilder addRequireProperty(String name, String type, String description) {
Reject.ifNull(name, description);
requiredProperties.add(name);
addProperty(name, type, description);
return this;
}
/**
* Add a property; default type is string.
*
* @param name
* property name
* @param description
* property description
*
* @return this builder
*/
public RequirementsBuilder addProperty(String name, String description) {
addProperty(name, "string", description);
return this;
}
/**
* Add a property.
*
* @param name
* property name
* @param type
* property type
* @param description
* property description
*
* @return this builder
*/
public RequirementsBuilder addProperty(String name, String type, String description) {
Reject.ifNull(name, description);
Map<String, String> entry = new HashMap<>();
entry.put("description", description);
entry.put("type", type);
properties.put(name, entry);
return this;
}
/**
* Add a required property of type object.
*
* @param name
* property name
* @param builder
* property value builder
*
* @return this builder
*/
public RequirementsBuilder addRequireProperty(String name, RequirementsBuilder builder) {
addProperty(name, builder);
requiredProperties.add(name);
return this;
}
/**
* Add a property of type object.
*
* @param name
* property name
* @param builder
* property value builder
*
* @return this builder
*/
public RequirementsBuilder addProperty(String name, RequirementsBuilder builder) {
Reject.ifNull(name, builder);
properties.put(name, prepareChildJsonValue(builder));
return this;
}
/**
* Add a definition to the main object.
*
* @param name
* property name
* @param builder
* definition value builder
*
* @return this builder
*/
public RequirementsBuilder addDefinition(String name, RequirementsBuilder builder) {
Reject.ifNull(name, builder);
definitions.put(name, prepareChildJsonValue(builder));
return this;
}
/**
* Add a custom Json snippet.
*
* @param name
* property name
* @param customJsonValue
* JasonValue instance
*
* @return this builder
*/
public RequirementsBuilder addCustomField(String name, JsonValue customJsonValue) {
Reject.ifNull(name, customJsonValue);
jsonValue.add(name, getUnderlyingObject(customJsonValue));
return this;
}
/**
* Builds a new json object representing the defined requirements.
*
* @return the json requirements
*/
public JsonValue build() {
if (BuilderType.JSON_SCHEMA == builderType) {
Reject.ifTrue(properties.isEmpty(), "There must be at least one property");
}
removePropertiesIfEmpty("definitions", jsonValue);
removePropertiesIfEmpty("required", jsonValue);
return jsonValue;
}
private void removePropertiesIfEmpty(String propertyName, JsonValue jsonValue) {
if (jsonValue.get(propertyName) != null) {
if (jsonValue.get(propertyName).size() == 0) {
jsonValue.remove(propertyName);
}
}
}
/**
* Creates a new builder instance for the json schema.
*
* @param description
* the overall requirements description
*
* @return a new builder instance
*/
public static RequirementsBuilder newInstance(String description) {
return new RequirementsBuilder(BuilderType.JSON_SCHEMA, description);
}
/**
* Creates a new builder instance for object type creation.
*
* @param description
* the object requirements description
*
* @return a new builder instance
*/
public static RequirementsBuilder newObject(String description) {
return new RequirementsBuilder(BuilderType.OBJECT, description);
}
/**
* Creates a new builder instance for empty object creation. All properties have to be set explicitly.
*
* @return a new builder instance
*/
public static RequirementsBuilder newEmptyObject() {
return new RequirementsBuilder(BuilderType.EMPTY_OBJECT, null);
}
/**
* Creates a new builder instance for array type creation.
*
* @param builder
* for the array item
*
* @return a new builder instance
*/
public static RequirementsBuilder newArray(RequirementsBuilder builder) {
return newArray(0, builder);
}
/**
* Creates a new builder instance for array type creation.
*
* @param minItems
* minimum number of items must present in the array
* @param builder
* for the array item
*
* @return a new builder instance
*/
public static RequirementsBuilder newArray(int minItems, RequirementsBuilder builder) {
RequirementsBuilder newBuilder = new RequirementsBuilder(BuilderType.ARRAY, null);
newBuilder.addMinItems(minItems);
newBuilder.addArrayItem(builder);
return newBuilder;
}
private void addMinItems(int minItems) {
if (minItems > 0) {
jsonValue.add("minItems", minItems);
}
}
private void addArrayItem(RequirementsBuilder builder) {
jsonValue.add("items", prepareChildJsonValue(builder));
}
/**
* Creates a new builder instance for oneOf keyword.
*
* @param oneOfElements
* for the oneOf keyword
*
* @return a new builder instance
*/
public static RequirementsBuilder oneOf(JsonValue... oneOfElements) {
RequirementsBuilder newBuilder = new RequirementsBuilder(BuilderType.ONE_OF, null);
newBuilder.addOneOfElements(oneOfElements);
return newBuilder;
}
private void addOneOfElements(JsonValue... oneOfElements) {
List<Object> elements = new ArrayList<>();
for (JsonValue jv : oneOfElements) {
elements.add(getUnderlyingObject(jv));
}
jsonValue.add("oneOf", elements);
}
private Object prepareChildJsonValue(RequirementsBuilder builder) {
JsonValue jsonValue = builder.build();
return getUnderlyingObject(jsonValue);
}
private Object getUnderlyingObject(JsonValue jsonValue) {
return jsonValue.getObject();
}
/**
* Creates an empty requirements json object.
*
* @return empty requirements json object
*/
public static JsonValue newEmptyRequirements() {
return emptyJson();
}
}