/* Copyright (C) 2006 EBI This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the itmplied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.biomart.builder.view.gui.diagrams.contexts; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JRadioButtonMenuItem; import org.biomart.builder.model.Column; import org.biomart.builder.model.ComponentStatus; import org.biomart.builder.model.Key; import org.biomart.builder.model.Relation; import org.biomart.builder.model.Schema; import org.biomart.builder.model.Table; import org.biomart.builder.model.Relation.Cardinality; import org.biomart.builder.view.gui.MartTabSet.MartTab; import org.biomart.builder.view.gui.diagrams.components.ColumnComponent; import org.biomart.builder.view.gui.diagrams.components.KeyComponent; import org.biomart.builder.view.gui.diagrams.components.RelationComponent; import org.biomart.builder.view.gui.diagrams.components.SchemaComponent; import org.biomart.builder.view.gui.diagrams.components.TableComponent; import org.biomart.common.resources.Resources; /** * Provides the context menus and colour schemes to use when viewing a schema in * its plain vanilla form, ie. not a dataset schema, and not a window from a * dataset onto a set of masked relations. * * @author Richard Holland <holland@ebi.ac.uk> * @version $Revision: 1.50 $, $Date: 2008-03-03 12:35:08 $, modified by * $Author: rh4 $ * @since 0.5 */ public class SchemaContext implements DiagramContext { private MartTab martTab; /** * Creates a new context which will pass any menu actions onto the given * mart tab. * * @param martTab * the mart tab which will receive any menu actions the user * selects. */ public SchemaContext(final MartTab martTab) { this.martTab = martTab; } /** * Obtain the mart tab to pass menu events onto. * * @return the mart tab this context is attached to. */ protected MartTab getMartTab() { return this.martTab; } public void customiseAppearance(final JComponent component, final Object object) { // This bit updates schema boxes. if (object instanceof Schema) { final Schema schema = (Schema) object; final SchemaComponent schcomp = (SchemaComponent) component; if (this.isMasked(schema)) schcomp.setBackground(SchemaComponent.MASKED_BACKGROUND); else schcomp.setBackground(SchemaComponent.BACKGROUND_COLOUR); } // This bit removes a restricted outline from any restricted tables. else if (object instanceof Table) { final TableComponent tblcomp = (TableComponent) component; final Table table = (Table) object; tblcomp.setRestricted(false); // Fade out all ignored tables. if (this.isMasked(table)) tblcomp.setBackground(TableComponent.IGNORE_COLOUR); // All others are normal. else tblcomp.setBackground(TableComponent.BACKGROUND_COLOUR); } // This bit removes a restricted outline from any restricted tables. else if (object instanceof Column) { final ColumnComponent colcomp = (ColumnComponent) component; final Column col = (Column) object; // Fade out all ignored tables. if (this.isMasked(col)) colcomp.setBackground(ColumnComponent.MASKED_COLOUR); // All others are normal. else colcomp.setBackground(ColumnComponent.NORMAL_COLOUR); } // Relations get pretty colours if they are incorrect or handmade. else if (object instanceof Relation) { // What relation is this? final Relation relation = (Relation) object; final RelationComponent relcomp = (RelationComponent) component; // Is it restricted? relcomp.setRestricted(false); // Is it compounded? relcomp.setCompounded(false); // Is it loopback? relcomp.setLoopback(false); // Fade out all INFERRED_INCORRECT relations and those which // head to ignored tables. if (this.isMasked(relation)) relcomp.setForeground(RelationComponent.INCORRECT_COLOUR); // Highlight all HANDMADE relations. else if (relation.getStatus().equals(ComponentStatus.HANDMADE)) relcomp.setForeground(RelationComponent.HANDMADE_COLOUR); // Highlight MODIFIED relations. else if (relation.getStatus().equals(ComponentStatus.MODIFIED)) relcomp.setForeground(RelationComponent.MODIFIED_COLOUR); // All others are normal. else relcomp.setForeground(RelationComponent.NORMAL_COLOUR); } // Keys also get pretty colours for being incorrect or handmade. else if (object instanceof Key) { // What key is this? final Key key = (Key) object; final KeyComponent keycomp = (KeyComponent) component; // Fade out all INFERRED_INCORRECT relations. if (key.getStatus().equals(ComponentStatus.INFERRED_INCORRECT)) keycomp.setForeground(KeyComponent.INCORRECT_COLOUR); // Highlight all HANDMADE relations. else if (key.getStatus().equals(ComponentStatus.HANDMADE)) keycomp.setForeground(KeyComponent.HANDMADE_COLOUR); // All others are normal. else keycomp.setForeground(KeyComponent.NORMAL_COLOUR); // Add drag-and-drop to all keys here. keycomp.setDraggable(true); } } public boolean isMasked(final Object object) { final String schemaPrefix = this.getMartTab() .getPartitionViewSelection(); if (object instanceof Schema) { final Schema schema = (Schema) object; if (schema.isMasked()) return true; // Fade out if has external tables and all are inapplicable. final Set extTbls = new HashSet(); for (final Iterator i = schema.getRelations().iterator(); i .hasNext();) { final Relation r = (Relation) i.next(); if (r.isExternal()) extTbls.add(r.getKeyForSchema(schema).getTable()); } for (final Iterator i = extTbls.iterator(); i.hasNext();) if (((Table) i.next()).existsForPartition(schemaPrefix)) return false; return !extTbls.isEmpty(); } // Incorrect and ignored stuff is 'masked'. else if (object instanceof Table) { final Table table = (Table) object; // Fade out all ignored and/or unreachable tables. if (table.isMasked() || !table.existsForPartition(schemaPrefix)) return true; else { for (final Iterator i = table.getRelations().iterator(); i .hasNext();) if (!((Relation) i.next()).getStatus().equals( ComponentStatus.INFERRED_INCORRECT)) return false; // If get here, it's unreachable. return true; } } // Relations get pretty colours if they are incorrect or handmade. else if (object instanceof Relation) { // What relation is this? final Relation relation = (Relation) object; // Fade out all INFERRED_INCORRECT relations and those which // head to ignored tables or masked schemas. if (relation.getStatus().equals(ComponentStatus.INFERRED_INCORRECT) || this.isMasked(relation.getFirstKey().getTable()) || this.isMasked(relation.getSecondKey().getTable()) || this.isMasked(relation.getFirstKey().getTable() .getSchema()) || this.isMasked(relation.getSecondKey().getTable() .getSchema())) return true; } // Keys also get pretty colours for being incorrect or handmade. else if (object instanceof Key) { // What key is this? final Key key = (Key) object; // Fade out all INFERRED_INCORRECT relations. if (key.getStatus().equals(ComponentStatus.INFERRED_INCORRECT)) return true; } // Columns get masked. else if (object instanceof Column) { final Column col = (Column) object; return !col.existsForPartition(schemaPrefix); } return false; } public void populateMultiContextMenu(final JPopupMenu contextMenu, final Collection selectedItems, final Class clazz) { // Nothing to do here. } public void populateContextMenu(final JPopupMenu contextMenu, final Object object) { if (object instanceof Schema) { if (contextMenu.getComponentCount() > 0) contextMenu.addSeparator(); final Schema sch = (Schema) object; // Accept/Reject changes - only enabled if dataset table // is visible modified. final JMenuItem accept = new JMenuItem(Resources .get("acceptChangesTitle")); accept .setMnemonic(Resources.get("acceptChangesMnemonic").charAt( 0)); accept.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestAcceptAll(sch); } }); accept.setEnabled(sch.isVisibleModified()); contextMenu.add(accept); final JMenuItem reject = new JMenuItem(Resources .get("rejectChangesTitle")); reject .setMnemonic(Resources.get("rejectChangesMnemonic").charAt( 0)); reject.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestRejectAll(sch); } }); reject.setEnabled(sch.isVisibleModified()); contextMenu.add(reject); } // Table objects have their own menus too. else if (object instanceof Table) { // Add a separator if the menu is not empty. if (contextMenu.getComponentCount() > 0) contextMenu.addSeparator(); // Work out what table we are using. final Table table = (Table) object; // Menu option to suggest a bunch of datasets based around that // table. final JMenuItem suggest = new JMenuItem(Resources.get( "suggestDataSetsTableTitle", table.getName())); suggest.setMnemonic(Resources.get("suggestDataSetsTableMnemonic") .charAt(0)); suggest.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getDataSetTabSet() .requestSuggestDataSets(table); } }); contextMenu.add(suggest); suggest.setEnabled(!table.getSchema().isMasked()); // Menu option to suggest a bunch of datasets based around that // table. final JMenuItem suggestPT = new JMenuItem(Resources.get( "suggestPartitionTableTitle", table.getName())); suggestPT.setMnemonic(Resources.get("suggestPartitionTableMnemonic") .charAt(0)); suggestPT.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getDataSetTabSet() .requestSuggestPartitionTable(table); } }); contextMenu.add(suggestPT); suggestPT.setEnabled(!table.getSchema().isMasked()); // Menu option to suggest a bunch of datasets based around that // table. final JMenuItem createOntologyDS = new JMenuItem(Resources.get( "suggestUnrolledDataSetsTableTitle", table.getName())); createOntologyDS.setMnemonic(Resources.get( "suggestUnrolledDataSetsTableMnemonic").charAt(0)); createOntologyDS.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getDataSetTabSet() .requestSuggestUnrolledDataSets(table); } }); contextMenu.add(createOntologyDS); createOntologyDS.setEnabled(!table.getSchema().isMasked()); // Separator. contextMenu.addSeparator(); // Menu option to show first few rows. final JMenuItem showRows = new JMenuItem(Resources.get( "showRowsTitle", table.getName())); showRows.setMnemonic(Resources.get("showRowsMnemonic").charAt(0)); showRows.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestShowRows(table, 10); } }); contextMenu.add(showRows); // Separator. contextMenu.addSeparator(); // Menu item to create a primary key. If it already has one, disable // the option. final JMenuItem pk = new JMenuItem(Resources .get("createPrimaryKeyTitle")); pk.setMnemonic(Resources.get("createPrimaryKeyMnemonic").charAt(0)); pk.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestCreatePrimaryKey(table); } }); if (table.getPrimaryKey() != null) pk.setEnabled(false); contextMenu.add(pk); // Menu item to create a foreign key. final JMenuItem fk = new JMenuItem(Resources .get("createForeignKeyTitle")); fk.setMnemonic(Resources.get("createForeignKeyMnemonic").charAt(0)); fk.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestCreateForeignKey(table); } }); contextMenu.add(fk); // Separator. contextMenu.addSeparator(); // Menu item to ignore the entire table. final JCheckBoxMenuItem ignore = new JCheckBoxMenuItem(Resources .get("ignoreTableTitle")); ignore.setMnemonic(Resources.get("ignoreTableMnemonic").charAt(0)); ignore.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestIgnoreTable(table, ignore.isSelected()); } }); ignore.setSelected(table.isMasked()); ignore.setEnabled(!table.getSchema().isMasked()); contextMenu.add(ignore); } // Relations have their own menus too. else if (object instanceof Relation) { // Add a separator if the menu is not empty. if (contextMenu.getComponentCount() > 0) contextMenu.addSeparator(); // What relation is this? And is it correct? final Relation relation = (Relation) object; final boolean relationIncorrect = relation.getStatus().equals( ComponentStatus.INFERRED_INCORRECT); // Set up a radio group for the cardinality. final ButtonGroup cardGroup = new ButtonGroup(); // Set the relation to be 1:1, but only if it is correct. final JRadioButtonMenuItem oneToOne = new JRadioButtonMenuItem( Resources.get("oneToOneTitle")); oneToOne.setMnemonic(Resources.get("oneToOneMnemonic").charAt(0)); oneToOne.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestChangeRelationCardinality(relation, Cardinality.ONE); } }); cardGroup.add(oneToOne); contextMenu.add(oneToOne); if (relationIncorrect) oneToOne.setEnabled(false); if (relation.isOneToOne()) oneToOne.setSelected(true); // Set the relation to be 1:M, but only if it is correct. final JRadioButtonMenuItem oneToManyA = new JRadioButtonMenuItem( Resources.get("oneToManyATitle", relation.getFirstKey().toString())); oneToManyA.setMnemonic(Resources.get("oneToManyAMnemonic").charAt(0)); oneToManyA.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestChangeRelationCardinality(relation, Cardinality.MANY_A); } }); cardGroup.add(oneToManyA); contextMenu.add(oneToManyA); if (relationIncorrect || !relation.isOneToManyAAllowed()) oneToManyA.setEnabled(false); if (relation.isOneToManyA()) oneToManyA.setSelected(true); // Set the relation to be 1:M, but only if it is correct. final JRadioButtonMenuItem oneToManyB = new JRadioButtonMenuItem( Resources.get("oneToManyBTitle", relation.getFirstKey().toString())); oneToManyB.setMnemonic(Resources.get("oneToManyBMnemonic").charAt(0)); oneToManyB.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestChangeRelationCardinality(relation, Cardinality.MANY_B); } }); cardGroup.add(oneToManyB); contextMenu.add(oneToManyB); if (relationIncorrect || !relation.isOneToManyBAllowed()) oneToManyB.setEnabled(false); if (relation.isOneToManyB()) oneToManyB.setSelected(true); // Separator. contextMenu.addSeparator(); // Masked? (Incorrect?) // Mark relation as incorrect, but only if not handmade. final JCheckBoxMenuItem incorrect = new JCheckBoxMenuItem(Resources .get("incorrectRelationTitle")); incorrect.setMnemonic(Resources.get("incorrectRelationMnemonic") .charAt(0)); incorrect.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab() .getSchemaTabSet() .requestChangeRelationStatus( relation, incorrect.isSelected() ? ComponentStatus.INFERRED_INCORRECT : ComponentStatus.INFERRED); } }); contextMenu.add(incorrect); incorrect.setSelected(relationIncorrect); if (relation.getStatus().equals(ComponentStatus.MODIFIED) || relation.getStatus().equals(ComponentStatus.HANDMADE)) incorrect.setEnabled(false); // Separator contextMenu.addSeparator(); // Remove the relation from the schema, but only if handmade. final JMenuItem remove = new JMenuItem(Resources .get("removeRelationTitle"), new ImageIcon(Resources .getResourceAsURL("cut.gif"))); remove.setMnemonic(Resources.get("removeRelationMnemonic") .charAt(0)); remove.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestRemoveRelation(relation); } }); contextMenu.add(remove); if (!relation.getStatus().equals(ComponentStatus.HANDMADE)) remove.setEnabled(false); } // Keys have menus too. else if (object instanceof Key) { // Then work out what key this is. final Key key = (Key) object; // Add a separator if the menu is not empty. if (contextMenu.getComponentCount() > 0) contextMenu.addSeparator(); // Option to edit an existing key. final JMenuItem editkey = new JMenuItem(Resources .get("editKeyTitle")); editkey.setMnemonic(Resources.get("editKeyMnemonic").charAt(0)); editkey.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestEditKey(key); } }); contextMenu.add(editkey); // Remove the key from the table, but only if handmade. final JMenuItem remove = new JMenuItem(Resources .get("removeKeyTitle"), new ImageIcon(Resources .getResourceAsURL("cut.gif"))); remove.setMnemonic(Resources.get("removeKeyMnemonic").charAt(0)); remove.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestRemoveKey(key); } }); contextMenu.add(remove); // Separator. contextMenu.addSeparator(); // Incorrect = masked. // Mark the key as incorrect, but not if handmade. final JCheckBoxMenuItem incorrect = new JCheckBoxMenuItem(Resources .get("incorrectKeyTitle")); incorrect.setMnemonic(Resources.get("incorrectKeyMnemonic").charAt( 0)); incorrect.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab() .getSchemaTabSet() .requestChangeKeyStatus( key, incorrect.isSelected() ? ComponentStatus.INFERRED_INCORRECT : ComponentStatus.INFERRED); } }); contextMenu.add(incorrect); incorrect.setSelected(key.getStatus().equals( ComponentStatus.INFERRED_INCORRECT)); if (key.getStatus().equals(ComponentStatus.HANDMADE)) incorrect.setEnabled(false); // Separator contextMenu.addSeparator(); // Option to establish a relation between this key and another. final JMenuItem createrel = new JMenuItem(Resources .get("createRelationTitle")); createrel.setMnemonic(Resources.get("createRelationMnemonic") .charAt(0)); createrel.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent evt) { SchemaContext.this.getMartTab().getSchemaTabSet() .requestCreateRelation(key); } }); contextMenu.add(createrel); if (key.getStatus().equals(ComponentStatus.INFERRED_INCORRECT)) createrel.setEnabled(false); } } }