/* FeatureEdit.java * * created: Tue Dec 1 1998 * * This file is part of Artemis * * Copyright (C) 1998,1999,2000,2001,2002 Genome Research Limited * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/FeatureEdit.java,v 1.70 2009-09-24 15:01:27 tjc Exp $ **/ package uk.ac.sanger.artemis.components; import uk.ac.sanger.artemis.util.*; import uk.ac.sanger.artemis.*; import uk.ac.sanger.artemis.sequence.MarkerRange; import uk.ac.sanger.artemis.io.ChadoCanonicalGene; import uk.ac.sanger.artemis.io.DatabaseDocumentEntry; import uk.ac.sanger.artemis.io.DocumentEntry; import uk.ac.sanger.artemis.io.GFFDocumentEntry; import uk.ac.sanger.artemis.io.GFFStreamFeature; import uk.ac.sanger.artemis.io.OutOfDateException; import uk.ac.sanger.artemis.io.LocationParseException; import uk.ac.sanger.artemis.io.PartialSequence; import uk.ac.sanger.artemis.io.QualifierLazyLoading; import uk.ac.sanger.artemis.io.QualifierParseException; import uk.ac.sanger.artemis.io.Range; import uk.ac.sanger.artemis.io.RangeVector; import uk.ac.sanger.artemis.io.Key; import uk.ac.sanger.artemis.io.KeyVector; import uk.ac.sanger.artemis.io.Location; import uk.ac.sanger.artemis.io.Qualifier; import uk.ac.sanger.artemis.io.QualifierVector; import uk.ac.sanger.artemis.io.EntryInformation; import uk.ac.sanger.artemis.io.EntryInformationException; import uk.ac.sanger.artemis.io.StreamQualifier; import uk.ac.sanger.artemis.io.QualifierInfo; import uk.ac.sanger.artemis.io.ValidateFeature; import uk.ac.sanger.artemis.components.ProgressThread; import uk.ac.sanger.artemis.components.genebuilder.BasicGeneBuilderFrame; import uk.ac.sanger.artemis.components.genebuilder.GeneBuilderFrame; import uk.ac.sanger.artemis.components.genebuilder.GeneEditorPanel; import uk.ac.sanger.artemis.components.genebuilder.GeneUtils; import uk.ac.sanger.artemis.components.genebuilder.ProteinMapPanel; import uk.ac.sanger.artemis.components.genebuilder.ReferencesPanel; import uk.ac.sanger.artemis.components.genebuilder.cv.CVPanel; import uk.ac.sanger.artemis.components.genebuilder.gff.PropertiesPanel; import uk.ac.sanger.artemis.components.genebuilder.ortholog.MatchPanel; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.Date; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; import java.util.Vector; import java.util.Collections; import java.util.Comparator; import javax.swing.*; /** * FeatureEdit class * * @author Kim Rutherford **/ public class FeatureEdit extends JPanel implements EntryChangeListener, FeatureChangeListener { private static final long serialVersionUID = 1L; /** The choice of feature keys - created in createComponents(). */ private KeyChoice key_choice; /** The choice of qualifiers - created in createComponents(). */ private QualifierChoice qualifier_choice = null; private final static int LOCATION_TEXT_WIDTH = 80; /** The location text - set by updateLocation(). */ private JTextField location_text = new JTextField(LOCATION_TEXT_WIDTH); /** When pressed - discard changes and dispose of the component. */ private JButton cancel_button = new JButton("Cancel"); /** When pressed - apply changes and keep the component open. */ private JButton apply_button = new JButton("Apply"); /** Edit area for qualifiers - created by createComponents(). */ private QualifierTextArea qualifier_text_area; /** The Feature this object is displaying. */ private Feature edit_feature; /** * The GotoEventSource that was passed to the constructor - used for the * "Goto Feature" button. **/ private GotoEventSource goto_event_source; /** Entry containing the Feature this object is displaying. */ private Entry edit_entry; /** EntryGroup that contains this Feature (passed to the constructor). */ private EntryGroup entry_group; private Date datestamp = null; /** The Selection that was passed to the constructor. */ private Selection selection; /** * The contents of the QualifierTextArea before the user edits anything. * This is used to work out if anything has changed since the creation of * the FeatureEdit. **/ private final String orig_qualifier_text; private JFrame frame; /** controlled vocabulary tab */ private CVPanel cvForm; /** GFF tab */ private PropertiesPanel propertiesPanel; /** similarity/ortholog/paralog tab */ private MatchPanel matchForm; private ReferencesPanel refPanel; private EntryInformation entry_information; private static boolean isTabbedView = false; private GeneEditorPanel editorPanel; private static UserDefinedQualifiers userDefinedQualifierFrame; /** * Create a new FeatureEdit object from the given Feature. * @param entry_group The EntryGroup that contains this Feature. * @param selection The Selection operate on. The operations are "Remove * Range" and "Grab Range" * @param goto_event_source The object the we will call gotoBase() on. **/ public FeatureEdit(final Feature edit_feature, final EntryGroup entry_group, final Selection selection, final GotoEventSource goto_event_source, final JFrame frame) { this.frame = frame; this.edit_feature = edit_feature; this.edit_entry = edit_feature.getEntry(); this.entry_group = entry_group; this.selection = selection; this.goto_event_source = goto_event_source; this.entry_information = edit_feature.getEntry().getEntryInformation(); setLayout(new BorderLayout()); createComponents(); updateFromFeature(); orig_qualifier_text = qualifier_text_area.getText(); if(edit_feature.getEntry() != null) { edit_feature.getEntry().addEntryChangeListener(this); edit_feature.addFeatureChangeListener(this); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent event) { stopListening(); frame.dispose(); if(userDefinedQualifierFrame != null) userDefinedQualifierFrame.setVisible(false); } }); } qualifier_text_area.requestFocus(); } private boolean isPartialSequence() { return entry_group.getSequenceEntry().getBases().getSequence() instanceof PartialSequence; } /** * Set the feature to edit * @param edit_feature * @param isSet */ public void setActiveFeature(final Feature edit_feature, final boolean isSet) { if(!isPartialSequence() && isSet) setFeature(); this.edit_feature = edit_feature; this.edit_entry = edit_feature.getEntry(); if(edit_feature.getEntry() != null) { edit_feature.getEntry().addEntryChangeListener(this); edit_feature.addFeatureChangeListener(this); } updateFromFeature(); } /** * Remove this object as a feature and entry change listener. **/ public void stopListening() { getEntry().removeEntryChangeListener(this); getFeature().removeFeatureChangeListener(this); if(cvForm != null) getFeature().removeFeatureChangeListener(cvForm); if(propertiesPanel != null) getFeature().removeFeatureChangeListener(propertiesPanel); if(matchForm != null) getFeature().removeFeatureChangeListener(matchForm); } /** * Implementation of the EntryChangeListener interface. We listen to * EntryChange events so we can notify the user if of this component if the * feature gets deleted. **/ public void entryChanged(EntryChangeEvent event) { switch(event.getType()) { case EntryChangeEvent.FEATURE_DELETED: if(event.getFeature() == edit_feature) { stopListening(); frame.dispose(); } break; default: // do nothing; break; } } /** * Add an ActionListener to the Cancel JButton of this FeatureEdit. **/ protected void addCancelActionListener(final ActionListener l) { cancel_button.addActionListener(l); } /** * Remove an ActionListener from the Cancel JButton of this FeatureEdit. **/ protected void removeCancelActionListener(final ActionListener l) { cancel_button.removeActionListener(l); } /** * Add an ActionListener to the Apply JButton of this FeatureEdit. **/ protected void addApplyActionListener(final ActionListener l) { apply_button.addActionListener(l); } /** * Implementation of the FeatureChangeListener interface. We need to * listen to feature change events from the Features in this object so that * we can update the display. * @param event The change event. **/ public void featureChanged(FeatureChangeEvent event) { getFeature().resetColour(); // re-read the information from the feature switch(event.getType()) { case FeatureChangeEvent.LOCATION_CHANGED: updateLocation(); break; case FeatureChangeEvent.SEGMENT_CHANGED: updateLocation(); break; case FeatureChangeEvent.KEY_CHANGED: updateKey(); break; case FeatureChangeEvent.QUALIFIER_CHANGED: if(qualifier_text_area.getText().equals(orig_qualifier_text)) updateFromFeature(); else { if(!event.getFeature().getIDString().equals(getFeature().getIDString())) break; final String message = "warning: the qualifiers have changed outside the editor - " + "view now?"; final YesNoDialog yes_no_dialog = new YesNoDialog(frame, message); if(yes_no_dialog.getResult()) new FeatureViewer(getFeature()); } break; default: updateFromFeature(); break; } } /** * Create all the components for this FeatureEdit component. **/ private void createComponents() { qualifier_text_area = new QualifierTextArea(); //qualifier_text_area.setWrapStyleWord(true); FlowLayout flowLayoutZeroHVgap = new FlowLayout(FlowLayout.LEADING, 0, 0); key_choice = new KeyChoice(getEntryInformation(),getFeature().getKey()); key_choice.setLayout(flowLayoutZeroHVgap); final JPanel key_and_qualifier_panel = new JPanel(); location_text.setBackground(Color.white); final JPanel key_panel = new JPanel(flowLayoutZeroHVgap); final JLabel locLabel = new JLabel("Location: "); final JLabel keyLabel = new JLabel("Key: "); keyLabel.setHorizontalAlignment(SwingConstants.RIGHT); keyLabel.setPreferredSize(locLabel.getPreferredSize()); key_panel.add(keyLabel); key_panel.add(key_choice); key_and_qualifier_panel.setLayout(new BorderLayout()); key_and_qualifier_panel.add(key_panel, "West"); boolean isGFF = false; if(edit_feature.getEmblFeature().getEntry() instanceof GFFDocumentEntry) isGFF = true; qualifier_choice = new QualifierChoice(getEntryInformation(), key_choice.getSelectedItem(),null, isGFF); final JPanel qualifier_panel = new JPanel(new FlowLayout(FlowLayout.TRAILING,0,0)); final JButton qualifier_add_button = new JButton("Add Qualifier:"); qualifier_panel.add(qualifier_add_button); qualifier_panel.add(qualifier_choice); key_and_qualifier_panel.add(qualifier_panel, "East"); key_choice.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent itemEvent) { qualifier_choice.setKey(key_choice.getSelectedItem()); } }); qualifier_add_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { final String qualifier_name = (String)qualifier_choice.getSelectedItem(); QualifierInfo qualifier_info = getEntryInformation().getQualifierInfo(qualifier_name); if(qualifier_info == null) qualifier_info = new QualifierInfo(qualifier_name, QualifierInfo.OPTIONAL_QUOTED_TEXT, null, null, false); qualifier_text_area.append("/" + qualifier_name); switch(qualifier_info.getType()) { case QualifierInfo.QUOTED_TEXT: if(qualifier_name.equals("GO")) { // special case for /GO final java.util.Calendar calendar = java.util.Calendar.getInstance(); final Date current_time = calendar.getTime(); final java.text.SimpleDateFormat date_format = new java.text.SimpleDateFormat("yyyyMMdd"); final StringBuffer result_buffer = new StringBuffer(); date_format.format(current_time, result_buffer, new java.text.FieldPosition(java.text.DateFormat.DATE_FIELD)); final String go_string = "aspect=; term=; GOid=GO:; "+ "evidence=ISS; db_xref=GO_REF:0000001; " + "with=UniProtKB:; date=" + result_buffer; qualifier_text_area.append("=\"" + go_string + "\""); } else if(qualifier_name.equals("controlled_curation")) { final java.util.Calendar calendar = java.util.Calendar.getInstance(); final Date current_time = calendar.getTime(); final java.text.SimpleDateFormat date_format = new java.text.SimpleDateFormat("yyyyMMdd"); final StringBuffer result_buffer = new StringBuffer(); date_format.format(current_time, result_buffer, new java.text.FieldPosition(java.text.DateFormat.DATE_FIELD)); final String cc_string = "term=; db_xref=; date="+ result_buffer; qualifier_text_area.append("=\"" + cc_string + "\""); } else qualifier_text_area.append ("=\"\""); break; case QualifierInfo.NO_VALUE: case QualifierInfo.OPTIONAL_QUOTED_TEXT: break; default: qualifier_text_area.append("="); } qualifier_text_area.append("\n"); } }); final JPanel middle_panel = new JPanel(); middle_panel.setLayout(new BorderLayout(0,0)); final JPanel lower_panel = new JPanel(); lower_panel.setLayout(new BorderLayout(0,0)); //final JPanel outer_location_button_panel = new JPanel(flowLayoutZeroHgap); //outer_location_button_panel.setLayout(new BorderLayout(0,0)); final JToolBar location_button_panel = new JToolBar(); location_button_panel.setRollover(true); //outer_location_button_panel.add(location_button_panel, "West"); lower_panel.add(location_button_panel, "North"); final JPanel location_panel = new JPanel(new BorderLayout(0,0)); location_panel.add(locLabel, "West"); location_panel.add(location_text, "Center"); final JButton complement_button = new JButton("Complement"); complement_button.setToolTipText("Complement position"); location_button_panel.add(complement_button); complement_button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { complementLocation(); } }); if(GeneUtils.isDatabaseEntry(getFeature().getEmblFeature())) { final JButton refresh_button = new JButton("Refresh"); location_button_panel.add(refresh_button); refresh_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { refresh(); } }); } final JButton grab_button = new JButton("Grab Range"); grab_button.setToolTipText("Add selected base range from feature coordinates"); location_button_panel.add(grab_button); grab_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { grabSelectedRange(); } }); final JButton remove_button = new JButton("Remove Range"); remove_button.setToolTipText("Remove selected base range from feature coordinates"); location_button_panel.add(remove_button); remove_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeSelectedRange(); } }); final JButton goto_button = new JButton("Goto Feature"); goto_button.setToolTipText("Goto and select this feature"); location_button_panel.add(goto_button); goto_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { goto_event_source.gotoBase(getFeature().getFirstBaseMarker()); getSelection().set(getFeature()); } }); /* final JButton select_button = new JButton("Select Feature"); location_button_panel.add(select_button); select_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { getSelection().set(getFeature()); } });*/ if(Options.getOptions().getPropertyTruthValue("sanger_options")) { // a PSU only hack final JButton tidy_button = new JButton("Tidy"); location_button_panel.add(tidy_button); tidy_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { tidy(); tidyGO(); } catch(QualifierParseException exception) { new MessageDialog(frame, "Cannot tidy - qualifier error: " + exception.getMessage()); } } }); } final JButton transferAnnotationBbutton = new JButton("TAT"); location_button_panel.add(transferAnnotationBbutton); transferAnnotationBbutton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new TransferAnnotationTool(getFeature(), entry_group, matchForm); } }); if(Options.isUnixHost()) { JButton oo_edit_button = new JButton("ObjectEdit"); location_button_panel.add(oo_edit_button); final uk.ac.sanger.artemis.editor.BigPane bp = new uk.ac.sanger.artemis.editor.BigPane(getEntryInformation()); oo_edit_button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { String qualifier_txt = qualifier_text_area.getText(); File base_dir = getBaseDirectoryFromEntry(edit_entry); String baseDirStr = ""; if(base_dir != null) baseDirStr = base_dir.getAbsolutePath() + System.getProperty("file.separator"); StringReader strRead = new StringReader(qualifier_txt); BufferedReader buff = new BufferedReader(strRead); String line; final Hashtable> dataFile = new Hashtable>(); try { int ind; while((line = buff.readLine()) != null) { if(line.startsWith("/fasta_file=")) { if((ind = line.indexOf(':'))>-1) line = baseDirStr+line.substring(ind+1); else line = baseDirStr+line.substring(13); ind = line.lastIndexOf("\""); if(ind > -1) line = line.substring(0, ind); Vector v; if(dataFile.containsKey("fasta")) v = dataFile.get("fasta"); else v = new Vector(); v.add(line); dataFile.put("fasta",v); } else if(line.startsWith("/blastp_file=")) { if((ind = line.indexOf(':'))>-1) line = baseDirStr+line.substring(ind+1); else line = baseDirStr+line.substring(14); ind = line.lastIndexOf("\""); if(ind > -1) line = line.substring(0, ind); Vector v; if(dataFile.containsKey("blastp")) v = dataFile.get("blastp"); else v = new Vector(); v.add(line); dataFile.put("blastp",v); } else if(line.startsWith("/blastp+go_file=")) { if((ind = line.indexOf(':'))>-1) line = line.substring(ind+1); else line = baseDirStr+line.substring(17); ind = line.lastIndexOf("\""); if(ind > -1) line = line.substring(0, ind); Vector v; if(dataFile.containsKey("blastp+go")) v = dataFile.get("blastp+go"); else v = new Vector(); v.add(line); dataFile.put("blastp+go",v); } } } catch(IOException ioe){} FeatureEdit.this.setCursor(new Cursor(Cursor.WAIT_CURSOR)); final ProgressThread progress = new ProgressThread(null, "Loading Data...."); progress.start(); SwingWorker ooEd = new SwingWorker() { public Object construct() { // find overlaping features final Location this_loc = edit_feature.getLocation(); final int this_start = this_loc.getFirstBase(); final int this_end = this_loc.getLastBase(); FeaturePredicate predicate = new FeaturePredicate() { public boolean testPredicate (final Feature feature) { final Location loc = feature.getLocation(); final int start = loc.getFirstBase(); final int end = loc.getLastBase(); if((start > this_start && start < this_end) || (end > this_start && end < this_end)) { final String note = feature.getNote(); if(note != null && note.indexOf("Pfam")>-1) return true; } return false; } }; final FeatureVector overlapFeatures = new FeatureVector(); final FeatureEnumeration featureEnum = entry_group.features(); while(featureEnum.hasMoreFeatures()) { Feature this_feature = featureEnum.nextFeature(); if(predicate.testPredicate(this_feature)) overlapFeatures.add(this_feature); } // show object editor try { if(dataFile.size() > 0) bp.set(dataFile, qualifier_text_area, overlapFeatures, edit_feature, matchForm, cvForm); else JOptionPane.showMessageDialog(null,"No results files.", "Warning", JOptionPane.WARNING_MESSAGE); } catch(ArrayIndexOutOfBoundsException e) { JOptionPane.showMessageDialog(null,"No results files.", "Warning", JOptionPane.WARNING_MESSAGE); } progress.finished(); FeatureEdit.this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); return null; } }; ooEd.start(); } }); } final JButton userQualifiers = new JButton("User Qualifiers"); userQualifiers.setToolTipText("User defined qualifier selection"); location_button_panel.add(userQualifiers); userQualifiers.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(userDefinedQualifierFrame == null) { userDefinedQualifierFrame = new UserDefinedQualifiers(); userDefinedQualifierFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); } userDefinedQualifierFrame.pack(); final JFrame topFrame = (JFrame) SwingUtilities.getWindowAncestor(FeatureEdit.this); Point p = topFrame.getLocationOnScreen(); p.x -= userDefinedQualifierFrame.getWidth(); if(p.x < 10) p.x = 10; userDefinedQualifierFrame.setLocation(p); userDefinedQualifierFrame.setQualifierTextArea(qualifier_text_area); userDefinedQualifierFrame.setSelection(selection); userDefinedQualifierFrame.setVisible(true); } }); middle_panel.add(location_panel, "North"); add(key_and_qualifier_panel, "North"); cancel_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(edit_feature.getEntry() != null) stopListening(); frame.dispose(); if(userDefinedQualifierFrame != null) userDefinedQualifierFrame.setVisible(false); } }); final JButton ok_button = new JButton("OK"); if(!getFeature().isReadOnly()) { ok_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(setFeature()) { stopListening(); if(propertiesPanel != null) propertiesPanel.updateSettings(); frame.dispose(); if(userDefinedQualifierFrame != null) userDefinedQualifierFrame.setVisible(false); } } }); apply_button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setFeature(); } }); } final FlowLayout flow_layout = new FlowLayout(FlowLayout.CENTER, 18, 1); final JPanel ok_cancel_update_panel = new JPanel(flow_layout); Box fillerBox = Box.createHorizontalBox(); if(GeneUtils.isDatabaseEntry(getFeature().getEmblFeature())) { cvForm = new CVPanel(getFeature()); cvForm.setBackground(Color.WHITE); matchForm = new MatchPanel(getFeature(), (DocumentEntry)getFeature().getEmblFeature().getEntry()); matchForm.setBackground(Color.WHITE); propertiesPanel = new PropertiesPanel(getFeature()); propertiesPanel.setBackground(Color.WHITE); refPanel = new ReferencesPanel(getFeature()); refPanel.setBackground(Color.WHITE); addGffAnnotationView(lower_panel); final JCheckBox tabbedView = new JCheckBox("Tab View", isTabbedView); tabbedView.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { isTabbedView = tabbedView.isSelected(); addGffAnnotationView(lower_panel); lower_panel.revalidate(); lower_panel.repaint(); } }); final JCheckBox oneView = new JCheckBox("Overview", false); oneView.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if(setFeature()) { stopListening(); if(propertiesPanel != null) propertiesPanel.updateSettings(); frame.dispose(); System.setProperty("basic", "true"); new BasicGeneBuilderFrame(getFeature(), entry_group, selection, null); } } }); if(((GFFStreamFeature)getFeature().getEmblFeature()).getChadoGene() != null) ok_cancel_update_panel.add(oneView); ok_cancel_update_panel.add(tabbedView); fillerBox.add(Box.createHorizontalStrut( tabbedView.getPreferredSize().width )); } else if(getFeature().getEmblFeature() instanceof GFFStreamFeature) { propertiesPanel = new PropertiesPanel(getFeature()); addGffAnnotationView(lower_panel); } else lower_panel.add(new JScrollPane(qualifier_text_area), "Center"); if(!getFeature().isReadOnly()) ok_cancel_update_panel.add(ok_button); ok_cancel_update_panel.add(cancel_button); if(!getFeature().isReadOnly()) ok_cancel_update_panel.add(apply_button); ok_cancel_update_panel.add(fillerBox); add(ok_cancel_update_panel, "South"); middle_panel.add(lower_panel, "Center"); add(middle_panel, "Center"); } /** * Refresh the annotation for the active feature */ private void refresh() { // refresh from the database final DatabaseDocument originalDocument = (DatabaseDocument)((DocumentEntry)edit_feature.getEmblFeature().getEntry()).getDocument(); final Set uniquenames = ((GFFStreamFeature)edit_feature.getEmblFeature()).getSegmentRangeStore().keySet(); final Iterator it = uniquenames.iterator(); final String uniquename = it.next(); final DatabaseDocument newDocument = new DatabaseDocument(originalDocument, uniquename, null, true, null); newDocument.setLazyFeatureLoad(false); newDocument.setReadChildren(false); try { DatabaseDocumentEntry dbentry = new DatabaseDocumentEntry(newDocument, null); uk.ac.sanger.artemis.io.Feature databaseFeature = dbentry.getAllFeatures().featureAt(0); // compare timelastmodified Qualifier qualifier = edit_feature.getQualifierByName("timelastmodified"); String active_timelastmodified = (String)qualifier.getValues().get(0); qualifier = databaseFeature.getQualifierByName("timelastmodified"); String database_timelastmodified = (String)qualifier.getValues().get(0); if(active_timelastmodified.equals(database_timelastmodified)) { JOptionPane.showMessageDialog(this, "No new changes found for the feature\n"+ uniquename+"\n"+ "in the database since:\n"+database_timelastmodified, "No Updates", JOptionPane.INFORMATION_MESSAGE); return; } else { JOptionPane.showMessageDialog(this, "Changes found for the feature\n"+ uniquename+"\n"+ "in the database at:\n"+database_timelastmodified, "Changes Found", JOptionPane.INFORMATION_MESSAGE); } final QualifierVector db_qv = databaseFeature.getQualifiers(); final QualifierVector qv = edit_feature.getQualifiers(); final QualifierVector new_qv = new QualifierVector(); for(int i=0; i qual_str = new Vector(); try { String line; while((line = buff.readLine()) != null) qual_str.add(line); } catch(IOException ioe){} Comparator comparator = new Comparator() { public int compare (String fst, String snd) { if( !fst.startsWith("/GO") || !snd.startsWith("/GO") ) return 0; return fst.compareTo(snd); } }; Collections.sort(qual_str, comparator); StringBuffer buffer = new StringBuffer(); for(String qualifier_string: qual_str) buffer.append(tidyHelper(qualifier_string) + "\n"); qualifier_text_area.setText(buffer.toString()); } /** * Perform the action of tidy() on the String version of one qualifier. **/ private String tidyHelper(final String qualifier_string) { final String temp_string; if (qualifier_string.startsWith("/transferred_")) temp_string = "/" + qualifier_string.substring(13); else temp_string = qualifier_string; final int start_index = temp_string.indexOf ("<>"); if(start_index != -1 && end_index != -1) { if(temp_string.length() > end_index + 2 && temp_string.charAt(end_index + 2) == ' ') return temp_string.substring(0, start_index) + temp_string.substring(end_index + 3); else return temp_string.substring(0, start_index) + temp_string.substring(end_index + 2); } else return temp_string; } /** * Add the currently selected range to location_text. **/ private void grabSelectedRange() { if(!rationalizeLocation ()) { new MessageDialog(frame, "grab failed - current location cannot be parsed"); return; } final Range selected_range = getSelection().getSelectionRange(); if(selected_range == null) { new MessageDialog(frame, "grab failed - nothing is selected"); return; } // save it in case it gets mangled final String old_location_text = location_text.getText(); if (old_location_text.endsWith ("))")) { final String new_text = old_location_text.substring(0, old_location_text.length () - 2); location_text.setText(new_text + "," + selected_range.getStart () + ".." + selected_range.getEnd () + "))"); } else { if(old_location_text.endsWith (")")) { final String new_text = old_location_text.substring(0, old_location_text.length () - 1); location_text.setText(new_text + "," + selected_range.getStart () + ".." + selected_range.getEnd () + ")"); } else location_text.setText(old_location_text + "," + selected_range.getStart () + ".." + selected_range.getEnd ()); } if(!rationalizeLocation()) { location_text.setText (old_location_text); new MessageDialog(frame, "grab failed - location cannot be parsed after " + "grabbing"); } } /** * Remove the currently selected range of bases from location_text. **/ private void removeSelectedRange() { if(!rationalizeLocation()) { new MessageDialog(frame, "grab failed - current location cannot be parsed"); return; } final MarkerRange selected_marker_range = getSelection().getMarkerRange(); if(selected_marker_range == null) { new MessageDialog(frame, "remove range failed - no bases are selected"); return; } final Range selected_range = selected_marker_range.getRawRange(); if (selected_marker_range.getStrand() != getFeature().getStrand()) { new MessageDialog(frame, "remove range failed - you need to select " + "some bases on the other strand"); return; } final Location location; try { location = new Location(location_text.getText ()); } catch (LocationParseException e) { // this shouldn't happen because we called rationalizeLocation () throw new Error("internal error - unexpected exception: " + e); } final Range location_total_range = location.getTotalRange(); if(!selected_range.overlaps(location_total_range)) { new MessageDialog(frame, "remove range failed - the range you " + "selected does not overlap the feature"); return; } if(selected_range.contains(location_total_range)) { new MessageDialog(frame, "remove range failed - the range you " + "selected overlaps the whole feature"); return; } final RangeVector location_ranges = location.getRanges(); final boolean location_is_complemented = location.isComplement(); final RangeVector new_ranges = new RangeVector(); // if the selected_range completely covers a range remove the // range. otherwise if the selected_range is completely within one of the // ranges two new ranges are created. if the selected_range is not // completely contained then the appropriate end of the range is truncated for(int i = 0; i < location_ranges.size(); ++i) { final Range this_range = (Range)location_ranges.elementAt(i); if(selected_range.overlaps(this_range)) { try { if(this_range.contains(selected_range) && this_range.getStart() != selected_range.getStart() && this_range.getEnd() != selected_range.getEnd()) { // chop a piece out of the middle and make two new ranges final Range new_start_range = this_range.change(selected_range.getEnd() + 1, this_range.getEnd()); new_ranges.add(new_start_range); final Range new_end_range = this_range.change(this_range.getStart(), selected_range.getStart() - 1); new_ranges.add(new_end_range); } else { if(selected_range.contains(this_range)) { // delete (ie. don't copy) the range } else { if(this_range.getStart() < selected_range.getStart()) { // truncate the end of the range final Range new_start_range = this_range.change(this_range.getStart(), selected_range.getStart() - 1); new_ranges.add(new_start_range); } else { if(this_range.getEnd() > selected_range.getEnd()) { // truncate the start of the range final Range new_end_range = this_range.change(selected_range.getEnd() + 1, this_range.getEnd()); new_ranges.add(new_end_range); } else throw new Error ("internal error - can't remove range"); } } } } catch(OutOfRangeException e) { throw new Error ("internal error - unexpected exception: " + e); } } else new_ranges.add(this_range); // copy it unchanged } final Location new_location = new Location(new_ranges, location_is_complemented); location_text.setText(new_location.toStringShort()); } /** * Attempt to parse the current location_text as a Location. If it can be * parsed it will be canonicalized (ie. the complement, if any, will be * outermost). Returns true if and only if the location_text could be * parsed. **/ private boolean rationalizeLocation() { try { final Location location = new Location(location_text.getText()); location_text.setText(location.toStringShort()); return true; } catch(LocationParseException e) { return false; } } /** * Read the qualifiers from the feature and update the qualifier JTextArea. **/ private void updateQualifiers() { if(GeneUtils.isDatabaseEntry(getFeature().getEmblFeature())) GeneUtils.addLazyQualifiers((GFFStreamFeature)getFeature().getEmblFeature()); qualifier_text_area.setText(getQualifierString()); if(getFeature().getEmblFeature() instanceof GFFStreamFeature) { // load synonym if(cvForm != null) cvForm.updateFromFeature(getFeature()); if(refPanel != null) refPanel.updateFromFeature(getFeature()); if(propertiesPanel != null) propertiesPanel.updateFromFeature(getFeature()); if(matchForm != null) matchForm.updateFromFeature(getFeature()); if(!isTabbedView && editorPanel != null) editorPanel.updatePanelState(); } } /** * Return a string containing one qualifier per line. These are the * original qualifiers, not the qualifiers from the qualifier_text_area. **/ private String getQualifierString() { final StringBuffer buffer = new StringBuffer(); final QualifierVector qualifiers = getFeature().getQualifiers(); for(Qualifier this_qualifier: qualifiers) { // // strip out CV qualifiers // if( (cvForm != null && CVPanel.isCvTag(this_qualifier)) || (refPanel != null && ReferencesPanel.isReferenceTag(this_qualifier)) || (propertiesPanel != null && PropertiesPanel.isPropertiesTag(this_qualifier, getFeature())) || (matchForm != null && MatchPanel.isMatchTag(this_qualifier)) || (propertiesPanel != null && ProteinMapPanel.isProteinMapElement(this_qualifier)) ) continue; if(this_qualifier instanceof QualifierLazyLoading) ((QualifierLazyLoading)this_qualifier).setForceLoad(true); final QualifierInfo qualifier_info = getEntryInformation().getQualifierInfo(this_qualifier.getName()); final StringVector qualifier_strings = StreamQualifier.toStringVector(qualifier_info, this_qualifier); for(String qualStr: qualifier_strings) buffer.append(qualStr + "\n"); } return buffer.toString(); } /** * Set the key, location and qualifiers of the feature to be the same as * what values currently shown in the components. * @return true if and only if action succeeds. It may fail because of an * illegal location or qualifier, in which case a message will be * displayed before returning. **/ private boolean setFeature() { final Key key = key_choice.getSelectedItem(); final KeyVector possible_keys = getEntryInformation().getValidKeys(); if(possible_keys != null && !possible_keys.contains(key)) { final YesNoDialog dialog = new YesNoDialog(frame, "Add this new key: " + key + "?"); if(dialog.getResult()) // yes getEntryInformation ().addKey (key); else return false; } final Location location; try { location = new Location(location_text.getText()); } catch(LocationParseException exception) { final String error_string = exception.getMessage (); System.out.println(error_string); new MessageDialog(frame, "Cannot apply changes because of location error: " + error_string); return false; } final QualifierVector qualifiers; try { qualifiers = qualifier_text_area.getParsedQualifiers(getEntryInformation ()); // if using controlled vocab form if(cvForm != null) { QualifierVector cvQualifiers = cvForm.getCvQualifiers(); if(cvQualifiers != null && cvQualifiers.size() > 0) qualifiers.addAll(cvQualifiers); } if(refPanel != null) { QualifierVector refQualifiers = refPanel.getQualifiers(); if(refQualifiers != null && refQualifiers.size() > 0) qualifiers.addAll(refQualifiers); } if(propertiesPanel != null) { QualifierVector gffQualifiers = propertiesPanel.getGffQualifiers(getFeature()); if(gffQualifiers != null && gffQualifiers.size() > 0) qualifiers.addAll(gffQualifiers); QualifierVector mapQualifiers = ProteinMapPanel.getProteinMapQualifiers(getFeature()); if(mapQualifiers != null && mapQualifiers.size() > 0) qualifiers.addAll(mapQualifiers); } updateGffIds(qualifiers); if(matchForm != null) { QualifierVector orthologQualifiers = matchForm.getMatchQualifiers(); if(orthologQualifiers != null && orthologQualifiers.size() > 0) qualifiers.addAll(orthologQualifiers); } final String goErrs = ValidateFeature.validateGO(qualifiers, getEntryInformation()); if(goErrs.length()>0) { Object[] options = { "CANCEL", "CONTINUE" }; int opt = JOptionPane.showOptionDialog(null, goErrs, "GO errors", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); if(opt == 0) return false; } //if(similarityTextArea != null) // similarityTextArea.checkForChanges(); } catch(QualifierParseException exception) { final String error_string = exception.getMessage(); System.out.println(error_string); new MessageDialog(frame, "Cannot apply changes because of a qualifier " + "error: " + error_string); return false; } try { entry_group.getActionController().startAction(); try { getFeature().set(datestamp, key, location, qualifiers); } catch(OutOfDateException e) { final YesNoDialog dialog = new YesNoDialog(frame, "the feature has changed since the edit " + "window was opened, continue?"); if(dialog.getResult()) // yes - ignore the datestamp getFeature().set(key, location, qualifiers); else return false; } catch(java.lang.Error err) { err.printStackTrace(); if(err.getMessage().indexOf("InvalidRelationException")>-1) { JScrollPane jsp = new JScrollPane(new JLabel(err.getMessage())); jsp.setPreferredSize(new Dimension(200,100)); JOptionPane.showMessageDialog(null, jsp, "Error", JOptionPane.ERROR_MESSAGE); } return false; } } catch(EntryInformationException e) { final String error_string = e.getMessage(); new MessageDialog(frame, "Cannot apply changes: " + error_string); return false; } catch(OutOfRangeException e) { new MessageDialog(frame, "Cannot apply changes - the location is out of " + "range for this sequence"); return false; } catch(ReadOnlyException e) { new MessageDialog(frame, "Cannot apply changes - the feature is " + "read only"); return false; } finally { entry_group.getActionController ().endAction (); } dribble(); return true; } /** * Propagate any changes to GFF ID qualifiers through the * gene model * @param qualifiers */ private void updateGffIds(QualifierVector qualifiers) { if( !GeneUtils.isDatabaseEntry(getFeature().getEmblFeature()) && getFeature().getEmblFeature() instanceof GFFStreamFeature ) { final GFFStreamFeature gffFeature = (GFFStreamFeature)getFeature().getEmblFeature(); if(gffFeature.getChadoGene() != null) { try { final String newName = ((String) (qualifiers.getQualifierByName("ID").getValues().get(0))).trim(); final String oldName = ((String) (gffFeature.getQualifierByName("ID").getValues().get(0))).trim(); if(!newName.equals(oldName)) { int val = JOptionPane.showConfirmDialog(null, "Change name of children based on this ["+newName+"]?", "Name Change", JOptionPane.OK_CANCEL_OPTION); if(val == JOptionPane.CANCEL_OPTION) return; final Set children = gffFeature.getChadoGene().getChildren(gffFeature); GeneUtils.propagateId(gffFeature, newName, children); GeneUtils.fixParentQualifier(oldName, newName, children); final Iterator it = children.iterator(); while(it.hasNext()) { final GFFStreamFeature child = (GFFStreamFeature)it.next(); if( child.getSegmentRangeStore().size() == 1 && !child.getKey().getKeyString().equals("CDS") && child.getKey().getKeyString().indexOf("exon") == -1) child.setSegmentRangeStore(null); } } } catch(Exception e){ e.printStackTrace(); } } } } /** * Return the Feature we are editing as passed to the constructor. **/ public Feature getFeature() { return edit_feature; } /** * On Unix machines this method will append the text of the feature to a * file in a current directory called .dribble + **/ private void dribble() { if(!Options.isUnixHost()) return; final String dribble_file_name; if(getEntry().getName() != null) dribble_file_name = ".dribble." + getEntry().getName(); else dribble_file_name = ".dribble.no_name"; try { final Writer writer = new FileWriter(dribble_file_name, true); getFeature().writeNative(writer); writer.flush(); writer.close(); } catch(IOException e) { System.err.println("IO exception while accessing " + dribble_file_name + ": " + e.getMessage()); } } /** * Return the Entry that contains the Feature this object is displaying. **/ private Entry getEntry() { return edit_entry; } /** * Read the key from the feature and update the key chooser. **/ private void updateKey() { final Key feature_key = getFeature().getKey(); key_choice.setKey(feature_key); } /** * Return the EntryInformation object of the entry containing the feature. **/ public EntryInformation getEntryInformation() { if(entry_information == null) entry_information = getEntry().getEntryInformation(); return entry_information; } /** * Return the Selection that was passed to the constructor. **/ private Selection getSelection() { return selection; } public static boolean isTabbedView() { return isTabbedView; } public static void setTabbedView(boolean isTabbedView) { FeatureEdit.isTabbedView = isTabbedView; } /** * Set whether the feature is obsolete (database mode). * @param obsoleteChanged */ public void setObsoleteChanged(boolean obsoleteChanged) { propertiesPanel.setObsoleteChanged(obsoleteChanged); } public QualifierTextArea getQualifierTextArea() { return qualifier_text_area; } }