/*******************************************************************************
* Copyright (c) 2009 xored software, 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:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.tcl.internal.core.packages;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.dltk.tcl.ast.AstFactory;
import org.eclipse.dltk.tcl.ast.StringArgument;
import org.eclipse.dltk.tcl.ast.Substitution;
import org.eclipse.dltk.tcl.ast.TclArgument;
import org.eclipse.dltk.tcl.ast.TclCommand;
import org.eclipse.dltk.tcl.ast.VariableReference;
import org.eclipse.dltk.tcl.core.TclParseUtil;
import org.eclipse.dltk.tcl.parser.TclErrorCollector;
import org.eclipse.dltk.tcl.parser.TclParser;
import org.eclipse.dltk.tcl.parser.TclParserUtils;
import org.eclipse.dltk.tcl.parser.TclVisitor;
import org.eclipse.dltk.tcl.parser.printer.SimpleCodePrinter;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* @since 2.0
*/
public class TclVariableResolver {
public static interface IVariableRegistry {
/**
* Returns value for the specified variable or <code>null</code> if not
* defined
*
* @param name
* @return
*/
String getValue(String name, String index);
}
public static class SimpleVariableRegistry implements IVariableRegistry {
private final Map<String, Object> values;
/**
* Accept String or Map<String, String> values
*
* @param values
*/
public SimpleVariableRegistry(Map<String, Object> values) {
this.values = values;
}
@SuppressWarnings("unchecked")
public String getValue(String name, String index) {
Object value = values.get(name);
if (value instanceof Map) {
if (index == null) {
index = "";
}
Map<String, String> map = (Map<String, String>) value;
return map.get(index);
}
return (String) value;
}
}
private final IVariableRegistry registry;
public TclVariableResolver(IVariableRegistry registry) {
this.registry = registry;
}
/**
* Returns resolved string or <code>null</code>
*
* @param value
* @return resolved string or <code>null</code> if unknown variable is
* encountered
*/
public String resolve(String value) {
return resolve(value, new HashSet<String>());
}
private String resolve(String value, Set<String> requests) {
if (value == null || value.indexOf('$') == -1) {
return value;
}
TclParser parser = new TclParser();
TclErrorCollector collector = new TclErrorCollector();
List<TclCommand> result = parser.parse(value, collector, null);
if (collector.getCount() > 0) {
return value;
}
final List<VariableReference> variables = new ArrayList<VariableReference>();
TclParserUtils.traverse(result, new TclVisitor() {
@Override
public boolean visit(VariableReference list) {
variables.add(list);
return super.visit(list);
}
// Skip substitutions
@Override
public boolean visit(Substitution substitution) {
return false;
}
});
if (variables.isEmpty()) {
return value;
}
for (VariableReference variableReference : variables) {
EObject container = variableReference.eContainer();
if (container == null) {
continue;
}
final String request = value.substring(
variableReference.getStart(), variableReference.getEnd());
final String resultValue;
if (!requests.add(request)) {
return null;
}
try {
resultValue = resolveVariable(variableReference, requests);
} finally {
requests.remove(request);
}
// If has unresolved value then return null
if (resultValue == null) {
return null;
}
StringArgument string = AstFactory.eINSTANCE.createStringArgument();
string.setValue(resultValue);
string.setStart(variableReference.getStart());
string.setEnd(variableReference.getEnd());
// Replace parent with StringArgument
EcoreUtil.replace(variableReference, string);
}
return SimpleCodePrinter.getCommandsString(result, false).trim();
}
private String resolveVariable(VariableReference variable,
Set<String> requests) {
final String indexValue;
final TclArgument index = variable.getIndex();
if (index != null) {
if (index instanceof StringArgument) {
indexValue = ((StringArgument) index).getValue();
} else {
indexValue = resolve(SimpleCodePrinter.getArgumentString(index,
0, false), requests);
}
if (indexValue != null) {
final String value = registry
.getValue(TclParseUtil.escapeName(variable.getName())
+ '(' + TclParseUtil.escapeName(indexValue)
+ ')', null);
if (value != null) {
return resolve(value, requests);
}
}
} else {
indexValue = null;
}
return resolve(registry.getValue(variable.getName(), indexValue),
requests);
}
public static String[] extractVariableNames(String value) {
TclParser parser = new TclParser();
TclErrorCollector collector = new TclErrorCollector();
List<TclCommand> result = parser.parse(value, collector, null);
if (collector.getCount() > 0) {
return null;
}
final List<String> variables = new ArrayList<String>();
TclParserUtils.traverse(result, new TclVisitor() {
@Override
public boolean visit(VariableReference list) {
variables.add(list.getName());
return super.visit(list);
}
// Skip substitutions
@Override
public boolean visit(Substitution substitution) {
return false;
}
});
if (variables.isEmpty()) {
return null;
}
return variables.toArray(new String[variables.size()]);
}
}