/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.cli.command.init;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Define the metadata available for a particular service instance.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
class InitializrServiceMetadata {
private static final String DEPENDENCIES_EL = "dependencies";
private static final String TYPE_EL = "type";
private static final String VALUES_EL = "values";
private static final String NAME_ATTRIBUTE = "name";
private static final String ID_ATTRIBUTE = "id";
private static final String DESCRIPTION_ATTRIBUTE = "description";
private static final String ACTION_ATTRIBUTE = "action";
private static final String DEFAULT_ATTRIBUTE = "default";
private final Map<String, Dependency> dependencies;
private final MetadataHolder<String, ProjectType> projectTypes;
private final Map<String, String> defaults;
/**
* Creates a new instance using the specified root {@link JSONObject}.
* @param root the root JSONObject
* @throws JSONException on JSON parsing failure
*/
InitializrServiceMetadata(JSONObject root) throws JSONException {
this.dependencies = parseDependencies(root);
this.projectTypes = parseProjectTypes(root);
this.defaults = Collections.unmodifiableMap(parseDefaults(root));
}
InitializrServiceMetadata(ProjectType defaultProjectType) {
this.dependencies = new HashMap<>();
this.projectTypes = new MetadataHolder<>();
this.projectTypes.getContent().put(defaultProjectType.getId(),
defaultProjectType);
this.projectTypes.setDefaultItem(defaultProjectType);
this.defaults = new HashMap<>();
}
/**
* Return the dependencies supported by the service.
* @return the supported dependencies
*/
public Collection<Dependency> getDependencies() {
return this.dependencies.values();
}
/**
* Return the dependency with the specified id or {@code null} if no such dependency
* exists.
* @param id the id
* @return the dependency or {@code null}
*/
public Dependency getDependency(String id) {
return this.dependencies.get(id);
}
/**
* Return the project types supported by the service.
* @return the supported project types
*/
public Map<String, ProjectType> getProjectTypes() {
return this.projectTypes.getContent();
}
/**
* Return the default type to use or {@code null} if the metadata does not define any
* default.
* @return the default project type or {@code null}
*/
public ProjectType getDefaultType() {
if (this.projectTypes.getDefaultItem() != null) {
return this.projectTypes.getDefaultItem();
}
String defaultTypeId = getDefaults().get("type");
if (defaultTypeId != null) {
return this.projectTypes.getContent().get(defaultTypeId);
}
return null;
}
/**
* Returns the defaults applicable to the service.
* @return the defaults of the service
*/
public Map<String, String> getDefaults() {
return this.defaults;
}
private Map<String, Dependency> parseDependencies(JSONObject root)
throws JSONException {
Map<String, Dependency> result = new HashMap<>();
if (!root.has(DEPENDENCIES_EL)) {
return result;
}
JSONObject dependencies = root.getJSONObject(DEPENDENCIES_EL);
JSONArray array = dependencies.getJSONArray(VALUES_EL);
for (int i = 0; i < array.length(); i++) {
JSONObject group = array.getJSONObject(i);
parseGroup(group, result);
}
return result;
}
private MetadataHolder<String, ProjectType> parseProjectTypes(JSONObject root)
throws JSONException {
MetadataHolder<String, ProjectType> result = new MetadataHolder<>();
if (!root.has(TYPE_EL)) {
return result;
}
JSONObject type = root.getJSONObject(TYPE_EL);
JSONArray array = type.getJSONArray(VALUES_EL);
String defaultType = type.has(DEFAULT_ATTRIBUTE)
? type.getString(DEFAULT_ATTRIBUTE) : null;
for (int i = 0; i < array.length(); i++) {
JSONObject typeJson = array.getJSONObject(i);
ProjectType projectType = parseType(typeJson, defaultType);
result.getContent().put(projectType.getId(), projectType);
if (projectType.isDefaultType()) {
result.setDefaultItem(projectType);
}
}
return result;
}
private Map<String, String> parseDefaults(JSONObject root) throws JSONException {
Map<String, String> result = new HashMap<>();
Iterator<?> keys = root.keys();
while (keys.hasNext()) {
String key = (String) keys.next();
Object o = root.get(key);
if (o instanceof JSONObject) {
JSONObject child = (JSONObject) o;
if (child.has(DEFAULT_ATTRIBUTE)) {
result.put(key, child.getString(DEFAULT_ATTRIBUTE));
}
}
}
return result;
}
private void parseGroup(JSONObject group, Map<String, Dependency> dependencies)
throws JSONException {
if (group.has(VALUES_EL)) {
JSONArray content = group.getJSONArray(VALUES_EL);
for (int i = 0; i < content.length(); i++) {
Dependency dependency = parseDependency(content.getJSONObject(i));
dependencies.put(dependency.getId(), dependency);
}
}
}
private Dependency parseDependency(JSONObject object) throws JSONException {
String id = getStringValue(object, ID_ATTRIBUTE, null);
String name = getStringValue(object, NAME_ATTRIBUTE, null);
String description = getStringValue(object, DESCRIPTION_ATTRIBUTE, null);
return new Dependency(id, name, description);
}
private ProjectType parseType(JSONObject object, String defaultId)
throws JSONException {
String id = getStringValue(object, ID_ATTRIBUTE, null);
String name = getStringValue(object, NAME_ATTRIBUTE, null);
String action = getStringValue(object, ACTION_ATTRIBUTE, null);
boolean defaultType = id.equals(defaultId);
Map<String, String> tags = new HashMap<>();
if (object.has("tags")) {
JSONObject jsonTags = object.getJSONObject("tags");
tags.putAll(parseStringItems(jsonTags));
}
return new ProjectType(id, name, action, defaultType, tags);
}
private String getStringValue(JSONObject object, String name, String defaultValue)
throws JSONException {
return object.has(name) ? object.getString(name) : defaultValue;
}
private Map<String, String> parseStringItems(JSONObject json) throws JSONException {
Map<String, String> result = new HashMap<>();
for (Iterator<?> iterator = json.keys(); iterator.hasNext();) {
String key = (String) iterator.next();
Object value = json.get(key);
if (value instanceof String) {
result.put(key, (String) value);
}
}
return result;
}
private final static class MetadataHolder<K, T> {
private final Map<K, T> content;
private T defaultItem;
private MetadataHolder() {
this.content = new HashMap<>();
}
public Map<K, T> getContent() {
return this.content;
}
public T getDefaultItem() {
return this.defaultItem;
}
public void setDefaultItem(T defaultItem) {
this.defaultItem = defaultItem;
}
}
}