/******************************************************************************* * Copyright (c) 2006, 2010 Spring IDE Developers * 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: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.ui.properties; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.ui.dialogs.PropertyPage; import org.eclipse.ui.progress.UIJob; import org.springframework.ide.eclipse.beans.core.BeansCorePlugin; import org.springframework.ide.eclipse.beans.core.model.IBeansConfig; import org.springframework.ide.eclipse.beans.core.model.IBeansProject; import org.springframework.ide.eclipse.beans.core.model.locate.BeansConfigLocatorFactory; import org.springframework.ide.eclipse.beans.ui.BeansUIPlugin; import org.springframework.ide.eclipse.beans.ui.model.BeansModelLabelDecorator; import org.springframework.ide.eclipse.beans.ui.properties.model.PropertiesModel; import org.springframework.ide.eclipse.beans.ui.properties.model.PropertiesProject; import org.springframework.ide.eclipse.core.MarkerUtils; import org.springframework.ide.eclipse.core.SpringCore; import org.springframework.ide.eclipse.core.model.IModelElement; import org.springsource.ide.eclipse.commons.core.SpringCorePreferences; /** * Spring project property page. * @author Torsten Juergeleit * @author Christian Dupuis */ public class ProjectPropertyPage extends PropertyPage { public static final String ID = BeansUIPlugin.PLUGIN_ID + ".properties.ProjectPropertyPage"; public static final String BLOCK_ID = ID + ".blockId"; public static final String SCAN = ID + ".scan"; public static final String SELECTED_RESOURCE = ID + ".selectedResource"; private static final String PREFIX = "ConfigurationPropertyPage."; private static final String TITLE = PREFIX + "title"; private static final String CONFIG_FILES_LABEL = PREFIX + "tabConfigFiles.label"; private static final String CONFIG_SETS_LABEL = PREFIX + "tabConfigSets.label"; private static final String CONFIG_LOCATORS_LABEL = PREFIX + "tabConfigLocators.label"; private PropertiesModel model; private ConfigFilesTab configFilesTab; private ConfigSetsTab configSetsTab; private int selectedTab; private IModelElement selectedModelElement; private Map<String, Object> pageData; private ConfigLocatorTab configLocatorTab; private boolean shouldTriggerScan = false; private AtomicBoolean contentCreated = new AtomicBoolean(false); private static long INIT_TIMEOUT = 40000; private static long INIT_WAIT_PERIOD = 300; public ProjectPropertyPage() { this(null, 0); } public ProjectPropertyPage(IProject project) { this(project, 0); } public ProjectPropertyPage(IProject project, int selectedTab) { setElement(project); setTitle(BeansUIPlugin.getResourceString(TITLE)); noDefaultAndApplyButton(); this.selectedTab = selectedTab; } @Override protected Control createContents(Composite parent) { final Composite contents = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(contents); contents.setLayout(new GridLayout()); final CLabel statusLabel = new CLabel(contents, SWT.NONE); statusLabel.setText("Initializing Beans model, please wait..."); statusLabel.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_INFO)); GridDataFactory.fillDefaults().grab(true, false).exclude(false).applyTo(statusLabel); // Build folder with tabs final TabFolder folder = new TabFolder(contents, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).exclude(true).applyTo(folder); final Display display = parent.getDisplay(); /* * BeansModel may not be ready by the time preference page opened. Populate preference page contents asynchronously * See: https://issuetracker.springsource.com/browse/STS-4175 */ new Job("Wait for bean model initialization") { @Override protected IStatus run(IProgressMonitor arg0) { final IProject project = (IProject) getElement(); model = new PropertiesModel(); PropertiesProject mp = null; for (long waitTime = 0; waitTime < INIT_TIMEOUT && mp == null; waitTime += INIT_WAIT_PERIOD) { if (BeansCorePlugin.getModel().isInitialized()) { mp = new PropertiesProject(model, BeansCorePlugin.getModel().getProject(project)); // Build temporary beans core model with a cloned "real" // Spring project model.addProject(mp); } else { try { Thread.sleep(INIT_WAIT_PERIOD); } catch (InterruptedException e) { new UIJob("Show error on property page") { @Override public IStatus runInUIThread(IProgressMonitor arg0) { if (!statusLabel.isDisposed()) { statusLabel.setText("Failed to wait for Beans model initialization."); statusLabel.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR)); } return Status.OK_STATUS; } }.schedule(); return new Status(IStatus.ERROR, BeansUIPlugin.PLUGIN_ID, "Failed to initialize preference page content", e); } } } final PropertiesProject modelProject = mp; new UIJob(display, "Create Preference Page Content") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { if (modelProject == null) { if (!statusLabel.isDisposed()) { statusLabel.setText("Timed out waiting for Beans model initialization."); statusLabel.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR)); } } else { if (!folder.isDisposed()) { configFilesTab = new ConfigFilesTab(model, modelProject, selectedModelElement); TabItem item = new TabItem(folder, SWT.NONE); item.setText(BeansUIPlugin.getResourceString(CONFIG_FILES_LABEL)); item.setControl(configFilesTab.createControl(folder)); configSetsTab = new ConfigSetsTab(model, modelProject, selectedModelElement); item = new TabItem(folder, SWT.NONE); item.setText(BeansUIPlugin.getResourceString(CONFIG_SETS_LABEL)); item.setControl(configSetsTab.createControl(folder)); if (BeansConfigLocatorFactory.hasEnabledBeansConfigLocatorDefinitions(project)) { configLocatorTab = new ConfigLocatorTab(modelProject.getProject()); item = new TabItem(folder, SWT.NONE); item.setText(BeansUIPlugin.getResourceString(CONFIG_LOCATORS_LABEL)); item.setControl(configLocatorTab.createContents(folder)); } Dialog.applyDialogFont(folder); // Pre-select specified tab item folder.setSelection(selectedTab); // Open the scan dialog if required if // coming from a nature added event if (shouldTriggerScan) { configFilesTab.handleScanButtonPressed(); } folder.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent arg0) { if (configFilesTab != null) { configFilesTab.dispose(); } if (configSetsTab != null) { configSetsTab.dispose(); } } }); statusLabel.dispose(); // Pack shell if initial control size == preferred size. It's very likely that this is the first opened page. GridDataFactory.fillDefaults().grab(true, true).exclude(false).applyTo(folder); contents.layout(); // Adjust sroll-bars Composite c = contents.getParent(); while (c != null && !(c instanceof ScrolledComposite)) { c = c.getParent(); } if (c != null) { ScrolledComposite sc = (ScrolledComposite) c; sc.setMinSize(getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT)); } getControl().getShell().pack(); contentCreated.set(true); } } return Status.OK_STATUS; } }.schedule(); return Status.OK_STATUS; } }.schedule(); return contents; } @Override public boolean performOk() { if (contentCreated.get()) { IProject project = (IProject) getElement(); IBeansProject currentProject = BeansCorePlugin.getModel().getProject(project); boolean userMadeChanges = configFilesTab.hasUserMadeChanges() || configSetsTab.hasUserMadeChanges(); PropertiesProject newProject = (PropertiesProject) model.getProject(project); // At first delete all problem markers from the removed config files if (configFilesTab.hasUserMadeChanges()) { for (IBeansConfig currentConfig : currentProject.getConfigs()) { if (!newProject.hasConfig(currentConfig.getElementName())) { MarkerUtils.deleteAllMarkers(currentConfig.getElementResource(), SpringCore.MARKER_ID); } } } // Now save modified project description if (userMadeChanges) { SpringCorePreferences.getProjectPreferences(project.getProject(), BeansCorePlugin.PLUGIN_ID).putBoolean( BeansCorePlugin.IGNORE_MISSING_NAMESPACEHANDLER_PROPERTY, configFilesTab.shouldIgnoreMissingNamespaceHandler()); newProject.saveDescription(); } if (configLocatorTab != null) { configLocatorTab.performOk(); } // Finally (after saving the modified project description!!!) refresh // the label decoration of all config files if (configFilesTab.hasUserMadeChanges()) { BeansModelLabelDecorator.update(); } } return super.performOk(); } @SuppressWarnings("unchecked") public void applyData(Object data) { super.applyData(data); if (data instanceof Map) { this.pageData = (Map<String, Object>) data; if (this.pageData.containsKey(BLOCK_ID)) { this.selectedTab = (Integer) this.pageData.get(BLOCK_ID); } if (this.pageData.containsKey(SELECTED_RESOURCE) && this.pageData.get(SELECTED_RESOURCE) instanceof IModelElement) { this.selectedModelElement = (IModelElement) this.pageData.get(SELECTED_RESOURCE); } if (this.pageData.containsKey(SCAN) && this.pageData.get(SCAN) instanceof Boolean) { this.shouldTriggerScan = (Boolean) this.pageData.get(SCAN); } } } }