/* UserDefinedQualifier * This file is part of Artemis * * Copyright(C) 2013 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. * */ package uk.ac.sanger.artemis.components; import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.UIManager; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import javax.swing.filechooser.FileFilter; import uk.ac.sanger.artemis.FeatureVector; import uk.ac.sanger.artemis.Options; import uk.ac.sanger.artemis.Selection; import uk.ac.sanger.artemis.components.genebuilder.JExtendedComboBox; import uk.ac.sanger.artemis.components.genebuilder.cv.DatePanel; import uk.ac.sanger.artemis.editor.MultiLineToolTipUI; import uk.ac.sanger.artemis.io.EntryInformationException; import uk.ac.sanger.artemis.io.Qualifier; import uk.ac.sanger.artemis.io.QualifierVector; import uk.ac.sanger.artemis.util.Document; import uk.ac.sanger.artemis.util.FileDocument; import uk.ac.sanger.artemis.util.LinePushBackReader; import uk.ac.sanger.artemis.util.ReadOnlyException; import uk.ac.sanger.artemis.util.StringVector; import uk.ac.sanger.artemis.util.URLDocument; /** * Load and make available qualifiers from a list in a file * of tag=value pairs and from OBO files. */ class UserDefinedQualifiers extends JFrame { private static final long serialVersionUID = 1L; private static org.apache.log4j.Logger logger4j = org.apache.log4j.Logger.getLogger(UserDefinedQualifiers.class); /* collection of loaded files */ private Vector obos = new Vector(); /* component containing list of available values */ private Box qualifierBox = Box.createHorizontalBox(); /* listener for adding qualifiers **/ private ActionListener addListener; /* text area in the feature editor to add qualifiers to */ private QualifierTextArea qualifier_text_area; private Selection selection; private JPanel mainPanel; /* line pattern for tag=value */ private static Pattern TAG_VALUE_PATTERN = Pattern.compile("^/?(\\w+) ?= ?([^\\n\\r]+)$"); UserDefinedQualifiers() { super("Qualifier List"); mainPanel = (JPanel) getContentPane(); MultiLineToolTipUI.initialize(); read(); createComponentToAddCvTerm(); createMenus(); } /** * Menu creation */ private void createMenus() { final JMenuBar menuBar = new JMenuBar(); final JMenu fileMenu = new JMenu("File"); menuBar.add(fileMenu); final JMenuItem importFile = new JMenuItem("Import qualifiers from file ..."); importFile.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { importFromFile(); } }); fileMenu.add(importFile); final JMenuItem importURL = new JMenuItem("Import qualifiers from URL ..."); importURL.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { importFromUrl(); } }); fileMenu.add(importURL); final JMenuItem pasteQualifiers = new JMenuItem("Paste qualifiers ..."); pasteQualifiers.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { final QualifierTextArea qualifierText = new QualifierTextArea(); int status = JOptionPane.showConfirmDialog(UserDefinedQualifiers.this, qualifierText, "Paste in qualifier lists in the format: name = value", JOptionPane.OK_CANCEL_OPTION); if(status == JOptionPane.OK_OPTION) { QualifierList obo = new QualifierList(); obo.qualifiers = loadText(new ByteArrayInputStream(qualifierText.getText().getBytes())); obos.add(obo); createComponentToAddCvTerm(); mainPanel.revalidate(); writeQualifierList(qualifierText.getText()); } } }); fileMenu.add(pasteQualifiers); fileMenu.addSeparator(); final JMenu removeFile = new JMenu("Qualifier source(s)"); removeFile.addMenuListener(new MenuListener(){ public void menuCanceled(MenuEvent e){} public void menuDeselected(MenuEvent e){} public void menuSelected(MenuEvent e) { removeFile.removeAll(); for(final QualifierList obo: obos) { final JCheckBoxMenuItem oboMenu = new JCheckBoxMenuItem(obo.doc.getName(), obo.isAcive); removeFile.add(oboMenu); oboMenu.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { obo.isAcive = oboMenu.isSelected(); createComponentToAddCvTerm(); mainPanel.revalidate(); mainPanel.repaint(); } }); } } }); fileMenu.add(removeFile); setJMenuBar(menuBar); } /** * Add components to the panel for adding CvTerm's to * the annotation. */ private void createComponentToAddCvTerm() { mainPanel.removeAll(); mainPanel.setLayout(new GridBagLayout()); final GridBagConstraints c = new GridBagConstraints(); final JCheckBox ignoreCase = new JCheckBox("Ignore case",true); final Vector names = getQualiferNames(); final JComboBox nameCombo = new JComboBox(names); final JButton addButton = new JButton("ADD"); final JCheckBox addToAllSelected = new JCheckBox("add to all selected features", false); final JTextField keyWord = new JTextField(45); final Dimension d = new Dimension(500, ignoreCase.getPreferredSize().height); final Dimension d2 = new Dimension(300, ignoreCase.getPreferredSize().height); int row = 0; c.gridx = 0; c.gridy = row; c.anchor = GridBagConstraints.EAST; if(names.size() < 1) { c.gridwidth = 2; mainPanel.add(new JLabel("Import a list of qualifiers from the File menu."), c); c.gridwidth = 1; ignoreCase.setEnabled(false); nameCombo.setEnabled(false); addButton.setEnabled(false); addToAllSelected.setEnabled(false); keyWord.setEnabled(false); } else { mainPanel.add(new JLabel("Name: "), c); c.gridx = 1; c.anchor = GridBagConstraints.WEST; mainPanel.add(nameCombo, c); } c.gridx = 2; mainPanel.add(Box.createHorizontalStrut(150), c); // keyword keyWord.setSelectionStart(0); keyWord.setSelectionEnd(keyWord.getText().length()); keyWord.setSelectedTextColor(Color.blue); keyWord.setMinimumSize(d2); keyWord.addActionListener(new ActionListener(){ // carry out search when enter key is pressed public void actionPerformed(ActionEvent event) { searchQualifiers(keyWord.getText(), nameCombo, ignoreCase.isSelected(), addButton, addToAllSelected, d); } }); c.gridy = ++row; c.gridx = 0; c.anchor = GridBagConstraints.EAST; mainPanel.add(new JLabel("Keywords: "),c); c.gridx = 1; c.anchor = GridBagConstraints.WEST; mainPanel.add(keyWord,c); c.gridy = ++row; mainPanel.add(ignoreCase,c); // search button c.gridx = 0; c.gridy = ++row; c.anchor = GridBagConstraints.WEST; final JButton search = new JButton("SEARCH"); search.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { searchQualifiers(keyWord.getText(), nameCombo, ignoreCase.isSelected(), addButton, addToAllSelected, d); } }); mainPanel.add(search,c); c.gridx = 0; c.gridy = ++row; c.gridwidth = 3; qualifierBox.add(Box.createVerticalStrut(25)); mainPanel.add(qualifierBox, c); c.gridx = 0; c.gridy = ++row; c.gridwidth = 1; mainPanel.add(addButton, c); c.gridx = 1; final JButton closeButton = new JButton("CLOSE"); mainPanel.add(closeButton, c); closeButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { dispose(); } }); c.gridx = 0; c.gridy = ++row; c.gridwidth = 2; mainPanel.add(addToAllSelected,c); c.gridy = ++row; mainPanel.add(Box.createVerticalStrut(15), c); } private StringVector searchOboQualifiers(final QualifierList obo, final String qName, final String keyWord, final boolean ignoreCase) { final Qualifier q = obo.qualifiers.getQualifierByName(qName); if(q == null) return null; final StringVector values = q.getValues(); if(keyWord != null && !keyWord.trim().equals("")) { final String keyWordLC = keyWord.toLowerCase(); final StringVector tmp = values.copy(); for(String val: tmp) { if(ignoreCase) { if(val.toLowerCase().indexOf(keyWordLC) == -1) values.remove(val); } else if(val.indexOf(keyWord) == -1) values.remove(val); } } return values; } /** * Search the qualifiers for the selected qualifier name and value. */ private void searchQualifiers(final String keyWord, final JComboBox nameCombo, final boolean ignoreCase, final JButton addButton, final JCheckBox addToAllSelected, final Dimension d) { final String qName = (String) nameCombo.getSelectedItem(); final StringVector results = new StringVector(); for(QualifierList obo: obos) { if(!obo.isAcive) continue; StringVector oboResult = searchOboQualifiers(obo, qName, keyWord, ignoreCase); if(oboResult != null) results.add(oboResult); } final JExtendedComboBox valuesList = new JExtendedComboBox(results, true); valuesList.getEditor().getEditorComponent().addMouseListener( new ComboMouseListener(valuesList)); valuesList.setPreferredSize(d); valuesList.setMaximumSize(d); if(valuesList.getSelectedItem() != null && valuesList.getSelectedItem().equals("") && valuesList.getItemCount() > 2) valuesList.setSelectedIndex(1); qualifierBox.removeAll(); qualifierBox.add(valuesList); if(addListener != null) addButton.removeActionListener(addListener); addListener = new ActionListener(){ public void actionPerformed(ActionEvent arg0) { String qval = (String) valuesList.getSelectedItem(); if(qName.equals("biological_process")) qval = "/GO=aspect=P;"+qval+";date="+DatePanel.getDate(); else if(qName.equals("molecular_function")) qval = "/GO=aspect=F;"+qval+";date="+DatePanel.getDate(); else if(qName.equals("cellular_component")) qval = "/GO=aspect=C;"+qval+";date="+DatePanel.getDate(); else qval = "/"+qName+"="+qval; if(addToAllSelected.isSelected()) { final FeatureVector features = selection.getAllFeatures(); if(!Options.isBlackBeltMode() && features.size() > 1) { int status = JOptionPane.showConfirmDialog(UserDefinedQualifiers.this, "Add "+qName+" to "+features.size()+" selected features?", "Add Qualifier To Selected Features", JOptionPane.OK_CANCEL_OPTION); if(status == JOptionPane.CANCEL_OPTION) return; } final int idx = qval.indexOf("="); final Qualifier q = new Qualifier(qval.substring(1, idx), qval.substring( idx+1 )); try { for(int i=0; i getQualiferNames() { final Vector names = new Vector(); for(QualifierList obo: obos) { if(!obo.isAcive) continue; QualifierVector qualifiers = obo.qualifiers; for(Qualifier q: qualifiers) if(!names.contains(q.getName())) names.add(q.getName()); } return names; } /** * Read qualifier lists */ private void read() { URL url = ClassLoader.getSystemResource("etc/artemis.qualifiers"); if(url != null) { Document doc = new URLDocument( ClassLoader.getSystemResource("etc/artemis.qualifiers")); createObo(doc); } final String [] qualFiles = { "artemis.qualifiers", System.getProperty("user.home") + File.separator + ".artemis.qualifiers" }; for(String fileName: qualFiles) { Document doc = new FileDocument(new File(fileName)); if(doc.readable()) createObo(doc); } } /** * Load the qualifiers from an input stream. * @param ins * @throws IOException */ private QualifierVector loadQualifiers(final Document doc) throws IOException { QualifierVector qualifiers = loadObo(doc); if(qualifiers.size() == 0) qualifiers = loadText(doc.getInputStream()); return qualifiers; } /** * Load in qualifiers from an input stream that are in OBO format. * @param ins * @return * @throws IOException */ private QualifierVector loadObo(final Document doc) throws IOException { final LinePushBackReader reader = new LinePushBackReader( new BufferedReader(new InputStreamReader(doc.getInputStream()))); final QualifierVector qualifiers = new QualifierVector(); try { String line; while((line = reader.readLine()) != null) { if(line.startsWith("#")) continue; if(startOfStanza(line)) { String val = null; String name = null; String id = null; while((line = reader.readLine()) != null && !startOfStanza(line) && line.indexOf("import:") == -1) { if(line.startsWith("namespace:")) name = line.substring(10).trim(); else if(line.startsWith("name:")) val = line.substring(5).trim(); else if(line.startsWith("id:")) id = line.substring(3).trim(); } if(line != null) reader.pushBack(line); if(id != null) { if( name != null && (name.equals("biological_process") || name.equals("molecular_function") || name.equals("cellular_component"))) val = "term="+val+"; GOid="+id; else val = "term="+val+"; id="+id; } if(name != null) qualifiers.addQualifierValues(new Qualifier(name, val)); } else if(line.startsWith("import:")) { // import OBO file from a URL final URLDocument urlDoc = new URLDocument( new URL(line.substring(7).trim()) ); // check to ensure not recursively reading from same file if(!doc.getLocation().equals(urlDoc.getLocation())) createObo(urlDoc); } } reader.close(); } catch (IOException e) { e.printStackTrace(); } return qualifiers; } /** * Each unit of the OBO file starts with one of the three supported stanza * types: [Term], [Typedef], and [Instance]. Parsers/serializers will round-trip * (successfully load and save) unrecognized stanzas. * @param line * @return */ private boolean startOfStanza(final String line) { return line.startsWith("["); } /** * Load in qualifiers from an input stream that are in the format: * name=value * @param ins * @return */ private QualifierVector loadText(final InputStream ins) { final BufferedReader reader = new BufferedReader(new InputStreamReader(ins)); final QualifierVector qualifiers = new QualifierVector(); try { String line; while((line = reader.readLine()) != null) { Matcher m = TAG_VALUE_PATTERN.matcher(line); if(m.matches()) qualifiers.addQualifierValues(new Qualifier(m.group(1), m.group(2))); } reader.close(); } catch (IOException e) { e.printStackTrace(); } return qualifiers; } /** * Import OBO */ private void importFromFile() { final StickyFileChooser fc = new StickyFileChooser(); //final JCheckBox autoImport = new JCheckBox("Automatically import", true); //fc.setAccessory(autoImport); OboFileFilter oboFilter = new OboFileFilter(new String[]{"obo", "OBO"}, "OBO files"); fc.addChoosableFileFilter(oboFilter); fc.setFileFilter(oboFilter); int status = fc.showOpenDialog(UserDefinedQualifiers.this); if(status != StickyFileChooser.APPROVE_OPTION) return; final FileDocument doc = new FileDocument(fc.getSelectedFile()); if(!doc.readable()) { JOptionPane.showMessageDialog(UserDefinedQualifiers.this, "Cannot read file "+doc.getName(), "Problem reading file", JOptionPane.WARNING_MESSAGE); return; } createObo(doc); createComponentToAddCvTerm(); mainPanel.revalidate(); mainPanel.repaint(); //if(autoImport.isSelected()) writeQualifierList(fc.getSelectedFile().getAbsolutePath()); } private void importFromUrl() { String s = (String)JOptionPane.showInputDialog( UserDefinedQualifiers.this, "URL:", "Import Qualifiers", JOptionPane.PLAIN_MESSAGE, null, null, "http://www.geneontology.org/GO_slims/goslim_generic.obo"); if(s == null || s.trim().equals("") || s.trim().equals("http://")) return; try { String urlStr = s.trim(); createObo(new URLDocument(new URL(urlStr))); createComponentToAddCvTerm(); mainPanel.revalidate(); mainPanel.repaint(); writeQualifierList(urlStr); } catch (MalformedURLException e) { e.printStackTrace(); } } private void createObo(Document doc) { try { logger4j.debug("Reading qualifiers from: "+doc.getName()); final QualifierList obo = new QualifierList(); obo.doc = doc; obo.qualifiers = loadQualifiers(doc); obos.add(obo); } catch (IOException e) { logger4j.error(e.getMessage()); JOptionPane.showMessageDialog(UserDefinedQualifiers.this, "Problem Loading:\n"+doc.getName()+"\n"+ e.getMessage(), "Problem Loading", JOptionPane.ERROR_MESSAGE); } } /** * Write or append to artemis qualifiers list * @param loc * @param isOBO */ private static void writeQualifierList(final String loc) { int status = JOptionPane.showConfirmDialog( null, "Automatically import:\n"+loc+"\nbetween session?", "Import Option", JOptionPane.YES_NO_OPTION); if(status != JOptionPane.YES_OPTION) return; final boolean isOBO; if(loc.toLowerCase().endsWith(".obo")) isOBO = true; else { File f = new File(loc); if(f.exists()) isOBO = true; else isOBO = false; } final String qualPath = System.getProperty("user.home") + File.separator + ".artemis.qualifiers"; try { final FileDocument doc = new FileDocument(new File(qualPath)); if(doc.readable()) { final BufferedReader reader = new BufferedReader(new InputStreamReader(doc.getInputStream())); try { String line; while((line = reader.readLine()) != null) { if(line.startsWith("import: file://"+loc)) { System.out.println("Import line already exists :\n"+line); return; } else if(line.startsWith("import: "+loc)) { System.out.println("Import line already exists :\n"+line); return; } } } finally { reader.close(); } } final File qualFile = new File(qualPath); final boolean exists = qualFile.exists(); final BufferedWriter bufferedwriter = new BufferedWriter( new FileWriter(qualFile, true)); if(!exists) { bufferedwriter.append("# User defined list of qualifiers in the format of:\n"); bufferedwriter.append("# name=value\n"); bufferedwriter.append("# The qualifiers are separated by lines.\n"); bufferedwriter.append("# OBO files can be used by defining import statements:\n"); bufferedwriter.append("#import: http://www.geneontology.org/GO_slims/goslim_generic.obo\n\n"); } if(isOBO) { if(loc.startsWith("http://")) bufferedwriter.append("\nimport: "+loc+"\n"); else bufferedwriter.append("\nimport: file://"+loc+"\n"); } else bufferedwriter.append(loc+"\n"); bufferedwriter.close(); } catch (FileNotFoundException filenotfoundexception) { System.err.println(qualPath+" read error"); } catch (IOException e) { System.err.println(qualPath+" i/o error"); } } private class OboFileFilter extends FileFilter { private String[] suffix; private String descr; OboFileFilter(final String[] suffix, final String descr) { this.suffix = suffix; this.descr = descr; } public boolean accept(final File file) { if(file.isDirectory()) return true; for(String s: suffix) { if(file.getName().endsWith("." + s) ) return true; } return false; } public String getDescription() { return descr; } } protected void setQualifierTextArea(final QualifierTextArea qualifier_text_area) { this.qualifier_text_area = qualifier_text_area; } protected void setSelection(final Selection selection) { this.selection = selection; } private class ComboMouseListener extends MouseAdapter { private JComboBox cb; ComboMouseListener(JComboBox cb) { this.cb = cb; } public void mouseEntered(MouseEvent me) { if(cb.getSelectedItem() != null) cb.setToolTipText(getWrappedStr((String) cb.getSelectedItem())); } private String getWrappedStr(String s) { final StringBuilder buff = new StringBuilder(); final int lineLen = 60; for(int k=0; k s.length()) end = s.length(); buff.append ( s.substring(k,end) ).append("\n"); } return buff.toString(); } } private class QualifierList { Document doc; QualifierVector qualifiers; boolean isAcive = true; } public static void main(String args[]) { final javax.swing.plaf.FontUIResource font_ui_resource = Options.getOptions().getFontUIResource(); final Enumeration keys = UIManager.getDefaults().keys(); while(keys.hasMoreElements()) { Object key = keys.nextElement(); Object value = UIManager.get(key); if(value instanceof javax.swing.plaf.FontUIResource) UIManager.put(key, font_ui_resource); } final JFrame f = new UserDefinedQualifiers(); f.pack(); f.setVisible(true); } }