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.ui.wizards;
011:
012: import org.eclipse.core.runtime.CoreException;
013: import org.eclipse.core.runtime.IAdaptable;
014: import org.eclipse.core.runtime.IPath;
015: import org.eclipse.core.runtime.IStatus;
016: import org.eclipse.core.runtime.Path;
017:
018: import org.eclipse.core.resources.IProject;
019: import org.eclipse.core.resources.IResource;
020: import org.eclipse.core.resources.IWorkspaceRoot;
021: import org.eclipse.core.resources.ResourcesPlugin;
022:
023: import org.eclipse.swt.widgets.Composite;
024:
025: import org.eclipse.jface.viewers.ILabelProvider;
026: import org.eclipse.jface.viewers.ISelection;
027: import org.eclipse.jface.viewers.ISelectionProvider;
028: import org.eclipse.jface.viewers.IStructuredSelection;
029: import org.eclipse.jface.viewers.Viewer;
030: import org.eclipse.jface.viewers.ViewerFilter;
031: import org.eclipse.jface.window.Window;
032:
033: import org.eclipse.jface.text.ITextSelection;
034:
035: import org.eclipse.ui.IEditorPart;
036: import org.eclipse.ui.IWorkbenchPart;
037: import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
038:
039: import org.eclipse.ui.views.contentoutline.ContentOutline;
040:
041: import org.eclipse.jdt.core.IJavaElement;
042: import org.eclipse.jdt.core.IJavaModel;
043: import org.eclipse.jdt.core.IJavaProject;
044: import org.eclipse.jdt.core.IPackageFragmentRoot;
045: import org.eclipse.jdt.core.JavaCore;
046: import org.eclipse.jdt.core.JavaModelException;
047:
048: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
049: import org.eclipse.jdt.internal.corext.util.Messages;
050:
051: import org.eclipse.jdt.ui.JavaElementComparator;
052: import org.eclipse.jdt.ui.JavaElementLabelProvider;
053: import org.eclipse.jdt.ui.StandardJavaElementContentProvider;
054:
055: import org.eclipse.jdt.internal.ui.JavaPlugin;
056: import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
057: import org.eclipse.jdt.internal.ui.viewsupport.IViewPartInputProvider;
058: import org.eclipse.jdt.internal.ui.wizards.NewWizardMessages;
059: import org.eclipse.jdt.internal.ui.wizards.TypedElementSelectionValidator;
060: import org.eclipse.jdt.internal.ui.wizards.TypedViewerFilter;
061: import org.eclipse.jdt.internal.ui.wizards.dialogfields.DialogField;
062: import org.eclipse.jdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
063: import org.eclipse.jdt.internal.ui.wizards.dialogfields.IStringButtonAdapter;
064: import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;
065: import org.eclipse.jdt.internal.ui.wizards.dialogfields.StringButtonDialogField;
066:
067: /**
068: * Wizard page that acts as a base class for wizard pages that create new Java elements.
069: * The class provides a input field for source folders (called container in this class) and
070: * API to validate the enter source folder name.
071: *
072: * <p>
073: * Clients may subclass.
074: * </p>
075: *
076: * @since 2.0
077: */
078: public abstract class NewContainerWizardPage extends
079: NewElementWizardPage {
080:
081: /** Id of the container field */
082: protected static final String CONTAINER = "NewContainerWizardPage.container"; //$NON-NLS-1$
083:
084: /** The status of the last validation. */
085: protected IStatus fContainerStatus;
086:
087: private StringButtonDialogField fContainerDialogField;
088:
089: /*
090: * package fragment root corresponding to the input type (can be null)
091: */
092: private IPackageFragmentRoot fCurrRoot;
093:
094: private IWorkspaceRoot fWorkspaceRoot;
095:
096: /**
097: * Create a new <code>NewContainerWizardPage</code>
098: *
099: * @param name the wizard page's name
100: */
101: public NewContainerWizardPage(String name) {
102: super (name);
103: fWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
104: ContainerFieldAdapter adapter = new ContainerFieldAdapter();
105:
106: fContainerDialogField = new StringButtonDialogField(adapter);
107: fContainerDialogField.setDialogFieldListener(adapter);
108: fContainerDialogField.setLabelText(getContainerLabel());
109: fContainerDialogField
110: .setButtonLabel(NewWizardMessages.NewContainerWizardPage_container_button);
111:
112: fContainerStatus = new StatusInfo();
113: fCurrRoot = null;
114: }
115:
116: /**
117: * Returns the label that is used for the container input field.
118: *
119: * @return the label that is used for the container input field.
120: * @since 3.2
121: */
122: protected String getContainerLabel() {
123: return NewWizardMessages.NewContainerWizardPage_container_label;
124: }
125:
126: /**
127: * Initializes the source folder field with a valid package fragment root.
128: * The package fragment root is computed from the given Java element.
129: *
130: * @param elem the Java element used to compute the initial package
131: * fragment root used as the source folder
132: */
133: protected void initContainerPage(IJavaElement elem) {
134: IPackageFragmentRoot initRoot = null;
135: if (elem != null) {
136: initRoot = JavaModelUtil.getPackageFragmentRoot(elem);
137: try {
138: if (initRoot == null
139: || initRoot.getKind() != IPackageFragmentRoot.K_SOURCE) {
140: IJavaProject jproject = elem.getJavaProject();
141: if (jproject != null) {
142: initRoot = null;
143: if (jproject.exists()) {
144: IPackageFragmentRoot[] roots = jproject
145: .getPackageFragmentRoots();
146: for (int i = 0; i < roots.length; i++) {
147: if (roots[i].getKind() == IPackageFragmentRoot.K_SOURCE) {
148: initRoot = roots[i];
149: break;
150: }
151: }
152: }
153: if (initRoot == null) {
154: initRoot = jproject
155: .getPackageFragmentRoot(jproject
156: .getResource());
157: }
158: }
159: }
160: } catch (JavaModelException e) {
161: JavaPlugin.log(e);
162: }
163: }
164: setPackageFragmentRoot(initRoot, true);
165: }
166:
167: /**
168: * Utility method to inspect a selection to find a Java element.
169: *
170: * @param selection the selection to be inspected
171: * @return a Java element to be used as the initial selection, or <code>null</code>,
172: * if no Java element exists in the given selection
173: */
174: protected IJavaElement getInitialJavaElement(
175: IStructuredSelection selection) {
176: IJavaElement jelem = null;
177: if (selection != null && !selection.isEmpty()) {
178: Object selectedElement = selection.getFirstElement();
179: if (selectedElement instanceof IAdaptable) {
180: IAdaptable adaptable = (IAdaptable) selectedElement;
181:
182: jelem = (IJavaElement) adaptable
183: .getAdapter(IJavaElement.class);
184: if (jelem == null) {
185: IResource resource = (IResource) adaptable
186: .getAdapter(IResource.class);
187: if (resource != null
188: && resource.getType() != IResource.ROOT) {
189: while (jelem == null
190: && resource.getType() != IResource.PROJECT) {
191: resource = resource.getParent();
192: jelem = (IJavaElement) resource
193: .getAdapter(IJavaElement.class);
194: }
195: if (jelem == null) {
196: jelem = JavaCore.create(resource); // java project
197: }
198: }
199: }
200: }
201: }
202: if (jelem == null) {
203: IWorkbenchPart part = JavaPlugin.getActivePage()
204: .getActivePart();
205: if (part instanceof ContentOutline) {
206: part = JavaPlugin.getActivePage().getActiveEditor();
207: }
208:
209: if (part instanceof IViewPartInputProvider) {
210: Object elem = ((IViewPartInputProvider) part)
211: .getViewPartInput();
212: if (elem instanceof IJavaElement) {
213: jelem = (IJavaElement) elem;
214: }
215: }
216: }
217:
218: if (jelem == null
219: || jelem.getElementType() == IJavaElement.JAVA_MODEL) {
220: try {
221: IJavaProject[] projects = JavaCore.create(
222: getWorkspaceRoot()).getJavaProjects();
223: if (projects.length == 1) {
224: jelem = projects[0];
225: }
226: } catch (JavaModelException e) {
227: JavaPlugin.log(e);
228: }
229: }
230: return jelem;
231: }
232:
233: /**
234: * Returns the text selection of the current editor. <code>null</code> is returned
235: * when the current editor does not have focus or does not return a text selection.
236: * @return Returns the text selection of the current editor or <code>null</code>.
237: *
238: * @since 3.0
239: */
240: protected ITextSelection getCurrentTextSelection() {
241: IWorkbenchPart part = JavaPlugin.getActivePage()
242: .getActivePart();
243: if (part instanceof IEditorPart) {
244: ISelectionProvider selectionProvider = part.getSite()
245: .getSelectionProvider();
246: if (selectionProvider != null) {
247: ISelection selection = selectionProvider.getSelection();
248: if (selection instanceof ITextSelection) {
249: return (ITextSelection) selection;
250: }
251: }
252: }
253: return null;
254: }
255:
256: /**
257: * Returns the recommended maximum width for text fields (in pixels). This
258: * method requires that createContent has been called before this method is
259: * call. Subclasses may override to change the maximum width for text
260: * fields.
261: *
262: * @return the recommended maximum width for text fields.
263: */
264: protected int getMaxFieldWidth() {
265: return convertWidthInCharsToPixels(40);
266: }
267:
268: /**
269: * Creates the necessary controls (label, text field and browse button) to edit
270: * the source folder location. The method expects that the parent composite
271: * uses a <code>GridLayout</code> as its layout manager and that the
272: * grid layout has at least 3 columns.
273: *
274: * @param parent the parent composite
275: * @param nColumns the number of columns to span. This number must be
276: * greater or equal three
277: */
278: protected void createContainerControls(Composite parent,
279: int nColumns) {
280: fContainerDialogField.doFillIntoGrid(parent, nColumns);
281: LayoutUtil.setWidthHint(fContainerDialogField
282: .getTextControl(null), getMaxFieldWidth());
283: }
284:
285: /**
286: * Sets the focus to the source folder's text field.
287: */
288: protected void setFocusOnContainer() {
289: fContainerDialogField.setFocus();
290: }
291:
292: // -------- ContainerFieldAdapter --------
293:
294: private class ContainerFieldAdapter implements
295: IStringButtonAdapter, IDialogFieldListener {
296:
297: // -------- IStringButtonAdapter
298: public void changeControlPressed(DialogField field) {
299: containerChangeControlPressed(field);
300: }
301:
302: // -------- IDialogFieldListener
303: public void dialogFieldChanged(DialogField field) {
304: containerDialogFieldChanged(field);
305: }
306: }
307:
308: private void containerChangeControlPressed(DialogField field) {
309: // take the current jproject as init element of the dialog
310: IPackageFragmentRoot root = chooseContainer();
311: if (root != null) {
312: setPackageFragmentRoot(root, true);
313: }
314: }
315:
316: private void containerDialogFieldChanged(DialogField field) {
317: if (field == fContainerDialogField) {
318: fContainerStatus = containerChanged();
319: }
320: // tell all others
321: handleFieldChanged(CONTAINER);
322: }
323:
324: // ----------- validation ----------
325:
326: /**
327: * This method is a hook which gets called after the source folder's
328: * text input field has changed. This default implementation updates
329: * the model and returns an error status. The underlying model
330: * is only valid if the returned status is OK.
331: *
332: * @return the model's error status
333: */
334: protected IStatus containerChanged() {
335: StatusInfo status = new StatusInfo();
336:
337: fCurrRoot = null;
338: String str = getPackageFragmentRootText();
339: if (str.length() == 0) {
340: status
341: .setError(NewWizardMessages.NewContainerWizardPage_error_EnterContainerName);
342: return status;
343: }
344: IPath path = new Path(str);
345: IResource res = fWorkspaceRoot.findMember(path);
346: if (res != null) {
347: int resType = res.getType();
348: if (resType == IResource.PROJECT
349: || resType == IResource.FOLDER) {
350: IProject proj = res.getProject();
351: if (!proj.isOpen()) {
352: status
353: .setError(Messages
354: .format(
355: NewWizardMessages.NewContainerWizardPage_error_ProjectClosed,
356: proj.getFullPath()
357: .toString()));
358: return status;
359: }
360: IJavaProject jproject = JavaCore.create(proj);
361: fCurrRoot = jproject.getPackageFragmentRoot(res);
362: if (res.exists()) {
363: try {
364: if (!proj.hasNature(JavaCore.NATURE_ID)) {
365: if (resType == IResource.PROJECT) {
366: status
367: .setError(NewWizardMessages.NewContainerWizardPage_warning_NotAJavaProject);
368: } else {
369: status
370: .setWarning(NewWizardMessages.NewContainerWizardPage_warning_NotInAJavaProject);
371: }
372: return status;
373: }
374: if (fCurrRoot.isArchive()) {
375: status
376: .setError(Messages
377: .format(
378: NewWizardMessages.NewContainerWizardPage_error_ContainerIsBinary,
379: str));
380: return status;
381: }
382: if (fCurrRoot.getKind() == IPackageFragmentRoot.K_BINARY) {
383: status
384: .setWarning(Messages
385: .format(
386: NewWizardMessages.NewContainerWizardPage_warning_inside_classfolder,
387: str));
388: } else if (!jproject.isOnClasspath(fCurrRoot)) {
389: status
390: .setWarning(Messages
391: .format(
392: NewWizardMessages.NewContainerWizardPage_warning_NotOnClassPath,
393: str));
394: }
395: } catch (CoreException e) {
396: status
397: .setWarning(NewWizardMessages.NewContainerWizardPage_warning_NotAJavaProject);
398: }
399: }
400: return status;
401: } else {
402: status
403: .setError(Messages
404: .format(
405: NewWizardMessages.NewContainerWizardPage_error_NotAFolder,
406: str));
407: return status;
408: }
409: } else {
410: status
411: .setError(Messages
412: .format(
413: NewWizardMessages.NewContainerWizardPage_error_ContainerDoesNotExist,
414: str));
415: return status;
416: }
417: }
418:
419: // -------- update message ----------------
420:
421: /**
422: * Hook method that gets called when a field on this page has changed. For this page the
423: * method gets called when the source folder field changes.
424: * <p>
425: * Every sub type is responsible to call this method when a field on its page has changed.
426: * Subtypes override (extend) the method to add verification when a own field has a
427: * dependency to an other field. For example the class name input must be verified
428: * again when the package field changes (check for duplicated class names).
429: *
430: * @param fieldName The name of the field that has changed (field id). For the
431: * source folder the field id is <code>CONTAINER</code>
432: */
433: protected void handleFieldChanged(String fieldName) {
434: }
435:
436: // ---- get ----------------
437:
438: /**
439: * Returns the workspace root.
440: *
441: * @return the workspace root
442: */
443: protected IWorkspaceRoot getWorkspaceRoot() {
444: return fWorkspaceRoot;
445: }
446:
447: /**
448: * Returns the Java project of the currently selected package fragment root or <code>null</code>
449: * if no package fragment root is configured.
450: *
451: * @return The current Java project or <code>null</code>.
452: * @since 3.3
453: */
454: public IJavaProject getJavaProject() {
455: IPackageFragmentRoot root = getPackageFragmentRoot();
456: if (root != null) {
457: return root.getJavaProject();
458: }
459: return null;
460: }
461:
462: /**
463: * Returns the <code>IPackageFragmentRoot</code> that corresponds to the current
464: * value of the source folder field.
465: *
466: * @return the IPackageFragmentRoot or <code>null</code> if the current source
467: * folder value is not a valid package fragment root
468: *
469: */
470: public IPackageFragmentRoot getPackageFragmentRoot() {
471: return fCurrRoot;
472: }
473:
474: /**
475: * Returns the current text of source folder text field.
476: *
477: * @return the text of the source folder text field
478: */
479: public String getPackageFragmentRootText() {
480: return fContainerDialogField.getText();
481: }
482:
483: /**
484: * Sets the current source folder (model and text field) to the given package
485: * fragment root.
486:
487: * @param root The new root.
488: * @param canBeModified if <code>false</code> the source folder field can
489: * not be changed by the user. If <code>true</code> the field is editable
490: */
491: public void setPackageFragmentRoot(IPackageFragmentRoot root,
492: boolean canBeModified) {
493: fCurrRoot = root;
494: String str = (root == null) ? "" : root.getPath().makeRelative().toString(); //$NON-NLS-1$
495: fContainerDialogField.setText(str);
496: fContainerDialogField.setEnabled(canBeModified);
497: }
498:
499: // ------------- choose source container dialog
500:
501: /**
502: * Opens a selection dialog that allows to select a source container.
503: *
504: * @return returns the selected package fragment root or <code>null</code> if the dialog has been canceled.
505: * The caller typically sets the result to the container input field.
506: * <p>
507: * Clients can override this method if they want to offer a different dialog.
508: * </p>
509: *
510: * @since 3.2
511: */
512: protected IPackageFragmentRoot chooseContainer() {
513: IJavaElement initElement = getPackageFragmentRoot();
514: Class[] acceptedClasses = new Class[] {
515: IPackageFragmentRoot.class, IJavaProject.class };
516: TypedElementSelectionValidator validator = new TypedElementSelectionValidator(
517: acceptedClasses, false) {
518: public boolean isSelectedValid(Object element) {
519: try {
520: if (element instanceof IJavaProject) {
521: IJavaProject jproject = (IJavaProject) element;
522: IPath path = jproject.getProject()
523: .getFullPath();
524: return (jproject.findPackageFragmentRoot(path) != null);
525: } else if (element instanceof IPackageFragmentRoot) {
526: return (((IPackageFragmentRoot) element)
527: .getKind() == IPackageFragmentRoot.K_SOURCE);
528: }
529: return true;
530: } catch (JavaModelException e) {
531: JavaPlugin.log(e.getStatus()); // just log, no UI in validation
532: }
533: return false;
534: }
535: };
536:
537: acceptedClasses = new Class[] { IJavaModel.class,
538: IPackageFragmentRoot.class, IJavaProject.class };
539: ViewerFilter filter = new TypedViewerFilter(acceptedClasses) {
540: public boolean select(Viewer viewer, Object parent,
541: Object element) {
542: if (element instanceof IPackageFragmentRoot) {
543: try {
544: return (((IPackageFragmentRoot) element)
545: .getKind() == IPackageFragmentRoot.K_SOURCE);
546: } catch (JavaModelException e) {
547: JavaPlugin.log(e.getStatus()); // just log, no UI in validation
548: return false;
549: }
550: }
551: return super .select(viewer, parent, element);
552: }
553: };
554:
555: StandardJavaElementContentProvider provider = new StandardJavaElementContentProvider();
556: ILabelProvider labelProvider = new JavaElementLabelProvider(
557: JavaElementLabelProvider.SHOW_DEFAULT);
558: ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(
559: getShell(), labelProvider, provider);
560: dialog.setValidator(validator);
561: dialog.setComparator(new JavaElementComparator());
562: dialog
563: .setTitle(NewWizardMessages.NewContainerWizardPage_ChooseSourceContainerDialog_title);
564: dialog
565: .setMessage(NewWizardMessages.NewContainerWizardPage_ChooseSourceContainerDialog_description);
566: dialog.addFilter(filter);
567: dialog.setInput(JavaCore.create(fWorkspaceRoot));
568: dialog.setInitialSelection(initElement);
569: dialog.setHelpAvailable(false);
570:
571: if (dialog.open() == Window.OK) {
572: Object element = dialog.getFirstResult();
573: if (element instanceof IJavaProject) {
574: IJavaProject jproject = (IJavaProject) element;
575: return jproject.getPackageFragmentRoot(jproject
576: .getProject());
577: } else if (element instanceof IPackageFragmentRoot) {
578: return (IPackageFragmentRoot) element;
579: }
580: return null;
581: }
582: return null;
583: }
584:
585: }
|