/*******************************************************************************
* Copyright (c) 2005, 2006 committers of openArchitectureWare and others.
* 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:
* committers of openArchitectureWare - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.mwe.internal.core.ast.util;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.emf.mwe.core.issues.Issues;
import org.eclipse.emf.mwe.internal.core.ast.AbstractASTBase;
import org.eclipse.emf.mwe.internal.core.ast.ComponentAST;
import org.eclipse.emf.mwe.internal.core.ast.DeclaredPropertyAST;
import org.eclipse.emf.mwe.internal.core.ast.DeclaredPropertyFileAST;
import org.eclipse.emf.mwe.internal.core.ast.InclusionAST;
import org.eclipse.emf.mwe.internal.core.ast.ReferenceAST;
import org.eclipse.emf.mwe.internal.core.ast.SimpleParamAST;
import org.eclipse.emf.mwe.internal.core.ast.parser.WorkflowParser;
public class VisitorInitializer extends VisitorBase {
private VisitorInitializer cloneThis() {
final VisitorInitializer newOne = new VisitorInitializer(issues, props, beans);
newOne.beans = beans;
newOne.initialProps = initialProps;
newOne.props = props;
newOne.declaredPropertyNames = declaredPropertyNames;
return newOne;
}
public VisitorInitializer(final Issues issues, final Map<String, String> initialProperties,
final Map<String, ComponentAST> initialBeans) {
this.issues = issues;
initialProps = new HashMap<String, String>(initialProperties);
props = new HashMap<String, String>();
beans = new HashMap<String, ComponentAST>(initialBeans);
}
private Set<String> declaredPropertyNames = new HashSet<String>();
private Issues issues = null;
private Map<String, ComponentAST> beans = null;
private Map<String, String> props = null;
private Map<String, String> initialProps = null;
private void traverseChildren(final ComponentAST c) {
for (final Object name : c.getChildren()) {
final AbstractASTBase element = (AbstractASTBase) name;
element.accept(cloneThis());
}
}
@Override
public Object visitComponentAST(final ComponentAST comp) {
comp.setClazz(replaceProperties(comp.getClazz(), comp));
handleIdentifiedAST(comp);
traverseChildren(comp);
return comp;
}
private void handleIdentifiedAST(final ComponentAST comp) {
if (comp.getId() != null) {
comp.setId(replaceProperties(comp.getId(), comp));
beans.put(comp.getId(), comp);
}
}
@Override
public Object visitInclusionAST(final InclusionAST comp) {
comp.setFile(replaceProperties(comp.getFile(), comp));
handleIdentifiedAST(comp);
traverseChildren(comp);
final Map<String, String> params = new HashMap<String, String>();
final Map<String, ComponentAST> paramBeans = new HashMap<String, ComponentAST>();
if (comp.isInheritAll()) {
params.putAll(this.initialProps);
params.putAll(this.props);
paramBeans.putAll(this.beans);
}
for (final Object o : comp.getChildren()) {
if (o instanceof SimpleParamAST) {
final SimpleParamAST p = (SimpleParamAST) o;
params.put(p.getName(), p.getValue());
}
else if (o instanceof ComponentAST) {
final ComponentAST p = (ComponentAST) o;
paramBeans.put(p.getName(), p);
}
else if (o instanceof ReferenceAST) {
final ReferenceAST ref = (ReferenceAST) o;
paramBeans.put(ref.getName(), ref.getReference());
}
}
comp.setPassedBeans(paramBeans);
comp.setPassedProperties(params);
if (comp.getFile() != null) {
final WorkflowParser p = new WorkflowParser();
final InputStream in = loader.getResourceAsStream(translateFileURI(comp.getFile()));
if (in == null) {
issues.addError("Couldn't load workflow fragment from " + comp.getFile(), comp);
return comp;
}
final AbstractASTBase ref = p.parse(in, comp.getFile(), issues);
comp.setImportedElement(ref);
if (ref == null) {
issues.addError("Couldn't parse nested workflow file " + comp.getFile(), comp);
return comp;
}
if (comp.getPassedProperties() == null) {
issues.addError("Workflow not initialized! (passedProperties is null)", comp);
return comp;
}
final VisitorInitializer vis = new VisitorInitializer(issues, comp.getPassedProperties(), comp
.getPassedBeans());
ref.accept(vis);
}
return comp;
}
private String translateFileURI(final String file) {
String fileName = new String(file);
if (fileName.indexOf("::") != -1) {
fileName = fileName.replaceAll("::", "/");
}
if (!fileName.toLowerCase().endsWith(".mwe") && !fileName.toLowerCase().endsWith(".oaw")) {
fileName += ".mwe";
}
return fileName;
}
@Override
public Object visitReferenceAST(final ReferenceAST comp) {
comp.setIdRef(replaceProperties(comp.getIdRef(), comp));
final ComponentAST ref = beans.get(comp.getIdRef());
if (ref == null) {
issues.addError("Couldn't find bean with id '" + comp.getIdRef() + "'", comp);
}
else {
comp.setReference(ref);
}
return comp;
}
@Override
public Object visitSimpleParamAST(final SimpleParamAST param) {
param.setValue(replaceProperties(param.getValue(), param));
return param;
}
@Override
public Object visitDeclaredPropertyAST(final DeclaredPropertyAST prop) {
if (prop.getValue() != null) {
final String n = replaceProperties(prop.getName(), prop);
if (!initialProps.containsKey(n)) {
props.put(n, replaceProperties(prop.getValue(), prop));
}
else {
if (!declaredPropertyNames.add(n)) {
issues.addError("Duplicate property " + n, prop);
}
}
}
return props;
}
@Override
public Object visitDeclaredPropertyFileAST(final DeclaredPropertyFileAST propFile) {
propFile.setFile(replaceProperties(propFile.getFile(), propFile));
final Properties properties = propFile.getProperties(loader);
if (properties == null) {
issues.addError("Couldn't resolve properties file!", propFile);
return new HashMap<Object, Object>();
}
else {
for (final String string : propFile.getPropertyNames(loader)) {
final String name = replaceProperties(string, propFile);
final String val = replaceProperties((String) properties.get(name), propFile);
if (!initialProps.containsKey(name)) {
props.put(name, val);
}
else {
if (!declaredPropertyNames.add(name)) {
issues.addError("Duplicate property " + name, propFile);
}
}
}
}
return props;
}
private static final Pattern PROPERTY_PATTERN = Pattern.compile("\\$\\{([\\w_\\.-]+)\\}");
private static final Pattern[] PROPERTY_WARN_PATTERN = { Pattern.compile("\\$\\(([\\w_\\.-]+)\\)") };
protected String replaceProperties(final String toResolve, final AbstractASTBase ast) {
return replaceProperties(toResolve, true, ast);
}
private final Stack<String> currentProp = new Stack<String>();
protected String replaceProperties(final String toResolve, final boolean logIssues, final AbstractASTBase ast) {
if (toResolve == null) {
return null;
}
// if (currentProp.contains(toResolve)) {
// issues.addError("property "+toResolve+" not found!");
// return null;
// }
//
// try {
currentProp.push(toResolve);
// check for expressions the user probably didn't want to use
if (logIssues) {
for (final Pattern p : PROPERTY_WARN_PATTERN) {
final Matcher m = p.matcher(toResolve);
while (m.find()) {
issues.addWarning("The expression \"" + m.group(0)
+ "\" is not a valid property and therefore not resolved."
+ " Properties need to be enclosed in curly brackets, like: ${myProperty}");
}
}
}
final Matcher m = PROPERTY_PATTERN.matcher(toResolve);
final StringBuffer buff = new StringBuffer();
int index = 0;
while (m.find()) {
final String varName = m.group(1);
String propValue = propGet(varName);
if (propValue == null) {
if (logIssues) {
issues.addError("property " + varName + " not specified. Dereferenced at "
+ ast.getLocation().toString());
}
return null;
}
else {
propValue = replaceProperties(propValue, logIssues, ast);
}
final int start = m.start();
final int end = m.end();
buff.append(toResolve.substring(index, start));
buff.append(propValue);
index = end;
}
buff.append(toResolve.substring(index));
return buff.toString();
// } finally {
// currentProp.pop();
// }
}
private String propGet(final String varName) {
final String val = initialProps.get(varName);
if (val != null) {
return val;
}
return props.get(varName);
}
}