/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * SwitchWorkspaceDialog.java * Creation date: May 18, 2006. * By: Joseph Wong */ package org.openquark.gems.client; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.HeadlessException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSeparator; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.WindowConstants; import javax.swing.filechooser.FileFilter; import org.openquark.cal.services.CALWorkspace; import org.openquark.cal.services.ResourceStore; import org.openquark.cal.services.Status; import org.openquark.cal.services.VaultRegistry; import org.openquark.cal.services.WorkspaceDeclaration; import org.openquark.cal.services.WorkspaceDeclarationManager; import org.openquark.cal.services.WorkspaceDeclarationPathMapper; import org.openquark.cal.services.WorkspaceManager; import org.openquark.cal.services.WorkspaceResource; import org.openquark.cal.services.WorkspaceDeclaration.StreamProvider; import org.openquark.util.TextEncodingUtilities; import org.openquark.util.ui.ExtensionFileFilter; /** * This class implements the dialog box for switching workspaces in the GemCutter. One of the functionalities * implemented here is the extraction of a list of workspace declaration stream providers that are * accessible from the current environment (e.g. the nullary environment) from the workspace manager. * * @author Joseph Wong */ final class SwitchWorkspaceDialog extends JDialog { private static final long serialVersionUID = 4574495053674302963L; /** * A workspace declaration stream provider for a minimal workspace. * * @author Joseph Wong */ private static final class MinimalWorkspaceDeclarationStreamProvider implements WorkspaceDeclaration.StreamProvider { private final String decl; private MinimalWorkspaceDeclarationStreamProvider(String decl) { this.decl = decl; } /** {@inheritDoc} */ public String getName() { return "(minimal)"; } /** {@inheritDoc} */ public String getLocation() { return "temporary"; } /** {@inheritDoc} */ public String getDebugInfo(VaultRegistry vaultRegistry) { return "constructed programmatically"; } /** {@inheritDoc} */ public InputStream getInputStream(VaultRegistry vaultRegistry, Status status) { return new ByteArrayInputStream(TextEncodingUtilities.getUTF8Bytes(decl)); } /** {@inheritDoc} */ @Override public String toString() { return getName(); } } /** * A workspace declaration stream provider that encapsulates a file. * * @author Joseph Wong */ private static final class FileBasedWorkspaceDeclarationStreamProvider implements WorkspaceDeclaration.StreamProvider { private final File file; private FileBasedWorkspaceDeclarationStreamProvider(File file) { this.file = file; } /** {@inheritDoc} */ public String getName() { return file.getName(); } /** {@inheritDoc} */ public String getLocation() { return file.getAbsolutePath(); } /** {@inheritDoc} */ public String getDebugInfo(VaultRegistry vaultRegistry) { return "from file: " + file.getAbsolutePath(); } /** {@inheritDoc} */ public InputStream getInputStream(VaultRegistry vaultRegistry, Status status) { try { return new FileInputStream(file); } catch (FileNotFoundException e) { status.add(new Status(Status.Severity.ERROR, "The specified workspace declaration file cannot be found.", e)); return null; } } /** {@inheritDoc} */ @Override public String toString() { return getLocation(); } } /** * A workspace declaration stream provider that encapsulates a workspace resource. * * @author Joseph Wong */ private static final class ResourceBasedWorkspaceDeclarationStreamProvider implements WorkspaceDeclaration.StreamProvider { private final WorkspaceResource decl; private final String location; private ResourceBasedWorkspaceDeclarationStreamProvider(WorkspaceResource decl, String location) { this.decl = decl; this.location = location; } /** {@inheritDoc} */ public String getName() { return decl.getIdentifier().getFeatureName().getName(); } /** {@inheritDoc} */ public String getLocation() { return location; } /** {@inheritDoc} */ public InputStream getInputStream(VaultRegistry vaultRegistry, Status status) { return decl.getInputStream(status); } /** {@inheritDoc} */ public String getDebugInfo(VaultRegistry vaultRegistry) { return decl.getDebugInfo(); } /** {@inheritDoc} */ @Override public String toString() { return getName(); } } /** * Typesafe enumeration for the kind of workspace to switch to. * * @author Joseph Wong */ private static final class Kind { static final Kind KNOWN_DECL = new Kind("knownDecl"); static final Kind FILE_BASED = new Kind("fileBased"); static final Kind MINIMAL = new Kind("minimal"); /** The name of the kind, which is used for persistence purposes. */ private final String name; /** Private constructor. */ private Kind(String name) { this.name = name; } /** {@inheritDoc} */ @Override public String toString() { return name; } /** @return the appropriate constant based on its string representation. */ static Kind fromString(String name) { if (name.equals(KNOWN_DECL.name)) { return KNOWN_DECL; } else if (name.equals(FILE_BASED.name)) { return FILE_BASED; } else if (name.equals(MINIMAL.name)) { return MINIMAL; } else { throw new IllegalArgumentException("Unknown switch workspace kind: " + name); } } } //// /// Preference Keys // private static final String SWITCH_WORKSPACE_KIND_PREF_KEY = "switchWorkspaceKind"; private static final String SWITCH_WORKSPACE_FILE_BASED_DECLARATION_FILE_NAME_PREF_KEY = "switchWorkspaceFileBasedDeclarationFileName"; /** The button group for the radio buttons for choosing what kind of declaration to use. */ private final ButtonGroup declarationKindOptionGroup = new ButtonGroup(); /** Radio button for specifying that a known declaration from the environment should be used. */ private final JRadioButton knownDeclarationRadioButton = new JRadioButton(GemCutter.getResourceString("SwitchWorkspaceKnownDeclarationDescription")); /** Radio button for specifying that a declaration file on the file system should be used. */ private final JRadioButton fileBasedDeclarationRadioButton = new JRadioButton(GemCutter.getResourceString("SwitchWorkspaceFileBasedDeclarationDescription")); /** Radio button for specifying that a minimal workspace declaration should be used. */ private final JRadioButton minimalDeclarationRadioButton = new JRadioButton(GemCutter.getResourceString("SwitchWorkspaceMinimalDeclarationDescription")); /** The combo box for specifying a known declaration from the environment. */ private JComboBox knownDeclarationComboBox; /** Text field for specifying the declaration file on the file system. */ private final JTextField fileBasedDeclarationFileNameField = new JTextField(); /** Button for launching a directory chooser for the declaration file on the file system. */ private final JButton browseFileBasedDeclarationFileNameButton = new JButton(GemCutter.getResourceString("SwitchWorkspaceBrowseFileBasedDeclarationFileNameButton")); /** The OK button. */ private final JButton okButton = new JButton(GemCutter.getResourceString("LOC_OK")); /** The Cancel button. */ private final JButton cancelButton = new JButton(GemCutter.getResourceString("LOC_Cancel")); /** Whether OK was selected at the closing of the dialog. */ private boolean okSelected = false; /** The associated workspace manager. */ private final WorkspaceManager workspaceManager; /** The panel containing controls for specifying the modules for the minimal workspace. */ private final CreateMinimalWorkspacePanel createMinimalWorkspacePanel; /** * Constructs a SwitchWorkspaceDialog. * @param owner the owner of the dialog. * @param workspaceManager manager the associated workspace manager. */ SwitchWorkspaceDialog(Frame owner, WorkspaceManager workspaceManager) throws HeadlessException { super(owner); if (workspaceManager == null) { throw new NullPointerException(); } this.workspaceManager = workspaceManager; createMinimalWorkspacePanel = new CreateMinimalWorkspacePanel(workspaceManager.getWorkspace()); initialize(); loadExistingValuesAndMakeEditable(); setupUI(); } /** * Loads the existing values of the fields from the GemCutter preferences and make the fields editable. */ private void loadExistingValuesAndMakeEditable() { String kindStr = GemCutter.getPreferences().get(SWITCH_WORKSPACE_KIND_PREF_KEY, Kind.KNOWN_DECL.toString()); Kind kind = Kind.fromString(kindStr); if (kind == Kind.KNOWN_DECL) { knownDeclarationRadioButton.setSelected(true); } else if (kind == Kind.FILE_BASED){ fileBasedDeclarationRadioButton.setSelected(true); } else if (kind == Kind.MINIMAL) { minimalDeclarationRadioButton.setSelected(true); } else { // default to known declaration knownDeclarationRadioButton.setSelected(true); } fileBasedDeclarationFileNameField.setText(GemCutter.getPreferences().get(SWITCH_WORKSPACE_FILE_BASED_DECLARATION_FILE_NAME_PREF_KEY, "")); // update the fields based on the radio buttons' new values updateFieldsBasedOnCurrentState(); } /** * Saves the values of the dialog fields into GemCutter's preferences. */ private void saveValuesToPreferences() { GemCutter.getPreferences().put(SWITCH_WORKSPACE_KIND_PREF_KEY, getKind().toString()); if (shouldUseFileBasedDeclaration()) { GemCutter.getPreferences().put(SWITCH_WORKSPACE_FILE_BASED_DECLARATION_FILE_NAME_PREF_KEY, getFileBasedDeclarationFileName().toString()); } } /** * Initializes the various member fields, e.g. hooking up listeners and actions, * but does not setup or layout the UI. */ private void initialize() { WorkspaceDeclaration.StreamProvider[] workspaceDeclarationStreamProvidersFromEnvironment = getWorkspaceDeclarationStreamProvidersFromEnvironment(); knownDeclarationComboBox = new JComboBox(workspaceDeclarationStreamProvidersFromEnvironment); String currentWorkspaceDeclarationName = getCurrentWorkspaceDeclarationName(); for (final StreamProvider streamProvider : workspaceDeclarationStreamProvidersFromEnvironment) { if (streamProvider.getName().equals(currentWorkspaceDeclarationName)) { knownDeclarationComboBox.setSelectedItem(streamProvider); } } // group together the radio buttons declarationKindOptionGroup.add(knownDeclarationRadioButton); declarationKindOptionGroup.add(fileBasedDeclarationRadioButton); declarationKindOptionGroup.add(minimalDeclarationRadioButton); // link the radio buttons to the components they enable/disable knownDeclarationRadioButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { updateFieldsBasedOnCurrentState(); }}); fileBasedDeclarationRadioButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { updateFieldsBasedOnCurrentState(); }}); minimalDeclarationRadioButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { updateFieldsBasedOnCurrentState(); }}); // set up the action for the browse button browseFileBasedDeclarationFileNameButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JFileChooser fileChooser = new JFileChooser(getFileBasedDeclarationFileName()); fileChooser.setDialogTitle(GemCutter.getResourceString("SwitchWorkspaceSelectDeclarationFile")); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); // Set up other customizations FileFilter filter = new ExtensionFileFilter(WorkspaceDeclarationPathMapper.INSTANCE.getFileExtension(), GemCutter.getResourceString("CWSFilesFilterName")); fileChooser.setAcceptAllFileFilterUsed(true); fileChooser.addChoosableFileFilter(filter); fileChooser.setFileFilter(filter); int result = fileChooser.showDialog(SwitchWorkspaceDialog.this, GemCutter.getResourceString("SwitchWorkspaceFileChooserApproveButtonText")); if (result == JFileChooser.APPROVE_OPTION) { fileBasedDeclarationFileNameField.setText(fileChooser.getSelectedFile().getAbsolutePath()); } }}); // set up the ok and cancel buttons' actions okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (inputValid()) { okSelected = true; saveValuesToPreferences(); SwitchWorkspaceDialog.this.dispose(); } else { showInvalidInputMessage(); } }}); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { okSelected = false; SwitchWorkspaceDialog.this.dispose(); }}); } /** * Setup and layout the UI. */ private void setupUI() { setTitle(GemCutter.getResourceString("SwitchWorkspaceDialogTitle")); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); JComponent contentPane = (JComponent)this.getContentPane(); contentPane.setLayout(new GridBagLayout()); GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER; gridBagConstraints.anchor = GridBagConstraints.LINE_START; gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1; gridBagConstraints.weighty = 0; GridBagConstraints gridBagConstraints2 = new GridBagConstraints(); gridBagConstraints2.gridx = 0; gridBagConstraints2.gridwidth = GridBagConstraints.REMAINDER; gridBagConstraints2.anchor = GridBagConstraints.LINE_START; gridBagConstraints2.fill = GridBagConstraints.BOTH; gridBagConstraints2.weightx = 1; gridBagConstraints2.weighty = 1; // the main section { JPanel knownDeclarationPanel = new JPanel(new GridBagLayout()); { knownDeclarationRadioButton.setVerticalTextPosition(SwingConstants.TOP); knownDeclarationPanel.add(knownDeclarationRadioButton, gridBagConstraints); JPanel comboBoxPanel = new JPanel(new BorderLayout()); { comboBoxPanel.add(knownDeclarationComboBox, BorderLayout.CENTER); comboBoxPanel.setBorder(BorderFactory.createEmptyBorder(6, 36, 0, 6)); } knownDeclarationPanel.add(comboBoxPanel, gridBagConstraints); knownDeclarationPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11)); } contentPane.add(knownDeclarationPanel, gridBagConstraints); contentPane.add(new JSeparator(), gridBagConstraints); JPanel fileBasedDeclarationPanel = new JPanel(new GridBagLayout()); { fileBasedDeclarationRadioButton.setVerticalTextPosition(SwingConstants.TOP); fileBasedDeclarationPanel.add(fileBasedDeclarationRadioButton, gridBagConstraints); JPanel fileNameFieldPanel = new JPanel(new BorderLayout()); { fileNameFieldPanel.add(fileBasedDeclarationFileNameField, BorderLayout.CENTER); Box browseButtonBox = Box.createHorizontalBox(); browseButtonBox.add(Box.createHorizontalStrut(12)); browseButtonBox.add(browseFileBasedDeclarationFileNameButton); fileNameFieldPanel.add(browseButtonBox, BorderLayout.EAST); fileNameFieldPanel.setBorder(BorderFactory.createEmptyBorder(6, 36, 0, 6)); } fileBasedDeclarationPanel.add(fileNameFieldPanel, gridBagConstraints); fileBasedDeclarationPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11)); } contentPane.add(fileBasedDeclarationPanel, gridBagConstraints); contentPane.add(new JSeparator(), gridBagConstraints); JPanel minimalDeclarationPanel = new JPanel(new GridBagLayout()); { minimalDeclarationRadioButton.setVerticalTextPosition(SwingConstants.TOP); minimalDeclarationPanel.add(minimalDeclarationRadioButton, gridBagConstraints); JPanel createMinimalWorkspaceWrapperPanel = new JPanel(new BorderLayout()); { createMinimalWorkspaceWrapperPanel.add(createMinimalWorkspacePanel, BorderLayout.CENTER); createMinimalWorkspaceWrapperPanel.setBorder(BorderFactory.createEmptyBorder(6, 36, 0, 6)); } minimalDeclarationPanel.add(createMinimalWorkspaceWrapperPanel, gridBagConstraints2); minimalDeclarationPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11)); } contentPane.add(minimalDeclarationPanel, gridBagConstraints2); } contentPane.add(new JSeparator(), gridBagConstraints); // the box for the OK and cancel buttons contentPane.add(Box.createGlue(), gridBagConstraints); Box okCancelBox = Box.createHorizontalBox(); okCancelBox.add(Box.createGlue()); okCancelBox.add(okButton); okCancelBox.add(Box.createHorizontalStrut(5)); okCancelBox.add(cancelButton); okCancelBox.setBorder(BorderFactory.createEmptyBorder(17, 12, 11, 11)); contentPane.add(okCancelBox, gridBagConstraints); Dimension dimension = new Dimension(480, 575); contentPane.setMinimumSize(dimension); contentPane.setPreferredSize(dimension); setModal(true); pack(); } /** * Updates which fields should be enabled/disabled based on the current state of the various fields. */ private void updateFieldsBasedOnCurrentState() { boolean useKnownDeclaration = shouldUseKnownDeclaration(); knownDeclarationComboBox.setEnabled(useKnownDeclaration); boolean useFileBasedDeclaration = shouldUseFileBasedDeclaration(); fileBasedDeclarationFileNameField.setEnabled(useFileBasedDeclaration); browseFileBasedDeclarationFileNameButton.setEnabled(useFileBasedDeclaration); boolean useMinimalDeclaration = shouldUseMinimalDeclaration(); createMinimalWorkspacePanel.setEnabled(useMinimalDeclaration); } /** * @return whether OK was selected at the closing of the dialog. */ boolean isOKSelected() { return okSelected; } /** * @return true if the user has provided appropriate input values. */ private boolean inputValid() { if (shouldUseFileBasedDeclaration()) { File file = getFileBasedDeclarationFileName(); return file.toString().length() > 0 && file.isFile(); } if (shouldUseMinimalDeclaration()) { return !createMinimalWorkspacePanel.getDependencyFinder().getRootSet().isEmpty(); } return true; } /** * Displays a message box stating that some input is invalid. */ private void showInvalidInputMessage() { if (shouldUseFileBasedDeclaration()) { File file = getFileBasedDeclarationFileName(); if (file.toString().length() > 0) { JOptionPane.showMessageDialog( this, GemCutter.getResourceString("SwitchWorkspaceInvalidCWSFile"), GemCutter.getResourceString("SwitchWorkspaceInvalidInputMessageDialogTitle"), JOptionPane.INFORMATION_MESSAGE); } else { JOptionPane.showMessageDialog( this, GemCutter.getResourceString("SwitchWorkspaceMissingCWSFile"), GemCutter.getResourceString("SwitchWorkspaceMissingInputMessageDialogTitle"), JOptionPane.INFORMATION_MESSAGE); } } else if (shouldUseMinimalDeclaration()) { JOptionPane.showMessageDialog( this, GemCutter.getResourceString("SwitchWorkspaceMissingMinimalRootSet"), GemCutter.getResourceString("SwitchWorkspaceMissingInputMessageDialogTitle"), JOptionPane.INFORMATION_MESSAGE); } } /** * @return the kind of declaration specified by the user. */ private Kind getKind() { if (shouldUseKnownDeclaration()) { return Kind.KNOWN_DECL; } else if (shouldUseFileBasedDeclaration()) { return Kind.FILE_BASED; } else if (shouldUseMinimalDeclaration()) { return Kind.MINIMAL; } else { throw new IllegalStateException(); } } /** * @return true if the user has specified that a known declaration from the environment should be used. */ private boolean shouldUseKnownDeclaration() { return knownDeclarationRadioButton.isSelected(); } /** * @return true if the user has specified that a declaration file on the file system should be used. */ private boolean shouldUseFileBasedDeclaration() { return fileBasedDeclarationRadioButton.isSelected(); } /** * @return true if the user has specified that a minimal workspace declaration should be used. */ private boolean shouldUseMinimalDeclaration() { return minimalDeclarationRadioButton.isSelected(); } /** * @return the declaration file on the file system. */ private File getFileBasedDeclarationFileName() { return new File(fileBasedDeclarationFileNameField.getText().trim()); } /** * @return the name of the workspace declaration used to initialize the current workspace. */ private String getCurrentWorkspaceDeclarationName() { return workspaceManager.getInitialWorkspaceDeclarationName(); } /** * @return the workspace declaration stream provider for the workspace to switch to. */ WorkspaceDeclaration.StreamProvider getNextWorkspaceDeclarationStreamProvider() { if (shouldUseKnownDeclaration()) { Object selectedItem = knownDeclarationComboBox.getSelectedItem(); return (WorkspaceDeclaration.StreamProvider)selectedItem; } if (shouldUseFileBasedDeclaration()) { return new FileBasedWorkspaceDeclarationStreamProvider(getFileBasedDeclarationFileName()); } if (shouldUseMinimalDeclaration()) { return new MinimalWorkspaceDeclarationStreamProvider(createMinimalWorkspacePanel.getMinimalWorkspaceDeclaration()); } throw new IllegalStateException("Invalid declaration kind."); } /** * @return an array of the stream providers for the workspace declarations that are accessible from the current environment * (e.g. the nullary environment). */ private WorkspaceDeclaration.StreamProvider[] getWorkspaceDeclarationStreamProvidersFromEnvironment() { List<WorkspaceDeclaration.StreamProvider> providers = new ArrayList<WorkspaceDeclaration.StreamProvider>(); CALWorkspace workspace = workspaceManager.getWorkspace(); final String workspaceLocation = workspace.getWorkspaceLocationString(); WorkspaceDeclarationManager workspaceDeclarationManager = workspace.getWorkspaceDeclarationManager(); if (workspaceDeclarationManager != null) { ResourceStore resourceStore = workspaceDeclarationManager.getResourceStore(); Iterator<WorkspaceResource> it = resourceStore.getResourceIterator(); while (it.hasNext()) { final WorkspaceResource decl = it.next(); providers.add(new ResourceBasedWorkspaceDeclarationStreamProvider(decl, workspaceLocation)); } } Collections.sort(providers, new Comparator<Object>() { public int compare(final Object a, final Object b) { return a.toString().compareTo(b.toString()); } }); return providers.toArray(new WorkspaceDeclaration.StreamProvider[providers.size()]); } }