/******************************************************************************* * Copyright (c) 2016 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.cloudfoundry; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.springframework.ide.eclipse.editor.support.yaml.schema.BasicYValueHint; import org.springframework.ide.eclipse.editor.support.yaml.schema.YValueHint; public class BuildpackHintGenerator { private static final String REMOVE_PATTERN = "api.run"; public static final List<YValueHint> DEFAULT_BUILDPACK_VALUES = Arrays.asList(new YValueHint[] { createHint("java_buildpack"), createHint("ruby_buildpack"), createHint("staticfile_buildpack"), createHint("nodejs_buildpack"), createHint("python_buildpack"), createHint("php_buildpack"), createHint("liberty_buildpack"), createHint("binary_buildpack"), createHint("go_buildpack") }); public Collection<YValueHint> getHints(List<BuildpackInfo> infos) { Set<String> allApiLabels = new HashSet<>(); Map<String, Set<String>> buildPacksWithApiLabels = new HashMap<>(); Set<YValueHint> hints = new HashSet<>(); // Hint creation description: // - Create hints with api labels ONLY if the buildpack is NOT found in // all target apis. The purpose of the label is to show which apis the // buildpac is applicable // - Example: if there are three targets: pivotal.io, pez.io, // custompcf.io, and "java_buildpack" is applicable // to only pivotal.io and pez.io, the hint label will be: // "java_buildpack (pivotal.io, pez.io). // - Example: same three targets, but "nodejs_buildpack" is applicable // to ALL three apis, then the label will simply // be "nodejs_buildpack" with no additional API information // Create the buildpack -> apiLabels map if (infos != null) { for (BuildpackInfo info : infos) { String apiLabel = getApiLabel(info.getTargetUrl()); allApiLabels.add(apiLabel); Collection<String> targetBuildpacks = info.getBuildpacks(); if (targetBuildpacks != null) { for (String buildPack : targetBuildpacks) { Set<String> applicableApis = buildPacksWithApiLabels.get(buildPack); if (applicableApis == null) { applicableApis = new HashSet<>(); buildPacksWithApiLabels.put(buildPack, applicableApis); } applicableApis.add(apiLabel); } } } } // create the hints for (Entry<String, Set<String>> entry : buildPacksWithApiLabels.entrySet()) { String buildpack = entry.getKey(); String label = getLabel(buildpack, entry.getValue(), allApiLabels); YValueHint hint = createHint(buildpack, label); hints.add(hint); } if (hints.isEmpty()) { return DEFAULT_BUILDPACK_VALUES; } return hints; } public static YValueHint createHint(String value, String label) { return label != null ? new BasicYValueHint(value, label) : new BasicYValueHint(value); } public static YValueHint createHint(String value) { return createHint(value, null); } protected String getLabel(String buildpack, Set<String> apiLabelsForBuildpack, Set<String> allApiLabels) { // If the list of apis for the buildpack equals the total number of apis, do not add an additional label as it // means the buildpack is applicable to all apis if (apiLabelsForBuildpack == null || apiLabelsForBuildpack.size() == allApiLabels.size() || apiLabelsForBuildpack.size() == 0) { return buildpack; } StringBuffer buf = new StringBuffer(); buf.append(buildpack); buf.append(' '); buf.append('('); int i = 0; for (String bp : apiLabelsForBuildpack) { buf.append(bp); if (i < apiLabelsForBuildpack.size() - 1) { buf.append(','); buf.append(' '); } i++; } buf.append(')'); return buf.toString(); } protected String getApiLabel(String url) { if (url != null) { try { URI uri = new URI(url); String toTrim = uri.getAuthority(); if (toTrim == null) { toTrim = uri.toString(); } int removePatternOffset = REMOVE_PATTERN.length() + 1; if (toTrim != null && toTrim.startsWith(REMOVE_PATTERN) && removePatternOffset < toTrim.length()) { return toTrim.substring(removePatternOffset, toTrim.length()); } } catch (URISyntaxException e) { // Ignore. return original URL. } } return url; } public static class BuildpackInfo { private final String targetUrl; private final Collection<String> buildpacks; public BuildpackInfo(CloudFoundryRunTarget target) throws Exception { this(target.getUrl(), target.getBuildpackValues()); } public BuildpackInfo(String targetUrl, Collection<String> buildpacks) { this.targetUrl = targetUrl; this.buildpacks = buildpacks; } public String getTargetUrl() { return targetUrl; } public Collection<String> getBuildpacks() { return buildpacks; } } }