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 java.io.File;
012: import java.util.Iterator;
013: import java.util.LinkedList;
014: import net.sourceforge.jrefactory.ast.ASTName;
015: import net.sourceforge.jrefactory.ast.ASTPrimitiveType;
016: import net.sourceforge.jrefactory.ast.ASTType;
017: import net.sourceforge.jrefactory.ast.SimpleNode;
018: import org.acm.seguin.refactor.AddImportTransform;
019: import org.acm.seguin.refactor.ComplexTransform;
020: import org.acm.seguin.refactor.Refactoring;
021: import org.acm.seguin.refactor.RefactoringException;
022: import org.acm.seguin.refactor.undo.UndoAction;
023: import org.acm.seguin.refactor.undo.UndoStack;
024: import org.acm.seguin.summary.FileSummary;
025: import org.acm.seguin.summary.PackageSummary;
026: import org.acm.seguin.summary.Summary;
027: import org.acm.seguin.summary.TypeDeclSummary;
028: import org.acm.seguin.summary.TypeSummary;
029: import org.acm.seguin.summary.query.FieldQuery;
030: import org.acm.seguin.summary.query.GetTypeSummary;
031:
032: /**
033: * Performs the push down field refactoring
034: *
035: *@author Chris Seguin
036: */
037: public class PushDownFieldRefactoring extends FieldRefactoring {
038: private LinkedList childTypes;
039:
040: /**
041: * Constructor for the PushDownFieldRefactoring object
042: */
043: protected PushDownFieldRefactoring() {
044: childTypes = new LinkedList();
045: field = null;
046: typeSummary = null;
047: }
048:
049: /**
050: * Gets the description of the refactoring
051: *
052: *@return the description
053: */
054: public String getDescription() {
055: return "Moves a field " + field + " into child classes of "
056: + typeSummary.getName();
057: }
058:
059: /**
060: * Gets the ID attribute of the PushDownFieldRefactoring object
061: *
062: *@return The ID value
063: */
064: public int getID() {
065: return PUSH_DOWN_FIELD;
066: }
067:
068: /**
069: * Adds a child class where the field should be pushed into
070: *
071: *@param packageName the package name
072: *@param className the class name
073: */
074: public void addChild(String packageName, String className) {
075: addChild(GetTypeSummary.query(PackageSummary
076: .getPackageSummary(packageName), className));
077: }
078:
079: /**
080: * Adds a child class where the field should be pushed into
081: *
082: *@param init The new Class value
083: */
084: public void addChild(TypeSummary init) {
085: if (init != null) {
086: System.out.println("Adding " + init.getName());
087: childTypes.add(init);
088: }
089: }
090:
091: /**
092: * Preconditions that must be true for the refactoring to work
093: *
094: *@exception RefactoringException a problem with performing this
095: * refactoring
096: */
097: protected void preconditions() throws RefactoringException {
098: if (field == null) {
099: throw new RefactoringException("No field specified");
100: }
101:
102: if (typeSummary == null) {
103: throw new RefactoringException("No type specified");
104: }
105:
106: if (childTypes.size() == 0) {
107: throw new RefactoringException("No child types specified");
108: }
109:
110: if (FieldQuery.query(typeSummary, field, FieldQuery.PRIVATE) == null) {
111: throw new RefactoringException("Field named " + field
112: + " does not exist in " + typeSummary.getName());
113: }
114:
115: if (((FileSummary) typeSummary.getParent()).getFile() == null) {
116: throw new RefactoringException(
117: "Can't push down a field from source code that you don't have");
118: }
119:
120: Iterator iter = childTypes.iterator();
121: while (iter.hasNext()) {
122: TypeSummary next = (TypeSummary) iter.next();
123:
124: if (next == null) {
125: throw new RefactoringException(
126: "Can't push down a field into source code that you don't have");
127: }
128:
129: if (FieldQuery.query(next, field, FieldQuery.PRIVATE) != null) {
130: throw new RefactoringException("Field named " + field
131: + " already exists in " + next.getName());
132: }
133:
134: if (((FileSummary) next.getParent()).getFile() == null) {
135: throw new RefactoringException(
136: "Can't push up a field into source code that you don't have");
137: }
138:
139: TypeDeclSummary parentDecl = next.getParentClass();
140: TypeSummary parentTypeSummary = GetTypeSummary
141: .query(parentDecl);
142:
143: if (parentTypeSummary != typeSummary) {
144: throw new RefactoringException(
145: "Trying to push a field from "
146: + typeSummary.getName()
147: + " to "
148: + next.getName()
149: + " and the destination is not a subclass of the source");
150: }
151: }
152: }
153:
154: /**
155: * Actually update the files
156: */
157: protected void transform() {
158: FileSummary fileSummary = (FileSummary) getFileSummary(typeSummary);
159: RemoveFieldTransform rft = new RemoveFieldTransform(field);
160: ComplexTransform transform = getComplexTransform();
161: transform.add(rft);
162: transform.apply(fileSummary.getFile(), fileSummary.getFile());
163:
164: // Update the field declaration to have the proper permissions
165: SimpleNode fieldDecl = rft.getFieldDeclaration();
166: if (fieldDecl == null) {
167: return;
168: }
169:
170: Iterator iter = childTypes.iterator();
171: while (iter.hasNext()) {
172: AddFieldTransform aft = new AddFieldTransform(fieldDecl);
173: transform.clear();
174: transform.add(aft);
175: Object fieldType = getFieldType(fieldDecl, fileSummary);
176: if (fieldType == null) {
177: // Do nothing
178: } else if ((fieldType instanceof TypeSummary)
179: && !isInJavaLang((TypeSummary) fieldType)) {
180: transform.add(new AddImportTransform(
181: (TypeSummary) fieldType));
182: } else if ((fieldType instanceof ASTName)
183: && !isInJavaLang((ASTName) fieldType)) {
184: transform.add(new AddImportTransform(
185: (ASTName) fieldType));
186: }
187:
188: TypeSummary next = (TypeSummary) iter.next();
189: FileSummary nextFileSummary = (FileSummary) next
190: .getParent();
191: transform.apply(nextFileSummary.getFile(), nextFileSummary
192: .getFile());
193: }
194: }
195: }
|