/******************************************************************************* * Copyright (c) 2010 Oracle. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0, which accompanies this distribution * and is available at http://www.eclipse.org/legal/epl-v10.html. * * Contributors: * Oracle - initial API and implementation *******************************************************************************/ package org.eclipse.jpt.jaxb.ui.internal.wizards.schemagen; import java.util.Iterator; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.ui.JavaElementComparator; import org.eclipse.jdt.ui.JavaElementLabelProvider; import org.eclipse.jdt.ui.ProblemsLabelDecorator; import org.eclipse.jdt.ui.StandardJavaElementContentProvider; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jpt.jaxb.core.internal.SchemaGenerator; import org.eclipse.jpt.jaxb.ui.internal.JptJaxbUiMessages; import org.eclipse.jpt.jaxb.ui.internal.filters.ContainerFilter; import org.eclipse.jpt.jaxb.ui.internal.filters.EmptyInnerPackageFilter; import org.eclipse.jpt.jaxb.ui.internal.filters.NonArchiveOrExternalElementFilter; import org.eclipse.jpt.jaxb.ui.internal.filters.NonContainerFilter; import org.eclipse.jpt.jaxb.ui.internal.filters.NonJavaElementFilter; import org.eclipse.jpt.utility.internal.ArrayTools; import org.eclipse.jpt.utility.internal.StringTools; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.TreeItem; import org.osgi.framework.Bundle; public class SchemaGeneratorWizardPage extends AbstractJarDestinationWizardPage { private IStructuredSelection initialSelection; private IJavaProject targetProject; // widgets private SettingsGroup settingsGroup; private Button usesMoxyCheckBox; private boolean usesMoxy; static public String JPT_ECLIPSELINK_UI_PLUGIN_ID = "org.eclipse.jpt.eclipselink.ui"; //$NON-NLS-1$ // other constants private static final int SIZING_SELECTION_WIDGET_WIDTH = 480; private static final int SIZING_SELECTION_WIDGET_HEIGHT = 150; // ********** constructor ********** public SchemaGeneratorWizardPage(IStructuredSelection selection) { super("JAXB Schema Generator", selection, null); //$NON-NLS-1$ this.initialSelection = selection; this.setUsesMoxy(false); this.setTitle(JptJaxbUiMessages.SchemaGeneratorWizardPage_title); this.setDescription(JptJaxbUiMessages.SchemaGeneratorWizardPage_desc); } // ********** IDialogPage implementation ********** @Override public void createControl(Composite parent) { this.setPageComplete(false); this.setControl(this.buildTopLevelControl(parent)); } @Override public void setVisible(boolean visible) { super.setVisible(visible); this.updateTargetProject(); this.setDefaultSchemaFile(); this.updateInputGroupTreeFilter(); // default usesMoxy to true only when JPT EclipseLink bundle exists and MOXy is on the classpath this.updateUsesMoxy(this.jptEclipseLinkBundleExists() && this.moxyIsOnClasspath()); // checkbox visible only if jpt.eclipselink.ui plugin is available // and EclipseLink MOXy is not on the classpath this.usesMoxyCheckBox.setVisible(this.jptEclipseLinkBundleExists() && ! this.moxyIsOnClasspath()); this.validateProjectClasspath(); this.giveFocusToDestination(); } // default the schema name to the project name protected void setDefaultSchemaFile() { String defaultSchemaName = this.targetProject.getProject().getName() + SchemaGeneratorWizard.XSD_EXTENSION; this.settingsGroup.schemaFileText.setText(defaultSchemaName); this.settingsGroup.schemaFileText.setSelection(0, defaultSchemaName.length()); } // ********** IWizardPage implementation ********** @Override public boolean isPageComplete() { boolean complete = this.validateDestinationGroup(); if(complete) { complete = this.validateSourceGroup(); if(complete) { this.validateProjectClasspath(); } } return complete; } @Override public void setPreviousPage(IWizardPage page) { super.setPreviousPage(page); if(this.getControl() != null) this.updatePageCompletion(); } // ********** intra-wizard methods ********** protected IJavaProject getJavaProject() { return this.targetProject; } /** * @return The schema relative path to the project. */ protected String getSchemaPath() { return this.settingsGroup.getSchemaPath(); } protected Object[] getAllCheckedItems() { return ArrayTools.array(this.getInputGroup().getAllCheckedListItems()); } protected boolean usesMoxy() { return this.usesMoxy; } // ********** validation ********** @Override @SuppressWarnings("restriction") protected void updatePageCompletion() { super.updatePageCompletion(); } @Override protected boolean validateDestinationGroup() { boolean complete = this.targetSchemaIsEmpty(); if( ! complete) { this.setErrorMessage(JptJaxbUiMessages.SchemaGeneratorWizardPage_errorNoSchema); return false; } this.setErrorMessage(null); return true; } @Override protected boolean validateSourceGroup() { if(this.getAllCheckedItems().length == 0) { if(this.getErrorMessage() == null) { this.setErrorMessage(JptJaxbUiMessages.SchemaGeneratorWizardPage_errorNoPackage); } return false; } this.setErrorMessage(null); return true; } private void validateProjectClasspath() { //this line will suppress the "default package" warning (which doesn't really apply here //as the JAXB gen uses an org.example.schemaName package by default) and will clear the classpath warnings when necessary setMessage(null); if( ! this.genericJaxbIsOnClasspath()) { this.displayWarning(JptJaxbUiMessages.ClassesGeneratorWizardPage_jaxbLibrariesNotAvailable); } else if(this.usesMoxy() && ! this.moxyIsOnClasspath()) { //this message is being truncated by the wizard width in some cases this.displayWarning(JptJaxbUiMessages.ClassesGeneratorWizardPage_moxyLibrariesNotAvailable); } //this code will intelligently remove our classpath warnings when they are present but no longer apply (as an alternative //to setting the message to null continuously as is currently done) // else if( this.getMessage() != null){ // if (this.getMessage().equals(JptJaxbUiMessages.ClassesGeneratorWizardPage_jaxbLibrariesNotAvailable) || // this.getMessage().equals(JptJaxbUiMessages.ClassesGeneratorWizardPage_moxyLibrariesNotAvailable)) { // setMessage(null); // } // } } /** * Test if the Jaxb compiler is on the classpath. */ private boolean genericJaxbIsOnClasspath() { try { String className = SchemaGenerator.JAXB_GENERIC_SCHEMA_GEN_CLASS; IType genClass = this.targetProject.findType(className); return (genClass != null); } catch(JavaModelException e) { throw new RuntimeException(e); } } // ********** internal methods ********** private Control buildTopLevelControl(Composite parent) { Composite composite = new Composite(parent, SWT.NULL); composite.setLayout(new GridLayout()); this.settingsGroup = new SettingsGroup(composite); this.usesMoxyCheckBox = this.buildUsesMoxyCheckBox(composite); Dialog.applyDialogFont(parent); return composite; } private void updateTargetProject() { IWizardPage previousPage = this.getPreviousPage(); if(previousPage instanceof ProjectWizardPage) { // get project from previousPage this.targetProject = ((ProjectWizardPage)previousPage).getProject(); } else if(initialSelection != null && ! this.initialSelection.isEmpty()) { // no previousPage - get project from initialSelection this.targetProject = this.getProjectFromInitialSelection(); } } private void updateInputGroupTreeFilter() { this.getInputGroup().addTreeFilter(new NonContainerFilter(this.targetProject.getProject().getName())); } private IJavaProject getProjectFromInitialSelection() { IJavaProject project = null; Object firstElement = initialSelection.getFirstElement(); if(firstElement instanceof IJavaElement) { IJavaElement javaElement = (IJavaElement)firstElement; int type = javaElement.getElementType(); if(type == IJavaElement.JAVA_PROJECT) { project = (IJavaProject)javaElement; } else if(type == IJavaElement.PACKAGE_FRAGMENT) { project = ((IPackageFragment)javaElement).getJavaProject(); } } return project; } private boolean targetSchemaIsEmpty() { return ! StringTools.stringIsEmpty(this.getSchemaPath()); } private boolean jptEclipseLinkBundleExists() { return (this.getJptEclipseLinkBundle() != null); } private Bundle getJptEclipseLinkBundle() { return Platform.getBundle(JPT_ECLIPSELINK_UI_PLUGIN_ID); // Cannot reference directly EL plugin. } private void setUsesMoxy(boolean usesMoxy){ this.usesMoxy = usesMoxy; } private void updateUsesMoxy(boolean usesMoxy){ this.setUsesMoxy(usesMoxy); this.usesMoxyCheckBox.setSelection(this.usesMoxy()); this.validateProjectClasspath(); } /** * Test if the EclipseLink Jaxb compiler is on the classpath. */ private boolean moxyIsOnClasspath() { try { String className = SchemaGenerator.JAXB_ECLIPSELINK_SCHEMA_GEN_CLASS; IType genClass = this.targetProject.findType(className); return (genClass != null); } catch (JavaModelException e) { throw new RuntimeException(e); } } private void displayWarning(String message) { this.setMessage(message, WARNING); } private CheckboxTreeAndListGroup getInputGroup() { return this.settingsGroup.inputGroup; } // ********** overrides ********** /** * Returns an iterator over this page's collection of currently-specified * elements to be exported. This is the primary element selection facility * accessor for subclasses. * * @return an iterator over the collection of elements currently selected for export */ @Override protected Iterator getSelectedResourcesIterator() { return this.getInputGroup().getAllCheckedListItems(); } @Override protected void update() { this.updatePageCompletion(); } @Override public final void saveWidgetValues() { // do nothing } @Override protected void internalSaveWidgetValues() { // do nothing } @Override protected void restoreWidgetValues() { // do nothing } @Override protected void initializeJarPackage() { // do nothing } @Override protected void giveFocusToDestination() { this.settingsGroup.giveFocusToDestination(); } // ********** UI components ********** private Button buildUsesMoxyCheckBox(Composite parent) { Button checkBox = new Button(parent, SWT.CHECK); GridData gridData = new GridData(); gridData.verticalIndent = 10; checkBox.setLayoutData(gridData); checkBox.setText(JptJaxbUiMessages.ClassesGeneratorWizardPage_usesMoxyImplementation); checkBox.setSelection(this.usesMoxy()); checkBox.addSelectionListener(this.buildUsesMoxySelectionListener()); return checkBox; } private SelectionListener buildUsesMoxySelectionListener() { return new SelectionListener() { public void widgetDefaultSelected(SelectionEvent event) { this.widgetSelected(event); } public void widgetSelected(SelectionEvent event) { updateUsesMoxy(usesMoxyCheckBox.getSelection()); validateProjectClasspath(); } }; } // ********** SettingsGroup class ********** private class SettingsGroup { private CheckboxTreeAndListGroup inputGroup; private Text schemaFileText; // ********** constructor ********** private SettingsGroup(Composite parent) { super(); initializeDialogUnits(parent); Composite composite = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); buildSchemaComposite(composite); // Input Tree createPlainLabel(composite, JptJaxbUiMessages.SchemaGeneratorWizardPage_packages); this.inputGroup = this.createInputGroup(composite); if(initialSelection != null) BusyIndicator.showWhile(parent.getDisplay(), new Runnable() { public void run() { setupBasedOnInitialSelections(); } }); } protected void buildSchemaComposite(Composite parent) { Composite composite = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(3, false); // false = do not make columns equal width layout.marginWidth = 0; layout.marginTop = 0; layout.marginBottom = 10; composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); // Schema Location this.buildLabel(composite, JptJaxbUiMessages.SchemaGeneratorWizardPage_shemaLocation); this.schemaFileText = this.buildSchemaText(composite); this.buildBrowseButton(composite, 1); } // ********** intra-wizard methods ********** /** * @return The schema relative path to the project. */ protected String getSchemaPath() { return this.schemaFileText.getText(); } protected void giveFocusToDestination() { this.schemaFileText.setFocus(); } // ********** UI components ********** private Label buildLabel(Composite parent, String text) { Label label = new Label(parent, SWT.LEFT); label.setText(text); label.setLayoutData(new GridData()); return label; } private Text buildSchemaText(Composite parent) { Text text = new Text(parent, SWT.BORDER); GridData gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.grabExcessHorizontalSpace = true; text.setLayoutData(gridData); text.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { updatePageCompletion(); } }); return text; } private Button buildBrowseButton(Composite parent, int horizontalSpan) { Button browseButton = new Button(parent, SWT.PUSH); browseButton.setText(JptJaxbUiMessages.SchemaGeneratorWizardPage_browse); GridData gridData = new GridData(); gridData.horizontalAlignment = GridData.FILL; gridData.horizontalSpan = horizontalSpan; browseButton.setLayoutData(gridData); browseButton.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) {} public void widgetSelected(SelectionEvent e) { String fileName = promptFile(); if( ! StringTools.stringIsEmpty(fileName)) { schemaFileText.setText(makeRelativeToProjectPath(fileName)); } } }); return browseButton; } /** * The browse button was clicked, its action invokes this action which should * prompt the user to select a file and return it. */ private String promptFile() { FileDialog dialog = new FileDialog(getShell()); dialog.setText(JptJaxbUiMessages.SchemaGeneratorWizardPage_chooseSchemaDialogTitle); dialog.setFilterPath(this.getFilterPath()); dialog.setFilterExtensions(new String[] {"*.xsd"}); //$NON-NLS-1$ String filePath = dialog.open(); return (filePath != null) ? filePath : null; } /** * Creates the checkbox tree and list for selecting resources. * * @param parent the parent control */ protected CheckboxTreeAndListGroup createInputGroup(Composite parent) { CheckboxTreeAndListGroup checkboxTreeGroup; int labelFlags = JavaElementLabelProvider.SHOW_BASICS | JavaElementLabelProvider.SHOW_OVERLAY_ICONS | JavaElementLabelProvider.SHOW_SMALL_ICONS; ITreeContentProvider treeContentProvider= new StandardJavaElementContentProvider() { @Override public boolean hasChildren(Object element) { // prevent the + from being shown in front of packages return !(element instanceof IPackageFragment) && super.hasChildren(element); } }; final DecoratingLabelProvider provider = new DecoratingLabelProvider(new JavaElementLabelProvider(labelFlags), new ProblemsLabelDecorator(null)); checkboxTreeGroup = new CheckboxTreeAndListGroup( parent, JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()), treeContentProvider, provider, new StandardJavaElementContentProvider(), provider, SWT.NONE, SIZING_SELECTION_WIDGET_WIDTH, SIZING_SELECTION_WIDGET_HEIGHT); checkboxTreeGroup.addTreeFilter(new EmptyInnerPackageFilter()); checkboxTreeGroup.setTreeComparator(new JavaElementComparator()); checkboxTreeGroup.setListComparator(new JavaElementComparator()); checkboxTreeGroup.addTreeFilter(new NonJavaElementFilter()); checkboxTreeGroup.addTreeFilter(new NonArchiveOrExternalElementFilter()); checkboxTreeGroup.addListFilter(new ContainerFilter()); checkboxTreeGroup.addListFilter(new NonJavaElementFilter()); checkboxTreeGroup.getTree().addListener(SWT.MouseUp, SchemaGeneratorWizardPage.this); checkboxTreeGroup.getTable().addListener(SWT.MouseUp, SchemaGeneratorWizardPage.this); ICheckStateListener listener = new ICheckStateListener() { public void checkStateChanged(CheckStateChangedEvent event) { update(); } }; checkboxTreeGroup.addCheckStateListener(listener); return checkboxTreeGroup; } // ********** internal methods ********** private String makeRelativeToProjectPath(String filePath) { Path path = new Path(filePath); IPath relativePath = path.makeRelativeTo(targetProject.getProject().getLocation()); return relativePath.toOSString(); } /** * Returns the path that the dialog will use to filter the directories it shows to * the argument, which may be null. * If the string is null, then the operating system's default filter path will be used. *

* Note that the path string is platform dependent. For convenience, either * '/' or '\' can be used as a path separator. *

* * @return The filter path */ private String getFilterPath() { return targetProject.getProject().getLocation().toOSString(); } private void setupBasedOnInitialSelections() { Iterator iterator = initialSelection.iterator(); while(iterator.hasNext()) { Object selectedElement = iterator.next(); if(selectedElement instanceof IResource && !((IResource)selectedElement).isAccessible()) continue; if(selectedElement instanceof IJavaElement && !((IJavaElement)selectedElement).exists()) continue; if(selectedElement instanceof ICompilationUnit || selectedElement instanceof IClassFile || selectedElement instanceof IFile) this.inputGroup.initialCheckListItem(selectedElement); else { if(selectedElement instanceof IFolder) { // Convert resource to Java element if possible IJavaElement je = JavaCore.create((IResource)selectedElement); if(je != null && je.exists() && je.getJavaProject().isOnClasspath((IResource)selectedElement)) { selectedElement = je; je.toString(); } } this.inputGroup.initialCheckTreeItem(selectedElement); } } TreeItem[] items = this.inputGroup.getTree().getItems(); int i = 0; while(i < items.length && ! items[i].getChecked()) i++; if(i < items.length) { this.inputGroup.getTree().setSelection(new TreeItem[] {items[i]}); this.inputGroup.getTree().showSelection(); this.inputGroup.populateListViewer(items[i].getData()); } } } }