/*******************************************************************************
* Copyright (c) 2005, 2009 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.internal.xpand2.codeassist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.internal.xpand2.XpandTokens;
import org.eclipse.internal.xpand2.ast.Advice;
import org.eclipse.internal.xpand2.model.XpandAdvice;
import org.eclipse.internal.xpand2.model.XpandDefinition;
import org.eclipse.internal.xpand2.model.XpandResource;
import org.eclipse.internal.xpand2.type.DefinitionType;
import org.eclipse.internal.xpand2.type.IteratorType;
import org.eclipse.internal.xtend.xtend.codeassist.Partition;
import org.eclipse.xpand2.XpandExecutionContext;
import org.eclipse.xtend.expression.AnalysationIssue;
import org.eclipse.xtend.expression.ExecutionContext;
import org.eclipse.xtend.expression.ExpressionFacade;
import org.eclipse.xtend.expression.Variable;
import org.eclipse.xtend.typesystem.ParameterizedType;
import org.eclipse.xtend.typesystem.Type;
public class FastAnalyzer {
private static final Pattern PARAM_PATTERN = Pattern.compile("([\\[\\]:\\w]+)\\s+([\\w]+)");
private final static Pattern IMPORT_PATTERN = Pattern.compile(XpandTokens.LT + "\\s*IMPORT" + "\\s+([\\w\\:]+)\\s*" + XpandTokens.RT);
private final static Pattern EXTENSION_PATTERN = Pattern.compile(XpandTokens.LT + "\\s*EXTENSION\\s+([\\w\\:]+)\\s*" + XpandTokens.RT);
private final static Pattern INCOMPLETE_IMPORT_PATTERN = Pattern.compile(XpandTokens.LT + "\\s*IMPORT\\s+[\\w\\:]*\\z");
private final static Pattern INCOMPLETE_EXTENSION_PATTERN = Pattern.compile(XpandTokens.LT + "\\s*EXTENSION\\s+[\\w\\:]*\\z");
private final static Pattern DEFINE_PATTERN = Pattern
.compile("(DEFINE|AROUND)\\s*(([\\w\\*:]+)\\s*(\\(([\\[\\]:\\w\\s\\,]*)\\*?\\s*\\))?\\s*FOR\\s*([\\[\\]:\\w\\s]+))");
private final static Pattern BLOCK_PATTERN = Pattern.compile(getBlockPattern());
private final static Pattern FOREACH_PATTERN = Pattern.compile("FOREACH\\s+(.+)\\s+AS\\s+(\\w+)(\\s+ITERATOR\\s+(\\w+))?");
public final static Pattern EXPAND_PATTERN = Pattern.compile("EXPAND\\s+([\\w:]*)\\z");
private final static Pattern LET_PATTERN = Pattern.compile("LET\\s+(.+)\\s+AS\\s+(\\w+)");
private final static Pattern TYPEDECL_DEFINE_PATTERN1 = Pattern.compile("(DEFINE|AROUND)\\s*[\\w\\*:]+\\s*\\(([^\\)]*)\\z");
private final static Pattern TYPEDECL_DEFINE_PATTERN2 = Pattern
.compile("(DEFINE|AROUND)\\s*[\\w\\*:]+\\s*(\\([\\[\\]:\\w\\s\\,]*\\*?\\s*\\))?\\s*FOR\\s+[^" + XpandTokens.RT + "\\s]*\\z");
private final static Pattern TYPEDECL_PARAM_PATTERN = Pattern.compile("(,|\\(|\\A)\\s*[\\[\\]:\\w]*\\z");
private final static Pattern TYPEDECL_TYPESELECT_PATTERN = Pattern.compile("typeSelect\\(\\s*[\\[\\]:\\w]*\\z");
private final static Pattern IN_TAG_PATTERN = Pattern.compile(XpandTokens.LT + "([^" + XpandTokens.RT + "]*)\\z");
protected FastAnalyzer() {
// only protected for testcases
}
public static boolean isInExpand(final String str) {
return EXPAND_PATTERN.matcher(str).find();
}
public static boolean isInComment(final String str) {
final int index = str.lastIndexOf(XpandTokens.LT + "REM" + XpandTokens.RT);
if (index >= 0) {
final int index2 = str.lastIndexOf(XpandTokens.LT + "ENDREM" + XpandTokens.RT);
if (index2 == -1)
return true;
return index2 < index;
}
return false;
}
public static boolean isInDefine(final String str) {
return !computeStack(str).isEmpty();
}
public static boolean isInTypeDecl(final String str) {
Matcher m = IN_TAG_PATTERN.matcher(str);
if (!m.find())
return false;
final String tag = m.group(1);
m = TYPEDECL_DEFINE_PATTERN1.matcher(tag);
if (m.find()) {
m = TYPEDECL_PARAM_PATTERN.matcher(m.group(2));
return m.find();
} else {
m = TYPEDECL_DEFINE_PATTERN2.matcher(tag);
if (m.find())
return true;
else {
m = TYPEDECL_TYPESELECT_PATTERN.matcher(tag);
return m.find();
}
}
}
private static String getBlockPattern() {
final String[] parts = new String[] { XpandTokens.DEFINE, XpandTokens.AROUND, XpandTokens.FOREACH, XpandTokens.LET, XpandTokens.IF, XpandTokens.FILE,
XpandTokens.PROTECT };
final StringBuffer buff = new StringBuffer();
for (int i = 0; i < parts.length; i++) {
final String part = parts[i];
buff.append(XpandTokens.LT).append("\\s*").append(part);
buff.append("|");
buff.append(XpandTokens.LT).append("\\s*").append("END").append(part);
if (i < parts.length - 1)
buff.append("|");
}
return buff.toString();
}
public final static List<String> findImports(final String template) {
final Matcher m = IMPORT_PATTERN.matcher(template);
final List<String> result = new ArrayList<String>();
while (m.find()) {
result.add(m.group(1));
}
return result;
}
public final static List<String> findExtensions(final String template) {
final Matcher m = EXTENSION_PATTERN.matcher(template);
final List<String> result = new ArrayList<String>();
while (m.find()) {
result.add(m.group(1));
}
return result;
}
public final static Stack<StackElement> computeStack(String templatePart) {
int start = templatePart.lastIndexOf(XpandTokens.ENDDEFINE);
final int start1 = templatePart.lastIndexOf(XpandTokens.ENDAROUND);
if (start1 > start) {
start = start1;
}
if (start > 0) {
templatePart = templatePart.substring(start);
}
final Stack<StackElement> stack = new Stack<StackElement>();
final Matcher matcher = BLOCK_PATTERN.matcher(templatePart);
while (matcher.find()) {
final String txt = matcher.group();
// handle variable scope
if (txt.endsWith(XpandTokens.ENDFOREACH)) {
stack.pop();
} else if (txt.endsWith(XpandTokens.ENDLET)) {
stack.pop();
} else if (txt.endsWith(XpandTokens.ENDIF)) {
stack.pop();
} else if (txt.endsWith(XpandTokens.ENDPROTECT)) {
stack.pop();
} else if (txt.endsWith(XpandTokens.ENDFILE)) {
stack.pop();
} else if (txt.endsWith(XpandTokens.ENDDEFINE)) {
stack.pop();
} else if (txt.endsWith(XpandTokens.ENDAROUND)) {
stack.pop();
} else if (txt.endsWith(XpandTokens.DEFINE) || txt.endsWith(XpandTokens.AROUND)) {
final StackElement se = new StackElement();
if (txt.endsWith(XpandTokens.AROUND)) {
se.block = XpandTokens.AROUND;
} else {
se.block = XpandTokens.DEFINE;
}
Matcher m = DEFINE_PATTERN.matcher(templatePart.substring(matcher.start()));
if (m.find()) {
LazyVar ctx = new LazyVar();
ctx.typeName = m.group(6).trim();
ctx.name = ExecutionContext.IMPLICIT_VARIABLE;
se.variables.put(ctx.name, ctx);
final String params = m.group(5);
if (params != null && !"".equals(params.trim())) {
final StringTokenizer st = new StringTokenizer(params, ",");
while (st.hasMoreTokens()) {
final String param = st.nextToken();
m = PARAM_PATTERN.matcher(param);
m.find();
ctx = new LazyVar();
ctx.typeName = m.group(1).trim();
ctx.name = m.group(2).trim();
se.variables.put(ctx.name, ctx);
}
}
if (se.block.equals(XpandTokens.AROUND)) {
ctx = new LazyVar();
ctx.typeName = DefinitionType.TYPE_NAME;
ctx.name = Advice.DEF_VAR_NAME;
se.variables.put(ctx.name, ctx);
}
stack.push(se);
}
} else if (txt.endsWith(XpandTokens.FOREACH)) {
final StackElement se = new StackElement();
se.block = XpandTokens.FOREACH;
final Matcher m = FOREACH_PATTERN.matcher(templatePart.substring(matcher.start()));
if (m.find()) {
LazyVar ctx = new LazyVar();
ctx.expression = m.group(1);
ctx.name = m.group(2);
ctx.forEach = true;
se.variables.put(ctx.name, ctx);
stack.push(se);
if (m.group(3) != null) {
ctx = new LazyVar();
ctx.typeName = IteratorType.TYPE_NAME;
ctx.name = m.group(4);
se.variables.put(ctx.name, ctx);
}
}
} else if (txt.endsWith(XpandTokens.LET)) {
final StackElement se = new StackElement();
se.block = XpandTokens.LET;
final Matcher m = LET_PATTERN.matcher(templatePart.substring(matcher.start()));
if (m.find()) {
final LazyVar ctx = new LazyVar();
ctx.expression = m.group(1);
ctx.name = m.group(2);
se.variables.put(ctx.name, ctx);
stack.push(se);
}
} else if (txt.endsWith(XpandTokens.IF)) {
final StackElement se = new StackElement();
se.block = XpandTokens.IF;
stack.push(se);
} else if (txt.endsWith(XpandTokens.PROTECT)) {
final StackElement se = new StackElement();
se.block = XpandTokens.PROTECT;
stack.push(se);
} else if (txt.endsWith(XpandTokens.FILE)) {
final StackElement se = new StackElement();
se.block = XpandTokens.FILE;
stack.push(se);
}
}
return stack;
}
/**
* DO NOT CALL THIS METHOD. PUBLIC FOR TEST ONLY
*/
public static boolean isInExtensionImport(final String s) {
final Matcher m = INCOMPLETE_EXTENSION_PATTERN.matcher(s);
return m.find();
}
/**
* DO NOT CALL THIS METHOD. PUBLIC FOR TEST ONLY
*/
public static boolean isInImport(final String s) {
final Matcher m = INCOMPLETE_IMPORT_PATTERN.matcher(s);
return m.find();
}
public final static Partition computePartition(final String str) {
if (isInComment(str))
return Partition.COMMENT;
if (!isInTag(str))
return Partition.DEFAULT;
if (isInExpand(str))
return XpandPartition.EXPAND_STATEMENT;
if (isInImport(str))
return Partition.NAMESPACE_IMPORT;
if (isInExtensionImport(str))
return Partition.EXTENSION_IMPORT;
if (isInTypeDecl(str))
return Partition.TYPE_DECLARATION;
return Partition.EXPRESSION;
}
public final static XpandExecutionContext computeExecutionContext(final String str, XpandExecutionContext ctx, final XpandDefinition[] definitions) {
final Partition p = computePartition(str);
if (p == Partition.TYPE_DECLARATION || p == Partition.EXPRESSION || p == XpandPartition.EXPAND_STATEMENT) {
final List<String> imports = findImports(str);
final List<String> extensions = findExtensions(str);
final XpandResource tpl = new XpandResource() {
public XpandDefinition[] getDefinitions() {
return definitions;
}
public XpandDefinition[] getDefinitionsByName(String name) {
List<XpandDefinition> result = new ArrayList<XpandDefinition>();
for (int i = 0; i < definitions.length; i++) {
XpandDefinition definition = definitions[i];
if (definition.getName().equals(name)) {
result.add(definition);
}
}
return extensions.toArray(new XpandDefinition[extensions.size()]);
}
private String fqn;
public String getFullyQualifiedName() {
return fqn;
}
public void setFullyQualifiedName(String fqn) {
this.fqn = fqn;
}
public String[] getImportedNamespaces() {
return imports.toArray(new String[imports.size()]);
}
public String[] getImportedExtensions() {
return extensions.toArray(new String[extensions.size()]);
}
public void analyze(XpandExecutionContext ctx, Set<AnalysationIssue> issues) {
}
public XpandAdvice[] getAdvices() {
return new XpandAdvice[0];
}
};
ctx = (XpandExecutionContext) ctx.cloneWithResource(tpl);
final Stack<StackElement> s = computeStack(str);
for (final Iterator<StackElement> iter = s.iterator(); iter.hasNext();) {
final StackElement element = iter.next();
final Collection<LazyVar> vars = element.variables.values();
for (final Iterator<LazyVar> iterator = vars.iterator(); iterator.hasNext();) {
final LazyVar v = iterator.next();
Type vType = null;
if (v.typeName != null) {
vType = ctx.getTypeForName(v.typeName);
} else {
vType = new ExpressionFacade(ctx).analyze(v.expression, new HashSet<AnalysationIssue>());
if (v.forEach) {
if (vType instanceof ParameterizedType) {
vType = ((ParameterizedType) vType).getInnerType();
} else {
vType = null;
}
}
}
ctx = (XpandExecutionContext) ctx.cloneWithVariable(new Variable(v.name, vType));
}
}
}
return ctx;
}
public static boolean isInTag(final String str) {
return IN_TAG_PATTERN.matcher(str).find();
}
}