001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.rename;
011:
012: import java.util.Collections;
013: import java.util.LinkedList;
014: import java.util.Map;
015:
016: import org.eclipse.core.runtime.Assert;
017:
018: import org.eclipse.jdt.core.IField;
019: import org.eclipse.jdt.core.IInitializer;
020: import org.eclipse.jdt.core.IJavaElement;
021: import org.eclipse.jdt.core.IMember;
022: import org.eclipse.jdt.core.IMethod;
023: import org.eclipse.jdt.core.IType;
024: import org.eclipse.jdt.core.Signature;
025:
026: /**
027: * Helper class to transplant a IJavaElement handle from a certain state of the
028: * Java Model into another.
029: *
030: * The changes to the workspace include one type rename, a number of field
031: * renames, and a number of method renames including signature changes.
032: *
033: * The returned handle exists in the target model state.
034: *
035: * @since 3.2
036: *
037: */
038: public class RefactoringHandleTransplanter {
039:
040: private final IType fOldType;
041: private final IType fNewType;
042: private final Map/*<IJavaElement, String>*/fRefactoredSimilarElements;
043:
044: /**
045: * @param oldType old type
046: * @param newType renamed type
047: * @param refactoredSimilarElements map from similar element (IJavaElement) to new name (String), or <code>null</code>
048: */
049: public RefactoringHandleTransplanter(IType oldType, IType newType,
050: Map/*<IJavaElement, String>*/refactoredSimilarElements) {
051: fOldType = oldType;
052: fNewType = newType;
053: if (refactoredSimilarElements == null)
054: fRefactoredSimilarElements = Collections.EMPTY_MAP;
055: else
056: fRefactoredSimilarElements = refactoredSimilarElements;
057: }
058:
059: /**
060: * Converts the handle. Handle need not exist, but must be a source
061: * reference.
062: *
063: * @param handle
064: * @return the new handle
065: */
066: public IMember transplantHandle(IMember handle) {
067:
068: /*
069: * Create a list of handles from top-level type to the handle
070: */
071: final LinkedList oldElements = new LinkedList();
072: addElements(handle, oldElements);
073:
074: /*
075: * Step through the elements and re-locate them in the new parents.
076: */
077: final IMember[] newElements = convertElements((IMember[]) oldElements
078: .toArray(new IMember[0]));
079:
080: return newElements[newElements.length - 1];
081: }
082:
083: private void addElements(IMember element, LinkedList chain) {
084: chain.addFirst(element);
085: IJavaElement parent = element.getParent();
086: if (parent instanceof IMember)
087: addElements((IMember) parent, chain);
088: }
089:
090: private IMember[] convertElements(IMember[] oldElements) {
091:
092: final IMember[] newElements = new IMember[oldElements.length];
093: final IMember first = oldElements[0];
094:
095: Assert.isTrue(first instanceof IType);
096:
097: if (first.equals(fOldType))
098: // We renamed a top level type.
099: newElements[0] = fNewType;
100: else
101: newElements[0] = first;
102:
103: /*
104: * Note that we only need to translate the information necessary to
105: * create new handles. For example, the return type of a method is not
106: * relevant; neither is information about generic specifics in types.
107: */
108:
109: for (int i = 1; i < oldElements.length; i++) {
110: final IJavaElement newParent = newElements[i - 1];
111: final IJavaElement currentElement = oldElements[i];
112: switch (newParent.getElementType()) {
113: case IJavaElement.TYPE: {
114: switch (currentElement.getElementType()) {
115: case IJavaElement.TYPE: {
116: final String newName = resolveTypeName((IType) currentElement);
117: newElements[i] = ((IType) newParent)
118: .getType(newName);
119: break;
120: }
121: case IJavaElement.METHOD: {
122: final String newName = resolveElementName(currentElement);
123: final String[] newParameterTypes = resolveParameterTypes((IMethod) currentElement);
124: newElements[i] = ((IType) newParent).getMethod(
125: newName, newParameterTypes);
126: break;
127: }
128: case IJavaElement.INITIALIZER: {
129: final IInitializer initializer = (IInitializer) currentElement;
130: newElements[i] = ((IType) newParent)
131: .getInitializer(initializer
132: .getOccurrenceCount());
133: break;
134: }
135: case IJavaElement.FIELD: {
136: final String newName = resolveElementName(currentElement);
137: newElements[i] = ((IType) newParent)
138: .getField(newName);
139: break;
140: }
141: }
142: break;
143: }
144: case IJavaElement.METHOD: {
145: switch (currentElement.getElementType()) {
146: case IJavaElement.TYPE: {
147: newElements[i] = resolveTypeInMember(
148: (IMethod) newParent, (IType) currentElement);
149: break;
150: }
151: }
152: break;
153: }
154: case IJavaElement.INITIALIZER: {
155: switch (currentElement.getElementType()) {
156: case IJavaElement.TYPE: {
157: newElements[i] = resolveTypeInMember(
158: (IInitializer) newParent,
159: (IType) currentElement);
160: break;
161: }
162: }
163: break;
164: }
165: case IJavaElement.FIELD: {
166: switch (currentElement.getElementType()) {
167: case IJavaElement.TYPE: {
168: // anonymous type in field declaration
169: newElements[i] = resolveTypeInMember(
170: (IField) newParent, (IType) currentElement);
171: break;
172: }
173: }
174: break;
175: }
176: }
177: }
178: return newElements;
179: }
180:
181: private String[] resolveParameterTypes(IMethod method) {
182: final String[] oldParameterTypes = method.getParameterTypes();
183: final String[] newparams = new String[oldParameterTypes.length];
184:
185: final String[] possibleOldSigs = new String[4];
186: possibleOldSigs[0] = Signature.createTypeSignature(fOldType
187: .getElementName(), false);
188: possibleOldSigs[1] = Signature.createTypeSignature(fOldType
189: .getElementName(), true);
190: possibleOldSigs[2] = Signature.createTypeSignature(fOldType
191: .getFullyQualifiedName(), false);
192: possibleOldSigs[3] = Signature.createTypeSignature(fOldType
193: .getFullyQualifiedName(), true);
194:
195: final String[] possibleNewSigs = new String[4];
196: possibleNewSigs[0] = Signature.createTypeSignature(fNewType
197: .getElementName(), false);
198: possibleNewSigs[1] = Signature.createTypeSignature(fNewType
199: .getElementName(), true);
200: possibleNewSigs[2] = Signature.createTypeSignature(fNewType
201: .getFullyQualifiedName(), false);
202: possibleNewSigs[3] = Signature.createTypeSignature(fNewType
203: .getFullyQualifiedName(), true);
204:
205: // Textually replace all occurrences
206: // This handles stuff like Map<SomeClass, some.package.SomeClass>
207: for (int i = 0; i < oldParameterTypes.length; i++) {
208: newparams[i] = oldParameterTypes[i];
209: for (int j = 0; j < possibleOldSigs.length; j++) {
210: newparams[i] = replaceAll(newparams[i],
211: possibleOldSigs[j], possibleNewSigs[j]);
212: }
213: }
214: return newparams;
215: }
216:
217: private String resolveElementName(IJavaElement element) {
218: final String newName = (String) fRefactoredSimilarElements
219: .get(element);
220: if (newName != null)
221: return newName;
222: else
223: return element.getElementName();
224: }
225:
226: private IMember resolveTypeInMember(final IMember newParent,
227: IType oldChild) {
228: // Local type or anonymous type. Only local types can be renamed.
229: String newName = ""; //$NON-NLS-1$
230: if (oldChild.getElementName().length() != 0)
231: newName = resolveTypeName(oldChild);
232: return newParent
233: .getType(newName, oldChild.getOccurrenceCount());
234: }
235:
236: private String resolveTypeName(IType type) {
237: return type.equals(fOldType) ? fNewType.getElementName() : type
238: .getElementName();
239: }
240:
241: private static String replaceAll(final String source,
242: final String replaceFrom, final String replaceTo) {
243: final StringBuffer buf = new StringBuffer(source.length());
244: int currentIndex = 0;
245: int matchIndex;
246: while ((matchIndex = source.indexOf(replaceFrom, currentIndex)) != -1) {
247: buf.append(source.substring(currentIndex, matchIndex));
248: buf.append(replaceTo);
249: currentIndex = matchIndex + replaceFrom.length();
250: }
251: buf.append(source.substring(currentIndex));
252: return buf.toString();
253: }
254: }
|