package org.netbeans.gradle.project.tasks.vars;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nonnull;
import org.jtrim.utils.ExceptionHelper;
import org.netbeans.gradle.project.api.task.TaskVariable;
import org.netbeans.gradle.project.api.task.TaskVariableMap;
import org.netbeans.gradle.project.util.NbCollectionsEx;
import org.netbeans.gradle.project.util.StringUtils;
final class LenientVariableResolver implements VariableResolver {
private static final char START_TYPE_CHAR = '[';
private static final char END_TYPE_CHAR = ']';
private static final char DISPLAY_NAME_SEPARATOR = ':';
@Override
public String replaceVars(String str, TaskVariableMap varReplaceMap) {
return replaceVars(str, varReplaceMap, NbCollectionsEx.getDevNullCollection());
}
@Override
public String replaceVars(String str, TaskVariableMap varReplaceMap, Collection<? super DisplayedTaskVariable> collectedVariables) {
ExceptionHelper.checkNotNullArgument(str, "str");
ExceptionHelper.checkNotNullArgument(varReplaceMap, "varReplaceMap");
ExceptionHelper.checkNotNullArgument(collectedVariables, "collectedVariables");
StringBuilder result = null;
int index = 0;
while (index < str.length()) {
char ch = str.charAt(index);
if (ch == '$') {
int varStart = str.indexOf('{', index + 1);
int varEnd = varStart >= 0
? StringUtils.unescapedIndexOf(str, varStart + 1, '}')
: -1;
if (varStart >= 0 && varEnd >= varStart) {
String varDef = str.substring(varStart + 1, varEnd);
DisplayedTaskVariable taskVar = tryParseTaskVariable(varDef);
if (taskVar != null) {
collectedVariables.add(taskVar);
int nextIndex = varEnd + 1;
String value = varReplaceMap.tryGetValueForVariable(taskVar.getVariable());
if (value != null) {
if (result == null) {
result = new StringBuilder(str.length() * 2);
result.append(str, 0, index);
}
result.append(value);
}
else {
if (result != null) {
result.append(str, index, nextIndex);
}
}
index = nextIndex;
continue;
}
}
}
if (result != null) {
result.append(ch);
}
index++;
}
return result != null ? result.toString() : str;
}
@Override
public void collectVars(String str, TaskVariableMap varReplaceMap, Collection<? super DisplayedTaskVariable> collectedVariables) {
replaceVars(str, varReplaceMap, collectedVariables);
}
@Override
public String replaceVarsIfValid(String str, TaskVariableMap varReplaceMap) {
List<DisplayedTaskVariable> vars = new ArrayList<>();
String result = replaceVars(str, varReplaceMap, vars);
for (DisplayedTaskVariable var: vars) {
if (varReplaceMap.tryGetValueForVariable(var.getVariable()) == null) {
return null;
}
}
return result;
}
public static DisplayedTaskVariable tryParseTaskVariable(String varDef) {
// It is expected by later code that the string is not empty.
if (varDef.isEmpty()) {
return null;
}
// variableName[type: typeDescr]: displayName
String varName;
String displayName;
VariableTypeDescription typeDescr;
int typeStartIndex = unescapedIndexOf(varDef, 0, START_TYPE_CHAR);
int nameSeparatorIndex = unescapedIndexOf(varDef, 0, DISPLAY_NAME_SEPARATOR);
if (nameSeparatorIndex >= 0 && typeStartIndex >= 0) {
if (nameSeparatorIndex < typeStartIndex) {
// variableName: Display Name[2]
varName = varDef.substring(0, nameSeparatorIndex);
displayName = varDef.substring(nameSeparatorIndex + 1, varDef.length());
typeDescr = VariableTypeDescription.DEFAULT_TYPE;
}
else {
int typeEndIndex = unescapedIndexOf(varDef, typeStartIndex, END_TYPE_CHAR);
varName = varDef.substring(0, typeStartIndex);
if (typeEndIndex > typeStartIndex) {
nameSeparatorIndex = unescapedIndexOf(varDef, typeEndIndex, DISPLAY_NAME_SEPARATOR);
if (nameSeparatorIndex < 0) {
// Missing ':' to separate display name
// Could be because there is no display name.
// E.g.: variableName[typeDescr: abcd] Display Name
// variableName[typeDescr: abcd]
nameSeparatorIndex = typeEndIndex;
}
// Else standard: variableName[typeDescr]: Display Name
displayName = varDef.substring(nameSeparatorIndex + 1, varDef.length());
typeDescr = parseType(varDef.substring(typeStartIndex + 1, typeEndIndex));
}
else {
// E.g.: variableName[unclosed typedef: abcd
// Assume a ']' character after the end.
typeDescr = parseType(varDef.substring(typeStartIndex + 1, varDef.length()));
displayName = "";
}
}
}
else if (typeStartIndex >= 0) {
varName = varDef.substring(0, typeStartIndex);
displayName = "";
int typeEndIndex = unescapedIndexOf(varDef, typeStartIndex, ']');
if (typeEndIndex < 0) {
// E.g.: variableName[unclosed typedef
// Assume a ']' character after the end.
typeEndIndex = varDef.length();
}
// Else: variableName[typeDef]
typeDescr = parseType(varDef.substring(typeStartIndex + 1, typeEndIndex));
}
else if (nameSeparatorIndex >= 0) {
// variableName: Display Name
varName = varDef.substring(0, nameSeparatorIndex);
displayName = varDef.substring(nameSeparatorIndex + 1, varDef.length());
typeDescr = VariableTypeDescription.DEFAULT_TYPE;
}
else {
varName = varDef;
displayName = "";
typeDescr = VariableTypeDescription.DEFAULT_TYPE;
}
varName = varName.trim();
displayName = normalizeEscapedString(displayName);
if (displayName.isEmpty()) {
displayName = varName;
}
if (!TaskVariable.isValidVariableName(varName)) {
return null;
}
return new DisplayedTaskVariable(new TaskVariable(varName), displayName, typeDescr);
}
@Nonnull
public static String getScriptReplaceConstant(DisplayedTaskVariable displayedVar) {
String varName = displayedVar.getVariable().getVariableName();
StringBuilder result = new StringBuilder();
result.append("${");
result.append(varName);
VariableTypeDescription typeDescription = displayedVar.getTypeDescription();
if (!typeDescription.isDefault()) {
result.append(START_TYPE_CHAR);
result.append(typeDescription.getScriptString());
result.append(END_TYPE_CHAR);
}
String displayName = displayedVar.getDisplayName();
if (!varName.equals(displayName)) {
result.append(DISPLAY_NAME_SEPARATOR);
String escapedDisplayName = displayName
.replace("\\", "\\\\")
.replace("}", "\\}");
result.append(escapedDisplayName);
}
result.append("}");
return result.toString();
}
private static int unescapedIndexOf(String str, int startIndex, char toFind) {
return StringUtils.unescapedIndexOf(str, startIndex, toFind);
}
private static String normalizeEscapedString(String str) {
String result = StringUtils.unescapeString(str);
return result.trim();
}
private static VariableTypeDescription parseType(String typeDef) {
String typeName;
String typeArguments;
int descrSeparatorIndex = unescapedIndexOf(typeDef, 0, ':');
if (descrSeparatorIndex >= 0) {
typeName = typeDef.substring(0, descrSeparatorIndex);
typeArguments = typeDef.substring(descrSeparatorIndex + 1, typeDef.length());
}
else {
typeName = typeDef;
typeArguments = "";
}
typeName = typeName.trim();
typeArguments = typeArguments.trim();
return new VariableTypeDescription(typeName, typeArguments);
}
}