blob: 985ba6bf2c56333bdacc955bd07b98c6c8c21089 [file] [log] [blame]
package org.eclipse.dltk.internal.launching;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.dltk.launching.EnvironmentVariable;
public final class EnvironmentResolver {
private static class REnvironmentVariable {
EnvironmentVariable var;
final Set<String> dependencies = new HashSet<>();
public REnvironmentVariable(EnvironmentVariable var) {
this.var = var;
}
}
/*
* Resolves specified set of environment variables with system environment
*/
public static EnvironmentVariable[] resolve(Map<String, String> penv,
EnvironmentVariable[] variables) {
return resolve(penv, variables, false);
}
/*
* Resolves specified set of environment variables with system environment
*/
public static EnvironmentVariable[] resolve(Map<String, String> penv,
EnvironmentVariable[] variables, boolean keepUnresolved) {
if (variables == null) {
return null;
}
Map<String, String> env = new HashMap<>();
Map<String, String> selfDep = new HashMap<>();
for (Map.Entry<String, String> entry : penv.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
env.put(name, value);
}
for (int i = 0; i < variables.length; i++) {
String name = variables[i].getName();
if (env.containsKey(name)) {
selfDep.put(name, env.get(name));
env.remove(name);
}
}
Map<String, String> resolved = new HashMap<>();
List<EnvironmentVariable> result = new ArrayList<>();
// 1) replace all top level environment variables
List<REnvironmentVariable> unresolved = new ArrayList<>();
for (int i = 0; i < variables.length; i++) {
REnvironmentVariable var = new REnvironmentVariable(
new EnvironmentVariable(variables[i]));
fillDependencies(var, variables);
unresolved.add(var);
}
// To be sure we exit while loop
int maxCycles = 1000;
while (unresolved.size() > 0) {
maxCycles--;
if (maxCycles < 0) {
break;
}
for (Iterator<REnvironmentVariable> iterator = unresolved
.iterator(); iterator.hasNext();) {
REnvironmentVariable var = iterator.next();
if (isResolved(var.var)) {
result.add(var.var);
resolved.put(var.var.getName(), var.var.getValue());
iterator.remove();
} else {
if (isCyclic(var, unresolved)) {
// Resolve self cycles to environment
if (isSelfCyclic(var)) {
resolveVariable(var, env);
resolveVariable(var, selfDep);
if (isResolved(var.var)) {
continue;
}
}
if (keepUnresolved) {
result.add(var.var);
}
iterator.remove();
continue;
}
resolveVariable(var, resolved);
resolveVariable(var, env);
if (isResolved(var.var)) {
continue;
}
if (isUnresolvable(var, unresolved)) {
if (keepUnresolved) {
result.add(var.var);
}
iterator.remove();
}
}
}
}
return result.toArray(new EnvironmentVariable[result.size()]);
}
private static boolean isSelfCyclic(REnvironmentVariable var) {
if (var.dependencies.isEmpty()) {
return false;
}
if (var.dependencies.contains(var.var.getName())) {
return true;
}
return false;
}
private static void fillDependencies(REnvironmentVariable var,
EnvironmentVariable[] variables) {
for (int j = 0; j < variables.length; j++) {
if (containVar(var.var, variables[j].getName())) {
var.dependencies.add(variables[j].getName());
}
}
}
private static boolean isUnresolvable(REnvironmentVariable var,
List<REnvironmentVariable> unresolved) {
EnvironmentVariable t = var.var;
while (true) {
boolean step = false;
for (REnvironmentVariable rvar : unresolved) {
if (!rvar.var.getName().equals(t.getName())
&& containVar(t, rvar.var.getName())) {
t = resolveVariable(t, rvar.var.getName(),
rvar.var.getValue());
step = true;
}
}
if (!step) {
break;
}
}
if (!isResolved(t)) {
return true;
}
return false;
}
private static EnvironmentVariable resolveVariable(EnvironmentVariable var,
String name, String value) {
String result = var.getValue();
String pattern = "$" + name; //$NON-NLS-1$
if (value.indexOf(pattern) != -1) {
return null;
}
int pos = result.indexOf(pattern);
while (pos != -1) {
result = result.substring(0, pos) + value
+ result.substring(pos + pattern.length());
pos = result.indexOf(pattern, pos);
}
return new EnvironmentVariable(var.getName(), result);
}
private static boolean isCyclic(REnvironmentVariable var,
List<REnvironmentVariable> unresolved) {
// Detect direct cycles
if (var.dependencies.size() == 0) {
return false;
}
for (REnvironmentVariable env2 : unresolved) {
if (var.dependencies.contains(env2.var.getName())
&& env2.dependencies.contains(var.var.getName())) {
return true;
}
}
return false;
}
private static void resolveVariable(REnvironmentVariable var,
Map<String, String> env) {
EnvironmentVariable v = var.var;
for (Map.Entry<String, String> entry : env.entrySet()) {
final String varName = entry.getKey();
if (containVar(v, varName)) {
v = resolveVariable(v, varName, entry.getValue());
}
}
var.var = v;
}
public static boolean isResolved(EnvironmentVariable var) {
if (var == null) {
throw new IllegalArgumentException();
}
String name = var.getValue();
if (name.indexOf("$") == -1) { //$NON-NLS-1$
return true;
}
return false;
}
public static boolean containVar(EnvironmentVariable var, String vName) {
if (var == null) {
throw new IllegalArgumentException();
}
final String value = var.getValue();
final String ref = "$" + vName; //$NON-NLS-1$
final int pos = value.indexOf(ref);
if (pos != -1 && (pos + ref.length() >= value.length() || !Character
.isLetterOrDigit(value.charAt(pos + ref.length())))) {
return true;
}
return false;
}
/**
* Finds the variable with the specified name
*
* @param vars
* @param name
* @return
* @since 2.0
*/
public static EnvironmentVariable find(EnvironmentVariable[] vars,
String name) {
if (vars != null && name != null) {
for (EnvironmentVariable var : vars) {
if (name.equals(var.getName())) {
return var;
}
}
}
return null;
}
}