/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.mappingsplugin; import java.awt.Component; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.prefs.Preferences; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import org.eclipse.persistence.tools.workbench.framework.OpenException; import org.eclipse.persistence.tools.workbench.framework.Plugin; import org.eclipse.persistence.tools.workbench.framework.UnsupportedFileException; import org.eclipse.persistence.tools.workbench.framework.action.FrameworkAction; import org.eclipse.persistence.tools.workbench.framework.app.ApplicationNode; import org.eclipse.persistence.tools.workbench.framework.app.ComponentContainerDescription; import org.eclipse.persistence.tools.workbench.framework.app.MenuDescription; import org.eclipse.persistence.tools.workbench.framework.app.MenuGroupDescription; import org.eclipse.persistence.tools.workbench.framework.app.MenuItemDescription; import org.eclipse.persistence.tools.workbench.framework.app.PreferencesNode; import org.eclipse.persistence.tools.workbench.framework.app.RootMenuDescription; import org.eclipse.persistence.tools.workbench.framework.app.ToolBarButtonDescription; import org.eclipse.persistence.tools.workbench.framework.app.ToolBarButtonGroupDescription; import org.eclipse.persistence.tools.workbench.framework.app.ToolBarDescription; import org.eclipse.persistence.tools.workbench.framework.context.ApplicationContext; import org.eclipse.persistence.tools.workbench.framework.context.PreferencesContext; import org.eclipse.persistence.tools.workbench.framework.context.ShellWorkbenchContext; import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext; import org.eclipse.persistence.tools.workbench.framework.ui.view.EmptyPropertiesPage; import org.eclipse.persistence.tools.workbench.mappingsio.FileNotFoundListener; import org.eclipse.persistence.tools.workbench.mappingsio.LegacyProjectReadCallback; import org.eclipse.persistence.tools.workbench.mappingsio.ProjectIOManager; import org.eclipse.persistence.tools.workbench.mappingsmodel.project.MWProject; import org.eclipse.persistence.tools.workbench.mappingsplugin.ui.preferences.MappingsPreferencesNode; import org.eclipse.persistence.tools.workbench.mappingsplugin.ui.project.ProjectNode; import org.eclipse.persistence.tools.workbench.mappingsplugin.ui.project.relational.ExportTableCreatorJavaSourceAction; import org.eclipse.persistence.tools.workbench.uitools.CancelException; import org.eclipse.persistence.tools.workbench.uitools.LabelArea; import org.eclipse.persistence.tools.workbench.utility.ClassTools; import org.eclipse.persistence.tools.workbench.utility.Command; import org.eclipse.persistence.tools.workbench.utility.Stack; import org.eclipse.persistence.tools.workbench.utility.SynchronizedObject; import org.eclipse.persistence.tools.workbench.utility.SynchronizedStack; import org.eclipse.persistence.tools.workbench.utility.TriStateBoolean; import org.eclipse.persistence.tools.workbench.utility.io.FileTools; import org.eclipse.persistence.tools.workbench.utility.string.StringTools; /** * Plug-in implementation for Mappings. This initializes and holds the * I/O Manager for reading and writing Mapping Workbench Projects (.mwp). * This also holds the mappings actions shared within a workbench window. */ // TODO possibly move properties page building and caching to separate (framework?) class public final class MappingsPlugin implements Plugin { /** the app context built during construction */ private ApplicationContext mwApplicationContext; /** this reads and writes Mapping Workbench projects */ private SynchronizedObject synchronizedIOManager; /** * properties page cache entries, keyed by the properties page's class; * access to this map must be synchronized */ private Map propertiesPageCache; /** * a stack of properties page classes; the properties page builder thread * will loop indefinitely, popping classes from this stack and building the * corresponding properties pages */ SynchronizedStack propertiesPageBuilderStack; private static final String MAPPINGS_PREFERENCES_NODE = "mappings"; // Preference options public static final String REMOVE_EJB_2X_INFO_DO_NOT_THIS_SHOW_AGAIN_PREFERENCE = "persistence type remove ejb2x info"; public static final String REMOVE_EJB_INFO_DO_NOT_THIS_SHOW_AGAIN_PREFERENCE = "persistence type remove ejb info"; public static final String CHANGE_QUERY_TYPE_DO_NOT_THIS_SHOW_AGAIN_PREFERENCE ="query change query type"; public static final String CHANGE_QUERY_FORMAT_DO_NOT_SHOW_THIS_AGAIN_PREFERENCE ="query change query format"; public static final String EXPORT_LOCATION_PREFERENCE = "export location"; /** * The properties pages will be pre-built in the order they are listed here. * These classes are stored here by name so we don't have to make * them public. Whether this is a good idea is open to debate. ~bjv */ private static final String[] PRE_BUILT_PROPERTIES_PAGES = { "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.project.relational.RelationalProjectTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.descriptor.relational.TableDescriptorTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.db.DatabasePropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.db.TableTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.descriptor.DescriptorPackagePropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.descriptor.relational.AggregateDescriptorTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.descriptor.relational.InterfaceDescriptorPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.descriptor.xml.EisCompositeDescriptorTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.descriptor.xml.EisRootDescriptorTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.descriptor.xml.OXDescriptorTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.framework.ui.view.EmptyPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.AggregateMappingTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.DirectToFieldMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.DirectToXmlTypePropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.ManyToManyMappingTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.OneToManyMappingTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.OneToOneMappingTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.RelationalDirectCollectionMappingTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.RelationalDirectMapMappingTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.RelationalTransformationMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.relational.VariableOneToOneMappingTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.EisCompositeCollectionMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.OXCompositeCollectionMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.EisCompositeObjectMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.OXCompositeObjectMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.EisOneToManyTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.EisOneToOneTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.OxDirectCollectionMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.EisDirectCollectionMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.OxDirectMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.EisDirectMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.EisTransformationMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.mapping.xml.OXTransformationMappingPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.project.xml.EisProjectTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.project.xml.OXProjectTabbedPropertiesPage", "org.eclipse.persistence.tools.workbench.mappingsplugin.ui.schema.XmlSchemaRepositoryPanel", }; // ********** constructor/initialization ********** MappingsPlugin(ApplicationContext context) { super(); this.mwApplicationContext = this.wrap(context); this.startInitializeSynchronizedIOManager(); this.initializePreferences(); this.initializePropertiesPageBuilder(); } // ********** I/O manager ********** /** * Build an empty "synchronized" i/o manager and * start a thread to initialize it with a i/o manager. */ private void startInitializeSynchronizedIOManager() { // put the synchronized object in place - // #getIOManager() will wait until it is initialized and unlocked this.synchronizedIOManager = new SynchronizedObject(); Thread t = new Thread(this.buildInitializeSynchronizedIOManagerRunnable(), "Initialize Mappings I/O Manager"); t.setPriority(Thread.MIN_PRIORITY); t.start(); } /** * Build a Runnable that will initialize the "synchronized" i/o manager. */ private Runnable buildInitializeSynchronizedIOManagerRunnable() { return new Runnable() { public void run() { MappingsPlugin.this.initializeSynchronizedIOManager(); } }; } /** * Hold the i/o manager lock while building the * i/o manager and putting it in the synchronized object. */ void initializeSynchronizedIOManager() { try { this.synchronizedIOManager.execute(this.buildInitializeIOManagerCommand()); } catch (InterruptedException ex) { throw new RuntimeException(ex); // shouldn't happen } } /** * Build the command that will be executed while the "synchronized" * i/o manager is locked. */ private Command buildInitializeIOManagerCommand() { return new Command() { public void execute() { MappingsPlugin.this.initializeIOManager(); } }; } /** * Build the appropriate i/o manager and put it in the * synchronized object that wraps it. */ void initializeIOManager() { this.synchronizedIOManager.setValue(new ProjectIOManager()); } // ********** Preferences *********** private void initializePreferences() { Preferences preferences = this.mwApplicationContext.getPreferences(); // Remove EJB Info String value = preferences.get(REMOVE_EJB_INFO_DO_NOT_THIS_SHOW_AGAIN_PREFERENCE, TriStateBoolean.UNDEFINED.toString()); preferences.put(REMOVE_EJB_INFO_DO_NOT_THIS_SHOW_AGAIN_PREFERENCE, value); // Remove EJB 2.x Info value = preferences.get(REMOVE_EJB_2X_INFO_DO_NOT_THIS_SHOW_AGAIN_PREFERENCE, TriStateBoolean.UNDEFINED.toString()); preferences.put(REMOVE_EJB_2X_INFO_DO_NOT_THIS_SHOW_AGAIN_PREFERENCE, value); } // ********** properties page cache ********** /** * Build the properties page cache and builder stack, prime the stack, * and start the properties page builder thread. */ private void initializePropertiesPageBuilder() { // we can use a HashMap because we synchronize manually this.propertiesPageCache = new HashMap(PRE_BUILT_PROPERTIES_PAGES.length); this.propertiesPageBuilderStack = new SynchronizedStack(); // push the classes on to the stack in reverse order so they get built in the expected order for (int i = PRE_BUILT_PROPERTIES_PAGES.length; i-- > 0; ) { this.propertiesPageBuilderStack.push(this.classForName(PRE_BUILT_PROPERTIES_PAGES[i])); } new PropertiesPageBuilder().start(); } // checked exceptions suck private Class classForName(String className) { try { return Class.forName(className); } catch (ClassNotFoundException ex) { throw new IllegalArgumentException(className); } } // ********** Plugin implementation ********** /** * @see org.eclipse.persistence.tools.workbench.framework.Plugin#buildNewMenuItems(org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext) */ public JMenuItem[] buildNewMenuItems(WorkbenchContext context) { context = this.wrap(context); JMenuItem[] menuItems = new JMenuItem[1]; menuItems[0] = new JMenuItem(this.buildNewProjectAction(context)); return menuItems; } /** * @see org.eclipse.persistence.tools.workbench.framework.Plugin#open(java.io.File, org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext) */ public ApplicationNode open(File file, WorkbenchContext context) throws UnsupportedFileException, OpenException { if ( ! FileTools.extension(file).equalsIgnoreCase(MWProject.FILE_NAME_EXTENSION)) { throw new UnsupportedFileException(); } MWProject project = null; // the context passed in is the application-wide context, we need the "mappings" context Preferences preferences = context.getApplicationContext().getPreferences().node(MAPPINGS_PREFERENCES_NODE); context = this.wrap(context); LocalCallback callback = new LocalCallback(context); try { // TODO build FileNotFoundListener and notify listener of any "missing" files project = this.getIOManager().read(file, preferences, FileNotFoundListener.NULL_INSTANCE, callback); } catch (CancelException ex) { throw ex; } catch (Throwable t) { throw new OpenException(t); } ApplicationNode projectNode = this.buildProjectNode(project, context); if (callback.saveLegacyProjectNow()) { if ( ! projectNode.saveAs(file, context)) { throw new CancelException(); } JOptionPane.showMessageDialog(context.getCurrentWindow(), new LabelArea(context.getApplicationContext().getResourceRepository().getString("LEGACY_MIGRATION_COMPLETE.MESSAGE")), context.getApplicationContext().getResourceRepository().getString("LEGACY_MIGRATION_COMPLETE.TITLE"), JOptionPane.INFORMATION_MESSAGE); } return projectNode; } /** * @see org.eclipse.persistence.tools.workbench.framework.Plugin#buildToolBarDescription(org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext) */ public ComponentContainerDescription buildToolBarDescription(WorkbenchContext context) { context = this.wrap(context); ToolBarDescription tbd = new ToolBarDescription(); ToolBarButtonGroupDescription tbbgd1 = new ToolBarButtonGroupDescription(); tbbgd1.add(new ToolBarButtonDescription(this.getExportDeploymentXmlAction(context))); tbd.add(tbbgd1); ToolBarButtonGroupDescription tbbgd2 = new ToolBarButtonGroupDescription(); tbbgd2.add(new ToolBarButtonDescription(this.getRefreshClassesAction(context))); tbbgd2.add(new ToolBarButtonDescription(this.getAddOrRefreshClassesAction(context))); tbbgd2.add(new ToolBarButtonDescription(this.getCreateNewClassAction(context))); tbd.add(tbbgd2); return tbd; } /** * @see org.eclipse.persistence.tools.workbench.framework.Plugin#buildMenuDescription(org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext) */ public ComponentContainerDescription buildMenuDescription(WorkbenchContext context) { context = this.wrap(context); RootMenuDescription md = new RootMenuDescription(); MenuGroupDescription mgd1 = new MenuGroupDescription(); mgd1.add(this.buildExportMenuDescription(context)); md.add(mgd1); MenuGroupDescription mgd2 = new MenuGroupDescription(); mgd2.add(new MenuItemDescription(this.getRefreshClassesAction(context))); mgd2.add(new MenuItemDescription(this.getAddOrRefreshClassesAction(context))); mgd2.add(new MenuItemDescription(this.getCreateNewClassAction(context))); mgd2.add(new MenuItemDescription(this.getManageNonDescriptorClassesAction(context))); md.add(mgd2); return md; } /** * @see org.eclipse.persistence.tools.workbench.framework.Plugin#buildPreferencesNodes(org.eclipse.persistence.tools.workbench.framework.context.PreferencesContext) */ public PreferencesNode[] buildPreferencesNodes(PreferencesContext context) { return new PreferencesNode[] {new MappingsPreferencesNode((PreferencesContext) this.wrap(context))}; } // ********** queries ********** /** * return the i/o manager once it has been built */ public ProjectIOManager getIOManager() { // once the i/o mgr is initialized, it should never change back to null try { this.synchronizedIOManager.waitUntilNotNull(); } catch (InterruptedException ex) { throw new RuntimeException(ex); // shouldn't happen } return (ProjectIOManager) this.synchronizedIOManager.getValue(); } public AddOrRefreshClassesAction getAddOrRefreshClassesAction(WorkbenchContext context) { return new AddOrRefreshClassesAction(context); } public AutomapAction getAutomapAction(WorkbenchContext context) { return new AutomapAction(context); } public CreateNewClassAction getCreateNewClassAction(WorkbenchContext context) { return new CreateNewClassAction(context); } public ManageNonDescriptorClassesAction getManageNonDescriptorClassesAction(WorkbenchContext context) { return new ManageNonDescriptorClassesAction(context); } public ExportDeploymentXmlAction getExportDeploymentXmlAction(WorkbenchContext context) { return new ExportDeploymentXmlAction(context); } public ExportDeploymentXmlAndInitializeRuntimeDescriptorsAction getExportDeploymentXmlAndInitializeRuntimeDescriptorsAction(WorkbenchContext context) { return new ExportDeploymentXmlAndInitializeRuntimeDescriptorsAction(context); } public ExportJavaSourceAction getExportProjectJavaSourceAction(WorkbenchContext context) { return new ExportJavaSourceAction(context); } public ExportModelJavaSourceAction getExportModelJavaSourceAction(WorkbenchContext context) { return new ExportModelJavaSourceAction(context); } public ExportSpecificDescriptorModelJavaSourceAction getExportSpecificDescriptorModelJavaSourceAction(WorkbenchContext context) { return new ExportSpecificDescriptorModelJavaSourceAction(context); } public RefreshClassesAction getRefreshClassesAction(WorkbenchContext context) { return new RefreshClassesAction(context); } public RemoveAction getRemoveAction(WorkbenchContext context) { return new RemoveAction(context); } public OracleHelpAction getHelpAction(WorkbenchContext context) { return new OracleHelpAction(context); } public ExportTableCreatorJavaSourceAction getExportTableCreatorJavaSourceAction(WorkbenchContext context) { return new ExportTableCreatorJavaSourceAction(context); } /** * return whether the application is being run in development mode */ public boolean isDevelopmentModeIn(WorkbenchContext context) { return context.getApplicationContext().getApplication().isDevelopmentMode(); } // ********** behavior ********** private WorkbenchContext wrap(WorkbenchContext context) { return context.buildExpandedApplicationContextWorkbenchContext(wrap(context.getApplicationContext())); } /** * wrap the specified context with the MW-specific * resources and preferences */ ApplicationContext wrap(ApplicationContext context) { ApplicationContext expandedContext = context.buildExpandedResourceRepositoryContext(MappingsPluginResourceBundle.class, new MappingsPluginIconResourceFileNameMap()); expandedContext = expandedContext.buildExpandedResourceRepositoryContext(ProblemsBundle.class); return expandedContext.buildRedirectedPreferencesContext(MAPPINGS_PREFERENCES_NODE); } FrameworkAction buildNewProjectAction(WorkbenchContext context) { return new NewProjectAction(this, context); } ApplicationNode buildProjectNode(MWProject project, WorkbenchContext context) { return ProjectNode.forProject(project, context.getApplicationContext(), this); } private MenuDescription buildExportMenuDescription(WorkbenchContext context) { MenuDescription md = new MenuDescription( context.getApplicationContext().getResourceRepository().getString("EXPORT_MENU"), null, context.getApplicationContext().getResourceRepository().getMnemonic("EXPORT_MENU"), context.getApplicationContext().getResourceRepository().getIcon("file.export")); MenuGroupDescription mgd = new MenuGroupDescription(); mgd.add(new MenuItemDescription(this.getExportDeploymentXmlAction(context))); if (isDevelopmentModeIn(context)) { mgd.add(new MenuItemDescription(this.getExportDeploymentXmlAndInitializeRuntimeDescriptorsAction(context))); } mgd.add(new MenuItemDescription(this.getExportProjectJavaSourceAction(context))); mgd.add(new MenuItemDescription(this.getExportModelJavaSourceAction(context))); mgd.add(new MenuItemDescription(this.getExportTableCreatorJavaSourceAction(context))); md.add(mgd); return md; } // ********** properties page cache ********** /** * Build and return the specified properties page. */ public Component buildPropertiesPage(Class propertiesPageClass) { return this.getPropertiesPageCacheEntry(propertiesPageClass).buildPropertiesPage(); } /** * Release the specified properties page back to the cache. */ public void releasePropertiesPage(Component propertiesPage) { this.getPropertiesPageCacheEntry(propertiesPage.getClass()).releasePropertiesPage(propertiesPage); } /** * Return the cache entry for the specified properties page, * building it if necessary. */ PropertiesPageCacheEntry getPropertiesPageCacheEntry(Class propertiesPageClass) { synchronized (this.propertiesPageCache) { PropertiesPageCacheEntry entry = (PropertiesPageCacheEntry) this.propertiesPageCache.get(propertiesPageClass); if (entry == null) { entry = new PropertiesPageCacheEntry(propertiesPageClass, this.mwApplicationContext, this.propertiesPageBuilderStack); this.propertiesPageCache.put(propertiesPageClass, entry); } return entry; } } // **************** member types *************** /** * All kinds of performance-related hackery here.... ~bjv * A properties page cache entry is constructed with a properties page class, * an application context, and the properties page builder stack. * We use reflection to fetch the class's list of required resource bundles. * Then we use these bundles to expand the application context appropriately * for when the properties page builder needs to instantiate the properties * page, using reflection. */ private static class PropertiesPageCacheEntry { private final Class propertiesPageClass; private final WorkbenchContext workbenchContext; private final Stack propertiesPageBuilderStack; private final SynchronizedStack propertiesPages; PropertiesPageCacheEntry(Class propertiesPageClass, ApplicationContext applicationContext, Stack propertiesPageBuilderStack) { super(); this.propertiesPageClass = propertiesPageClass; this.workbenchContext = this.buildWorkbenchContext(applicationContext); this.propertiesPageBuilderStack = propertiesPageBuilderStack; this.propertiesPages = new SynchronizedStack(); } private WorkbenchContext buildWorkbenchContext(ApplicationContext applicationContext) { Class[] resourceBundleClasses = (Class[]) ClassTools.getStaticFieldValue(this.propertiesPageClass, "REQUIRED_RESOURCE_BUNDLES"); for (int i = 0; i < resourceBundleClasses.length; i++) { applicationContext = applicationContext.buildExpandedResourceRepositoryContext(resourceBundleClasses[i]); } return new ShellWorkbenchContext(applicationContext); } /** * if the stack is empty, notify the builder and wait for a page to be * pushed on the stack; if we pop the last page off the stack, * trigger another one to be built asynchronously in the background */ Component buildPropertiesPage() { synchronized (this.propertiesPages) { if (this.propertiesPages.isEmpty()) { this.propertiesPageBuilderStack.push(this.propertiesPageClass); } Component propertiesPage; try { propertiesPage = (Component) this.propertiesPages.waitToPop(); } catch (InterruptedException ex) { // should only happen during shutdown throw new RuntimeException(ex); } if (this.propertiesPages.isEmpty()) { this.propertiesPageBuilderStack.push(this.propertiesPageClass); } return propertiesPage; } } void releasePropertiesPage(Component propertiesPage) { this.propertiesPages.push(propertiesPage); } /** * this is called by the properties page builder when our class * reaches the top of the stack; if there are any problems, * force *something* on to the stack so the AWT Event Queue * does not hang on the #waitToPop() in #buildPropertiesPage() */ void installPropertiesPage() throws Throwable { try { // System.out.println(ClassTools.shortNameFor(propertiesPageClass)); this.propertiesPages.push(ClassTools.newInstance(this.propertiesPageClass, WorkbenchContext.class, this.workbenchContext)); } catch (Throwable ex) { try { this.propertiesPages.push(new EmptyPropertiesPage(this.workbenchContext)); } catch (Throwable ex2) { // we're in trouble now...trigger a NPE in the AWT Event Queue (just don't hang it!) this.propertiesPages.push(null); throw ex2; } throw ex; } } } /** * This thread loops continuously, waiting for a properties page class * to be pushed on to the stack. Once a class has been pushed on to the * stack, the builder pops it off and delegates to the appropriate * properties page cache entry for instantiation. */ private class PropertiesPageBuilder extends Thread { PropertiesPageBuilder() { super("Properties Page Builder"); this.setPriority(Thread.MIN_PRIORITY); } public void run() { // loop until we are interrupted while (true) { Class propertiesPageClass = null; try { // wait for a class to be pushed on to the stack propertiesPageClass = (Class) MappingsPlugin.this.propertiesPageBuilderStack.waitToPop(); } catch (InterruptedException ex) { // we were interrupted while waiting, must be quittin' time return; } this.buildPropertiesPage(propertiesPageClass); } } private void buildPropertiesPage(Class propertiesPageClass) { try { MappingsPlugin.this.getPropertiesPageCacheEntry(propertiesPageClass).installPropertiesPage(); } catch (Throwable ex) { // if we have any problems building the page, start a new thread and let this one die; // the ThreadGroup will handle the runtime exception if appropriate new PropertiesPageBuilder().start(); throw new RuntimeException(ex); } } } // ********** misc??? ********** /** * Given the current value of a project's "export" directory (e.g. the * project source directory), calculate an appropriate directory to * start in when prompting the user. * * I'm not so sure where this code should go, so I'm * dumping it here for the time being. ~bjv */ public static File buildExportDirectory(MWProject project, String directoryName, Preferences preferences) { File directory = new File(directoryName); if (StringTools.stringIsEmpty(directoryName) || directory.isAbsolute()) { if ( ! directory.exists()) { // the project's current export directory does not exist, // try to use the project save location directory = project.getSaveDirectory(); } if (directory == null) { // a newly-created project does not have a save directory, // try to use the user preference directory directory = new File(preferences.get(EXPORT_LOCATION_PREFERENCE, "")); } if ( ! directory.exists()) { // the user preference directory does not exist, // use the user home directory = FileTools.userHomeDirectory(); } } else { // the directory is supposed to be relative to the project save directory File projectSaveDirectory = project.getSaveDirectory(); if (projectSaveDirectory == null) { // if we don't have a project save directory yet // (a newly-created project does not have a save directory), // we have to ignore a directory that's supposed to be relative to it; // use the preference or user home directory instead directory = new File(preferences.get(EXPORT_LOCATION_PREFERENCE, "")); if ( ! directory.exists()) { directory = FileTools.userHomeDirectory(); } } else { directory = new File(projectSaveDirectory, directory.getPath()); if ( ! directory.exists()) { // if the fully-expanded directory does not exist, // revert to the project save directory directory = projectSaveDirectory; } } } return directory; } // ********** private member class ********** /** * When a legacy project is being read, this callback prompts * the user with three options: * - save now * - save later * - cancel */ private static class LocalCallback implements LegacyProjectReadCallback { private WorkbenchContext context; private boolean saveLegacyProjectNow; LocalCallback(WorkbenchContext context) { super(); this.context = context; this.saveLegacyProjectNow = false; } public void checkLegacyRead(String schemaVersion) { LegacyProjectMigrationDialog dialog = new LegacyProjectMigrationDialog(this.context); dialog.show(); if (dialog.wasCanceled()) { throw new CancelException(); } // either "save now" or "save later" was pressed this.saveLegacyProjectNow = ! dialog.saveLater(); } boolean saveLegacyProjectNow() { return this.saveLegacyProjectNow; } } }