Newer
Older
* This file is part of Artemis
*
* Copyright(C) 2012 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.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;
import java.util.Map.Entry;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import uk.ac.sanger.artemis.util.Document;
import uk.ac.sanger.artemis.util.FileDocument;
/**
* Project file management system using a properties file.
*
* Example of the syntax for defining a project in the property file:
* project.Pknowlsei.sequence = Pknowlsei:Pk_strainH_chr01
* project.Pknowlsei.chado = genedb-db.sanger.ac.uk:5432/snapshot?genedb_ro
* project.Pknowlsei.title = Pknowlsei
*/
public class ProjectProperty extends JFrame
private static HashMap<String, HashMap<String, String>> centralProjects;
private static HashMap<String, HashMap<String, String>> userProjects;
private Splash splash;
private final static int REFERENCE = 1;
private final static int ANNOTATION = 2;
private final static int NEXT_GEN_DATA = 3;
private final static int CHADO = 4;
private static org.apache.log4j.Logger logger4j =
org.apache.log4j.Logger.getLogger(ProjectProperty.class);
private final static String[] TYPES =
{ "title", "sequence", "annotation", "bam", "vcf", "userplot", "chado" };
final javax.swing.plaf.FontUIResource font_ui_resource =
Options.getOptions().getFontUIResource();
java.util.Enumeration<Object> 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);
}
this.getClass().getClassLoader().getResourceAsStream("etc/project.properties");
try
{
logger4j.debug("Reading properties from: "+
this.getClass().getClassLoader().getResource("etc/project.properties").toURI());
}
catch (URISyntaxException e1){}
final Properties projectProps = new Properties();
try
{
projectProps.load(ins);
ins.close();
}
catch (IOException e)
{
e.printStackTrace();
}
centralProjects = getProjectMap(projectProps);
projectProps.clear();
final String [] propFiles =
{
"project.properties",
System.getProperty("user.home") + File.separator + ".artemis.project.properties"
};
for(int i=0; i<propFiles.length; i++)
{
final Document doc =
new FileDocument(new File(propFiles[i]));
if(doc.readable())
{
try
{
ins = doc.getInputStream();
projectProps.load(ins);
}
catch (IOException e)
{
e.printStackTrace();
}
logger4j.debug("Reading properties from: "+propFiles[i]);
}
}
createProjectViewer((JPanel) getContentPane());
pack();
setVisible(true);
}
private void createProjectViewer(JPanel panel)
{
final DefaultListModel model = new DefaultListModel();
final JList projectList = new JList(model);
final JScrollPane jspList = new JScrollPane(projectList);
Object[] items = centralProjects.keySet().toArray();
Arrays.sort(items);
for (int i=0; i<items.length; i++)
model.add(i, items[i]);
items = userProjects.keySet().toArray();
Arrays.sort(items);
for (int i=0; i<items.length; i++)
model.add(i, items[i]);
final Box yBox = Box.createVerticalBox();
final JScrollPane jspProp = new JScrollPane(yBox);
final LaunchActionListener listener = new LaunchActionListener();
projectList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
projectList.setVisibleRowCount(-1);
panel.add(jspList, BorderLayout.WEST);
panel.add(jspProp, BorderLayout.CENTER);
final JToolBar toolBar = new JToolBar();
panel.add(toolBar, BorderLayout.PAGE_START);
final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
panel.setPreferredSize(new Dimension((int)(screen.width/2.5),screen.height/3));
final JButton addProjectButton = new JButton("+");
addProjectButton.setOpaque(false);
Font font = addProjectButton.getFont().deriveFont(Font.BOLD).deriveFont(14.f);
addProjectButton.setFont(font);
addProjectButton.setToolTipText("ADD PROJECT");
addProjectButton.setForeground(new Color(35, 149, 35));
addProjectButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0)
{
addProject(projectList);
}
});
toolBar.add(addProjectButton);
final JButton removeProjectButton = new JButton("-");
removeProjectButton.setOpaque(false);
removeProjectButton.setFont(font);
removeProjectButton.setToolTipText("REMOVE PROJECT");
removeProjectButton.setForeground(new Color(149, 35, 35));
removeProjectButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0)
{
removeProject(projectList, yBox);
}
});
toolBar.add(removeProjectButton);
final JButton openArt = new JButton("OPEN");
openArt.addActionListener(listener);
final JButton closeButton = new JButton("CLOSE");
closeButton .addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
dispose();
}
});
Box xBox = Box.createHorizontalBox();
xBox.add(openArt);
xBox.add(closeButton);
xBox.add(Box.createHorizontalGlue());
panel.add(xBox, BorderLayout.SOUTH);
final GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.NORTHWEST;
c.fill = GridBagConstraints.VERTICAL;
c.ipadx = 10;
c.ipady = 10;
projectList.addListSelectionListener(new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent e)
{
if(e.getValueIsAdjusting() == false &&
projectList.getSelectedIndex() > -1)
refreshProperties(projectList, yBox, listener);
}
});
}
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
private void addProject(final JList projectList)
{
DefaultListModel model = (DefaultListModel) projectList.getModel();
String projName =
JOptionPane.showInputDialog(ProjectProperty.this,
"Project Name", "New Project", JOptionPane.QUESTION_MESSAGE);
if(projName == null)
return;
userProjects.put(projName, new HashMap<String, String>());
model.add(model.getSize(), projName);
projectList.repaint();
projectList.setSelectedIndex(model.getSize()-1);
}
private void removeProject(final JList projectList, final Box yBox)
{
if(projectList.getSelectedValue() == null)
{
JOptionPane.showMessageDialog(ProjectProperty.this,
"Select a project from the list to be removed.",
"Remove", JOptionPane.INFORMATION_MESSAGE);
return;
}
DefaultListModel model = (DefaultListModel) projectList.getModel();
int status = JOptionPane.showConfirmDialog(
ProjectProperty.this, "Remove "+projectList.getSelectedValue()+"?",
"Remove Project", JOptionPane.YES_NO_OPTION);
if(status != JOptionPane.YES_OPTION)
return;
userProjects.remove(projectList.getSelectedValue());
model.remove(projectList.getSelectedIndex());
projectList.repaint();
yBox.removeAll();
yBox.repaint();
}
/**
* Refresh components in the properties panel.
* @param projectList
* @param yBox
* @param listener
*/
private void refreshProperties(final JList projectList,
final Box yBox,
final LaunchActionListener listener)
{
yBox.removeAll();
final HashMap<String, String> projProps;
if(centralProjects.containsKey(projectList.getSelectedValue()))
projProps = centralProjects.get(projectList.getSelectedValue());
else
projProps = userProjects.get(projectList.getSelectedValue());
final HashMap<Integer, Vector<JTextField>> settings = new HashMap<Integer, Vector<JTextField>>();
// order the keys
Object keys[] = projProps.keySet().toArray();
Arrays.sort(keys, new TypeComparator());
for(final Object key: keys)
final String keyStr = (String) key;
final Vector<JTextField> vText = new Vector<JTextField>();
Border lineBorder = BorderFactory.createLineBorder(Color.DARK_GRAY);
final JPanel propPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridy = 0;
c.fill = GridBagConstraints.BOTH;
propPanel.setBorder(title);
propPanel.setBackground(Color.WHITE);
//
final Vector<JCheckBox> checkBoxes = new Vector<JCheckBox>();
final JButton toggle = new JButton("Toggle");
toggle.setToolTipText("toggle "+keyStr+" selection");
toggle.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0)
{
for(JCheckBox cb: checkBoxes)
cb.setSelected(!cb.isSelected());
}
});
final Vector<String> anns = splitLine(projProps.get(keyStr).trim());
for (int i=0; i<anns.size(); i++)
{
c.gridx = 0;
addProperyToPanel(projectList, propPanel, vText, c, i, anns.get(i), projProps, keyStr, yBox, listener, checkBoxes);
if (!keyStr.equals("title") && !keyStr.equals("chado"))
Box xBox = Box.createHorizontalBox();
final JButton selectButton = new JButton(
keyStr.startsWith("seq") ? "Select " : "Add file");
selectButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
StickyFileChooser fileChooser = new StickyFileChooser();
int status = fileChooser.showOpenDialog(ProjectProperty.this);
if(keyStr.startsWith("seq"))
vText.get(0).setText(fileChooser.getSelectedFile().getAbsolutePath());
else
{
projProps.put(keyStr, projProps.get(keyStr)+" "+
fileChooser.getSelectedFile().getAbsolutePath());
refreshProperties(projectList, yBox, listener);
}
xBox.add(selectButton);
xBox.add(Box.createHorizontalGlue());
if(checkBoxes.size() > 1) // add toggle option
xBox.add(toggle, c);
c.gridwidth = 2;
if(keyStr.startsWith("seq"))
settings.put(ProjectProperty.REFERENCE, vText);
else if(keyStr.equals("annotation"))
settings.put(ProjectProperty.ANNOTATION, vText);
else if(keyStr.equals("bam") || keyStr.equals("vcf") || keyStr.equals("bcf"))
settings.put(ProjectProperty.NEXT_GEN_DATA, vText);
else if(keyStr.equals("chado"))
settings.put(ProjectProperty.CHADO, vText);
else if(keyStr.equals("userplot"))
settings.put(ProjectProperty.USERPLOT, vText);
}
// ADD property
Box xBox = Box.createHorizontalBox();
final JButton addPropertyButton = new JButton("NEW PROPERTY");
final JComboBox propertyList = new JComboBox(TYPES);
xBox.add(addPropertyButton);
xBox.add(propertyList);
xBox.add(Box.createHorizontalGlue());
yBox.add(xBox);
addPropertyButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0)
{
String key = (String) propertyList.getSelectedItem();
if(!projProps.containsKey(key))
{
projProps.put(key, "");
refreshProperties(projectList, yBox, listener);
//
yBox.add(Box.createVerticalGlue());
yBox.revalidate();
yBox.repaint();
listener.setSettings(settings);
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
private Vector<String> splitLine(String line)
{
Vector<String> parts = new Vector<String>();
int index = line.indexOf(" ");
if(index < 0)
parts.add(line);
else
{
int startIndex = 0;
line = line.replaceAll("\\s+", " ");
while((index = line.indexOf(" ", startIndex)) > -1)
{
if(line.charAt(index-1) == '\\')
{
startIndex = index+1;
continue;
}
parts.add(line.substring(0, index));
line = line.substring(index+1);
startIndex = 0;
}
parts.add(line);
}
return parts;
}
private void addProperyToPanel(final JList projectList,
final JPanel propPanel,
final Vector<JTextField> vText,
final GridBagConstraints c,
final int index,
final String ann,
final HashMap<String, String> projProps,
final String key,
final Box yBox,
final LaunchActionListener listener,
final Vector<JCheckBox> cbs)
if(key.equals("title"))
qta.setText(removeSpaceEscape(ann));
else
qta.setText(ann);
qta.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
qta.getDocument().addDocumentListener(new DocumentListener()
{
private void update()
{
final String anns[];
if(key.equals("title")) // only takes one value
anns = new String[]{ escapeSpace(projProps.get(key).trim()) };
else
anns = projProps.get(key).trim().split("\\s{2,}");
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
String value = "";
for(int i=0;i<anns.length;i++)
{
if(i == index)
value += " "+qta.getText();
else
value += " "+anns[i];
}
projProps.put(key, value);
}
public void changedUpdate(DocumentEvent e)
{
update();
}
public void insertUpdate(DocumentEvent e)
{
update();
}
public void removeUpdate(DocumentEvent e)
{
update();
}
});
// REMOVE PROPERTY
Box xButtons = Box.createHorizontalBox();
final JButton removeProperty = new JButton("X");
removeProperty.setOpaque(false);
Font font = removeProperty.getFont().deriveFont(Font.BOLD);
removeProperty.setFont(font);
removeProperty.setToolTipText("REMOVE");
removeProperty.setForeground(new Color(139, 35, 35));
removeProperty.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
int status = JOptionPane.showConfirmDialog(
ProjectProperty.this, "Remove "+key+"?",
"Remove", JOptionPane.YES_NO_OPTION);
if(status != JOptionPane.YES_OPTION)
return;
final String anns[] = projProps.get(key).trim().split("\\s+");
String value = "";
for(int i=0;i<anns.length;i++)
{
if(i != index)
value += " "+anns[i];
}
if(value.equals(""))
projProps.remove(key);
else
projProps.put(key, value.trim());
refreshProperties(projectList, yBox, listener);
}
});
xButtons.add(removeProperty);
//
if(!key.equals("title") && !key.startsWith("seq") && !key.equals("chado"))
{
final JCheckBox useProperty = new JCheckBox("",true);
useProperty.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent arg0)
{
qta.setEnabled(useProperty.isSelected());
}
});
}
xButtons.add(Box.createHorizontalGlue());
c.gridy = c.gridy+1;
propPanel.add(qta, c);
propPanel.add(xButtons, c);
}
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
/**
* Create a project hash of the properties.
* @param projectProps
* @return
*/
private HashMap<String, HashMap<String, String>> getProjectMap(final Properties projectProps)
{
final HashMap<String, HashMap<String, String>> projects =
new HashMap<String, HashMap<String, String>>();
for (Entry<Object, Object> propItem : projectProps.entrySet())
{
String key = (String) propItem.getKey();
String value = (String) propItem.getValue();
if (key.startsWith("project."))
{
key = key.substring(8);
int ind = key.indexOf(".");
if (ind > -1)
{
String projName = key.substring(0, ind);
key = key.substring(ind + 1);
final HashMap<String, String> thisProj;
if (projects.containsKey(projName))
thisProj = projects.get(projName);
else
thisProj = new HashMap<String, String>();
thisProj.put(key, value);
projects.put(projName, thisProj);
}
}
}
return projects;
}
/**
* Write or re-write properties and insert/update the user.dir property
* @param jemProp properties file
* @param uHome user working directory
*/
protected static void writeProperties()
{
if(userProjects == null)
return;
final String prop = System.getProperty("user.home") + File.separator + ".artemis.project.properties";
File propFile = new File(prop);
try
{
if(userProjects.size() > 0)
{
final BufferedWriter bufferedwriter = new BufferedWriter(new FileWriter(propFile));
for (String project: userProjects.keySet())
{
bufferedwriter.write("#");
bufferedwriter.newLine();
HashMap<String, String> projProps = userProjects.get(project);
for(final String key: projProps.keySet())
{
final String val;
if(key.equals("title"))
val = escapeSpace(projProps.get(key).trim());
else
val = projProps.get(key).trim().replaceAll("\\s{2,}", " ");
bufferedwriter.write("project."+project+"."+key+"="+val );
// unfortunately Properties.store() adds a timestamp as a comment
/*myProps.clear();
HashMap<String, String> projProps = userProjects.get(project);
for(final String key: projProps.keySet())
myProps.setProperty("project."+project+"."+key,
projProps.get(key).trim().replaceAll("\\s+", " "));
myProps.store(bufferedwriter, null);*/
}
bufferedwriter.close();
}
else
propFile.delete();
}
catch (FileNotFoundException filenotfoundexception)
{
System.err.println(prop+" read error");
}
catch (IOException e)
{
System.err.println(prop+" i/o error");
}
}
/**
* Escape the spaces with a double backslash (i.e. '\\ ').
* @param s
* @return
*/
private static String escapeSpace(String s)
{
s = removeSpaceEscape(s).replace(" ", "\\\\ ");
return s;
}
private static String removeSpaceEscape(String s)
{
return s.replace("\\ ", " ");
}
class LaunchActionListener implements ActionListener
{
private HashMap<Integer, Vector<JTextField>> settings;
private void setSettings(HashMap<Integer, Vector<JTextField>> settings)
{
this.settings = settings;
}
private String[] getArgs()
{
try
{
System.getProperties().remove("bam");
System.getProperties().remove("chado");
}
catch(Exception e){ e.printStackTrace(); }
final Set<Integer> keys = settings.keySet();
final Vector<String> vargs = new Vector<String>();
final Vector<String> vann = new Vector<String>();
for(Integer key: keys)
{
final Vector<JTextField> vText = settings.get(key);
vargs.add( vText.get(0).getText().trim() );
for(JTextField ann: vText)
if(ann.isEnabled())
vann.add( ann.getText().trim() );
String bam = "";
for(JTextField ann: vText)
if(ann.isEnabled())
bam += ","+ann.getText().trim();
if(!bam.equals(""))
System.setProperty("bam", bam.replaceFirst(",", ""));
String userplot = "";
for(JTextField ann: vText)
{
if(ann.isEnabled())
userplot += ","+ann.getText().trim();
}
if(!userplot.equals(""))
System.setProperty("userplot", userplot.replaceFirst(",", ""));
System.setProperty("chado", vText.get(0).getText().trim());
String[] args = new String[vargs.size()+(vann.size()*2)];
for(int i=0; i<vargs.size(); i++)
args[i] = vargs.get(i);
for(int i=0; i<vann.size(); i++)
{
args[vargs.size()+(i*2)] = "+";
args[vargs.size()+(i*2)+1] = vann.get(i);
}
}
public void actionPerformed(ActionEvent arg0)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
setCursor(new Cursor(Cursor.WAIT_CURSOR));
try
{
String[] args = getArgs();
final ArtemisMain main_window;
if (splash == null)
{
main_window = new ArtemisMain(args);
main_window.setVisible(true);
}
else
main_window = (ArtemisMain) splash;
main_window.readArgsAndOptions(args);
}
finally
{
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
class TypeComparator implements Comparator<Object>
{
public int compare(Object o1, Object o2)
{
String s1 = (String)o1;
String s2 = (String)o2;
if( s1.equals("title") )
return -1;
else if(s2.equals("title"))
return 1;
return s1.compareTo(s2);
}
}