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.ui.refactoring;
011:
012: import java.lang.reflect.InvocationTargetException;
013: import java.util.Collection;
014: import java.util.HashMap;
015: import java.util.HashSet;
016: import java.util.Iterator;
017:
018: import org.eclipse.core.runtime.IProgressMonitor;
019: import org.eclipse.core.runtime.SubProgressMonitor;
020:
021: import org.eclipse.swt.SWT;
022: import org.eclipse.swt.graphics.Color;
023: import org.eclipse.swt.graphics.GC;
024: import org.eclipse.swt.graphics.Image;
025: import org.eclipse.swt.layout.GridData;
026: import org.eclipse.swt.layout.GridLayout;
027: import org.eclipse.swt.widgets.Composite;
028: import org.eclipse.swt.widgets.Display;
029: import org.eclipse.swt.widgets.Label;
030: import org.eclipse.swt.widgets.TreeItem;
031:
032: import org.eclipse.jface.dialogs.Dialog;
033: import org.eclipse.jface.operation.IRunnableWithProgress;
034: import org.eclipse.jface.viewers.IColorProvider;
035: import org.eclipse.jface.viewers.ISelectionChangedListener;
036: import org.eclipse.jface.viewers.IStructuredSelection;
037: import org.eclipse.jface.viewers.LabelProviderChangedEvent;
038: import org.eclipse.jface.viewers.SelectionChangedEvent;
039: import org.eclipse.jface.viewers.TreeViewer;
040: import org.eclipse.jface.wizard.IWizardPage;
041:
042: import org.eclipse.ui.PlatformUI;
043:
044: import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
045: import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
046:
047: import org.eclipse.jdt.core.dom.ITypeBinding;
048:
049: import org.eclipse.jdt.internal.corext.refactoring.structure.ChangeTypeRefactoring;
050: import org.eclipse.jdt.internal.corext.util.Messages;
051:
052: import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
053: import org.eclipse.jdt.internal.ui.JavaPlugin;
054: import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
055:
056: /**
057: * @author tip
058: */
059: public class ChangeTypeWizard extends RefactoringWizard {
060:
061: private ChangeTypeRefactoring fCT;
062:
063: public ChangeTypeWizard(ChangeTypeRefactoring ref) {
064: super (ref, DIALOG_BASED_USER_INTERFACE);
065: setDefaultPageTitle(RefactoringMessages.ChangeTypeWizard_title);
066: fCT = ref;
067: }
068:
069: /* non java-doc
070: * @see RefactoringWizard#addUserInputPages
071: */
072: protected void addUserInputPages() {
073: addPage(new ChangeTypeInputPage());
074: }
075:
076: // For debugging
077: static String print(Collection/*<ITypeBinding>*/types) {
078: if (types.isEmpty())
079: return "{ }"; //$NON-NLS-1$
080: String result = "{ "; //$NON-NLS-1$
081: for (Iterator it = types.iterator(); it.hasNext();) {
082: ITypeBinding type = (ITypeBinding) it.next();
083: result += type.getQualifiedName();
084: if (it.hasNext()) {
085: result += ", "; //$NON-NLS-1$
086: } else {
087: result += " }"; //$NON-NLS-1$
088: }
089: }
090: return result;
091: }
092:
093: /**
094: * A JavaElementLabelProvider that supports graying out of invalid types.
095: */
096: private class ChangeTypeLabelProvider extends BindingLabelProvider
097: implements IColorProvider {
098:
099: private Color fGrayColor;
100: private HashMap/*<Image color, Image gray>*/fGrayImages;
101:
102: public ChangeTypeLabelProvider() {
103: fGrayColor = Display.getCurrent().getSystemColor(
104: SWT.COLOR_WIDGET_NORMAL_SHADOW);
105: fGrayImages = new HashMap();
106: }
107:
108: private Collection/*<ITypeBinding>*/fInvalidTypes;
109:
110: public void grayOut(Collection/*<ITypeBinding>*/invalidTypes) {
111: fInvalidTypes = invalidTypes;
112: /*
113: * Invalidate all labels. Invalidating only invalid types doesn't
114: * work since there can be multiple nodes in the tree that
115: * correspond to the same invalid IType. The TreeViewer only updates
116: * the label of one of these ITypes and leaves the others in their
117: * old state.
118: */
119: fireLabelProviderChanged(new LabelProviderChangedEvent(this ));
120: }
121:
122: /* (non-Javadoc)
123: * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object)
124: */
125: public Color getForeground(Object element) {
126: if (isInvalid(element))
127: return fGrayColor;
128: else
129: return null;
130: }
131:
132: private boolean isInvalid(Object element) {
133: if (fInvalidTypes == null)
134: return false; // initially, everything is enabled
135: else
136: return fInvalidTypes.contains(element);
137: }
138:
139: /* (non-Javadoc)
140: * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object)
141: */
142: public Color getBackground(Object element) {
143: return null;
144: }
145:
146: /*
147: * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
148: */
149: public Image getImage(Object element) {
150: Image image = super .getImage(element);
151: if (isInvalid(element) && image != null) {
152: Image grayImage = (Image) fGrayImages.get(image);
153: if (grayImage == null) {
154: grayImage = new Image(Display.getCurrent(), image,
155: SWT.IMAGE_GRAY);
156: fGrayImages.put(image, grayImage);
157: }
158: return grayImage;
159: } else {
160: return image;
161: }
162: }
163:
164: public void dispose() {
165: for (Iterator iter = fGrayImages.values().iterator(); iter
166: .hasNext();) {
167: Image image = (Image) iter.next();
168: image.dispose();
169: }
170: fGrayImages.clear();
171: super .dispose();
172: }
173: }
174:
175: private class ChangeTypeInputPage extends UserInputWizardPage {
176:
177: public static final String PAGE_NAME = "ChangeTypeInputPage";//$NON-NLS-1$
178: private final String MESSAGE = RefactoringMessages.ChangeTypeInputPage_Select_Type;
179: private ChangeTypeLabelProvider fLabelProvider;
180: private TreeViewer fTreeViewer;
181: private boolean fTreeUpdated = false;
182:
183: public ChangeTypeInputPage() {
184: super (PAGE_NAME);
185: setMessage(MESSAGE);
186: }
187:
188: private class ValidTypesTask implements Runnable {
189: private Collection/*<ITypeBinding>*/fInvalidTypes;
190: private Collection/*<ITypeBinding>*/fValidTypes;
191:
192: public void run() {
193: IRunnableWithProgress runnable = new IRunnableWithProgress() {
194: public void run(IProgressMonitor pm) {
195: pm
196: .beginTask(
197: RefactoringMessages.ChangeTypeWizard_analyzing,
198: 1000);
199: ChangeTypeRefactoring ct = (ChangeTypeRefactoring) ChangeTypeWizard.this
200: .getRefactoring();
201: fInvalidTypes = new HashSet();
202: fInvalidTypes.addAll(fCT.getAllSuperTypes(ct
203: .getOriginalType()));
204: fValidTypes = ct
205: .computeValidTypes(new SubProgressMonitor(
206: pm, 950));
207: fInvalidTypes.add(ct.getOriginalType());
208: fInvalidTypes.removeAll(fValidTypes);
209: pm.worked(50);
210: pm.done();
211: }
212: };
213: boolean internalError = false;
214: try {
215: getWizard().getContainer()
216: .run(true, true, runnable);
217: } catch (InvocationTargetException e) {
218: internalError = true;
219: JavaPlugin.log(e);
220: ChangeTypeInputPage.this
221: .setErrorMessage(RefactoringMessages.ChangeTypeWizard_internalError);
222: } catch (InterruptedException e) {
223: ChangeTypeInputPage.this
224: .setMessage(RefactoringMessages.ChangeTypeWizard_computationInterrupted);
225: }
226:
227: fLabelProvider.grayOut(fInvalidTypes);
228:
229: if (internalError) {
230: setPageComplete(false);
231: } else if (fValidTypes == null
232: || fValidTypes.size() == 0) {
233: ChangeTypeInputPage.this
234: .setErrorMessage(RefactoringMessages.ChangeTypeWizard_declCannotBeChanged);
235: setPageComplete(false);
236: } else {
237: TreeItem selection = getInitialSelection(fValidTypes);
238: fTreeViewer.getTree().setSelection(
239: new TreeItem[] { selection });
240: setPageComplete(true);
241: ChangeTypeInputPage.this .setMessage(""); //$NON-NLS-1$
242: }
243: }
244: }
245:
246: private TreeItem getInitialSelection(
247: Collection/*<ITypeBinding>*/types) {
248:
249: // first, find a most general valid type (there may be more than one)
250: ITypeBinding type = (ITypeBinding) types.iterator().next();
251: for (Iterator it = types.iterator(); it.hasNext();) {
252: ITypeBinding other = (ITypeBinding) it.next();
253: if (getGeneralizeTypeRefactoring().isSubTypeOf(type,
254: other)) {
255: type = other;
256: }
257: }
258:
259: // now find a corresponding TreeItem (there may be more than one)
260: return findItem(fTreeViewer.getTree().getItems(), type);
261: }
262:
263: private TreeItem findItem(TreeItem[] items, ITypeBinding type) {
264: for (int i = 0; i < items.length; i++) {
265: if (items[i].getData().equals(type))
266: return items[i];
267: }
268: for (int i = 0; i < items.length; i++) {
269: TreeItem item = findItem(items[i].getItems(), type);
270: if (item != null)
271: return item;
272: }
273: return null;
274: }
275:
276: public void createControl(Composite parent) {
277: Composite composite = new Composite(parent, SWT.NONE);
278: setControl(composite);
279: composite.setLayout(new GridLayout());
280: composite.setLayoutData(new GridData());
281:
282: Label label = new Label(composite, SWT.NONE);
283: label
284: .setText(Messages
285: .format(
286: RefactoringMessages.ChangeTypeWizard_pleaseChooseType,
287: ((ChangeTypeRefactoring) getRefactoring())
288: .getTarget()));
289: label.setLayoutData(new GridData());
290:
291: addTreeComponent(composite);
292: Dialog.applyDialogFont(composite);
293: PlatformUI.getWorkbench().getHelpSystem().setHelp(
294: getControl(),
295: IJavaHelpContextIds.CHANGE_TYPE_WIZARD_PAGE);
296: }
297:
298: /**
299: * Tree-viewer that shows the allowable types in a tree view.
300: */
301: private void addTreeComponent(Composite parent) {
302: fTreeViewer = new TreeViewer(parent, SWT.SINGLE
303: | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
304: GridData gd = new GridData(GridData.FILL_BOTH);
305: gd.grabExcessHorizontalSpace = true;
306: gd.grabExcessVerticalSpace = true;
307: GC gc = null;
308: try {
309: gc = new GC(parent);
310: gc.setFont(gc.getFont());
311: gd.heightHint = Dialog.convertHeightInCharsToPixels(gc
312: .getFontMetrics(), 6); // 6 characters tall
313: } finally {
314: if (gc != null) {
315: gc.dispose();
316: gc = null;
317: }
318: }
319: fTreeViewer.getTree().setLayoutData(gd);
320:
321: fTreeViewer
322: .setContentProvider(new ChangeTypeContentProvider(
323: ((ChangeTypeRefactoring) getRefactoring())));
324: fLabelProvider = new ChangeTypeLabelProvider();
325: fTreeViewer.setLabelProvider(fLabelProvider);
326: ISelectionChangedListener listener = new ISelectionChangedListener() {
327: public void selectionChanged(SelectionChangedEvent event) {
328: IStructuredSelection selection = (IStructuredSelection) event
329: .getSelection();
330: typeSelected((ITypeBinding) selection
331: .getFirstElement());
332: }
333: };
334: fTreeViewer.addSelectionChangedListener(listener);
335: fTreeViewer
336: .setInput(new ChangeTypeContentProvider.RootType(
337: getGeneralizeTypeRefactoring()
338: .getOriginalType()));
339: fTreeViewer.expandToLevel(10);
340: }
341:
342: private void typeSelected(ITypeBinding type) {
343: boolean isValid = getGeneralizeTypeRefactoring()
344: .getValidTypes().contains(type);
345: ChangeTypeInputPage.this .setPageComplete(isValid);
346: if (isValid) {
347: ChangeTypeInputPage.this .setMessage(""); //$NON-NLS-1$
348: } else {
349: if (getGeneralizeTypeRefactoring().getOriginalType()
350: .equals(type)) {
351: ChangeTypeInputPage.this
352: .setMessage(Messages
353: .format(
354: RefactoringMessages.ChangeTypeWizard_with_itself,
355: type.getName()));
356:
357: } else {
358: ChangeTypeInputPage.this
359: .setMessage(Messages
360: .format(
361: RefactoringMessages.ChangeTypeWizard_grayed_types,
362: new Object[] {
363: type.getName(),
364: getGeneralizeTypeRefactoring()
365: .getOriginalType()
366: .getName() }));
367: }
368: }
369: }
370:
371: private ChangeTypeRefactoring getGeneralizeTypeRefactoring() {
372: return (ChangeTypeRefactoring) getRefactoring();
373: }
374:
375: /*
376: * @see org.eclipse.jface.wizard.IWizardPage#getNextPage()
377: */
378: public IWizardPage getNextPage() {
379: initializeRefactoring();
380: return super .getNextPage();
381: }
382:
383: private ITypeBinding getSelectedType() {
384: IStructuredSelection ss = (IStructuredSelection) fTreeViewer
385: .getSelection();
386: return (ITypeBinding) ss.getFirstElement();
387: }
388:
389: /*
390: * @see org.eclipse.jdt.internal.ui.refactoring.RefactoringWizardPage#performFinish()
391: */
392: public boolean performFinish() {
393: initializeRefactoring();
394: return super .performFinish();
395: }
396:
397: private void initializeRefactoring() {
398: getGeneralizeTypeRefactoring().setSelectedType(
399: getSelectedType());
400: }
401:
402: /*
403: * @see org.eclipse.jface.dialogs.IDialogPage#dispose()
404: */
405: public void dispose() {
406: fTreeViewer = null;
407: super .dispose();
408: }
409:
410: /* (non-Javadoc)
411: * @see org.eclipse.jface.dialogs.IDialogPage#setVisible(boolean)
412: */
413: public void setVisible(boolean visible) {
414: super .setVisible(visible);
415: if (visible && fTreeViewer != null)
416: fTreeViewer.getTree().setFocus();
417: if (!fTreeUpdated) {
418: fTreeViewer.getTree().getDisplay().asyncExec(
419: new ValidTypesTask());
420: fTreeUpdated = true;
421: }
422: }
423: }
424: }
|