/* * 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. */ /* * JDBCGemGenerator.java * Creation date: Oct 9, 2003 * By: Richard Webster */ package org.openquark.gems.client.generators; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListModel; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.LayoutFocusTraversalPolicy; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.border.Border; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.openquark.cal.compiler.LanguageInfo; import org.openquark.cal.compiler.ModuleTypeInfo; import org.openquark.cal.compiler.Scope; import org.openquark.cal.compiler.TypeChecker; import org.openquark.cal.compiler.TypeConstructor; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.compiler.SourceModel.ModuleDefn; import org.openquark.cal.module.Cal.Collections.CAL_List; import org.openquark.cal.module.Cal.Core.CAL_Prelude; import org.openquark.cal.module.Cal.Data.CAL_DataGems; import org.openquark.cal.module.Cal.Data.CAL_Sql; import org.openquark.cal.services.GemEntity; import org.openquark.cal.services.GemFilter; import org.openquark.cal.services.GemViewer; import org.openquark.cal.services.IdentifierUtils; import org.openquark.cal.services.Perspective; import org.openquark.cal.valuenode.Target; import org.openquark.cal.valuenode.ValueNode; import org.openquark.gems.client.GemCutter; import org.openquark.gems.client.ValueRunner; import org.openquark.gems.client.valueentry.JDBCResultSetEditor; import org.openquark.gems.client.valueentry.ValueEditor; import org.openquark.gems.client.valueentry.ValueEditorHierarchyManager; import org.openquark.gems.client.valueentry.ValueEditorManager; import org.openquark.util.UnsafeCast; import org.openquark.util.datadictionary.ValueType; /** * A JDBC data source gem generator. * @author Richard Webster */ public class JDBCGemGenerator implements GemGenerator { /** The icon to use for the generator. */ private static final Icon GENERATOR_ICON = new ImageIcon(GemGenerator.class.getResource("/Resources/supercombinator.gif")); /** * @see org.openquark.gems.client.generators.GemGenerator#launchGenerator(javax.swing.JFrame, org.openquark.cal.services.Perspective, org.openquark.gems.client.ValueRunner, org.openquark.gems.client.valueentry.ValueEditorManager, org.openquark.cal.compiler.TypeChecker) */ public GemGenerator.GeneratedDefinitions launchGenerator(JFrame parent, Perspective perspective, ValueRunner valueRunner, ValueEditorManager valueEditorManager, TypeChecker typeChecker) { if (parent == null || perspective == null) { throw new NullPointerException(); } final JDBCGemGeneratorDialog generatorUI = new JDBCGemGeneratorDialog(parent, perspective, valueRunner, valueEditorManager, typeChecker); generatorUI.setVisible(true); return new GemGenerator.GeneratedDefinitions() { public ModuleDefn getModuleDefn() { return null; } public Map<String, String> getSourceElementMap() { return generatorUI.getSourceDefinitions(); } }; } /** * @see org.openquark.gems.client.generators.GemGenerator#getGeneratorMenuName() */ public String getGeneratorMenuName() { return GeneratorMessages.getString("JDBCGF_MenuName"); } /** * @see org.openquark.gems.client.generators.GemGenerator#getGeneratorTitle() */ public String getGeneratorTitle() { return GeneratorMessages.getString("JDBCGF_GenerateDataSourceTitle"); } /** * @see org.openquark.gems.client.generators.GemGenerator#getGeneratorIcon() */ public Icon getGeneratorIcon() { return GENERATOR_ICON; } /** * This is the user interface class for the JDBC gem generator. * @author Richard Webster */ private static class JDBCGemGeneratorDialog extends JDialog { private static final long serialVersionUID = -803397038103407688L; /** The icon to use for error messages. */ private static final Icon ERROR_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/error.gif")); /** The icon to use for warning messages. */ private static final Icon WARNING_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/warning.gif")); // /** The icon to use if everything is ok. */ // private static final Icon OK_ICON = new ImageIcon(GemCutter.class.getResource("/Resources/checkmark.gif")); /** Default width for list boxes in the dialog. */ private static final int DEFAULT_LIST_WIDTH = 160; /** Default height for list boxes in the dialog. */ private static final int DEFAULT_LIST_HEIGHT = 160; /** The perspective this UI is running in. */ private final Perspective perspective; /** A value runner for running simple CAL programs. */ private final ValueRunner valueRunner; /** The manager for value editors in the dialog. */ private final ValueEditorManager valueEditorManager; /** The hierarchy manager for value editors in the dialog. */ private final ValueEditorHierarchyManager valueEditorHierarchyManager; /** A type checker for fetching type information. */ private final TypeChecker typeChecker; /** * Map from source name to source code. * The list of source definitions we want to create. Ordered by insertion order, so that * definitions we want to create first will be created first. */ private final Map<String, String> sourceDefinitions = new LinkedHashMap<String, String>(); /** The panel which holds the pages of the wizard. */ private JPanel cardPanel; /** The card layout manager for the wizard. */ private final CardLayout cardLayout = new CardLayout(); /** The Finish button for the dialog. */ private JButton finishButton = null; /** The cancel button for the dialog. */ private JButton cancelButton = null; /** The previous button for the dialog. */ private JButton previousButton = null; /** The next button for the dialog. */ private JButton nextButton = null; /** A map of page names to pages. */ private Map<String, WizardCard> nameToPageMap = new HashMap<String, WizardCard>(); /** A stack of the previous and current pages. */ private Stack<WizardCard> viewedPageStack = new Stack<WizardCard>(); // The following fields are used to store the values when the pages of the wizard are committed. private String gemName = ""; private String gemComment = ""; private Scope gemScope = Scope.PUBLIC; private String connectionGemName = ""; private String sqlBuilderGemName = ""; private String tableName = ""; private final List <FieldInfo> selectedFields = new ArrayList<FieldInfo>(); private final List <SortField> sortFields = new ArrayList<SortField>(); private boolean includeRecordExtractorGem = false; /** * Constructor for a new generator ui. * @param parent the parent of the dialog * @param perspective the perspective the UI should use */ public JDBCGemGeneratorDialog(JFrame parent, Perspective perspective, ValueRunner valueRunner, ValueEditorManager valueEditorManager, TypeChecker typeChecker) { super(parent, true); if (perspective == null || valueRunner == null) { throw new NullPointerException(); } this.perspective = perspective; this.valueRunner = valueRunner; this.valueEditorManager = valueEditorManager; // this.valueEditorHierarchyManager = valueEditorHierarchyManager; this.valueEditorHierarchyManager = new ValueEditorHierarchyManager(valueEditorManager); this.typeChecker = typeChecker; setTitle(GeneratorMessages.getString("JDBCGF_CreateResultsetGemTitle")); getContentPane().setLayout(new BorderLayout()); getContentPane().add(getCardPanel(), BorderLayout.CENTER); getContentPane().add(getButtonPanel(), BorderLayout.SOUTH); pack(); // position in the center of the parent window int x = parent.getX() + parent.getWidth() / 2 - getWidth() / 2; int y = parent.getY() + parent.getHeight() / 2 - getHeight() / 2; setLocation(Math.max(parent.getX(), x), Math.max(parent.getY(), y)); // Prevent the dialog from being resized too small. // TODO: use the SizeConstrainer class here instead. final Dimension minimumSize = getSize(); addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { super.componentResized(e); Dimension size = getSize (); size.width = Math.max(minimumSize.width, size.width); size.height = Math.max(minimumSize.height, size.height); setSize (size); } }); } /** * @return the new source definitions that should be created */ public Map<String, String> getSourceDefinitions() { return sourceDefinitions; } /** * Returns a panel which holds the cards for each page of the wizard. */ private JPanel getCardPanel() { if (cardPanel == null) { cardPanel = new JPanel(); cardPanel.setLayout(cardLayout); WizardCard firstPage = new GemInfoCard(); addWizardPage(firstPage); addWizardPage(new SelectConnectionCard()); addWizardPage(new SelectTablesCard()); addWizardPage(new SelectFieldsCard()); addWizardPage(new SortingCard()); addWizardPage(new SqlQueryCard()); addWizardPage(new PreviewCard()); showFirstPage(firstPage); } return cardPanel; } /** * Adds a page to the wizard and builds a mapping of page names to pages. */ private void addWizardPage(WizardCard page) { page.buildUI(); String pageName = page.getCardName(); cardPanel.add(page, pageName); nameToPageMap.put(pageName, page); } /** * A helper class to hold information about a field. */ private static class FieldInfo { public final String fieldName; public final ValueType valueType; FieldInfo(String fieldName, ValueType valueType) { this.fieldName = fieldName; this.valueType = valueType; } public String toString() { return fieldName; } public boolean equals(Object other) { if (other == null || other.getClass() != getClass()) { return false; } FieldInfo otherFieldInfo = (FieldInfo) other; return fieldName.equals(otherFieldInfo.fieldName); } public int hashCode() { return fieldName.hashCode(); } } /** * A helper class to hold information about a sort field. */ private static class SortField { public final FieldInfo fieldInfo; public boolean ascending = true; SortField(FieldInfo fieldInfo, boolean ascending) { this.fieldInfo = fieldInfo; this.ascending = ascending; } public String toString() { String directionString = ascending ? "ASC" : "DESC"; return fieldInfo.toString() + " - " + directionString; } public boolean equals(Object other) { if (other == null || other.getClass() != getClass()) { return false; } SortField otherSortField = (SortField) other; return fieldInfo.equals(otherSortField.fieldInfo); } public int hashCode() { return fieldInfo.hashCode(); } } /** * Base class for UI panels of a wizard. */ private abstract class WizardCard extends JPanel { /** Indicates whether the Finish button will enable when the contents of this page are valid. */ private final boolean canFinishOnPage; /** * WizardCard constructor. */ WizardCard(boolean canFinishOnPage) { this.canFinishOnPage = canFinishOnPage; } /** * Constructs the UI. * This needs to be done after the constructor since it will call methods implemented * in subclasses. */ void buildUI() { setLayout(new BorderLayout()); add(getTitlePanel(), BorderLayout.NORTH); add(getMainPanel(), BorderLayout.CENTER); } /** * Returns a string which indentifies the page. */ abstract String getCardName(); /** * Returns the name of the following card, or null if this is the last card. */ abstract String getNextCardName(); /** * Returns the resource ID for the card title. */ abstract String getTitleResourceID(); /** * Returns the resource ID for the card subtitle. */ abstract String getSubtitleResourceID(); /** * Returns the panel containing the main UI for the card. */ abstract JPanel getMainPanel(); /** * Returns whether the values in the UI controls of this page are acceptable. */ abstract boolean isPageContentValid(); /** * If the values in the UI are acceptable, the values are copied to * the result info and true is returned. * Otherwise, a message should be displayed and false returned. */ abstract boolean commitChanges(); /** * This is called before displaying the page to allow the UI controls * to be set with the current values. */ void initializeControls() { updateButtonBar(); } /** * Enables/disables the Previous, Next, and Finish buttons in the dialog * depending on the current state of the page. */ void updateButtonBar() { boolean contentValid = isPageContentValid(); // Enable the Previous button, unless this is the first page. getPreviousButton().setEnabled(!isFirstPage()); // Enable the Next button, unless this is the last page. getNextButton().setEnabled(contentValid && !isLastPage()); // Enable the Finish button if it is allowed on this page. getFinishButton().setEnabled(contentValid && canFinishOnPage); } /** * Returns whether this is the first page of the wizard. */ boolean isFirstPage() { return (getFirstCard() == this); } /** * Returns whether this is the last page of the wizard. */ boolean isLastPage() { return (getNextCardName() == null); } /** * @return the white title panel that appears at the top of the dialog */ private JPanel getTitlePanel() { JPanel titlePanel = new JPanel(); titlePanel.setBackground(Color.WHITE); Border compoundBorder = BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder(5, 5, 5, 5)); titlePanel.setBorder(compoundBorder); titlePanel.setLayout(new BorderLayout(5, 5)); JLabel titleLabel = new JLabel(GeneratorMessages.getString(getTitleResourceID())); titleLabel.setFont(getFont().deriveFont(Font.BOLD, getFont().getSize() + 2)); titlePanel.add(titleLabel, BorderLayout.NORTH); JLabel subTitleLabel = new JLabel(GeneratorMessages.getString(getSubtitleResourceID())); titlePanel.add(subTitleLabel, BorderLayout.SOUTH); return titlePanel; } } /** * Panel to display UI for setting general properties for the gem (name, scope, comments, etc...). */ private class GemInfoCard extends WizardCard { private static final long serialVersionUID = 3421717262252083704L; static final String GEMINFO_CARDNAME = "GemInfo"; /** The text field for entering the name of the new gem. */ private final JTextField gemNameField = new JTextField(); /** The text field for entering the comment for the new gem. */ private final JTextField commentField = new JTextField(); /** The radio button for selecting private scope. */ private final JRadioButton privateButton = new JRadioButton(GeneratorMessages.getString("PrivateLabel")); /** The radio button for selecting public scope. */ private final JRadioButton publicButton = new JRadioButton(GeneratorMessages.getString("PublicLabel")); /** The button group for the radio buttons. */ private final ButtonGroup buttonGroup = new ButtonGroup(); /** A check box to indicate whether a record extractor gem should also be generated. */ private final JCheckBox addRecordExtractorCheck = new JCheckBox(GeneratorMessages.getString("JDBCGF_IncludeRecordExtractorGemLabel")); /** The label for displaying status messages. */ private final JLabel statusLabel = new JLabel(); /** Whether or not the user has typed into the name field. */ private boolean userHasTyped = false; /** * GemInfoCard constructor. */ GemInfoCard() { super(false); buttonGroup.add(publicButton); buttonGroup.add(privateButton); buttonGroup.setSelected(publicButton.getModel(), true); updateState(); // Make the class name field have default focus setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() { private static final long serialVersionUID = -4407775698002965050L; public Component getDefaultComponent(Container c) { return gemNameField; } }); // Add listeners to update the error message if things change gemNameField.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { userHasTyped = true; updateState(); } }); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getCardName() */ String getCardName() { return GEMINFO_CARDNAME; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getNextCardName() */ String getNextCardName() { return SelectConnectionCard.SELECTCONNECTION_CARDNAME; } /** * Returns the resource ID for the card title. */ String getTitleResourceID() { return "JDBCGF_GemInfoPageTitle"; } /** * Returns the resource ID for the card subtitle. */ String getSubtitleResourceID() { return "JDBCGF_GemInfoPageSubTitle"; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#isPageContentValid() */ boolean isPageContentValid() { return checkForValidName(); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#commitChanges() */ boolean commitChanges() { gemName = gemNameField.getText(); gemComment = commentField.getText(); gemScope = publicButton.getModel().isSelected() ? Scope.PUBLIC : Scope.PRIVATE; includeRecordExtractorGem = addRecordExtractorCheck.isSelected(); return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#initializeControls() */ void initializeControls() { super.initializeControls(); // Update the controls with the current values. gemNameField.setText(gemName); commentField.setText(gemComment); if (gemScope == Scope.PUBLIC) { publicButton.setSelected(true); } else { privateButton.setSelected(true); } addRecordExtractorCheck.setSelected(includeRecordExtractorGem); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getMainPanel() */ JPanel getMainPanel() { JPanel javaPanel = new JPanel(); javaPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); javaPanel.setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; constraints.insets = new Insets(5, 5, 10, 5); javaPanel.add(statusLabel, constraints); statusLabel.setFont(getFont().deriveFont(Font.BOLD)); constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; constraints.insets = new Insets(5, 5, 5, 5); javaPanel.add(new JLabel(GeneratorMessages.getString("JGF_GemNameHeader")), constraints); constraints.gridx = 2; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; gemNameField.setColumns(20); javaPanel.add(gemNameField, constraints); constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; javaPanel.add(new JLabel(GeneratorMessages.getString("JGF_CommentHeader")), constraints); constraints.gridx = 2; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; gemNameField.setColumns(20); javaPanel.add(commentField, constraints); constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; javaPanel.add(new JLabel(GeneratorMessages.getString("JGF_VisibilityHeader")), constraints); constraints.gridx = 2; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; javaPanel.add(publicButton, constraints); constraints.gridx = 3; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; javaPanel.add(privateButton, constraints); constraints.gridx = 1; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; javaPanel.add(addRecordExtractorCheck, constraints); constraints.gridx = 4; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; javaPanel.add(new JLabel(""), constraints); return javaPanel; } /** * Updates the state of the Ok button to only be enabled if the user has entered * all required information. */ private void updateState() { if (!checkForValidName()) { if (userHasTyped) { statusLabel.setText(GeneratorMessages.getString("JGF_InvalidGemName")); statusLabel.setIcon(ERROR_ICON); } } else { // check if the gem or data type already exists String gemName = gemNameField.getText(); if (perspective.getWorkingModuleTypeInfo().getFunction(gemName) != null) { statusLabel.setText(GeneratorMessages.getString("JGF_GemExists")); statusLabel.setIcon(WARNING_ICON); } else { // statusLabel.setText(GeneratorMessages.getString("JDBCGF_OkCreateGem")); // statusLabel.setIcon(OK_ICON); statusLabel.setText(""); statusLabel.setIcon(null); } } // Enable/disable the Next and/or Finish buttons in the dialog. updateButtonBar(); } /** * Checks the vailidity of the name in the gem name field. * @return true if the provided name is valid, false otherwise */ private boolean checkForValidName() { String gemName = gemNameField.getText(); // If the user has typed a name, check if for validity. boolean validGemName = LanguageInfo.isValidFunctionName(gemName); // if (!validGemName) { // okButton.setEnabled(false); // // if (userHasTyped) { // statusLabel.setText(GeneratorMessages.getString("JGF_InvalidGemName")); // statusLabel.setIcon(ERROR_ICON); // } // } return validGemName; } } /** * Panel to display UI for selecting the JDBC connection gem. */ private class SelectConnectionCard extends WizardCard { private static final long serialVersionUID = 7494176766603605837L; static final String SELECTCONNECTION_CARDNAME = "SelectConnection"; /** The combobox for selecting a connection gem. */ private final JComboBox connectionGemCombo = new JComboBox(new DefaultComboBoxModel()); /** The combobox for selecting a SQL builder gem. */ private final JComboBox sqlBuilderGemCombo = new JComboBox(new DefaultComboBoxModel()); /** * SelectConnectionCard constructor. */ SelectConnectionCard() { super(false); // Make the connection combo box have default focus setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() { private static final long serialVersionUID = -226727560145900885L; public Component getDefaultComponent(Container c) { return connectionGemCombo; } }); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getCardName() */ String getCardName() { return SELECTCONNECTION_CARDNAME; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getNextCardName() */ String getNextCardName() { return SelectTablesCard.SELECTTABLES_CARDNAME; } /** * Returns the resource ID for the card title. */ String getTitleResourceID() { return "JDBCGF_SelectConnectionPageTitle"; } /** * Returns the resource ID for the card subtitle. */ String getSubtitleResourceID() { return "JDBCGF_SelectConnectionPageSubTitle"; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#isPageContentValid() */ boolean isPageContentValid() { return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#commitChanges() */ boolean commitChanges() { connectionGemName = connectionGemCombo.getSelectedItem().toString(); sqlBuilderGemName = sqlBuilderGemCombo.getSelectedItem().toString(); if (connectionGemName.equals("<new>")) { JOptionPane.showMessageDialog(this, "The '<new>' connection gem option is not implemented yet."); return false; } // TODO: attempt to open the connection. // If this fails, display the error message and return false. // TODO: invalidate all info about the query if the connection gem changes... return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#initializeControls() */ void initializeControls() { super.initializeControls(); // Populate the connection gems list, if necessary. if (connectionGemCombo.getItemCount() == 0) { // Add a special option to create a new connection. connectionGemCombo.addItem("<new>"); // Add each available connection gem. List<String> connectionGems = fetchConnectionGemNames(); for (final String aConnectionGemName : connectionGems) { connectionGemCombo.addItem(aConnectionGemName); } } // Populate the SQL builder gem list, if necessary. if (sqlBuilderGemCombo.getItemCount() == 0) { // Add each available SQL builder gem. List<String> sqlBuilderGems = fetchSqlBuilderGemNames(); for (final String aSqlBuilderGemName : sqlBuilderGems) { sqlBuilderGemCombo.addItem(aSqlBuilderGemName); } } // Update the controls with the current values. connectionGemCombo.setSelectedItem(connectionGemName); sqlBuilderGemCombo.setSelectedItem(sqlBuilderGemName); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getMainPanel() */ JPanel getMainPanel() { JPanel javaPanel = new JPanel(); javaPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); javaPanel.setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.fill = GridBagConstraints.HORIZONTAL; // constraints.gridx = 1; // constraints.weightx = 0; // constraints.weighty = 0; // constraints.gridwidth = GridBagConstraints.REMAINDER; // constraints.insets = new Insets(5, 5, 10, 5); // javaPanel.add(statusLabel, constraints); // // statusLabel.setFont(getFont().deriveFont(Font.BOLD)); constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; constraints.insets = new Insets(5, 5, 5, 5); javaPanel.add(new JLabel(GeneratorMessages.getString("JDBCGF_ConnectionGemNameHeader")), constraints); constraints.gridx = 2; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; // connectionGemCombo.setColumns(20); javaPanel.add(connectionGemCombo, constraints); constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; constraints.insets = new Insets(5, 5, 5, 5); javaPanel.add(new JLabel(GeneratorMessages.getString("JDBCGF_SqlBuilderGemNameHeader")), constraints); constraints.gridx = 2; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; javaPanel.add(sqlBuilderGemCombo, constraints); constraints.gridx = 4; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; javaPanel.add(new JLabel(""), constraints); return javaPanel; } /** * Returns a list of the names of the available connection gems. */ private List <String> fetchConnectionGemNames() { TypeConstructor typeConstructor = perspective.getTypeConstructor(CAL_DataGems.TypeConstructors.JDBCConnection); final TypeExpr typeExpr = TypeExpr.makeNonParametricType(typeConstructor); return fetchGemsOfType(typeExpr); } /** * Returns a list of the names of the available connection gems. */ private List <String> fetchSqlBuilderGemNames() { TypeExpr typeExpr = typeChecker.getTypeFromString(CAL_Sql.TypeConstructors.SqlBuilder.getQualifiedName(), perspective.getWorkingModule().getName(), null); return fetchGemsOfType(typeExpr); } /** * Returns a list of the names of the gems which have the specified return type and no inputs. */ private List <String> fetchGemsOfType(final TypeExpr returnTypeExpr) { GemViewer gemViewer = new GemViewer(); final ModuleTypeInfo moduleTypeInfo = perspective.getWorkingModuleTypeInfo(); // Create a filter which will find all gems which return the specified type // and take no inputs. // TODO: add support for gems which do take inputs... GemFilter filter = new GemFilter() { public boolean select(GemEntity gemEntity) { TypeExpr gemType = gemEntity.getTypeExpr(); return TypeExpr.canPatternMatch(gemType, returnTypeExpr, moduleTypeInfo); } }; gemViewer.addFilter(filter); Perspective newPerspective = new Perspective(perspective.getWorkspace(), perspective.getWorkingModule()); newPerspective.setGemEntityViewer(gemViewer); Set<GemEntity> matchingGems = newPerspective.getVisibleGemEntities(); // Extract the gem names from the list. List<String> gemNames = new ArrayList<String>(); for (final GemEntity gemEntity : matchingGems) { gemNames.add(gemEntity.getName().getQualifiedName()); } return gemNames; } } /** * Panel to display UI for selecting the tables. */ private class SelectTablesCard extends WizardCard { private static final long serialVersionUID = 255937577857138828L; static final String SELECTTABLES_CARDNAME = "SelectTables"; /** The connection gem used to fetch the list of tables currently in the combobox. */ private String connectionForTables = ""; /** The combobox for selecting a table. */ private final JComboBox tableCombo = new JComboBox(new DefaultComboBoxModel()); // TODO: use this later... // /** The list displaying the available tables. */ // private final JListBox sourceTableList; // // /** The list display the selected tables. */ // private final JListBox destTableList; /** * SelectTablesCard constructor. */ SelectTablesCard() { super(false); // TODO: use this later to tell the user to select at least one table... // updateState(); // Make the connection combo box have default focus setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() { private static final long serialVersionUID = 4742375628497579705L; public Component getDefaultComponent(Container c) { return tableCombo; } }); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getCardName() */ String getCardName() { return SELECTTABLES_CARDNAME; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getNextCardName() */ String getNextCardName() { return SelectFieldsCard.SELECTFIELDS_CARDNAME; } /** * Returns the resource ID for the card title. */ String getTitleResourceID() { return "JDBCGF_SelectTablesPageTitle"; } /** * Returns the resource ID for the card subtitle. */ String getSubtitleResourceID() { return "JDBCGF_SelectTablesPageSubTitle"; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#isPageContentValid() */ boolean isPageContentValid() { // TODO: handle the case where no tables can be found in the connection... // TODO: check that at least one table is selected (once multi-table support is added)... return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#commitChanges() */ boolean commitChanges() { tableName = tableCombo.getSelectedItem().toString(); // TODO: invalidate all info on subsequent tabs (field selection, etc...) if the connection gem changes... return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#initializeControls() */ void initializeControls() { super.initializeControls(); // If the connection has been changed since the tables were fetched, rebuild the list. if (!connectionForTables.equals(connectionGemName)) { tableCombo.removeAllItems(); } // Populate the table list, if necessary. if (tableCombo.getItemCount() == 0) { // Add each available table. List<String> tableNames = fetchTableNames(); for (final String aTableName : tableNames) { tableCombo.addItem(aTableName); } connectionForTables = connectionGemName; } // Update the controls with the current values. tableCombo.setSelectedItem(tableName); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getMainPanel() */ JPanel getMainPanel() { JPanel javaPanel = new JPanel(); javaPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); javaPanel.setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.fill = GridBagConstraints.HORIZONTAL; // constraints.gridx = 1; // constraints.weightx = 0; // constraints.weighty = 0; // constraints.gridwidth = GridBagConstraints.REMAINDER; // constraints.insets = new Insets(5, 5, 10, 5); // javaPanel.add(statusLabel, constraints); // // statusLabel.setFont(getFont().deriveFont(Font.BOLD)); constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; constraints.insets = new Insets(5, 5, 5, 5); javaPanel.add(new JLabel(GeneratorMessages.getString("JDBCGF_TableNameHeader")), constraints); constraints.gridx = 2; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; javaPanel.add(tableCombo, constraints); constraints.gridx = 4; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; javaPanel.add(new JLabel(""), constraints); return javaPanel; } // /** // * Updates the state of the Ok button to only be enabled if the user has entered // * all required information. // */ // private void updateState() { //// //// if (!checkForValidName()) { //// if (userHasTyped) { //// statusLabel.setText(GeneratorMessages.getString("JGF_InvalidGemName")); //// statusLabel.setIcon(ERROR_ICON); //// } //// } //// else { //// // check if the gem or data type already exists //// String gemName = gemNameField.getText(); //// if (perspective.getWorkingModuleTypeInfo().getSupercombinator(gemName) != null) { //// statusLabel.setText(GeneratorMessages.getString("JGF_GemExists")); //// statusLabel.setIcon(WARNING_ICON); //// } //// else { ////// statusLabel.setText(GeneratorMessages.getString("JDBCGF_OkCreateGem")); ////// statusLabel.setIcon(OK_ICON); //// statusLabel.setText(""); //// statusLabel.setIcon(null); //// } //// } // // // Enable/disable the Next and/or Finish buttons in the dialog. // updateButtonBar(); // } /** * Returns a list of the table names for the current connection. */ private List <String> fetchTableNames() { // TODO: return qualified table names... String code = CAL_Prelude.Functions.output.getQualifiedName() + " (" + CAL_DataGems.Functions.jdbcGetConnectionTableNames.getQualifiedName() + " " + connectionGemName + ")"; return UnsafeCast.unsafeCast(getListFromCode(code)); } } /** * Panel to display UI for selecting fields. */ private class SelectFieldsCard extends WizardCard { private static final long serialVersionUID = 1702879395054351612L; static final String SELECTFIELDS_CARDNAME = "SelectFields"; /** The label for displaying status messages. */ private final JLabel statusLabel = new JLabel(); /** Whether or not the user has added a field to the selected list. */ private boolean fieldAdded = false; /** The connection gem used to fetch the list of tables currently in the combobox. */ private String connectionForFields = ""; /** The table used to fetch the list of fields currently in the source fields list. */ private String tableForFields = ""; /** The list displaying the available fields. */ private final JList availableFieldsList = new JList(new DefaultListModel()); /** The list display the selected fields. */ private final JList selectedFieldsList = new JList(new DefaultListModel()); /** The button for adding a field to the selected list. */ private final JButton addButton = new JButton(">"); /** The button for removing a field from the selected list. */ private final JButton removeButton = new JButton("<"); /** The button for moving a field up in the selected list. */ private final JButton upButton = new JButton(new ImageIcon(GemCutter.class.getResource("/Resources/up.gif"))); /** The button for moving a field up in the selected list. */ private final JButton downButton = new JButton(new ImageIcon(GemCutter.class.getResource("/Resources/down.gif"))); /** * SelectFieldsCard constructor. */ SelectFieldsCard() { super(true); updateState(); // Enable the 'add' button when a field is selected in the available fields list. availableFieldsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); availableFieldsList.addListSelectionListener(new ListSelectionListener () { public void valueChanged(ListSelectionEvent e) { boolean itemSelected = (availableFieldsList.getSelectedIndex () >= 0); addButton.setEnabled(itemSelected); } }); // Add fields when double clicked in the available fields list. availableFieldsList.addMouseListener(new MouseAdapter() { public void mouseReleased(MouseEvent e) { if (e.getClickCount() > 1) { onAddFields(); } } }); // Enable the 'remove' button when a field is selected in the selected fields list. // Also, enable the 'up' and 'down' buttons where appropriate. final DefaultListModel selectedListModel = (DefaultListModel) selectedFieldsList.getModel(); selectedFieldsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); selectedFieldsList.addListSelectionListener(new ListSelectionListener () { public void valueChanged(ListSelectionEvent e) { int itemSelected = selectedFieldsList.getSelectedIndex (); removeButton.setEnabled(itemSelected >= 0); upButton.setEnabled(itemSelected > 0); downButton.setEnabled(itemSelected < selectedListModel.getSize() - 1); } }); // Remove fields when double clicked in the selected fields list. selectedFieldsList.addMouseListener(new MouseAdapter() { public void mouseReleased(MouseEvent e) { if (e.getClickCount() > 1) { onRemoveFields(); } } }); // Set the action event handler for the 'add' button. addButton.setEnabled(false); addButton.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ onAddFields(); } }); // Set the action event handler for the 'remove' button. removeButton.setEnabled(false); removeButton.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ onRemoveFields(); } }); // Set the action event handler for the 'up' button. upButton.setEnabled(false); upButton.setMargin(new Insets (0, 0, 0, 0)); upButton.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ int selectedIndex = selectedFieldsList.getSelectedIndex(); if (selectedIndex > 0) { // Remove the item from the list and insert it at the previous position. Object item = selectedListModel.remove(selectedIndex); selectedListModel.insertElementAt(item, selectedIndex - 1); // Reselect an appropriate item in the list. selectedFieldsList.setSelectedIndex(selectedIndex - 1); } } }); // Set the action event handler for the 'down' button. downButton.setEnabled(false); downButton.setMargin(new Insets (0, 0, 0, 0)); downButton.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ int selectedIndex = selectedFieldsList.getSelectedIndex(); if (selectedIndex >= 0 && selectedIndex < selectedListModel.getSize() - 1) { // Remove the item from the list and insert it at the next position. Object item = selectedListModel.remove(selectedIndex); selectedListModel.insertElementAt(item, selectedIndex + 1); // Reselect an appropriate item in the list. selectedFieldsList.setSelectedIndex(selectedIndex + 1); } } }); // Make the source fields list box have default focus setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() { private static final long serialVersionUID = -4997301051799673595L; public Component getDefaultComponent(Container c) { return availableFieldsList; } }); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getCardName() */ String getCardName() { return SELECTFIELDS_CARDNAME; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getNextCardName() */ String getNextCardName() { return SortingCard.SORTING_CARDNAME; } /** * Returns the resource ID for the card title. */ String getTitleResourceID() { return "JDBCGF_SelectFieldsPageTitle"; } /** * Returns the resource ID for the card subtitle. */ String getSubtitleResourceID() { return "JDBCGF_SelectFieldsPageSubTitle"; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#isPageContentValid() */ boolean isPageContentValid() { // Check that at least one field has been selected. return selectedFieldsList.getModel().getSize() > 0; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#commitChanges() */ boolean commitChanges() { selectedFields.clear(); ListModel model = selectedFieldsList.getModel(); for (int itemN = 0, nItems = model.getSize(); itemN < nItems; ++itemN) { FieldInfo fieldInfo = (FieldInfo) model.getElementAt(itemN); selectedFields.add(fieldInfo); } return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#initializeControls() */ void initializeControls() { super.initializeControls(); DefaultListModel sourceModel = (DefaultListModel) availableFieldsList.getModel(); DefaultListModel destModel = (DefaultListModel) selectedFieldsList.getModel(); // If the connection or table has been changed since the fields were fetched, rebuild the list. if (!connectionForFields.equals(connectionGemName) || !tableForFields.equals(tableName)) { sourceModel.clear(); destModel.clear(); } // Populate the source fields list, if necessary. if (sourceModel.isEmpty()) { // Add each available field. List<FieldInfo> fieldsInfo = fetchFieldsInfo(); for (final FieldInfo fieldInfo : fieldsInfo) { sourceModel.addElement(fieldInfo); } connectionForFields = connectionGemName; tableForFields = tableName; // Select the first item in the selected fields list, if any. if (!sourceModel.isEmpty()) { availableFieldsList.setSelectedIndex(0); } } // Update the controls with the current values. destModel.clear(); for (final FieldInfo fieldInfo : selectedFields) { destModel.addElement(fieldInfo); } // Select the first item in the selected fields list, if any. if (!destModel.isEmpty()) { selectedFieldsList.setSelectedIndex(0); } } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getMainPanel() */ JPanel getMainPanel() { JPanel javaPanel = new JPanel(); javaPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); javaPanel.setLayout(new GridBagLayout()); JLabel availableLabel = new JLabel (GeneratorMessages.getString("JDBCGF_AvailableFieldsHeader")); JLabel selectedLabel = new JLabel (GeneratorMessages.getString("JDBCGF_SelectedFieldsHeader")); JScrollPane availableFieldsScrollPane = new JScrollPane(availableFieldsList); JScrollPane selectedFieldsScrollPane = new JScrollPane(selectedFieldsList); availableFieldsScrollPane.setPreferredSize(new Dimension(DEFAULT_LIST_WIDTH, DEFAULT_LIST_HEIGHT)); selectedFieldsScrollPane.setPreferredSize(new Dimension(DEFAULT_LIST_WIDTH, DEFAULT_LIST_HEIGHT)); Box addRemoveBox = Box.createVerticalBox(); addRemoveBox.add(addButton); addRemoveBox.add(removeButton); Box upDownBox = Box.createVerticalBox(); upDownBox.add(upButton); upDownBox.add(downButton); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; constraints.insets = new Insets(5, 5, 10, 5); javaPanel.add(statusLabel, constraints); statusLabel.setFont(getFont().deriveFont(Font.BOLD)); constraints.gridx = 1; constraints.gridy = 1; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = 1; constraints.insets = new Insets(5, 5, 5, 5); javaPanel.add(availableLabel, constraints); constraints.gridx = 3; javaPanel.add(selectedLabel, constraints); constraints.gridx = 1; constraints.gridy = 2; constraints.weighty = 1; constraints.fill = GridBagConstraints.BOTH; javaPanel.add(availableFieldsScrollPane, constraints); constraints.gridx = 3; javaPanel.add(selectedFieldsScrollPane, constraints); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 2; constraints.weightx = 0; constraints.weighty = 0; constraints.anchor = GridBagConstraints.CENTER; javaPanel.add(addRemoveBox, constraints); constraints.gridx = 4; javaPanel.add(upDownBox, constraints); constraints.anchor = GridBagConstraints.NORTHWEST; // constraints.gridx = 4; // constraints.weightx = 1; // constraints.weighty = 0; // constraints.gridwidth = GridBagConstraints.REMAINDER; // javaPanel.add(new JLabel(""), constraints); return javaPanel; } /** * Updates the state of the buttons depending on what is available and selected. */ private void updateState() { if (fieldAdded && selectedFieldsList.getModel().getSize() == 0) { statusLabel.setText(GeneratorMessages.getString("JDBCGF_FieldRequired")); statusLabel.setIcon(ERROR_ICON); } else { statusLabel.setText(""); statusLabel.setIcon(null); } // // if (!checkForValidName()) { // if (userHasTyped) { // statusLabel.setText(GeneratorMessages.getString("JGF_InvalidGemName")); // statusLabel.setIcon(ERROR_ICON); // } // } // else { // // check if the gem or data type already exists // String gemName = gemNameField.getText(); // if (perspective.getWorkingModuleTypeInfo().getSupercombinator(gemName) != null) { // statusLabel.setText(GeneratorMessages.getString("JGF_GemExists")); // statusLabel.setIcon(WARNING_ICON); // } // else { //// statusLabel.setText(GeneratorMessages.getString("JDBCGF_OkCreateGem")); //// statusLabel.setIcon(OK_ICON); // statusLabel.setText(""); // statusLabel.setIcon(null); // } // } // Enable/disable the Next and/or Finish buttons in the dialog. updateButtonBar(); } /** * Returns a list of the field names for the current table and connection. */ private List<FieldInfo> fetchFieldsInfo() { // TODO: return qualified field names if multiple tables are selected... // TODO: also fetch info about the field type, max length, etc... // TODO: marshal the list of records directly instead of converting to a tuple once this is implemented for records... String code = CAL_Prelude.Functions.output.getQualifiedName() + " (" + CAL_List.Functions.map.getQualifiedName() + " (\\rec -> (rec.columnName, rec.valueType)) (" + CAL_DataGems.Functions.jdbcGetTableFieldInfo.getQualifiedName() + " " + connectionGemName + " \"" + tableName + "\"))"; List<List<?>> tupleList = UnsafeCast.unsafeCast(getListFromCode(code)); List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>(); for (final List<?> tuple : tupleList) { String fieldName = (String) tuple.get(0); ValueType valueType = (ValueType) tuple.get(1); fieldInfoList.add(new FieldInfo(fieldName, valueType)); } return fieldInfoList; } /** * Add any selected fields from the available fields list to the selected fields list. */ private void onAddFields(){ final DefaultListModel availableListModel = (DefaultListModel) availableFieldsList.getModel(); final DefaultListModel selectedListModel = (DefaultListModel) selectedFieldsList.getModel(); Object[] selectedValues = availableFieldsList.getSelectedValues(); int nSelected = selectedValues.length; for (int selectionN = 0; selectionN < nSelected; ++selectionN) { FieldInfo selectedValue = (FieldInfo) selectedValues[selectionN]; // Don't add the same field multiple times. if (!selectedListModel.contains(selectedValue)) { selectedListModel.addElement(selectedValue); fieldAdded = true; } } if (nSelected == 1) { // Select the new item in the selected fields list. selectedFieldsList.setSelectedIndex(selectedListModel.getSize() - 1); // Move the selection to the next field in the available fields list. int[] selectedIndexes = availableFieldsList.getSelectedIndices(); if (selectedIndexes.length == 1) { int itemSelected = selectedIndexes[0]; // availableFieldsList.getSelectedIndex(); if (itemSelected >= 0 && itemSelected < (availableListModel.getSize() - 1)) { availableFieldsList.setSelectedIndex(itemSelected + 1); } } } // Update the state of the wizard's buttons. updateState(); } /** * Removes any selected fields from the selected fields list. */ public void onRemoveFields(){ final DefaultListModel selectedListModel = (DefaultListModel) selectedFieldsList.getModel(); int selectedIndex = selectedFieldsList.getSelectedIndex(); if (selectedIndex >= 0) { selectedListModel.remove(selectedIndex); // Reselect an appropriate item in the list. int newSelection = selectedIndex; if (newSelection >= selectedListModel.getSize()) { newSelection = selectedListModel.getSize() - 1; } selectedFieldsList.setSelectedIndex(newSelection); // Update the state of the wizard's buttons. updateState(); } } } /** * Panel to display UI for specifying sorting. */ private class SortingCard extends WizardCard { private static final long serialVersionUID = 434382250419609741L; static final String SORTING_CARDNAME = "Sorting"; /** The label for displaying status messages. */ private final JLabel statusLabel = new JLabel(); /** The connection gem used to fetch the list of tables currently in the combobox. */ private String connectionForFields = ""; /** The table used to fetch the list of fields currently in the source fields list. */ private String tableForFields = ""; /** The list displaying the available fields. */ private final JList availableFieldsList = new JList(new DefaultListModel()); /** The list display the sorting fields. */ private final JList sortFieldsList = new JList(new DefaultListModel()); /** The button for adding a field to the sorting list. */ private final JButton addButton = new JButton(">"); /** The button for removing a field from the sorting list. */ private final JButton removeButton = new JButton("<"); /** The button for moving a field up in the sorting list. */ private final JButton upButton = new JButton(new ImageIcon(GemCutter.class.getResource("/Resources/up.gif"))); /** The button for moving a field up in the sorting list. */ private final JButton downButton = new JButton(new ImageIcon(GemCutter.class.getResource("/Resources/down.gif"))); /** The check box which controls whether the sorting is ascending or descending. */ private final JCheckBox ascendingCheckBox = new JCheckBox(GeneratorMessages.getString("JDBCGF_SortAscendingLabel")); /** * SortingCard constructor. */ SortingCard() { super(true); // Enable the 'add' button when a field is selected in the available fields list. availableFieldsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); availableFieldsList.addListSelectionListener(new ListSelectionListener () { public void valueChanged(ListSelectionEvent e) { boolean itemSelected = (availableFieldsList.getSelectedIndex () >= 0); addButton.setEnabled(itemSelected); } }); // Add fields when double clicked in the available fields list. availableFieldsList.addMouseListener(new MouseAdapter() { public void mouseReleased(MouseEvent e) { if (e.getClickCount() > 1) { onAddFields(); } } }); // Enable the 'remove' button when a field is selected in the sorting fields list. // Also, enable the 'up' and 'down' buttons where appropriate. // Set the correct value for the Ascending check box as well. final DefaultListModel sortListModel = (DefaultListModel) sortFieldsList.getModel(); sortFieldsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); sortFieldsList.addListSelectionListener(new ListSelectionListener () { public void valueChanged(ListSelectionEvent e) { int itemSelected = sortFieldsList.getSelectedIndex (); removeButton.setEnabled(itemSelected >= 0); ascendingCheckBox.setEnabled(itemSelected >= 0); if (itemSelected < 0) { ascendingCheckBox.setSelected(false); } upButton.setEnabled(itemSelected > 0); downButton.setEnabled(itemSelected < sortListModel.getSize() - 1); SortField sortField = (SortField) sortFieldsList.getSelectedValue(); if (sortField != null) { ascendingCheckBox.setSelected(sortField.ascending); } } }); // Upate the selected sort field when the ascending check box is changed. ascendingCheckBox.setEnabled(false); ascendingCheckBox.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ SortField sortField = (SortField) sortFieldsList.getSelectedValue(); if (sortField != null) { sortField.ascending = ascendingCheckBox.isSelected(); sortFieldsList.repaint(); } } }); // Remove fields when double clicked in the sorting fields list. sortFieldsList.addMouseListener(new MouseAdapter() { public void mouseReleased(MouseEvent e) { if (e.getClickCount() > 1) { onRemoveFields(); } } }); // Set the action event handler for the 'add' button. addButton.setEnabled(false); addButton.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ onAddFields(); } }); // Set the action event handler for the 'remove' button. removeButton.setEnabled(false); removeButton.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ onRemoveFields(); } }); // Set the action event handler for the 'up' button. upButton.setEnabled(false); upButton.setMargin(new Insets (0, 0, 0, 0)); upButton.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ int selectedIndex = sortFieldsList.getSelectedIndex(); if (selectedIndex > 0) { // Remove the item from the list and insert it at the previous position. Object item = sortListModel.remove(selectedIndex); sortListModel.insertElementAt(item, selectedIndex - 1); // Reselect an appropriate item in the list. sortFieldsList.setSelectedIndex(selectedIndex - 1); } } }); // Set the action event handler for the 'down' button. downButton.setEnabled(false); downButton.setMargin(new Insets (0, 0, 0, 0)); downButton.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e){ int selectedIndex = sortFieldsList.getSelectedIndex(); if (selectedIndex >= 0 && selectedIndex < sortListModel.getSize() - 1) { // Remove the item from the list and insert it at the next position. Object item = sortListModel.remove(selectedIndex); sortListModel.insertElementAt(item, selectedIndex + 1); // Reselect an appropriate item in the list. sortFieldsList.setSelectedIndex(selectedIndex + 1); } } }); // Make the source fields list box have default focus setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() { private static final long serialVersionUID = 5994841658123338234L; public Component getDefaultComponent(Container c) { return availableFieldsList; } }); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getCardName() */ String getCardName() { return SORTING_CARDNAME; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getNextCardName() */ String getNextCardName() { return SqlQueryCard.SQLQUERY_CARDNAME; } /** * Returns the resource ID for the card title. */ String getTitleResourceID() { return "JDBCGF_SortingPageTitle"; } /** * Returns the resource ID for the card subtitle. */ String getSubtitleResourceID() { return "JDBCGF_SortingPageSubTitle"; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#isPageContentValid() */ boolean isPageContentValid() { return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#commitChanges() */ boolean commitChanges() { sortFields.clear(); ListModel model = sortFieldsList.getModel(); for (int itemN = 0, nItems = model.getSize(); itemN < nItems; ++itemN) { SortField sortField = (SortField) model.getElementAt(itemN); sortFields.add(sortField); } return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#initializeControls() */ void initializeControls() { super.initializeControls(); DefaultListModel sourceModel = (DefaultListModel) availableFieldsList.getModel(); DefaultListModel destModel = (DefaultListModel) sortFieldsList.getModel(); // If the connection or table has been changed since the fields were fetched, rebuild the list. if (!connectionForFields.equals(connectionGemName) || !tableForFields.equals(tableName)) { sourceModel.clear(); destModel.clear(); } // Populate the source fields list, if necessary. if (sourceModel.isEmpty()) { // Add each available field. List<FieldInfo> fieldsInfo = fetchFieldsInfo(); for (final FieldInfo fieldInfo : fieldsInfo) { sourceModel.addElement(fieldInfo); } connectionForFields = connectionGemName; tableForFields = tableName; // Select the first item in the available fields list, if any. if (!sourceModel.isEmpty()) { availableFieldsList.setSelectedIndex(0); } } // Update the controls with the current values. destModel.clear(); for (final SortField sortField : sortFields) { destModel.addElement(sortField); } // Select the first item in the sort fields list, if any. if (!destModel.isEmpty()) { sortFieldsList.setSelectedIndex(0); } } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getMainPanel() */ JPanel getMainPanel() { JPanel javaPanel = new JPanel(); javaPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); javaPanel.setLayout(new GridBagLayout()); JLabel availableLabel = new JLabel (GeneratorMessages.getString("JDBCGF_AvailableFieldsHeader")); JLabel selectedLabel = new JLabel (GeneratorMessages.getString("JDBCGF_SortFieldsHeader")); JScrollPane availableFieldsScrollPane = new JScrollPane(availableFieldsList); JScrollPane sortFieldsScrollPane = new JScrollPane(sortFieldsList); availableFieldsScrollPane.setPreferredSize(new Dimension(DEFAULT_LIST_WIDTH, DEFAULT_LIST_HEIGHT)); sortFieldsScrollPane.setPreferredSize(new Dimension(DEFAULT_LIST_WIDTH, DEFAULT_LIST_HEIGHT)); Box addRemoveBox = Box.createVerticalBox(); addRemoveBox.add(addButton); addRemoveBox.add(removeButton); Box upDownBox = Box.createVerticalBox(); upDownBox.add(upButton); upDownBox.add(downButton); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; constraints.insets = new Insets(5, 5, 10, 5); javaPanel.add(statusLabel, constraints); statusLabel.setFont(getFont().deriveFont(Font.BOLD)); constraints.gridx = 1; constraints.gridy = 1; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = 1; constraints.insets = new Insets(5, 5, 5, 5); javaPanel.add(availableLabel, constraints); constraints.gridx = 3; javaPanel.add(selectedLabel, constraints); constraints.gridx = 1; constraints.gridy = 2; constraints.weighty = 1; constraints.fill = GridBagConstraints.BOTH; javaPanel.add(availableFieldsScrollPane, constraints); constraints.gridx = 3; javaPanel.add(sortFieldsScrollPane, constraints); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 2; constraints.weightx = 0; constraints.weighty = 0; constraints.anchor = GridBagConstraints.CENTER; javaPanel.add(addRemoveBox, constraints); constraints.gridx = 4; javaPanel.add(upDownBox, constraints); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.gridx = 3; constraints.gridy = 3; constraints.weightx = 1; constraints.weighty = 0; constraints.gridwidth = 1; constraints.insets = new Insets(5, 5, 5, 5); javaPanel.add(ascendingCheckBox, constraints); // constraints.gridx = 4; // constraints.weightx = 1; // constraints.weighty = 0; // constraints.gridwidth = GridBagConstraints.REMAINDER; // javaPanel.add(new JLabel(""), constraints); return javaPanel; } /** * Returns a list of the field names for the current table and connection. */ private List<FieldInfo> fetchFieldsInfo() { // Fetch info about the available fields. // Exclude any long-type fields (blobs, clobs, etc...) since these cannot typically be sorted on. // TODO: return qualified field names if multiple tables are selected... // TODO: also fetch info about the max length, etc... // TODO: marshal the list of records directly instead of converting to a tuple once this is implemented for records... String code = CAL_Prelude.Functions.output.getQualifiedName() + " (" + CAL_List.Functions.map.getQualifiedName() + " (\\rec -> (rec.columnName, rec.valueType)) (" + CAL_List.Functions.filter.getQualifiedName() + " (\\rec -> " + CAL_Prelude.Functions.not.getQualifiedName() + " rec.isLongType) (" + CAL_DataGems.Functions.jdbcGetTableFieldInfo.getQualifiedName() + " " + connectionGemName + " \"" + tableName + "\")))"; List<List<?>> tupleList = UnsafeCast.unsafeCast(getListFromCode(code)); List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>(); for (final List<?> tuple : tupleList) { String fieldName = (String) tuple.get(0); ValueType valueType = (ValueType) tuple.get(1); fieldInfoList.add(new FieldInfo(fieldName, valueType)); } return fieldInfoList; } /** * Add any selected fields from the available fields list to the sort fields list. */ private void onAddFields(){ final DefaultListModel availableListModel = (DefaultListModel) availableFieldsList.getModel(); final DefaultListModel sortListModel = (DefaultListModel) sortFieldsList.getModel(); Object[] selectedValues = availableFieldsList.getSelectedValues(); int nSelected = selectedValues.length; for (int selectionN = 0; selectionN < nSelected; ++selectionN) { FieldInfo selectedValue = (FieldInfo) selectedValues[selectionN]; SortField sortField = new SortField(selectedValue, true); // Don't add the same field multiple times. if (!sortListModel.contains(sortField)) { sortListModel.addElement(sortField); } } if (nSelected == 1) { // Select the new item in the selected fields list. sortFieldsList.setSelectedIndex(sortListModel.getSize() - 1); // Move the selection to the next field in the available fields list. int[] selectedIndexes = availableFieldsList.getSelectedIndices(); if (selectedIndexes.length == 1) { int itemSelected = selectedIndexes[0]; // availableFieldsList.getSelectedIndex(); if (itemSelected >= 0 && itemSelected < (availableListModel.getSize() - 1)) { availableFieldsList.setSelectedIndex(itemSelected + 1); } } } } /** * Removes any selected fields from the selected fields list. */ public void onRemoveFields(){ final DefaultListModel sortListModel = (DefaultListModel) sortFieldsList.getModel(); int selectedIndex = sortFieldsList.getSelectedIndex(); if (selectedIndex >= 0) { sortListModel.remove(selectedIndex); // Reselect an appropriate item in the list. int newSelection = selectedIndex; if (newSelection >= sortListModel.getSize()) { newSelection = sortListModel.getSize() - 1; } sortFieldsList.setSelectedIndex(newSelection); } } } /** * Panel to display UI for displaying the current SQL for the query. */ private class SqlQueryCard extends WizardCard { private static final long serialVersionUID = 673118312331546643L; static final String SQLQUERY_CARDNAME = "SqlQuery"; /** A text area for displaying the SQL query. */ private final JTextArea sqlQueryEdit = new JTextArea(); // TODO: add an 'Edit' button which switches to edit mode... // /** The label for displaying status messages. */ // private final JLabel statusLabel = new JLabel(); /** * PreviewCard constructor. */ SqlQueryCard() { super(true); // The query should be read-only initially. sqlQueryEdit.setEditable(false); // Make the source fields list box have default focus setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() { private static final long serialVersionUID = 4202556912362889526L; public Component getDefaultComponent(Container c) { return sqlQueryEdit; } }); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getCardName() */ String getCardName() { return SQLQUERY_CARDNAME; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getNextCardName() */ String getNextCardName() { return PreviewCard.PREVIEW_CARDNAME; } /** * Returns the resource ID for the card title. */ String getTitleResourceID() { return "JDBCGF_SqlQueryPageTitle"; } /** * Returns the resource ID for the card subtitle. */ String getSubtitleResourceID() { return "JDBCGF_SqlQueryPageSubTitle"; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#isPageContentValid() */ boolean isPageContentValid() { return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#commitChanges() */ boolean commitChanges() { return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#initializeControls() */ void initializeControls() { super.initializeControls(); // Set the current SQL into the edit box. String sqlQuery = generateSqlQuery(); sqlQueryEdit.setText(sqlQuery); sqlQueryEdit.setCaretPosition(0); // Move the caret back to the top of the edit box. } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getMainPanel() */ JPanel getMainPanel() { JPanel javaPanel = new JPanel(); javaPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); javaPanel.setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.fill = GridBagConstraints.HORIZONTAL; // constraints.gridx = 1; // constraints.weightx = 0; // constraints.weighty = 0; // constraints.gridwidth = GridBagConstraints.REMAINDER; // constraints.insets = new Insets(5, 5, 10, 5); // javaPanel.add(statusLabel, constraints); // // statusLabel.setFont(getFont().deriveFont(Font.BOLD)); constraints.gridx = 1; constraints.weightx = 0; constraints.weighty = 0; constraints.gridwidth = 1; constraints.insets = new Insets(5, 5, 5, 5); javaPanel.add(new JLabel(GeneratorMessages.getString("JDBCGF_SqlQueryLabel")), constraints); constraints.gridx = 1; constraints.weightx = 1; constraints.weighty = 1; constraints.fill = GridBagConstraints.BOTH; JScrollPane scrollPane = new JScrollPane(sqlQueryEdit); javaPanel.add(scrollPane, constraints); // constraints.gridx = 4; // constraints.weightx = 1; // constraints.weighty = 0; // constraints.gridwidth = GridBagConstraints.REMAINDER; // javaPanel.add(new JLabel(""), constraints); return javaPanel; } } /** * Panel to display UI for viewing the results of the current query. */ private class PreviewCard extends WizardCard { private static final long serialVersionUID = -1750021788640630651L; static final String PREVIEW_CARDNAME = "Preview"; /** A panel which will hold the resultset editor. */ private final JPanel editorPanel = new JPanel(new BorderLayout()); /** The value editor provider for the resultset editor. */ private final JDBCResultSetEditor.JDBCResultSetEditorProvider provider; /** The current value editor for displaying the resultset data. */ private ValueEditor valueEditor; /** * PreviewCard constructor. */ PreviewCard() { super(true); provider = new JDBCResultSetEditor.JDBCResultSetEditorProvider(valueEditorManager); // this.valueEditor = provider.getEditorInstance(valueEditorHierarchyManager, null); // // Make the source fields list box have default focus // setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() { // // public Component getDefaultComponent(Container c) { // return valueEditor; // } // // public Component getInitialComponent(Container c) { // return valueEditor; // } // }); } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getCardName() */ String getCardName() { return PREVIEW_CARDNAME; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getNextCardName() */ String getNextCardName() { return null; } /** * Returns the resource ID for the card title. */ String getTitleResourceID() { return "JDBCGF_PreviewPageTitle"; } /** * Returns the resource ID for the card subtitle. */ String getSubtitleResourceID() { return "JDBCGF_PreviewPageSubTitle"; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#isPageContentValid() */ boolean isPageContentValid() { return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#commitChanges() */ boolean commitChanges() { return true; } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#initializeControls() */ void initializeControls() { super.initializeControls(); // Remove the previous value editor, if any. if (valueEditor != null) { valueEditorHierarchyManager.removeValueEditor(valueEditor, true); valueEditor = null; } editorPanel.removeAll(); // Build the code necessary to execute the query. // TODO: what should happen if there are parameters in the code? String queryCode = generateResultsetFunctionBody(); ValueNode vn = getValueNodeFromCode(queryCode); // If unable to run the query, display a message instead of the value editor. if (vn == null) { // TODO: display the error message as well... JTextArea messageText = new JTextArea(GeneratorMessages.getString("JDBCGF_PreviewErrorLabel")); messageText.setEditable(false); editorPanel.add(new JScrollPane(messageText), BorderLayout.CENTER); } else { // Create a new value editor for displaying the resultset data. valueEditor = provider.getEditorInstance(valueEditorHierarchyManager, vn); // Add the editor as a top level panel. valueEditorHierarchyManager.addTopValueEditor(valueEditor); // Turn off the border on the editor. valueEditor.setBorder(null); // Set the value editor into the editor panel. editorPanel.add(valueEditor, BorderLayout.CENTER); } } /** * @see org.openquark.gems.client.generators.JDBCGemGenerator.JDBCGemGeneratorDialog.WizardCard#getMainPanel() */ JPanel getMainPanel() { JPanel javaPanel = new JPanel(); javaPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); javaPanel.setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.fill = GridBagConstraints.HORIZONTAL; // constraints.gridx = 1; // constraints.weightx = 0; // constraints.weighty = 0; // constraints.gridwidth = GridBagConstraints.REMAINDER; // constraints.insets = new Insets(5, 5, 10, 5); // javaPanel.add(statusLabel, constraints); // // statusLabel.setFont(getFont().deriveFont(Font.BOLD)); // constraints.gridx = 1; // constraints.weightx = 0; // constraints.weighty = 0; // constraints.gridwidth = 1; // constraints.insets = new Insets(5, 5, 5, 5); // javaPanel.add(new JLabel(GeneratorMessages.getString("JDBCGF_PreviewLabel")), constraints); constraints.gridx = 1; constraints.weightx = 1; constraints.weighty = 1; constraints.fill = GridBagConstraints.BOTH; javaPanel.add(editorPanel, constraints); // constraints.gridx = 4; // constraints.weightx = 1; // constraints.weighty = 0; // constraints.gridwidth = GridBagConstraints.REMAINDER; // javaPanel.add(new JLabel(""), constraints); return javaPanel; } } /** * @return the panel that contains the buttons at the bottom of the dialog */ private JPanel getButtonPanel() { JPanel buttonPanel = new JPanel(); buttonPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); buttonPanel.add(Box.createHorizontalGlue()); buttonPanel.add(getPreviousButton()); buttonPanel.add(Box.createHorizontalStrut(5)); buttonPanel.add(getNextButton()); buttonPanel.add(Box.createHorizontalGlue()); buttonPanel.add(getFinishButton()); buttonPanel.add(Box.createHorizontalStrut(5)); buttonPanel.add(getCancelButton()); return buttonPanel; } /** * @return the Finish button for the dialog */ private JButton getFinishButton() { if (finishButton == null) { Action finishAction = new AbstractAction(GeneratorMessages.getString("JDBCGF_FinishButton")) { private static final long serialVersionUID = 2893908785764439019L; public void actionPerformed(ActionEvent e) { // Commit any changes from the current card. WizardCard currentCard = getCurrentCard(); if (!currentCard.commitChanges()) { return; } // Generate the source for the resultset gem. generateSource(); // Generate a gem to extract a list of records from the resultset, if requested. if (includeRecordExtractorGem) { generateRecordExtractorSource(); } dispose(); } }; finishButton = new JButton(finishAction); } return finishButton; } /** * @return the cancel button for the dialog */ private JButton getCancelButton() { if (cancelButton == null) { Action cancelAction = new AbstractAction(GeneratorMessages.getString("JGF_CancelButton")) { private static final long serialVersionUID = -3095854626817672401L; public void actionPerformed(ActionEvent e) { dispose(); } }; cancelButton = new JButton(cancelAction); } return cancelButton; } /** * @return the Previous button for the dialog */ private JButton getPreviousButton() { if (previousButton == null) { Action previousAction = new AbstractAction(GeneratorMessages.getString("JDBCGF_PreviousButton")) { private static final long serialVersionUID = -5893020996076600881L; public void actionPerformed(ActionEvent e) { switchToPreviousTab(); } }; previousButton = new JButton(previousAction); } return previousButton; } /** * @return the Next button for the dialog */ private JButton getNextButton() { if (nextButton == null) { Action nextAction = new AbstractAction(GeneratorMessages.getString("JDBCGF_NextButton")) { private static final long serialVersionUID = 2323270174994712464L; public void actionPerformed(ActionEvent e) { switchToNextTab(); } }; nextButton = new JButton(nextAction); } return nextButton; } /** * Returns the first page of the wizard. */ private WizardCard getFirstCard() { // The current page will be at the bottom of the stack. if (viewedPageStack.isEmpty()) { return null; } return viewedPageStack.get(0); } /** * Returns the current wizard page being viewed. */ private WizardCard getCurrentCard() { // The current page will be at the top of the stack. if (viewedPageStack.isEmpty()) { return null; } return viewedPageStack.peek(); } /** * Sets up the wizard to display the first page. */ private void showFirstPage(WizardCard firstPage) { viewedPageStack.push(firstPage); // firstPage.updateButtonBar(); firstPage.initializeControls(); } /** * Changes to the previous tab in the dialog. */ private void switchToPreviousTab() { viewedPageStack.pop(); // Initialize the UI for the new current card. WizardCard currentCard = getCurrentCard(); currentCard.initializeControls(); // Display the new current page. cardLayout.show(cardPanel, currentCard.getCardName()); } /** * Changes to the next tab in the dialog. */ private void switchToNextTab() { WizardCard currentCard = getCurrentCard(); if (currentCard.isLastPage()) { return; } // Commit any changes from the current card. if (!currentCard.commitChanges()) { return; } // Determine the next card to activate. String nextCardName = currentCard.getNextCardName(); WizardCard nextCard = nameToPageMap.get(nextCardName); if (nextCard == null) { return; } // Initialize the UI for the next card. nextCard.initializeControls(); // Push this card onto the stack. viewedPageStack.push(nextCard); // Display the next card. cardLayout.show(cardPanel, nextCardName); } /** * Generates the source definitions for the Java import that the user has selected. */ private void generateSource() { StringBuilder source = new StringBuilder(GeneratorMessages.getString("JDBCGF_FunctionDeclComment")); if (gemComment != null && gemComment.trim().length() > 0) { source.append("// " + gemComment + "\n"); } // TODO: include a type signature... if (gemScope == Scope.PUBLIC) { source.append("public "); } else { source.append("private "); } source.append(gemName); // TODO: add arguments... source.append(" = \n"); String functionBody = generateResultsetFunctionBody(); source.append(functionBody); source.append(";\n"); String gemSource = source.toString(); // Finally add the main definition sourceDefinitions.put(gemName, gemSource); } /** * Generates the body of the function which creates the JDBC resultset. */ private String generateResultsetFunctionBody() { return generateLetBlock(false) + CAL_DataGems.Functions.jdbcQueryToResultSet.getQualifiedName() + " conn sql"; } /** * Generates the body of the function which creates the SQL query. */ private String generateSqlFunctionBody() { return generateLetBlock(true) + "sql"; } /** * Produces the body of the let block with values for 'conn' and 'sql'. * @param forDisplay whether the SQL should be formatted for display or not * @return the body of the let block */ private String generateLetBlock(boolean forDisplay) { StringBuilder sb = new StringBuilder(" let\n"); sb.append(" conn = "); sb.append(connectionGemName); sb.append(";\n\n"); sb.append(" table = " + CAL_Sql.Functions.makeQueryTable.getQualifiedName() + " \""); sb.append(tableName); sb.append("\";\n"); sb.append(" resultFields = ["); for (int fieldN = 0, nFields = selectedFields.size(); fieldN < nFields; ++fieldN) { if (fieldN > 0) { sb.append(", "); } FieldInfo fieldInfo = selectedFields.get(fieldN); sb.append(CAL_Sql.Functions.untypedField.getQualifiedName() + " table \""); sb.append(fieldInfo.fieldName); sb.append("\""); } sb.append("];\n"); sb.append(" query1 = " + CAL_Sql.Functions.project.getQualifiedName() + " " + CAL_Sql.Functions.newQuery.getQualifiedName() + " resultFields;\n\n"); sb.append(" orderFields = ["); for (int sortFieldN = 0, nSortFields = sortFields.size(); sortFieldN < nSortFields; ++sortFieldN) { if (sortFieldN > 0) { sb.append(", "); } SortField sortField = sortFields.get(sortFieldN); sb.append("(" + CAL_Sql.Functions.untypedField.getQualifiedName() + " table \""); sb.append(sortField.fieldInfo.fieldName); sb.append("\", "); sb.append(sortField.ascending ? CAL_Prelude.DataConstructors.True.getQualifiedName() : CAL_Prelude.DataConstructors.False.getQualifiedName()); sb.append(')'); } sb.append("];\n"); sb.append(" query2 = " + CAL_Sql.Functions.order2.getQualifiedName() + " query1 orderFields;\n\n"); sb.append(" sqlBuilder = "); sb.append(sqlBuilderGemName); sb.append(";\n"); sb.append(" sql = " + CAL_Sql.Functions.queryText.getQualifiedName() + " sqlBuilder "); sb.append(forDisplay ? CAL_Prelude.DataConstructors.True.getQualifiedName() : CAL_Prelude.DataConstructors.False.getQualifiedName()); sb.append(" query2;\n"); sb.append(" in\n"); sb.append(" "); return sb.toString(); } /** * Generates the source definitions for a record extractor gem for the recordset. */ private void generateRecordExtractorSource() { StringBuilder source = new StringBuilder(GeneratorMessages.getString("JDBCGF_FunctionDeclComment")); if (gemComment != null && gemComment.trim().length() > 0) { source.append("// " + gemComment + "\n"); } // TODO: include a type signature... source.append(gemScope.toString()).append(' '); // TODO: perhaps the name of this gem should be configurable... String extractorGemName = gemName + "Records"; source.append(extractorGemName); // TODO: add arguments... source.append(" = \n"); source.append(" let\n"); source.append(" extractFn rs = \n"); source.append(" let\n"); for (int fieldN = 0, nFields = selectedFields.size(); fieldN < nFields; ++fieldN) { FieldInfo fieldInfo = selectedFields.get(fieldN); String extractorFunction = getFieldValueExtractorFunction(fieldInfo); source.append(" val"); source.append(fieldN + 1); source.append(" = "); source.append(extractorFunction); source.append(" "); source.append(fieldN + 1); source.append(" rs;\n"); } source.append(" in\n"); source.append(" "); for (int fieldN = 0, nFields = selectedFields.size(); fieldN < nFields; ++fieldN) { if (fieldN > 0) { source.append('('); } source.append(CAL_Prelude.Functions.seq.getQualifiedName() + " val"); source.append(fieldN + 1); source.append(" "); } source.append("{ "); List<String> recordFieldNames = makeRecordFieldNames(selectedFields); for (int fieldN = 0, nFields = selectedFields.size(); fieldN < nFields; ++fieldN) { if (fieldN > 0) { source.append(", "); } String recordFieldName = recordFieldNames.get(fieldN); source.append(recordFieldName); source.append(" = val"); source.append(fieldN + 1); } source.append(" }"); for (int fieldN = 0, nFields = selectedFields.size() - 1; fieldN < nFields; ++fieldN) { source.append(')'); } source.append(";\n"); source.append(" in\n"); source.append(" " + CAL_DataGems.Functions.dataFromResultSet.getQualifiedName() + " "); source.append(gemName); source.append(" extractFn;\n"); String gemSource = source.toString(); // Finally add the main definition sourceDefinitions.put(extractorGemName, gemSource); } /** * Returns a list of the record field names corresponding to the database field names. * @param databaseFieldsInfo a list of database field names * @return a list of record field names */ private List<String> makeRecordFieldNames(List<FieldInfo> databaseFieldsInfo) { List<String> recordFieldNames = new ArrayList<String>(); for (int fieldN = 0, nFields = databaseFieldsInfo.size(); fieldN < nFields; ++fieldN) { FieldInfo dbFieldInfo = databaseFieldsInfo.get(fieldN); // Create the base record name by removing any invalid chars, //and ensuring that the first char is a lower case letter. String baseRecordFieldName = IdentifierUtils.makeIdentifierName(dbFieldInfo.fieldName); if (baseRecordFieldName == null || baseRecordFieldName.length() == 0) { baseRecordFieldName = "field" + (fieldN + 1); } // Ensure that the name is unique within the record. // If necessary, append a number to the end of the base name to make it unique. String uniqueName = baseRecordFieldName; int counter = 1; while (recordFieldNames.contains(uniqueName)) { uniqueName = baseRecordFieldName + counter; ++counter; } recordFieldNames.add(uniqueName); } return recordFieldNames; } /** * Returns the name of the gem to use extract the value of the specified database field. * @param fieldInfo information about the field * @return the name of the gem to use extract the database field value */ private String getFieldValueExtractorFunction(FieldInfo fieldInfo) { switch (fieldInfo.valueType.value()) { case ValueType._stringType : return CAL_DataGems.Functions.extractString.getQualifiedName(); case ValueType._intType : return CAL_DataGems.Functions.extractInt.getQualifiedName(); case ValueType._doubleType : return CAL_DataGems.Functions.extractDouble.getQualifiedName(); case ValueType._booleanType : return CAL_DataGems.Functions.extractBoolean.getQualifiedName(); case ValueType._timeType : return CAL_DataGems.Functions.extractTime.getQualifiedName(); case ValueType._nullType : default : // By default, just return a string value for the column. return CAL_DataGems.Functions.extractString.getQualifiedName(); } } /** * Generates the SQL for the current query. */ private String generateSqlQuery() { // Build the code necessary to execute the query. // TODO: what should happen if there are parameters in the code? String queryCode = generateSqlFunctionBody(); ValueNode vn = getValueNodeFromCode(queryCode); Object sqlObj = vn.getValue(); if (sqlObj instanceof String) { return (String) sqlObj; } else { return "<Failed to generate SQL query.>"; } } /** * Returns the ValueNode corresponding to the specified value expression, or null if it is not valid. */ private ValueNode getValueNodeFromCode(String valueText) { // Now create a target to be run by a value runner, and return the result. Target valueTarget = new Target.SimpleTarget(valueText); try { return valueRunner.getValue (valueTarget, perspective.getWorkingModuleName()); } catch (Exception e) { e.printStackTrace (); return null; } } /** * Runs the specified code and returns the result as a Java Object. */ private Object getObjectFromCode(String code) { ValueNode vn = getValueNodeFromCode(code); return (vn == null) ? null : vn.getValue(); } /** * Runs the specified code and returns the list result. * Returns an empty list if the result is not a list. */ private List<?> getListFromCode(String code) { Object obj = getObjectFromCode(code); return (obj instanceof List<?>) ? (List<?>) obj : Collections.EMPTY_LIST; } } }