/***** 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>
*
* 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.nodewrapper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import org.jruby.ast.ArrayNode;
import org.jruby.ast.BlockNode;
import org.jruby.ast.CallNode;
import org.jruby.ast.ClassNode;
import org.jruby.ast.Colon2Node;
import org.jruby.ast.FCallNode;
import org.jruby.ast.MethodDefNode;
import org.jruby.ast.ModuleNode;
import org.jruby.ast.NewlineNode;
import org.jruby.ast.Node;
import org.jruby.ast.SClassNode;
import org.jruby.ast.SymbolNode;
import org.jruby.ast.VCallNode;
import org.rubypeople.rdt.refactoring.core.NodeProvider;
import org.rubypeople.rdt.refactoring.core.renamemodule.ModuleSpecifierWrapper;
import org.rubypeople.rdt.refactoring.exception.NoClassNodeException;
import org.rubypeople.rdt.refactoring.nodewrapper.VisibilityNodeWrapper.METHOD_VISIBILITY;
import org.rubypeople.rdt.refactoring.util.NodeUtil;
public abstract class PartialClassNodeWrapper implements INodeWrapper {
private Node wrappedNode;
private Collection<ModuleNode> enclosingModules;
private Collection<MethodNodeWrapper> methods;
private Collection<FieldNodeWrapper> fields;
private Collection<Node> attributes;
public PartialClassNodeWrapper(Node node) {
wrappedNode = node;
}
public Collection<FieldNodeWrapper> getFields() {
if(fields == null) {
fields = getFieldsFromNode(wrappedNode);
}
return fields;
}
public static Collection<FieldNodeWrapper> getFieldsFromNode(Node wrappedNode) {
Collection<FieldNodeWrapper> fields = new ArrayList<FieldNodeWrapper>();
Collection<Node> fieldNodes = NodeProvider.getSubNodes(wrappedNode, FieldNodeWrapper.FIELD_NODE_CLASSES_WITHOUT_SYMBOL_NODE);
for (Node currentField : fieldNodes) {
fields.add(new FieldNodeWrapper(currentField));
}
addSymbolNodeFields(fields, wrappedNode);
return fields;
}
private static void addSymbolNodeFields(Collection<FieldNodeWrapper> fields, Node wrappedNode) {
Collection<AttrAccessorNodeWrapper> accessors = NodeProvider.getAccessorNodes(wrappedNode);
for(AttrAccessorNodeWrapper aktAcessor : accessors) {
fields.add(new FieldNodeWrapper(aktAcessor.getSymbolNode()));
}
for(Node aktNode : NodeProvider.getSubNodes(wrappedNode, FCallNode.class)) {
FCallNode aktFCallNode = (FCallNode) aktNode;
if(aktFCallNode.getName().equals(FieldNodeWrapper.ATTR_NAME)) {
addAttrNodeFields(aktFCallNode, fields);
}
}
}
private static void addAttrNodeFields(FCallNode callNode, Collection<FieldNodeWrapper> fields) {
if (NodeUtil.nodeAssignableFrom(callNode.getArgsNode(), ArrayNode.class)) {
for (Object o : callNode.getArgsNode().childNodes()) {
Node aktNode = (Node) o;
if (NodeUtil.nodeAssignableFrom(aktNode, SymbolNode.class)) {
SymbolNode symbolNode = ((SymbolNode) aktNode);
fields.add(new FieldNodeWrapper(symbolNode));
}
}
}
}
public Collection<MethodNodeWrapper> getMethods() {
if(methods == null) {
methods = new ArrayList<MethodNodeWrapper>();
Collection<Node> methodNodes = NodeProvider.getSubNodes(wrappedNode, MethodDefNode.class);
for (Node methodNode : methodNodes) {
methods.add(new MethodNodeWrapper((MethodDefNode) methodNode, new ClassNodeWrapper(this)));
}
}
return methods;
}
public abstract Node getClassBodyNode();
public abstract String getClassName();
public Node getWrappedNode() {
return wrappedNode;
}
public abstract Node getDeclarationEndNode();
public abstract String getSuperClassName();
public Collection<Node> getAttrNodes() {
if(attributes == null) {
Collection<Node> allAttrs = NodeProvider.getAttributeNodes(wrappedNode);
attributes = new ArrayList<Node>();
for (Node node : allAttrs) {
if (!isDirectChild(node))
attributes.add(node);
}
}
return attributes;
}
private boolean isDirectChild(Node child) {
return isDirectChild(child, wrappedNode);
}
private boolean isDirectChild(Node child, Node parent) {
for (Object aktChild : parent.childNodes()) {
if (aktChild.equals(child))
return true;
if (ignoreInDirectChildLine((Node) aktChild))
if (isDirectChild(child, (Node) aktChild))
return true;
}
return false;
}
private boolean ignoreInDirectChildLine(Node node) {
return node instanceof NewlineNode || node instanceof BlockNode;
}
public static PartialClassNodeWrapper getPartialClassNodeWrapper(Node node, Node rootNode) throws NoClassNodeException {
if (node instanceof ClassNode)
return new RealClassNodeWrapper(node);
if (node instanceof SClassNode)
return new SClassNodeWrapper(node, rootNode);
throw new NoClassNodeException();
}
public Collection<AttrAccessorNodeWrapper> getAccessorNodes() {
return NodeProvider.getAccessorNodes(wrappedNode);
}
public String getFile() {
return wrappedNode.getPosition().getFile();
}
public void setEnclosingModules(Collection<ModuleNode> enclosingModules) {
if(enclosingModules.size() > 0) {
this.enclosingModules = enclosingModules;
}
}
public String getModulePrefix() {
if (enclosingModules == null) {
return ""; //$NON-NLS-1$
}
StringBuilder modulePrefix = new StringBuilder();
Iterator<ModuleNode> it = enclosingModules.iterator();
while (it.hasNext()) {
ModuleNode currentModule = it.next();
Node cPath = currentModule.getCPath();
if (cPath instanceof Colon2Node) {
modulePrefix.append(((Colon2Node) cPath).getName());
if(it.hasNext()) {
modulePrefix.append("::"); //$NON-NLS-1$
}
}
}
return modulePrefix.toString();
}
public Collection<Node> getInstFieldOccurences() {
return NodeProvider.getInstFieldOccurences(wrappedNode);
}
public Collection<Node> getClassFieldOccurences() {
return NodeProvider.getClassFieldOccurences(wrappedNode);
}
public Collection<ModuleSpecifierWrapper> getIncludeCalls() {
Collection<ModuleSpecifierWrapper> includes = new ArrayList<ModuleSpecifierWrapper>();
for (Node node : NodeProvider.getSubNodes(wrappedNode, FCallNode.class)) {
FCallNode call = (FCallNode) node;
if("include".equals(call.getName())) { //$NON-NLS-1$
includes.add(ModuleSpecifierWrapper.create(((ArrayNode) call.getArgsNode()).get(0), getModulePrefix()));
}
}
return includes;
}
public Collection<MethodCallNodeWrapper> getMethodCalls(MethodDefNode decoratedMethod) {
ArrayList<MethodCallNodeWrapper> localCalls = new ArrayList<MethodCallNodeWrapper>();
Collection<Node> callNodes = NodeProvider.getSubNodes(this.wrappedNode, VCallNode.class, CallNode.class, FCallNode.class);
for(Node currentCall : callNodes){
MethodCallNodeWrapper callNode = new MethodCallNodeWrapper(currentCall);
if(callNode.getName().equals(decoratedMethod.getName())){
localCalls.add(callNode);
}
}
return localCalls;
}
public Collection<SymbolNode> getMethodSymbols(MethodDefNode decoratedNode) {
ArrayList<SymbolNode> localSymbols = new ArrayList<SymbolNode>();
Collection<Node> fCallNodes = NodeProvider.getSubNodes(this.wrappedNode, FCallNode.class);
for(Node currentCall : fCallNodes){
FCallNode currentFCall = (FCallNode)currentCall;
String callName = currentFCall.getName();
if(VisibilityNodeWrapper.isVisibilityString(callName) || "alias_method".equals(callName)){
Collection<Node> symbolNodes = NodeProvider.getSubNodes(currentFCall, SymbolNode.class);
for(Node currentItem : symbolNodes){
SymbolNode currentSymbol = (SymbolNode)currentItem;
if(currentSymbol.getName().equals(decoratedNode.getName())){
localSymbols.add(currentSymbol);
}
}
}
}
return localSymbols;
}
public METHOD_VISIBILITY getPosVisibility(int pos) {
Collection<Node> vCallNodes = NodeProvider.getSubNodes(wrappedNode, VCallNode.class);
VCallNode lastMatch = null;
for(Node aktNode : vCallNodes) {
VCallNode aktVCallNode = (VCallNode) aktNode;
if(VisibilityNodeWrapper.isVisibilityString(aktVCallNode.getName())) {
if(aktVCallNode.getPosition().getEndOffset() <= pos) {
lastMatch = aktVCallNode;
} else {
break;
}
}
}
if(lastMatch == null) {
return METHOD_VISIBILITY.PUBLIC;
}
return VisibilityNodeWrapper.getVisibility(lastMatch.getName());
}
public Collection<MethodCallNodeWrapper> getMethodCallNodes() {
return NodeProvider.getMethodCallNodes(wrappedNode);
}
public Collection<MethodNodeWrapper> getExistingConstructors() {
Collection<MethodNodeWrapper> methodNodes = getMethods();
Collection<MethodNodeWrapper> constructors = new ArrayList<MethodNodeWrapper>();
for(MethodNodeWrapper aktMethod : methodNodes) {
if(aktMethod.isConstructor()) {
constructors.add(aktMethod);
}
}
return constructors;
}
public Collection<VisibilityNodeWrapper> getMethodVisibilityNodes() {
Collection<Node> fCallNodes = NodeProvider.getSubNodes(wrappedNode, FCallNode.class);
Collection<VisibilityNodeWrapper> visibilities = new ArrayList<VisibilityNodeWrapper>();
for(Node aktNode : fCallNodes) {
FCallNode aktFCallNode = (FCallNode) aktNode;
if(VisibilityNodeWrapper.isVisibilityString(aktFCallNode.getName())) {
visibilities.add(new VisibilityNodeWrapper(aktFCallNode));
}
}
return visibilities;
}
}