/* * 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; } } }