/***** BEGIN LICENSE BLOCK *****
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Common Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/cpl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2006 Lukas Felber <lfelber@hsr.ch>
* Copyright (C) 2006 Mirko Stocker <me@misto.ch>
* Copyright (C) 2006 Thomas Corbat <tcorbat@hsr.ch>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the CPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the CPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.rubypeople.rdt.refactoring.core.renamefield;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import org.jruby.ast.ArrayNode;
import org.jruby.ast.ClassVarAsgnNode;
import org.jruby.ast.ClassVarNode;
import org.jruby.ast.FCallNode;
import org.jruby.ast.InstAsgnNode;
import org.jruby.ast.InstVarNode;
import org.jruby.ast.Node;
import org.jruby.ast.SymbolNode;
import org.rubypeople.rdt.refactoring.core.SelectionNodeProvider;
import org.rubypeople.rdt.refactoring.core.renamefield.fielditems.AttrFieldItem;
import org.rubypeople.rdt.refactoring.core.renamefield.fielditems.ClassVarAsgnFieldItem;
import org.rubypeople.rdt.refactoring.core.renamefield.fielditems.ClassVarFieldItem;
import org.rubypeople.rdt.refactoring.core.renamefield.fielditems.FieldItem;
import org.rubypeople.rdt.refactoring.core.renamefield.fielditems.InstAsgnFieldItem;
import org.rubypeople.rdt.refactoring.core.renamefield.fielditems.InstVarFieldItem;
import org.rubypeople.rdt.refactoring.documentprovider.IDocumentProvider;
import org.rubypeople.rdt.refactoring.nodewrapper.AttrAccessorNodeWrapper;
import org.rubypeople.rdt.refactoring.nodewrapper.ClassNodeWrapper;
public class FieldProvider {
private ClassNodeWrapper classNode;
private Collection<ClassNodeWrapper> relatedClasses;
private LinkedHashMap<String, ArrayList<FieldItem>> fields;
private IDocumentProvider docProvider;
public FieldProvider(ClassNodeWrapper classNode, IDocumentProvider docProvider) {
this.classNode = classNode;
this.fields = new LinkedHashMap<String, ArrayList<FieldItem>>();
this.docProvider = docProvider;
initRelatedClasses();
for(ClassNodeWrapper currentClass : relatedClasses){
initAttrs(currentClass);
initAccessors(currentClass);
initClassFields(currentClass);
}
}
private void initRelatedClasses() {
relatedClasses = new ArrayList<ClassNodeWrapper>();
Collection<ClassNodeWrapper> itselfAndSuperclasses = docProvider.getProjectClassNodeProvider().getClassAndAllSuperClasses(classNode);
relatedClasses.addAll(itselfAndSuperclasses);
Collection<ClassNodeWrapper> subclasses = docProvider.getProjectClassNodeProvider().getSubClassesOf(classNode.getName());
relatedClasses.addAll(subclasses);
}
private void initClassFields(ClassNodeWrapper classWrapper) {
Collection<Node> allOccurences = classWrapper
.getClassFieldOccurences();
for (Node currentAttr : allOccurences) {
if (currentAttr instanceof ClassVarNode) {
ClassVarNode classVar = (ClassVarNode) currentAttr;
if (!isVarSubNodeOfAsgn(classVar, allOccurences, ClassVarAsgnNode.class)) {
addClassVar(classVar);
}
} else if (currentAttr instanceof ClassVarAsgnNode) {
addClassVarAsgn((ClassVarAsgnNode) currentAttr);
} else {
System.out.println(Messages.FieldProvider_UnexpectedNodeOfType
+ currentAttr.getClass()
+ Messages.FieldProvider_RetrievedAsField
+ currentAttr.toString());
}
}
}
private void addClassVarAsgn(ClassVarAsgnNode classVarAsgnNode) {
String name = FieldItem.fieldName(classVarAsgnNode.getName());
initNameList(name);
ArrayList<FieldItem> fieldList = fields.get(name);
fieldList.add(new ClassVarAsgnFieldItem(classVarAsgnNode));
}
private void addClassVar(ClassVarNode classVarNode) {
String name = FieldItem.fieldName(classVarNode.getName());
initNameList(name);
ArrayList<FieldItem> fieldList = fields.get(name);
fieldList.add(new ClassVarFieldItem(classVarNode));
}
private void initAccessors(ClassNodeWrapper classWrapper) {
for (AttrAccessorNodeWrapper currentAccessor : classWrapper
.getAccessorNodes()) {
String name = FieldItem.fieldName(currentAccessor.getAttrName());
initNameList(name);
ArrayList<FieldItem> itemList = fields.get(name);
for (FCallNode accessorPart : currentAccessor.getAccessorNodes()) {
ArrayNode arrayNode = (ArrayNode) accessorPart.getArgsNode();
for(Object actObj : arrayNode.childNodes()) {
SymbolNode aktSymbol = (SymbolNode) actObj;
if(name.equals(aktSymbol.getName())) {
itemList.add(new AttrFieldItem(aktSymbol));
}
}
}
}
}
private void initAttrs(ClassNodeWrapper classWrapper) {
Collection<Node> allOccurences = classWrapper.getInstFieldOccurences();
for (Node currentAttr : allOccurences) {
if (currentAttr instanceof SymbolNode) {
addAttr((SymbolNode) currentAttr);
} else if (currentAttr instanceof InstVarNode) {
InstVarNode instVar = (InstVarNode) currentAttr;
if (!isVarSubNodeOfAsgn(instVar, allOccurences, InstAsgnNode.class)) {
addInstVar(instVar);
}
} else if (currentAttr instanceof InstAsgnNode) {
addInstAsgn((InstAsgnNode) currentAttr);
} else {
System.out.println(Messages.FieldProvider_UnexpectedNodeOfType
+ currentAttr.getClass()
+ Messages.FieldProvider_RetrievedAsAttribute
+ currentAttr.toString());
}
}
}
private boolean isVarSubNodeOfAsgn(Node instVar,
Collection<Node> allNodes, Class kind) {
for (Node currentNode : allNodes) {
if (currentNode.getClass().isAssignableFrom(kind)) {
if (currentNode.getPosition().getFile().equals(instVar.getPosition().getFile())) {
if (SelectionNodeProvider.nodeContainsPosition(currentNode,
instVar.getPosition().getStartOffset())) {
return true;
}
}
}
}
return false;
}
private void addInstAsgn(InstAsgnNode currentAttr) {
String name = FieldItem.fieldName(currentAttr.getName());
initNameList(name);
ArrayList<FieldItem> fieldList = fields.get(name);
fieldList.add(new InstAsgnFieldItem(currentAttr));
}
private void addInstVar(InstVarNode instVar) {
String name = FieldItem.fieldName(instVar.getName());
initNameList(name);
ArrayList<FieldItem> varList = fields.get(name);
varList.add(new InstVarFieldItem(instVar));
}
private void initNameList(String name) {
if (!fields.containsKey(name)) {
fields.put(name, new ArrayList<FieldItem>());
}
}
private void addAttr(SymbolNode symbol) {
String name = FieldItem.fieldName(symbol.getName());
initNameList(name);
ArrayList<FieldItem> attrList = fields.get(name);
attrList.add(new AttrFieldItem(symbol));
}
public ArrayList<FieldItem> getFieldItems(String fieldName, boolean concernsClassField) {
ArrayList<FieldItem> matchingItems = new ArrayList<FieldItem>();
for(FieldItem currentItem : fields.get(fieldName)){
if(currentItem.concernsClassField() == concernsClassField){
matchingItems.add(currentItem);
}
}
return matchingItems;
}
public Collection<String> getFieldNames() {
HashSet<String> names = new HashSet<String>(fields.keySet());
return names;
}
public FieldItem getNameAtPosition(int caretPosition, String fileName) {
for (String currentName : fields.keySet()) {
for (FieldItem currentItem : fields.get(currentName)) {
if (currentItem.getFieldNode().getPosition().getFile().equals(fileName) &&
SelectionNodeProvider.nodeContainsPosition(currentItem.getFieldNode(), caretPosition)) {
return currentItem;
}
}
}
return null;
}
}