/*******************************************************************************
* Copyright (c) 2007, 2008 Edgar Espina.
* 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
*
*******************************************************************************/
package org.deved.antlride.core.model.ast;
import java.util.StringTokenizer;
import org.deved.antlride.core.model.ElementKind;
import org.deved.antlride.core.model.IAssign;
import org.deved.antlride.core.model.ICallExpression;
import org.deved.antlride.core.model.IGrammar;
import org.deved.antlride.core.model.IModelElement;
import org.deved.antlride.core.model.IParameter;
import org.deved.antlride.core.model.IReference;
import org.deved.antlride.core.model.IRule;
import org.deved.antlride.core.model.IRuleAction;
import org.deved.antlride.core.model.IScope;
import org.deved.antlride.core.model.IScopeAttribute;
import org.deved.antlride.core.model.IScopeReference;
import org.deved.antlride.core.model.ISourceElement;
import org.deved.antlride.core.model.IStatementAction;
import org.deved.antlride.core.model.IVariable;
import org.deved.antlride.core.model.ast.criteria.IModelElementCriteria;
import org.deved.antlride.core.model.ast.criteria.ModelElementCriteriaFactory;
public class AntlrModelElementLocator {
private IModelElement element;
public AntlrModelElementLocator(IModelElement element) {
this.element = element;
}
public IModelElement getElementAt(int position) {
IModelElement[] elements = internalGetElementAt(position);
int[] positions = { Integer.MAX_VALUE, Integer.MAX_VALUE,
Integer.MAX_VALUE };
for (int i = 0; i < elements.length; i++) {
if (elements[i] != null && elements[i].isIn(position)) {
int min1 = Math.abs(elements[i].sourceStart() - position);
int min2 = Math.abs(elements[i].sourceEnd() - position);
positions[i] = Math.min(min1, min2);
}
}
int min = Integer.MAX_VALUE;
int element = 0;
for (int i = 0; i < positions.length; i++) {
if (positions[i] < min || (positions[i] == min && element != 1)) {
min = positions[i];
element = i;
}
}
IModelElement e = elements[element];
if (e != null) {
IRule r = e.getAdapter(IRule.class);
if (r != null) {
ISourceElement ruleName = r.getName();
if (ruleName.isIn(position)) {
return r;
}
}
}
return e;
}
private IModelElement[] internalGetElementAt(int position) {
IModelElement[] elements = { null, null, null };
IModelElement e = null;
try {
e = findElement(position);
} catch (Exception ex) {
ex.printStackTrace();
}
if (e != null) {
elements[1] = e;
int previous = position - 1;
while (previous >= 0 && elements[1] == e) {
try {
e = findElement(previous);
previous--;
} catch (Exception ex) {
e = null;
}
}
elements[0] = e;
e = elements[1];
position++;
IGrammar grammar = e.getAdapter(IGrammar.class);
int sourceEnd = grammar.sourceEnd();
while (position < sourceEnd && elements[1] == e) {
try {
e = findElement(position);
position++;
} catch (Exception ex) {
e = null;
}
}
elements[2] = e;
}
return elements;
}
private IModelElement findElement(int position) {
AntlrModelElementLocatorVisitor locator = new AntlrModelElementLocatorVisitor(
position);
locator.accept(element);
IModelElement e = locator.getResult();
IModelElement p = null;
while (e != null) {
p = e;
locator.accept(e);
e = locator.getResult();
}
return p;
}
public IModelElement getElementDeclaration(int position) {
IModelElement element = getElementAt(position);
if (element == null || !element.isIn(position)) {
return null;
}
IModelElement e = null;
if (element.getElementKind() == ElementKind.CALL) {
e = process((ICallExpression) element);
} else if (element.getElementKind() == ElementKind.REFERENCE) {
e = ((IReference) element).getReference();
} else if (element.getElementKind() == ElementKind.STATEMENT_ACTION) {
IStatementAction statementAction = (IStatementAction) element;
e = processAction(statementAction, statementAction
.getEnclosingRule(), statementAction.getAction().getText(),
statementAction.sourceStart(), position);
} else if (element.getElementKind() == ElementKind.CALL_PARAMETER) {
e = processCallParameter((IParameter) element, position);
} else if (element.getElementKind() == ElementKind.SCOPE_REFERENCE) {
IScopeReference scopeReference = (IScopeReference) element;
e = scopeReference.getReference();
} else if (element.getElementKind() == ElementKind.VARIABLE) {
IVariable variable = (IVariable) element;
IAssign assign = variable.getAdapter(IAssign.class);
if (assign != null) {
e = assign.getStatement();
}
} else if (element.getElementKind() == ElementKind.RULE) {
IRule rule = (IRule) element;
ISourceElement name = rule.getName();
if (name.isIn(position)) {
e = rule;
}
} else if (element.getElementKind() == ElementKind.RULE_ACTION) {
IRuleAction ruleAction = (IRuleAction) element;
String text = ruleAction.getAction().getText();
int start = ruleAction.getAction().sourceStart();
e = processAction(ruleAction, (IRule) ruleAction.getParent(), text,
start, position);
}
return e;
}
private IModelElement processCallParameter(IParameter parameter,
int position) {
// try to find a var decl
String parameterName = parameter.getElementName();
if (parameterName.charAt(0) == '$')
parameterName = parameterName.substring(1);
IModelElementCriteria criteria = ModelElementCriteriaFactory.callOrVariable(parameterName);
AntlrModelElementCollectorVisitor collector = new AntlrModelElementCollectorVisitor(
criteria);
String pattern = ".";
int index = parameterName.indexOf(pattern);
if (index > -1) {
parameterName = parameterName.substring(0, index);
} else {
pattern = "::";
index = parameterName.indexOf(pattern);
if (index > -1) {
parameterName = parameterName.substring(0, index);
}
}
if (position <= parameter.sourceStart() + parameterName.length()) {
Object[] elements = findClosestStatement(collector, parameter
.getParent(), parameterName);
if (elements != null && elements.length > 0) {
return (IModelElement) elements[0];
}
}
return null;
}
private IModelElement processAction(IModelElement statement, IRule rule,
String text, int sourceStart, int position) {
int offset = sourceStart + 1;
IModelElement e = null;
String split = " \t\n\r\f=,;+-*/^()!&|<>%[]{}";
StringTokenizer tokenizer = new StringTokenizer(text, split, true);
IModelElement parent = (IModelElement) statement.getParent();
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (split.indexOf(token) == -1 && token.charAt(0) == '$') {
int start = offset + 1;
int end = start + token.length() - 1;
if (position >= start && position < end) {
int index = token.indexOf("::");
if (index != -1) {
// scope
String scopeName = token.substring(1, index);
IScope scope = rule.findScope(scopeName);
IScopeReference scopeReference = null;
if (scope == null) {
scopeReference = rule.findScopeReference(scopeName);
scope = (IScope) scopeReference.getReference();
}
if (scope == null) {
continue;
}
int scopeLen = start + scopeName.length();
if (position < scopeLen) {
e = scopeReference == null ? scope : scopeReference;
break;
}
if (position == scopeLen) {
break;
}
int attrStart = scopeLen + 2;
if (position < attrStart) {
break;
}
int i1 = attrStart - offset;
String attrName = token.substring(i1);
IScopeAttribute scopeAttribute = scope
.findAttribute(attrName);
if (scopeAttribute == null) {
e = scope;
break;
}
e = scopeAttribute;
break;
} else {
index = token.indexOf('.');
String ruleName = token.substring(1);
if (index != -1) {
ruleName = token.substring(1, index);
}
if (position < start + ruleName.length()) {
IModelElementCriteria criteria = ModelElementCriteriaFactory.callOrVariable(ruleName);
AntlrModelElementCollectorVisitor collector = new AntlrModelElementCollectorVisitor(
criteria);
// try to find a var decl
Object[] elements = findClosestStatement(collector,
parent, ruleName);
if (elements != null && elements.length > 0) {
e = (IModelElement) elements[0];
}
break;
}
}
}
}
offset += token.length();
}
return e;
}
private Object[] findClosestStatement(
AntlrModelElementCollectorVisitor collector, IModelElement p,
String elementName) {
boolean notMatch = true;
while (notMatch && p.getElementKind() != ElementKind.RULE) {
collector.accept(p);
if (collector.getResultSize() > 0) {
notMatch = false;
}
p = p.getParent();
}
return notMatch ? null : collector.getResult();
}
private IModelElement process(ICallExpression callExpression) {
IModelElement e = null;
IRule rule = callExpression.getEnclosingRule();
if (callExpression.hasLabel()) {
String varName = callExpression.getElementName();
IModelElementCriteria criteria = ModelElementCriteriaFactory.call(varName);
AntlrModelElementCollectorVisitor collector = new AntlrModelElementCollectorVisitor(
criteria);
Object[] elements = findClosestStatement(collector, callExpression
.getParent(), varName);
if (elements != null && elements.length > 0)
return (IModelElement) elements[0];
if (varName.equals(rule.getElementName()))
return rule;
} else {
String ruleName = callExpression.getRuleName().getText();
IGrammar grammar = (IGrammar) rule.getParent();
e = grammar.findRule(ruleName);
if (e == null) {
e = grammar.findToken(ruleName);
}
}
return e;
}
}