| /******************************************************************************* |
| * Copyright (c) 2005, 2016 IBM Corporation 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 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.ruby.internal.parser.visitors; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.dltk.ast.ASTNode; |
| import org.eclipse.dltk.ast.Modifiers; |
| import org.eclipse.dltk.ast.PositionInformation; |
| import org.eclipse.dltk.ast.declarations.MethodDeclaration; |
| import org.eclipse.dltk.ast.expressions.BigNumericLiteral; |
| import org.eclipse.dltk.ast.expressions.BooleanLiteral; |
| import org.eclipse.dltk.ast.expressions.CallArgumentsList; |
| import org.eclipse.dltk.ast.expressions.CallExpression; |
| import org.eclipse.dltk.ast.expressions.Expression; |
| import org.eclipse.dltk.ast.expressions.FloatNumericLiteral; |
| import org.eclipse.dltk.ast.expressions.Literal; |
| import org.eclipse.dltk.ast.expressions.NilLiteral; |
| import org.eclipse.dltk.ast.expressions.NumericLiteral; |
| import org.eclipse.dltk.ast.expressions.StringLiteral; |
| import org.eclipse.dltk.ast.references.ConstantReference; |
| import org.eclipse.dltk.ast.references.Reference; |
| import org.eclipse.dltk.ast.references.SimpleReference; |
| import org.eclipse.dltk.ast.references.VariableReference; |
| import org.eclipse.dltk.ast.statements.Statement; |
| import org.eclipse.dltk.compiler.ISourceElementRequestor; |
| import org.eclipse.dltk.compiler.SourceElementRequestVisitor; |
| import org.eclipse.dltk.compiler.IElementRequestor.ImportInfo; |
| import org.eclipse.dltk.compiler.IElementRequestor.MethodInfo; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.ruby.ast.IRubyASTVisitor; |
| import org.eclipse.dltk.ruby.ast.RubyAliasExpression; |
| import org.eclipse.dltk.ruby.ast.RubyAssignment; |
| import org.eclipse.dltk.ruby.ast.RubyCallArgument; |
| import org.eclipse.dltk.ruby.ast.RubyColonExpression; |
| import org.eclipse.dltk.ruby.ast.RubyConstantDeclaration; |
| import org.eclipse.dltk.ruby.ast.RubyHashExpression; |
| import org.eclipse.dltk.ruby.ast.RubyHashPairExpression; |
| import org.eclipse.dltk.ruby.ast.RubyRegexpExpression; |
| import org.eclipse.dltk.ruby.ast.RubySymbolReference; |
| import org.eclipse.dltk.ruby.core.RubyConstants; |
| |
| public class RubySourceElementRequestor extends SourceElementRequestVisitor |
| implements IRubyASTVisitor { |
| |
| private static final String VALUE = "value"; //$NON-NLS-1$ |
| private static final String INITIALIZE = "initialize"; //$NON-NLS-1$ |
| |
| private static class TypeField { |
| private String fName; |
| |
| private String fInitValue; |
| |
| private PositionInformation fPos; |
| |
| private ASTNode fExpression; |
| |
| private ASTNode fToNode; |
| |
| public TypeField(String name, String initValue, |
| PositionInformation pos, ASTNode expression, ASTNode toNode) { |
| |
| this.fName = name; |
| this.fInitValue = initValue; |
| this.fPos = pos; |
| this.fExpression = expression; |
| this.fToNode = toNode; |
| } |
| |
| public String getName() { |
| return fName; |
| } |
| |
| public String getInitValue() { |
| return fInitValue; |
| } |
| |
| public PositionInformation getPosition() { |
| return fPos; |
| } |
| |
| public ASTNode getExpression() { |
| return fExpression; |
| } |
| |
| public ASTNode getASTNode() { |
| return fToNode; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof TypeField) { |
| TypeField typeFileld = (TypeField) obj; |
| return typeFileld.fName.equals(fName) |
| && typeFileld.fToNode.equals(fToNode); |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| return fName; |
| } |
| } |
| |
| private List<TypeField> fNotAddedFields = new ArrayList<TypeField>(); // Used to prehold fields if |
| // adding in methods. |
| private Map<ASTNode, List<String>> fTypeVariables = new HashMap<ASTNode, List<String>>(); // Used to depermine duplicate |
| |
| // names, ASTNode -> List of |
| // variables |
| |
| private boolean canAddVariables(ASTNode type, String name) { |
| if (fTypeVariables.containsKey(type)) { |
| List<String> variables = fTypeVariables.get(type); |
| if (variables.contains(name)) { |
| return false; |
| } |
| variables.add(name); |
| return true; |
| } else { |
| List<String> variables = new ArrayList<String>(); |
| variables.add(name); |
| fTypeVariables.put(type, variables); |
| return true; |
| } |
| } |
| |
| /** |
| * Parsers Expresssion and extract correct variable reference. |
| */ |
| private void addVariableReference(ASTNode left, ASTNode right, |
| boolean inClass, boolean inMethod) { |
| |
| if (left == null) { |
| throw new IllegalArgumentException( |
| Messages.RubySourceElementRequestor_addVariableExpressionCantBeNull); |
| } |
| |
| if (left instanceof VariableReference) { |
| VariableReference var = (VariableReference) left; |
| |
| if (!inMethod) { |
| // For module static of class static variables. |
| if (canAddVariables(fNodes.peek(), var.getName())) { |
| ISourceElementRequestor.FieldInfo info = new ISourceElementRequestor.FieldInfo(); |
| info.modifiers = Modifiers.AccStatic; |
| info.name = var.getName(); |
| info.nameSourceEnd = var.sourceEnd() - 1; |
| info.nameSourceStart = var.sourceStart(); |
| info.declarationStart = var.sourceStart(); |
| fRequestor.enterField(info); |
| if (right != null) { |
| fRequestor.exitField(right.sourceEnd() - 1); |
| } else { |
| fRequestor.exitField(var.sourceEnd() - 1); |
| } |
| } |
| } else { |
| |
| } |
| |
| } |
| } |
| |
| @Override |
| protected String makeLanguageDependentValue(ASTNode value) { |
| String outValue = ""; //$NON-NLS-1$ |
| /* |
| * if (value instanceof ExtendedVariableReference) { StringWriter |
| * stringWriter = new StringWriter(); CorePrinter printer = new |
| * CorePrinter(stringWriter); value.printNode(printer); printer.flush(); |
| * return stringWriter.getBuffer().toString(); } |
| */ |
| return outValue; |
| } |
| |
| public RubySourceElementRequestor(ISourceElementRequestor requestor) { |
| super(requestor); |
| } |
| |
| // Visiting methods |
| @Override |
| protected void onEndVisitMethod(MethodDeclaration method) { |
| if (DLTKCore.DEBUG) { |
| System.out.println("==> Method: " + method.getName()); //$NON-NLS-1$ |
| } |
| |
| Iterator<TypeField> it = fNotAddedFields.iterator(); |
| |
| while (it.hasNext()) { |
| TypeField field = it.next(); |
| |
| if (canAddVariables(field.getASTNode(), field.getName())) { |
| PositionInformation pos = field.getPosition(); |
| |
| ISourceElementRequestor.FieldInfo info = new ISourceElementRequestor.FieldInfo(); |
| info.modifiers = Modifiers.AccStatic; |
| info.name = field.getName(); |
| info.nameSourceEnd = pos.nameEnd - 1; |
| info.nameSourceStart = pos.nameStart; |
| info.declarationStart = pos.sourceStart; |
| |
| fRequestor.enterField(info); |
| fRequestor.exitField(pos.sourceEnd); |
| } |
| } |
| |
| fNotAddedFields.clear(); |
| } |
| |
| private void reportTypeReferences(ASTNode node) { |
| String typeName; |
| while (node != null) { |
| if (node instanceof RubyColonExpression) { |
| typeName = ((RubyColonExpression) node).getName(); |
| fRequestor.acceptTypeReference(typeName, node.sourceStart()); |
| node = ((RubyColonExpression) node).getLeft(); |
| } else if (node instanceof ConstantReference) { |
| typeName = ((ConstantReference) node).getName(); |
| fRequestor.acceptTypeReference(typeName, node.sourceStart()); |
| node = null; |
| } else { |
| node = null; |
| } |
| } |
| } |
| |
| public void visitTypeName(ASTNode node) { |
| // empty |
| } |
| |
| // Visiting expressions |
| @Override |
| public boolean visit(ASTNode expression) throws Exception { |
| if (DLTKCore.DEBUG) { |
| System.out.println("==> Expression: " + expression.toString()); //$NON-NLS-1$ |
| } |
| |
| if (expression instanceof RubyAssignment) { |
| // Assignment handling (this is static variable assignment.) |
| |
| RubyAssignment assignment = (RubyAssignment) expression; |
| ASTNode left = assignment.getLeft(); |
| ASTNode right = assignment.getRight(); |
| |
| // Handle static variables |
| addVariableReference(left, right, fInClass, fInMethod); |
| } else if (expression instanceof CallExpression) { |
| // CallExpression handling |
| CallExpression callExpression = (CallExpression) expression; |
| |
| if (RubyAttributeHandler.isAttributeCreationCall(callExpression)) { |
| RubyAttributeHandler info = new RubyAttributeHandler( |
| callExpression); |
| List<ASTNode> readers = info.getReaders(); |
| for (Iterator<ASTNode> iterator = readers.iterator(); iterator.hasNext();) { |
| ASTNode n = iterator.next(); |
| String attr = RubyAttributeHandler.getText(n); |
| ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo(); |
| mi.name = attr; |
| mi.modifiers = RubyConstants.RubyAttributeModifier; |
| mi.nameSourceStart = n.sourceStart(); |
| mi.nameSourceEnd = n.sourceEnd() - 1; |
| mi.declarationStart = n.sourceStart(); |
| |
| fRequestor.enterMethod(mi); |
| fRequestor.exitMethod(n.sourceEnd()); |
| } |
| List<ASTNode> writers = info.getWriters(); |
| for (Iterator<ASTNode> iterator = writers.iterator(); iterator.hasNext();) { |
| ASTNode n = iterator.next(); |
| String attr = RubyAttributeHandler.getText(n); |
| ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo(); |
| mi.parameterNames = new String[] { VALUE }; |
| mi.name = attr + "="; //$NON-NLS-1$ |
| mi.modifiers = RubyConstants.RubyAttributeModifier; |
| mi.nameSourceStart = n.sourceStart(); |
| mi.nameSourceEnd = n.sourceEnd() - 1; |
| mi.declarationStart = n.sourceStart(); |
| |
| fRequestor.enterMethod(mi); |
| fRequestor.exitMethod(n.sourceEnd()); |
| } |
| } else if ("delegate".equals(callExpression.getName())) { //$NON-NLS-1$ |
| RubyCallArgument argNode; |
| RubyHashPairExpression hashNode; |
| String oldName = ""; //$NON-NLS-1$ |
| String dName; |
| for (Iterator<ASTNode> iterator = callExpression.getArgs().getChilds() |
| .iterator(); iterator.hasNext();) { |
| argNode = (RubyCallArgument) iterator.next(); |
| if (argNode.getValue() instanceof RubyHashExpression) { |
| for (Iterator<ASTNode> iterator2 = argNode.getValue() |
| .getChilds().iterator(); iterator2.hasNext();) { |
| hashNode = (RubyHashPairExpression) iterator2 |
| .next(); |
| if (hashNode.getValue() instanceof RubySymbolReference) { |
| oldName = ((RubySymbolReference) hashNode |
| .getValue()).getName(); |
| } else if (hashNode.getValue() instanceof StringLiteral) { |
| oldName = ((StringLiteral) hashNode.getValue()) |
| .getValue(); |
| } |
| } |
| } |
| } |
| for (Iterator<ASTNode> iterator = callExpression.getArgs().getChilds() |
| .iterator(); iterator.hasNext();) { |
| argNode = (RubyCallArgument) iterator.next(); |
| dName = null; |
| if (argNode.getValue() instanceof RubySymbolReference) { |
| dName = ((RubySymbolReference) argNode.getValue()) |
| .getName(); |
| } else if (argNode.getValue() instanceof StringLiteral) { |
| dName = ((StringLiteral) argNode.getValue()).getValue(); |
| } |
| if (dName != null) { |
| ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo(); |
| mi.name = dName; |
| mi.modifiers = RubyConstants.RubyAliasModifier; |
| mi.nameSourceStart = argNode.sourceStart(); |
| mi.nameSourceEnd = argNode.sourceEnd() - 1; |
| mi.declarationStart = argNode.sourceStart(); |
| // mi.parameterNames = new String[] { oldName }; |
| |
| fRequestor.enterMethod(mi); |
| fRequestor.exitMethod(argNode.sourceEnd()); |
| } |
| } |
| } else if (RubyConstants.REQUIRE.equals(callExpression.getName())) { |
| final List<?> args = callExpression.getArgs().getChilds(); |
| if (args.size() == 1 && args.get(0) instanceof RubyCallArgument) { |
| RubyCallArgument argument = (RubyCallArgument) args.get(0); |
| final ImportInfo importInfo = new ImportInfo(); |
| importInfo.sourceStart = callExpression.sourceStart(); |
| importInfo.sourceEnd = callExpression.sourceEnd(); |
| importInfo.containerName = RubyConstants.REQUIRE; |
| if (argument.getValue() instanceof StringLiteral) { |
| StringLiteral lit = (StringLiteral) argument.getValue(); |
| importInfo.name = lit.getValue(); |
| } else { |
| // TODO add expression as text |
| } |
| if (importInfo.name != null) { |
| this.fRequestor.acceptImport(importInfo); |
| } |
| } |
| } |
| |
| // Arguments |
| int argsCount = 0; |
| CallArgumentsList args = callExpression.getArgs(); |
| if (args != null && args.getChilds() != null) { |
| argsCount = args.getChilds().size(); |
| } |
| |
| // Start |
| int start = callExpression.sourceStart(); |
| if (start < 0) { |
| start = 0; |
| } |
| |
| // End |
| int end = callExpression.sourceEnd(); |
| if (end < 0) { |
| end = 1; |
| } |
| |
| // Accept |
| fRequestor.acceptMethodReference(callExpression.getName(), |
| argsCount, start, end); |
| } else if (expression instanceof Literal) { |
| if (expression instanceof RubyRegexpExpression) { |
| fRequestor.acceptTypeReference( |
| "Regexp", expression.sourceStart()); //$NON-NLS-1$ |
| } else if (expression instanceof StringLiteral) { |
| fRequestor.acceptTypeReference( |
| "String", expression.sourceStart()); //$NON-NLS-1$ |
| } else if (expression instanceof BooleanLiteral) { |
| BooleanLiteral boolLit = (BooleanLiteral) expression; |
| if (boolLit.boolValue()) { |
| fRequestor.acceptTypeReference( |
| "TrueClass", expression.sourceStart()); //$NON-NLS-1$$ |
| } else { |
| fRequestor.acceptTypeReference( |
| "FalseClass", expression.sourceStart()); //$NON-NLS-1$ |
| } |
| } else if (expression instanceof NumericLiteral) { |
| fRequestor.acceptTypeReference( |
| "Fixnum", expression.sourceStart()); //$NON-NLS-1$ |
| } else if (expression instanceof NilLiteral) { |
| fRequestor.acceptTypeReference( |
| "NilClass", expression.sourceStart()); //$NON-NLS-1$ |
| } else if (expression instanceof FloatNumericLiteral) { |
| fRequestor.acceptTypeReference( |
| "Float", expression.sourceStart()); //$NON-NLS-1$ |
| } else if (expression instanceof BigNumericLiteral) { |
| fRequestor.acceptTypeReference( |
| "Bignum", expression.sourceStart()); //$NON-NLS-1$ |
| } |
| } else if (expression instanceof Reference) { |
| if (expression instanceof RubySymbolReference) { |
| fRequestor.acceptTypeReference( |
| "Symbol", expression.sourceStart()); //$NON-NLS-1$ |
| } else if (expression instanceof VariableReference) { |
| // VariableReference handling |
| VariableReference variableReference = (VariableReference) expression; |
| |
| int pos = variableReference.sourceStart(); |
| if (pos < 0) { |
| pos = 0; |
| } |
| |
| // Accept |
| fRequestor.acceptFieldReference(variableReference.getName(), |
| pos); |
| } else if (expression instanceof ConstantReference) { |
| reportTypeReferences(expression); |
| } |
| } else if (expression instanceof RubyColonExpression) { |
| reportTypeReferences(expression); |
| } else if (expression instanceof RubyConstantDeclaration) { |
| RubyConstantDeclaration constant = (RubyConstantDeclaration) expression; |
| SimpleReference constName = constant.getName(); |
| |
| ISourceElementRequestor.FieldInfo info = new ISourceElementRequestor.FieldInfo(); |
| info.modifiers = Modifiers.AccConstant; |
| info.name = constName.getName(); |
| info.nameSourceEnd = constName.sourceEnd() - 1; |
| info.nameSourceStart = constName.sourceStart(); |
| info.declarationStart = constName.sourceStart(); |
| |
| fRequestor.enterField(info); |
| fRequestor.exitField(constName.sourceEnd() - 1); |
| } else if (expression instanceof RubyAliasExpression) { |
| RubyAliasExpression alias = (RubyAliasExpression) expression; |
| String oldValue = alias.getOldValue(); |
| if (!oldValue.startsWith("$")) { //$NON-NLS-1$ |
| String newValue = alias.getNewValue(); |
| ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo(); |
| |
| mi.name = newValue; |
| mi.modifiers = RubyConstants.RubyAliasModifier; |
| mi.nameSourceStart = alias.sourceStart(); |
| mi.nameSourceEnd = alias.sourceEnd() - 1; |
| mi.declarationStart = alias.sourceStart(); |
| // mi.parameterNames = new String[] { oldValue }; |
| |
| fRequestor.enterMethod(mi); |
| fRequestor.exitMethod(alias.sourceEnd()); |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean endvisit(ASTNode expression) throws Exception { |
| return true; |
| } |
| |
| @Override |
| public boolean visit(Expression expression) throws Exception { |
| super.visit(expression); |
| return visit((ASTNode) expression); |
| } |
| |
| @Override |
| public boolean visit(Statement statement) throws Exception { |
| super.visit(statement); |
| return visit((ASTNode) statement); |
| } |
| |
| @Override |
| protected void modifyMethodInfo(MethodDeclaration methodDeclaration, |
| MethodInfo mi) { |
| if (fInClass) { |
| mi.isConstructor = methodDeclaration.getName().equals(INITIALIZE); |
| } |
| } |
| } |