package org.rubypeople.rdt.internal.core.search.matching;
import org.eclipse.core.runtime.CoreException;
import org.jruby.ast.ClassVarAsgnNode;
import org.jruby.ast.ClassVarNode;
import org.jruby.ast.ConstDeclNode;
import org.jruby.ast.GlobalAsgnNode;
import org.jruby.ast.GlobalVarNode;
import org.jruby.ast.InstAsgnNode;
import org.jruby.ast.InstVarNode;
import org.jruby.ast.Node;
import org.jruby.ast.types.INameNode;
import org.rubypeople.rdt.core.IMember;
import org.rubypeople.rdt.core.IParent;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.ISourceRange;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.internal.core.RubyScript;
import org.rubypeople.rdt.internal.core.parser.InOrderVisitor;
import org.rubypeople.rdt.internal.core.parser.RubyParser;
public class FieldLocator extends PatternLocator {
private FieldPattern pattern;
public FieldLocator(FieldPattern pattern) {
super(pattern);
this.pattern = pattern;
}
@Override
public void reportMatches(final RubyScript script, final MatchLocator locator) {
if (!this.pattern.findReferences) { // just traverse our own model
reportMatches((IParent) script, locator);
} else { // they want references too, so we need to traverse the AST
reportASTMatches(script, locator);
}
}
private void reportASTMatches(final RubyScript script, final MatchLocator locator) {
try {
Node ast = script.lastGoodAST;
if (ast == null) {
ast = new RubyParser().parse(script.getElementName(), script.getSource()).getAST();
}
final boolean findDeclarations = this.pattern.findDeclarations;
new InOrderVisitor() {
@Override
public Object visitInstVarNode(InstVarNode iVisited) {
match(iVisited);
return super.visitInstVarNode(iVisited);
}
// XXX Handle constant references!
@Override
public Object visitGlobalVarNode(GlobalVarNode iVisited) {
match(iVisited);
return super.visitGlobalVarNode(iVisited);
}
@Override
public Object visitConstDeclNode(ConstDeclNode iVisited) {
if (findDeclarations) match(iVisited);
return super.visitConstDeclNode(iVisited);
}
@Override
public Object visitGlobalAsgnNode(GlobalAsgnNode iVisited) {
match(iVisited); // TODO check whether we want to find write references
return super.visitGlobalAsgnNode(iVisited);
}
@Override
public Object visitClassVarNode(ClassVarNode iVisited) {
match(iVisited);
return super.visitClassVarNode(iVisited);
}
@Override
public Object visitClassVarAsgnNode(ClassVarAsgnNode iVisited) {
match(iVisited); // TODO check whether we want to find write references
return super.visitClassVarAsgnNode(iVisited);
}
@Override
public Object visitInstAsgnNode(InstAsgnNode iVisited) {
match(iVisited); // TODO check whether we want to find write references
return super.visitInstAsgnNode(iVisited);
}
private void match(Node iVisited) {
int accuracy = getAccuracy(((INameNode)iVisited).getName());
if (accuracy != IMPOSSIBLE_MATCH) {
try {
IRubyElement element = script.getElementAt(iVisited.getPosition().getStartOffset());
if (element == null) element = script;
if (locator.encloses(element)) {
IRubyElement binding = resolve(element, ((INameNode)iVisited).getName());
locator.report(locator.newFieldReferenceMatch(element, binding, accuracy,
iVisited.getPosition().getStartOffset(), iVisited.getPosition().getEndOffset() - iVisited.getPosition().getStartOffset(), iVisited));
}
} catch (CoreException e) {
RubyCore.log(e);
}
}
}
private IRubyElement resolve(IRubyElement element, String name) {
if (element instanceof IMember) {
IMember member = (IMember) element;
IType type = member.getDeclaringType();
return type.getField(name);
}
return null;
}
}.acceptNode(ast);
} catch(RubyModelException e) {
RubyCore.log(e);
}
}
private void reportMatches(IParent parent, MatchLocator locator) {
try {
IRubyElement[] children = parent.getChildren();
for (int i = 0; i < children.length; i++) {
IRubyElement child = children[i];
if ((child.isType(IRubyElement.FIELD) ||
child.isType(IRubyElement.GLOBAL) ||
child.isType(IRubyElement.CONSTANT) ||
child.isType(IRubyElement.CLASS_VAR) ||
child.isType(IRubyElement.INSTANCE_VAR)) && (locator.encloses(child))) {
int accuracy = getAccuracy(child.getElementName());
if (accuracy != IMPOSSIBLE_MATCH) {
IMember member = (IMember) child;
ISourceRange range = member.getSourceRange();
try {
locator.report(locator.newDeclarationMatch(child, accuracy, range.getOffset(), range.getLength()));
} catch (CoreException e) {
RubyCore.log(e);
}
}
}
if (child instanceof IParent) {
IParent parentTwo = (IParent) child;
reportMatches(parentTwo, locator);
}
}
} catch (RubyModelException e) {
RubyCore.log(e);
}
}
private int getAccuracy(String name) {
if (this.pattern.findReferences)
if (matchesName(this.pattern.name, name.toCharArray()))
return ACCURATE_MATCH;
if (this.pattern.findDeclarations) {
if (matchesName(this.pattern.name, name.toCharArray()))
return ACCURATE_MATCH;
}
return IMPOSSIBLE_MATCH;
}
}