/************************************************************************* * Copyright 2013-2014 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.cloudformation.template; import com.eucalyptus.cloudformation.ValidationErrorException; import com.google.common.collect.Lists; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** * Created by ethomas on 11/1/16. */ public class FnSubHelper { public static Collection<String> extractVariables(String value) throws ValidationErrorException { List<String> variables = Lists.newArrayList(); recursivelyExtractVariablesToCollection(value, variables); return variables; } private static void recursivelyExtractVariablesToCollection(String s, Collection<String> variables) throws ValidationErrorException { if (s != null) { int beginPos = s.indexOf("${"); // only proceed if we can find a ${ not at the end of the string if (beginPos != -1 && s.length() != beginPos + 2) { if (s.charAt(beginPos + 2) == '!') { // literal recursivelyExtractVariablesToCollection(s.substring(beginPos + 3), variables); } else { int endPos = s.indexOf("}", beginPos + 2); if (endPos != -1) { String variable = s.substring(beginPos + 2, endPos); Pattern pattern = Pattern.compile("[A-Za-z0-9_\\:\\.]+"); if (!pattern.matcher(variable).matches()) { throw new ValidationErrorException("Template error: variable names in Fn::Sub syntax must contain only " + "alphanumeric characters, underscores, periods, and colons"); } variables.add(variable); recursivelyExtractVariablesToCollection(s.substring(endPos + 1), variables); } } } } } public static String replaceVariables(String value, Map<String, String> variableMapping) throws ValidationErrorException { StringBuilder builder = new StringBuilder(); recursivelyReplaceVariablesWithStringBuilder(value, variableMapping, builder); return builder.toString(); } private static void recursivelyReplaceVariablesWithStringBuilder(String value, Map<String, String> variableMapping, StringBuilder builder) throws ValidationErrorException { if (value != null) { int beginPos = value.indexOf("${"); // only proceed if we can find a ${ not at the end of the string if (beginPos != -1 && value.length() != beginPos + 2) { if (value.charAt(beginPos + 2) == '!') { // literal builder.append(value.substring(0, beginPos+2)); // add everything up to here recursivelyReplaceVariablesWithStringBuilder(value.substring(beginPos + 3), variableMapping, builder); } else { int endPos = value.indexOf("}", beginPos + 2); if (endPos != -1) { String variable = value.substring(beginPos + 2, endPos); Pattern pattern = Pattern.compile("[A-Za-z0-9_\\:\\.]+"); if (!pattern.matcher(variable).matches()) { throw new ValidationErrorException("Template error: variable names in Fn::Sub syntax must contain only " + "alphanumeric characters, underscores, periods, and colons"); } if (!variableMapping.containsKey(variable)) { throw new ValidationErrorException("Template error: unmapped variable " + variable + " in string for Fn::Sub"); } builder.append(value.substring(0, beginPos)); builder.append(variableMapping.get(variable)); recursivelyReplaceVariablesWithStringBuilder(value.substring(endPos + 1), variableMapping, builder); } else { // might have been a variable but no closing }. Use whole thing builder.append(value); } } } else { // no variables left in string, use the whole thing builder.append(value); } } } }