001: /*
002: * Author: Chris Seguin
003: *
004: * This software has been developed under the copyleft
005: * rules of the GNU General Public License. Please
006: * consult the GNU General Public License for more
007: * details about use and distribution of this software.
008: */
009: package org.acm.seguin.refactor.field;
010:
011: import net.sourceforge.jrefactory.parser.ChildrenVisitor;
012: import net.sourceforge.jrefactory.ast.ASTClassBody;
013: import net.sourceforge.jrefactory.ast.ASTClassBodyDeclaration;
014: import net.sourceforge.jrefactory.ast.ASTFieldDeclaration;
015: import net.sourceforge.jrefactory.ast.ASTInterfaceBody;
016: import net.sourceforge.jrefactory.ast.ASTInterfaceMemberDeclaration;
017: import net.sourceforge.jrefactory.ast.ASTNestedClassDeclaration;
018: import net.sourceforge.jrefactory.ast.ASTNestedInterfaceDeclaration;
019: import net.sourceforge.jrefactory.ast.ASTVariableDeclarator;
020: import net.sourceforge.jrefactory.ast.ASTVariableDeclaratorId;
021: import net.sourceforge.jrefactory.ast.SimpleNode;
022: import net.sourceforge.jrefactory.parser.JavaParserTreeConstants;
023:
024: /**
025: * Visitor that traverses an AST and removes a specified field
026: *
027: *@author Chris Seguin
028: */
029: public class RemoveFieldVisitor extends ChildrenVisitor {
030: private String fieldName;
031: private SimpleNode fieldDecl;
032:
033: /**
034: * Constructor for RemoveFieldVisitor that specifies the field to remove
035: *
036: *@param field the name of the field
037: */
038: public RemoveFieldVisitor(String field) {
039: fieldName = field;
040: fieldDecl = null;
041: }
042:
043: /**
044: * Gets the FieldDeclaration attribute of the RemoveFieldVisitor object
045: *
046: *@return The FieldDeclaration value
047: */
048: public SimpleNode getFieldDeclaration() {
049: return fieldDecl;
050: }
051:
052: /**
053: * Visit a class body
054: *
055: *@param node the class body node
056: *@param data the data for the visitor
057: *@return the field if it is found
058: */
059: public Object visit(ASTClassBody node, Object data) {
060: Object result = removeField(node);
061: if (result == null) {
062: result = super .visit(node, data);
063: }
064: return result;
065: }
066:
067: /**
068: * Visit an interface body
069: *
070: *@param node the interface body node
071: *@param data data for the visitor
072: *@return the field that was removed
073: */
074: public Object visit(ASTInterfaceBody node, Object data) {
075: Object result = removeField(node);
076: if (result == null) {
077: result = super .visit(node, data);
078: }
079: return result;
080: }
081:
082: /**
083: * Skip nested classes
084: *
085: *@param node the nested class
086: *@param data the data for the visitor
087: *@return the field if it is found
088: */
089: public Object visit(ASTNestedClassDeclaration node, Object data) {
090: return null;
091: }
092:
093: /**
094: * Skip nested interfaces
095: *
096: *@param node the nested interface
097: *@param data the data for the visitor
098: *@return the field if it is found
099: */
100: public Object visit(ASTNestedInterfaceDeclaration node, Object data) {
101: return null;
102: }
103:
104: /**
105: * Have we found the field declaration that we are going to move?
106: *
107: *@param next Description of Parameter
108: *@return The Found value
109: */
110: private boolean isFound(SimpleNode next) {
111: if (!(next instanceof ASTFieldDeclaration)) {
112: return false;
113: }
114:
115: int loop = next.jjtGetNumChildren();
116: for (int ndx = 1; ndx < loop; ndx++) {
117: if (checkDeclaration(next, ndx)) {
118: return true;
119: }
120: }
121:
122: return false;
123: }
124:
125: /**
126: * Determines if we are visiting a node that has multiple fields declared in
127: * a single statement
128: *
129: *@param field Description of Parameter
130: *@return The Multiple value
131: */
132: private boolean isMultiple(SimpleNode field) {
133: return (field.jjtGetNumChildren() > 2);
134: }
135:
136: /**
137: * Remove the field, if it is the correct one. Return the field declaration
138: * that was removed
139: *
140: *@param node The node we are considering removing the field from
141: *@return The field declaration
142: */
143: private Object removeField(SimpleNode node) {
144: int loop = node.jjtGetNumChildren();
145: for (int ndx = 0; ndx < loop; ndx++) {
146: SimpleNode next = (SimpleNode) node.jjtGetChild(ndx);
147: SimpleNode possible = (SimpleNode) next.jjtGetFirstChild();
148: if (isFound(possible)) {
149: if (isMultiple(possible)) {
150: removeMultiple((ASTFieldDeclaration) possible,
151: next instanceof ASTClassBodyDeclaration);
152: } else {
153: removeSingle(node, next, ndx);
154: }
155: return next;
156: }
157: }
158: return null;
159: }
160:
161: /**
162: * Removes a field declaration where only a single variable was declared
163: *
164: *@param node the class or interface body node
165: *@param next the container for the field declaration
166: *@param ndx the index of the node to delete
167: */
168: private void removeSingle(SimpleNode node, SimpleNode next, int ndx) {
169: fieldDecl = next;
170: node.jjtDeleteChild(ndx);
171: }
172:
173: /**
174: * Removes a field that is declared as one of many
175: *
176: *@param next the field declaration
177: *@param isClass was this in a class or an interface
178: */
179: private void removeMultiple(ASTFieldDeclaration next,
180: boolean isClass) {
181: if (isClass) {
182: fieldDecl = new ASTClassBodyDeclaration(
183: JavaParserTreeConstants.JJTCLASSBODYDECLARATION);
184: } else {
185: fieldDecl = new ASTInterfaceMemberDeclaration(
186: JavaParserTreeConstants.JJTINTERFACEMEMBERDECLARATION);
187: }
188:
189: // Create the field declaration
190: ASTFieldDeclaration afd = new ASTFieldDeclaration(
191: JavaParserTreeConstants.JJTFIELDDECLARATION);
192: fieldDecl.jjtInsertChild(afd, 0);
193:
194: // Copy the type
195: afd.jjtInsertChild(next.jjtGetFirstChild(), 0);
196:
197: // Find the variable and remove it from the old and add it to the new
198: int loop = next.jjtGetNumChildren();
199: for (int ndx = 1; ndx < loop; ndx++) {
200: if (checkDeclaration(next, ndx)) {
201: afd.jjtInsertChild(next.jjtGetChild(ndx), 1);
202: next.jjtDeleteChild(ndx);
203: return;
204: }
205: }
206: }
207:
208: /**
209: * Checks a single variable declaration to see if it is the one we are
210: * looking for
211: *
212: *@param next the field declaration that we are checking
213: *@param index the index of the id that we are checking
214: *@return true if we have found the field
215: */
216: private boolean checkDeclaration(SimpleNode next, int index) {
217: ASTVariableDeclarator decl = (ASTVariableDeclarator) next
218: .jjtGetChild(index);
219: ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) decl
220: .jjtGetFirstChild();
221: return (id.getName().equals(fieldName));
222: }
223: }
|