1 /* Options.java
2  *
3  * created: Thu Dec 10 1998
4  *
5  * This file is part of Artemis
6  *
7  * Copyright (C) 1998,1999,2000  Genome Research Limited
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/Options.java,v 1.15 2009-06-19 15:18:04 tjc Exp $
24  **/
25 
26 package uk.ac.sanger.artemis;
27 
28 import uk.ac.sanger.artemis.util.*;
29 import uk.ac.sanger.artemis.components.Splash;
30 import uk.ac.sanger.artemis.io.Key;
31 import uk.ac.sanger.artemis.io.KeyVector;
32 import uk.ac.sanger.artemis.io.QualifierInfo;
33 import uk.ac.sanger.artemis.io.QualifierInfoVector;
34 import uk.ac.sanger.artemis.io.QualifierInfoException;
35 import uk.ac.sanger.artemis.io.EntryInformation;
36 import uk.ac.sanger.artemis.io.SimpleEntryInformation;
37 
38 
39 import java.awt.Color;
40 import java.awt.Font;
41 import java.io.File;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.util.Enumeration;
45 import java.util.Hashtable;
46 import java.util.Properties;
47 import java.util.Vector;
48 
49 
50 /**
51  *  An object of this class is used to read, write and store all the
52  *  configurable options in Diana.  The method Properties.load () is used to
53  *  read in new options.
54  *
55  *  @author Kim Rutherford
56  *  @version $Id: Options.java,v 1.15 2009-06-19 15:18:04 tjc Exp $
57  **/
58 
59 public class Options extends Properties
60 {
61   private static final long serialVersionUID = 1L;
62 
63   /**
64    *  This is the object that will be returned by getOptions().
65    **/
66   static private Options options = new Options();
67 
68   /** The name of the "direct edit" option. */
69   static private final String direct_edit_string = "direct_edit";
70 
71   /** The name of the "eukaryotic mode" option. */
72   static private final String eukaryotic_mode_string = "organism_type";
73 
74   /** The name of the "highlight active entry" option. */
75   static private final String highlight_active_entry_string =
76     "highlight_active_entry";
77 
78   /** Cache for getDisplayQualifierNames(). */
79   private static StringVector display_gene_qualifier_names = null;
80 
81   /** Cache for getSystematicQualifierNames(). */
82   private static StringVector systematic_gene_qualifier_names = null;
83 
84   /** Cache for getAllGeneNames(). */
85   private static StringVector all_gene_qualifier_names = null;
86 
87   /** Used as cache by readWritePossible(). */
88   private static Boolean read_succeeded = null;
89 
90   /** The EntryInformation object to use for EMBL and GENBANK entries. */
91   private static EntryInformation extended_entry_information;
92 
93   /** The EntryInformation object to use for EMBL and GENBANK entries. */
94   private static EntryInformation db_entry_information;
95 
96   /** The default font that should be used for all windows. */
97   private Font font = null;
98 
99   /**
100    *  A map of colour numbers to Color object.  This is initialised by
101    *  setDefaultColourMap().
102    **/
103   private Vector<Color> colour_map = null;
104 
105   /** A vector containing ExternalProgram objects that we can run. */
106   private ExternalProgramVector external_programs = null;
107 
108   /** A vector containing ExternalProgram objects that we can run. */
109   private ExternalProgramVector ncbi_programs = null;
110 
111   /** A vector of those objects listening for option change events. */
112   private Hashtable option_listener_hash = new Hashtable();
113 
114   /** Set by getInvisibleQualifiers() and reset by resetCachedValues() */
115   private StringVector invisible_qualifiers = null;
116 
117   public static String CACHE_PATH =
118       System.getProperty("user.home") + File.separatorChar +
119       ".artemis" + File.separatorChar + "cache" + File.separatorChar;
120 
121   /**
122    *  Create a new Options object with default settings for the options.
123    **/
Options()124   public Options()
125   {
126     try
127     {
128       put("font_size", "14");
129       reset();
130     }
131     catch(Throwable e)
132     {
133       e.printStackTrace();
134     }
135   }
136 
137   /**
138    *  Returns true if and only if the given String is not "no", "false", "n"
139    *  or "f".
140    **/
getPropertyTruthValueInternal(final String value)141   private boolean getPropertyTruthValueInternal(final String value)
142   {
143     if(value == null)
144       return false;
145 
146     final String lowercase_value = value.toLowerCase();
147 
148     if(lowercase_value.equals("false") ||
149         lowercase_value.equals("f") || lowercase_value.equals("no") ||
150         lowercase_value.equals("n"))
151       return false;
152     else
153       return true;
154   }
155 
156   /**
157    *  Returns true if and only if the given property is set and is not "no",
158    *  "false", "n" or "f".
159    **/
getPropertyTruthValue(final String name)160   public boolean getPropertyTruthValue(final String name)
161   {
162     return getPropertyTruthValueInternal(getProperty(name));
163   }
164 
165   /**
166    *  Names of qualifiers to search when attempting to find the primary or
167    *  display name of a gene.
168    **/
getDisplayQualifierNames()169   public StringVector getDisplayQualifierNames()
170   {
171     if(display_gene_qualifier_names == null)
172     {
173       final String name_string = getProperty("display_name_qualifiers");
174 
175       if(name_string == null)
176       {
177         display_gene_qualifier_names = new StringVector();
178         display_gene_qualifier_names.add("gene");
179       }
180       else
181         display_gene_qualifier_names = StringVector.getStrings(name_string);
182     }
183     return display_gene_qualifier_names;
184   }
185 
186   /**
187    *  Names of qualifiers to search when attempting to find the systematic
188    *  name of a gene.
189    **/
getSystematicQualifierNames()190   public StringVector getSystematicQualifierNames()
191   {
192     if(systematic_gene_qualifier_names == null)
193     {
194       final String name_string = getProperty("systematic_name_qualifiers");
195 
196       if(name_string == null)
197       {
198         systematic_gene_qualifier_names = new StringVector();
199         systematic_gene_qualifier_names.add("gene");
200       }
201       else
202         systematic_gene_qualifier_names =
203           StringVector.getStrings(name_string);
204     }
205     return systematic_gene_qualifier_names;
206   }
207 
208   /**
209    *  Return all possible gene names by combining the return values of
210    *  getSystematicQualifierNames() and getDisplayQualifierNames()
211    **/
getAllGeneNames()212   public StringVector getAllGeneNames()
213   {
214     if(all_gene_qualifier_names == null)
215     {
216       all_gene_qualifier_names = getSystematicQualifierNames().copy();
217       all_gene_qualifier_names.add(getDisplayQualifierNames());
218     }
219 
220     return all_gene_qualifier_names;
221   }
222 
223 
224   /**
225    *  Read the options from the options file and uk.ac.sanger.artemis.ini
226    **/
readOptions()227   private void readOptions()
228   {
229     try
230     {
231       final InputStream options_input_stream =
232         Options.class.getResourceAsStream("/etc/options");
233 
234       if(options_input_stream == null)
235         return;
236 
237       load(options_input_stream);
238 
239       final boolean run_quietly =
240         getPropertyTruthValueInternal(System.getProperty("run_quietly"));
241 
242       if(readWritePossible())
243       {
244         final String user_home = System.getProperty("user.home");
245 
246         // properties are read in order from these files.
247         // the null is replaced by the file name given by the extra_options
248         // system property (if it is set)
249         final String [] standard_option_file_names =
250         {
251           "Diana.ini",
252           "options",
253           "options.txt",
254           "options.text",
255           user_home + File.separator + ".artemis_options"
256         };
257 
258         String [] option_file_names = standard_option_file_names;
259 
260         final Properties system_properties = System.getProperties();
261 
262         if(system_properties != null)
263         {
264           final String extra_options_prop =
265             system_properties.getProperty("extra_options");
266 
267           if(extra_options_prop !=null)
268           {
269             final StringVector extra_option_file_names =
270               StringVector.getStrings(extra_options_prop, ":");
271 
272             option_file_names =
273               new String [option_file_names.length +
274                           extra_option_file_names.size()];
275 
276             for(int i = 0 ; i < extra_option_file_names.size() ; ++i)
277             {
278               final String extra_option_file_name =
279                 (String)extra_option_file_names.elementAt(i);
280 
281               if(new File(extra_option_file_name).canRead())
282                 option_file_names[i] = extra_option_file_name;
283               else
284               {
285                 System.err.println("warning: could not read options from \"" +
286                                     extra_option_file_name + "\"");
287                 option_file_names[i] = null;
288               }
289             }
290 
291             for(int i = 0 ; i < standard_option_file_names.length ; ++i)
292               option_file_names[i + extra_option_file_names.size()] =
293                 standard_option_file_names[i];
294           }
295         }
296 
297         for(int i = 0 ; i < option_file_names.length ; ++i)
298         {
299           final String this_options_file = option_file_names[i];
300 
301           if(this_options_file == null)
302             continue;
303 
304           final Document options_document =
305             new FileDocument(new File(this_options_file));
306 
307           // read the option files if they exist
308           if(options_document.readable())
309           {
310 
311             final InputStream options_document_stream =
312               options_document.getInputStream();
313 
314             /*if(!run_quietly)
315               System.err.println("reading options from \"" +
316                                   this_options_file + "\"");*/
317 
318             Splash.appendToLog(this_options_file+" options read");
319             load(options_document_stream);
320           }
321           else
322             Splash.appendToLog(this_options_file+" not found");
323         }
324       }
325     }
326     catch(IOException e)
327     {
328       System.err.println("could not read an options file : " + e);
329     }
330 
331     for(Enumeration enumProp = propertyNames(); enumProp.hasMoreElements();)
332     {
333       final String property_name = (String)enumProp.nextElement();
334       fireChangeEvent(property_name);
335     }
336   }
337 
338   /**
339    *  Reset this Options object by setting all the options to their default
340    *  values and then call readOptions().
341    **/
reset()342   public void reset()
343   {
344     clear();
345 
346     setDefaultFeatureColours();
347     setDefaultColourMap();
348 
349     readOptions();
350     setChadoOptions();
351 
352     readSystemOptions();
353     resetCachedValues();
354 
355 /////
356 //  final Enumeration keys = propertyNames();
357 //  while (keys.hasMoreElements())
358 //  {
359 //    final String key   = (String)keys.nextElement();
360 //    final String value = getProperty(key);
361 //    if(key.startsWith("colo"))
362 //      System.out.println(key+"   \t"+value);
363 //  }
364 /////
365 
366   }
367 
setChadoOptions()368   private void setChadoOptions()
369   {
370     final StringVector exon_models = getOptionValues("chado_exon_model");
371     if(exon_models != null)
372     {
373       DatabaseDocument.EXONMODEL = (String)exon_models.elementAt(0);
374       if(getProperty("colour_of_" + DatabaseDocument.EXONMODEL) == null)
375         put("colour_of_" + DatabaseDocument.EXONMODEL, "7");
376     }
377 
378     final StringVector chado_transcript = getOptionValues("chado_transcript");
379     if(chado_transcript != null)
380     {
381       DatabaseDocument.TRANSCRIPT = (String)chado_transcript.elementAt(0);
382       if(getProperty("colour_of_" + DatabaseDocument.TRANSCRIPT) == null)
383         put("colour_of_" + DatabaseDocument.TRANSCRIPT, "1");
384     }
385 
386     DatabaseDocument.CHADO_INFER_CDS = getPropertyTruthValue("chado_infer_CDS_UTR");
387     if(DatabaseDocument.CHADO_INFER_CDS)
388       DatabaseDocument.EXONMODEL = "exon";
389   }
390 
391   /**
392    *  Call load() from the super class and then reset the cached values for
393    *  Font and Color objects.
394    **/
load(InputStream in_stream)395   public void load(InputStream in_stream)
396       throws IOException
397   {
398     super.load(in_stream);
399 
400     resetCachedValues();
401   }
402 
403   /**
404    *  Return the reference of the global Options object.
405    **/
getOptions()406   public static Options getOptions()
407   {
408     return options;
409   }
410 
411   /**
412    *  Return true if and only if we are running on a Unix machine.
413    **/
isUnixHost()414   public static boolean isUnixHost()
415   {
416     if(System.getProperty("artemis.environment") != null &&
417         System.getProperty("artemis.environment").equals("UNIX"))
418       return true;
419     else
420       return false;
421   }
422 
423   /**
424    *  Return true if and only if Artemis is in "Noddy mode", meaning there
425    *  will be lots of requesters asking for confirmation when delete/editing.
426    **/
isNoddyMode()427   public static boolean isNoddyMode()
428   {
429     return !isBlackBeltMode();
430   }
431 
432   /**
433    *  Return true if and only if isNoddyMode() will return false.
434    **/
isBlackBeltMode()435   public static boolean isBlackBeltMode()
436   {
437     return getOptions().getPropertyTruthValue("black_belt_mode");
438   }
439 
440   /**
441    *  Return true if and only if we are running in an applet.
442    **/
readWritePossible()443   public static boolean readWritePossible()
444   {
445     if(read_succeeded == null)
446     {
447       try
448       {
449         final File temp_file = File.createTempFile("dummy", "dummy");
450         read_succeeded = new Boolean(true);
451       }
452       catch(Throwable t)
453       {
454         read_succeeded = new Boolean(false);
455       }
456     }
457 
458     return read_succeeded.booleanValue();
459   }
460 
461   /**
462    *  Return a StringVector containing the values of the given option or null
463    *  if there is no such option.  The values are separated by spaces in the
464    *  properties file.
465    *
466    *  @see #getPropertyValues()
467    **/
getOptionValues(final String option)468   public StringVector getOptionValues(final String option)
469   {
470     return getPropertyValues(this, option);
471   }
472 
473   /**
474    *  Return a StringVector containing the values of the given option in the
475    *  given Properties object or null if there is no such option.  The values
476    *  are separated by spaces in the properties file.  For example, asking for
477    *  the property foo in a Properties object where foo has the value: "thing1
478    *  thing2 thing3", will return a vector containing three String objects:
479    *  "thing1", "thing2", "thing3"
480    **/
getPropertyValues(final Properties properties, final String option)481   public static StringVector getPropertyValues(final Properties properties,
482                                                final String option)
483   {
484     final String option_value = properties.getProperty(option);
485 
486     if(option_value == null)
487       return null;
488 
489     return StringVector.getStrings(option_value);
490   }
491 
492   /**
493    *  Return a QualifierInfoVector object containing one object for each
494    *  qualifier given in extra_qualifiers option.
495    **/
getExtraQualifiers()496   public QualifierInfoVector getExtraQualifiers()
497   {
498     return getExtraQualifiers("extra_qualifiers");
499   }
500 
501   /**
502    *  Return a QualifierInfoVector object containing one object for each
503    *  qualifier given in extra_qualifiers option.
504    **/
getExtraGffQualifiers()505   public QualifierInfoVector getExtraGffQualifiers()
506   {
507     return getExtraQualifiers("extra_qualifiers_gff");
508   }
509 
510   /**
511    *  Return a QualifierInfoVector object containing one object for each
512    *  qualifier given in extra_qualifiers option.
513    **/
getExtraQualifiers(final String qualifier_options_flag)514   private QualifierInfoVector getExtraQualifiers(final String qualifier_options_flag)
515   {
516     final QualifierInfoVector return_vector =
517       new QualifierInfoVector();
518 
519     final StringVector extra_qualifiers_strings =
520       getOptionValues(qualifier_options_flag);
521 
522     for(int i = 0 ; i < extra_qualifiers_strings.size() / 2 ; ++i)
523     {
524       final String name = (String)extra_qualifiers_strings.elementAt(i * 2);
525       final String type_string =
526         (String)extra_qualifiers_strings.elementAt(i * 2 + 1);
527       final int type = QualifierInfo.getQualifierTypeID(type_string);
528 
529       return_vector.add(new QualifierInfo(name, type, null, null, false));
530     }
531 
532     return return_vector;
533   }
534 
535   /**
536    *  Return a Vector of the names of those qualifiers that shouldn't be shown
537    *  by the QualifierChoice popup.
538    **/
getInvisibleQualifiers(boolean isGFF)539   public StringVector getInvisibleQualifiers(boolean isGFF)
540   {
541     if(invisible_qualifiers == null)
542     {
543       invisible_qualifiers = getOptionValues("invisible_qualifiers");
544 
545       if(isGFF)
546         invisible_qualifiers.add( getOptionValues("invisible_qualifiers_gff") );
547 
548       if(invisible_qualifiers == null)
549         invisible_qualifiers = new StringVector();
550     }
551 
552     return invisible_qualifiers;
553   }
554 
555   /**
556    *  Return the path of the default sequence file or null if there is no
557    *  default.  This will usually have been set in the uk.ac.sanger.artemis.ini file,
558    *  eg. SEQUENCE_FILE='c5H10.seq'
559    **/
getDefaultSequenceFileName()560   public String getDefaultSequenceFileName()
561   {
562     final String property_string = getProperty("SEQUENCE_FILE");
563 
564     if(property_string == null)
565       return null;
566     else
567     {
568       if(property_string.length() == 0)
569         return null;
570 
571       // trim the quotes from the name
572       if(property_string.startsWith("'") &&
573           property_string.endsWith("'") &&
574           property_string.length() > 2)
575         return property_string.substring(1, property_string.length() - 1);
576       else
577         return property_string;
578     }
579   }
580 
581   /**
582    *  Return the path of the default feature file or null if there is no
583    *  default.  This will usually have been set in the uk.ac.sanger.artemis.ini file,
584    *  eg. FEATURE_FILE='c5H10_embl.tab'
585    **/
getDefaultFeatureFileName()586   public String getDefaultFeatureFileName()
587   {
588     final String property_string = getProperty("FEATURE_FILE");
589 
590     if(property_string == null)
591       return null;
592     else
593     {
594       if(property_string.length() == 0)
595         return null;
596 
597       // trim the quotes from the name
598       if(property_string.startsWith("'") &&
599           property_string.endsWith("'") &&
600           property_string.length() > 2)
601         return property_string.substring(1, property_string.length() - 1);
602       else
603         return property_string;
604     }
605   }
606 
607   /**
608    *  Return the minimum open reading frame size used when automatically
609    *  creating ORFs with the "Mark Open Reading Frames" function.
610    **/
getMinimumORFSize()611   public int getMinimumORFSize()
612   {
613     final Integer minimum_orf_size = getIntegerProperty("minimum_orf_size");
614 
615     if(minimum_orf_size == null) // default value
616       return 100;
617     else
618       return minimum_orf_size.intValue();
619   }
620 
621   /**
622    *  Get the default colour for the given feature key, as specified in the
623    *  options file.  If no colour is specified in the file then null is
624    *  returned.
625    *  @param key The feature key to get the colour of.
626    **/
getDefaultFeatureColour(Key key)627   public Color getDefaultFeatureColour(Key key)
628   {
629     final Integer colour_integer = getIntegerProperty("colour_of_" + key);
630 
631     if(colour_integer == null)
632       return null;
633     else
634       return getColorFromColourNumber(colour_integer.intValue());
635   }
636 
637   /**
638    *  Given a colour number (perhaps from a /colour qualifier) return an
639    *  appropriate Color object.
640    **/
getColorFromColourNumber(int colour_number)641   public Color getColorFromColourNumber(int colour_number)
642   {
643     // first look up colour_map for speed.  if that fails we then try to look
644     // for an appropriate property and turn into a Color object.
645     if(colour_number < 0)
646       return null;
647 
648     if(colour_number >= colour_map.size() ||
649         colour_map.elementAt(colour_number) == null)
650     {
651       String col = getProperty("colour_" + colour_number);
652       if(col != null)
653         return parseColour(col);
654 
655       // there is no colour for this colour_number
656       return null;
657     }
658     else
659       return colour_map.elementAt(colour_number);
660   }
661 
662   /**
663    *  Given a String containing three space separated integers (0-255), return
664    *  a Color object.  The three integers represent red, green and blue
665    *  respectively.  If a Color can't be parsed null is returned.
666    **/
parseColour(String colour_string)667   private Color parseColour(String colour_string)
668   {
669     try
670     {
671       // first get three integers from the String
672 
673       // trim any whitespace from the ends
674       final StringVector value_strings =
675         StringVector.getStrings(colour_string.trim());
676 
677       final int first_int =
678         Integer.valueOf((String)value_strings.elementAt(0)).intValue();
679       final int second_int =
680         Integer.valueOf((String)value_strings.elementAt(1)).intValue();
681       final int third_int =
682         Integer.valueOf((String)value_strings.elementAt(2)).intValue();
683 
684       return new Color(first_int, second_int, third_int);
685 
686     }
687     catch(NumberFormatException e)
688     {
689       return null;
690     }
691   }
692 
693   /**
694    *  Get the default Color for a colour
695    **/
setDefaultColourMap()696   public void setDefaultColourMap()
697   {
698     put("colour_0", "255 255 255");  // white
699     put("colour_1", "100 100 100");  // dark grey
700     put("colour_2", "255 0 0");      // red
701     put("colour_3", "0 255 0");      // green
702     put("colour_4", "0 0 255");      // blue
703     put("colour_5", "0 255 255");    // cyan
704     put("colour_6", "255 0 255");    // magenta
705     put("colour_7", "255 255 0");    // yellow
706     put("colour_8", "152 251 152");  // pale green
707     put("colour_9", "135 206 250");  // light sky blue
708     put("colour_10", "255 165 0");   // orange
709     put("colour_11", "200 150 100"); // brown
710     put("colour_12", "255 200 200"); // pink
711   }
712 
713   /**
714    *  Return a vector containing the ExternalProgram objects of all the
715    *  external programs that we can use.
716    **/
getExternalPrograms()717   public ExternalProgramVector getExternalPrograms()
718   {
719     if(external_programs == null)
720     {
721       external_programs = new ExternalProgramVector();
722 
723       final StringVector protein_value_strings =
724             getOptionValues("feature_protein_programs");
725 
726       if(protein_value_strings != null)
727       {
728         for(int i = 0; i < protein_value_strings.size() / 2; ++i)
729         {
730           final String program_name =
731             (String)protein_value_strings.elementAt(i * 2);
732           final String program_options =
733             (String)protein_value_strings.elementAt(i * 2 + 1);
734 
735           final ExternalProgram program =
736             new ExternalProgram(program_name, program_options,
737                                 ExternalProgram.AA_PROGRAM);
738 
739           external_programs.add(program);
740         }
741       }
742 
743       final StringVector dna_value_strings =
744         getOptionValues("feature_dna_programs");
745 
746       if(dna_value_strings != null)
747       {
748         for(int i = 0; i < dna_value_strings.size() / 2; ++i)
749         {
750           final String program_name =
751             (String)dna_value_strings.elementAt(i * 2);
752           final String program_options =
753             (String)dna_value_strings.elementAt(i * 2 + 1);
754 
755           final ExternalProgram program =
756             new ExternalProgram(program_name, program_options,
757                                        ExternalProgram.DNA_PROGRAM);
758 
759           external_programs.add(program);
760         }
761       }
762 
763       final StringVector application_value_strings =
764         getOptionValues("application_programs");
765 
766       if(application_value_strings != null)
767       {
768         for(int i = 0; i < application_value_strings.size(); ++i)
769         {
770           final String program_name = (String)application_value_strings.elementAt(i);
771 
772           final ExternalProgram program =
773             new ExternalProgram(program_name, null,
774                                  ExternalProgram.APPLICATION);
775 
776           external_programs.add(program);
777         }
778       }
779     }
780     return external_programs;
781   }
782 
783 
784 
785   /**
786    *  Return a vector containing the ncbi ExternalProgram objects of all the
787    *  external programs that we can use.
788    **/
getNCBIPrograms()789   public ExternalProgramVector getNCBIPrograms()
790   {
791     if(ncbi_programs == null)
792     {
793       ncbi_programs = new ExternalProgramVector();
794 
795       final StringVector protein_value_strings =
796             getOptionValues("ncbi_protein_search");
797 
798       if(protein_value_strings != null)
799       {
800         for(int i = 0; i < protein_value_strings.size() / 2; ++i)
801         {
802           final String program_name =
803             (String)protein_value_strings.elementAt(i * 2);
804           final String program_options =
805             (String)protein_value_strings.elementAt(i * 2 + 1);
806 
807           final ExternalProgram program =
808             new ExternalProgram(program_name, program_options,
809                                 ExternalProgram.AA_PROGRAM);
810 
811           ncbi_programs.add(program);
812         }
813       }
814 
815       final StringVector dna_value_strings =
816         getOptionValues("ncbi_dna_search");
817 
818       if(dna_value_strings != null)
819       {
820         for(int i = 0; i < dna_value_strings.size() / 2; ++i)
821         {
822           final String program_name =
823             (String)dna_value_strings.elementAt(i * 2);
824           final String program_options =
825             (String)dna_value_strings.elementAt(i * 2 + 1);
826 
827           final ExternalProgram program =
828             new ExternalProgram(program_name, program_options,
829                                        ExternalProgram.DNA_PROGRAM);
830 
831           ncbi_programs.add(program);
832         }
833       }
834     }
835     return ncbi_programs;
836   }
837 
838   /**
839    *  Return a StringVector containing the bases of the possible start codons
840    *  for prokaryotes.  This is stored in the prokaryotic_start_codons option
841    *  in the options file.
842    **/
843 //public StringVector getProkaryoticStartCodons()
844 //{
845 //  final StringVector option_values =
846 //    getOptionValues("prokaryotic_start_codons");
847 
848 //  for(int i = 0; i<option_values.size() ; ++i)
849 //  {
850 //    final String new_value = option_values.elementAt(i).toLowerCase();
851 //    option_values.setElementAt(new_value, i);
852 //  }
853 //  return option_values;
854 //}
855 
856   /**
857    *  Return a StringVector containing the bases of the possible eukaryotic
858    *  start codons.  This is stored in the eukaryotic_start_codons option in
859    *  the options file.
860    **/
861 //public StringVector getEukaryoticStartCodons()
862 //{
863 //  final StringVector option_values =
864 //    getOptionValues("eukaryotic_start_codons");
865 
866 //  for(int i = 0; i<option_values.size() ; ++i)
867 //  {
868 //    final String new_value = option_values.elementAt(i).toLowerCase();
869 //    option_values.setElementAt(new_value, i);
870 //  }
871 
872 //  return option_values;
873 //}
874 
getStartCodons()875   public StringVector getStartCodons()
876   {
877     final StringVector option_values;
878 
879     if(getProperty("start_codons") == null)
880     {
881       if(isEukaryoticMode())
882         option_values = getOptionValues("eukaryotic_start_codons");
883       else
884         option_values = getOptionValues("prokaryotic_start_codons");
885     }
886     else
887       option_values = getOptionValues("start_codons");
888 
889     for(int i = 0; i<option_values.size() ; ++i)
890     {
891       final String new_value = ((String)option_values.elementAt(i)).toLowerCase();
892       option_values.setElementAt(new_value, i);
893     }
894 
895     return option_values;
896   }
897 
898   /**
899    *  Return the default font that should be used for all windows.
900    **/
getFont()901   public Font getFont()
902   {
903     return font;
904   }
905 
906   /**
907    *  Return the UIResource for the default font that should be used for all
908    *  windows.
909    **/
getFontUIResource()910   public javax.swing.plaf.FontUIResource getFontUIResource()
911   {
912     return new javax.swing.plaf.FontUIResource(getFont());
913   }
914 
915   /**
916    *  Adds the specified event listener to receive option change events from
917    *  this object.
918    *  @param l the event change listener.
919    **/
addOptionChangeListener(OptionChangeListener l)920   public void addOptionChangeListener(OptionChangeListener l)
921   {
922     option_listener_hash.put(l, this);
923   }
924 
925   /**
926    *  Removes the specified event listener so that it no longer receives
927    *  option change events from this object.
928    *  @param l the event change listener.
929    **/
removeOptionChangeListener(OptionChangeListener l)930   public void removeOptionChangeListener(OptionChangeListener l)
931   {
932     option_listener_hash.remove(l);
933   }
934 
935   /**
936    *  Read all the system properties and overwrite this Options object with
937    *  those values.
938    **/
readSystemOptions()939   private void readSystemOptions()
940   {
941     if(readWritePossible())
942     {
943       final Properties system_properties = System.getProperties();
944       if(system_properties != null)
945       {
946         final Enumeration enumeration = system_properties.keys();
947         while(enumeration.hasMoreElements())
948         {
949           final String key   = (String)enumeration.nextElement();
950           final String value = system_properties.getProperty(key);
951           put(key, value);
952         }
953       }
954     }
955   }
956 
957   /**
958    *  Set the default colour number for each a feature key.
959    **/
setDefaultFeatureColours()960   private void setDefaultFeatureColours()
961   {
962     final Object[] key_colour_map =
963     {
964       "CDS", "5",
965       "cds?", "7",
966       "BLASTCDS", "2",
967       "BLASTN_HIT", "6",
968       "source", "0",
969       "prim_tran", "0",
970       "stem_loop", "2",
971       "misc_feature", "3",
972       "delta", "3",
973       "repeat_region", "9",
974       "repeat_unit", "9",
975       "terminator", "3",
976       "promoter", "3",
977       "intron", "1",
978       "exon", "7",
979       DatabaseDocument.EXONMODEL, "7",
980       "mRNA", "1",
981       "tRNA", "8",
982       "TATA", "3",
983       "bldA", "2"
984     };
985 
986     for(int i = 0 ; i < key_colour_map.length / 2 ; ++i)
987       put("colour_of_" + key_colour_map[i*2], key_colour_map[i*2+1]);
988   }
989 
990   /**
991    *  Clear all cached values (such as the font) and then recalculate.
992    **/
resetCachedValues()993   private void resetCachedValues()
994   {
995     /*final*/ int font_size;
996 
997     try
998     {
999       final Integer font_size_integer = getIntegerProperty("font_size");
1000 
1001       if(font_size_integer == null)
1002         font_size = 12;
1003       else
1004         font_size = font_size_integer.intValue();
1005     }
1006     catch(NumberFormatException e)
1007     {
1008       System.err.println("error in options file - " +
1009                           "font_size should be an integer");
1010       // a default value
1011       font_size = 14;
1012       put("font_size", String.valueOf(font_size));
1013     }
1014 
1015     if(getProperty("font_name") == null)
1016       put("font_name", "Monospaced");
1017 
1018     font = new Font(getProperty("font_name"), Font.PLAIN, font_size);
1019 
1020     colour_map = new Vector<Color>(25);
1021 
1022     int colour_number = 0;
1023 
1024     while(true)
1025     {
1026       final String colour_value_string =
1027         getProperty("colour_" + colour_number);
1028 
1029       if(colour_value_string == null)
1030         // we know nothing about this colour number so we assume that there
1031         // aren't any more
1032         break;
1033       else
1034       {
1035         final Color new_colour = parseColour(colour_value_string);
1036 
1037         if(new_colour == null)
1038         {
1039           // we couldn't parse the colour
1040           System.err.println("error in options file could not understand " +
1041                               "this number: " + colour_value_string);
1042         }
1043         else
1044         {
1045           if(colour_number >= colour_map.size())
1046             colour_map.setSize(colour_number + 50);
1047           colour_map.setElementAt(new_colour, colour_number);
1048         }
1049       }
1050       ++colour_number;
1051     }
1052 
1053     invisible_qualifiers = null;
1054 
1055     display_gene_qualifier_names = null;
1056     systematic_gene_qualifier_names = null;
1057     all_gene_qualifier_names = null;
1058   }
1059 
1060   /**
1061    *  Return the value of a property/option as an Integer.  If the property
1062    *  does not exist or cannot be parsed as an Integer, the method returns
1063    *  null.
1064    **/
getIntegerProperty(String property_name)1065   public Integer getIntegerProperty(String property_name)
1066   {
1067     final String property_string = getProperty(property_name);
1068 
1069     if(property_string == null)
1070       return null;
1071     else
1072       return Integer.valueOf(property_string);
1073   }
1074 
1075   /**
1076    *  Return the number of levels of undo to save or 0 if undo is disabled.
1077    **/
getUndoLevels()1078   public int getUndoLevels()
1079   {
1080     final Integer undo_integer = getIntegerProperty("undo_levels");
1081     if(undo_integer == null)
1082       return 0;
1083     else
1084     {
1085       if(undo_integer.intValue() < 0)
1086         return 0;
1087       else
1088         return undo_integer.intValue();
1089     }
1090   }
1091 
1092   /**
1093    *  Enable direct editing if and only if the argument is true.  "Direct
1094    *  Editing" allows feature locations to be changed by dragging the ends of
1095    *  the features around with the mouse.
1096    **/
setDirectEdit(final boolean direct_edit)1097   public void setDirectEdit(final boolean direct_edit)
1098   {
1099     if(direct_edit)
1100     {
1101       if(!canDirectEdit())
1102       {
1103         put(direct_edit_string, "true");
1104         fireChangeEvent(direct_edit_string);
1105       }
1106     }
1107     else
1108     {
1109       if(canDirectEdit())
1110       {
1111         put(direct_edit_string, "false");
1112         fireChangeEvent(direct_edit_string);
1113       }
1114     }
1115   }
1116 
1117   /**
1118    *  Returns true if and only if direct editing is enabled.
1119    **/
canDirectEdit()1120   public boolean canDirectEdit()
1121   {
1122     if(getPropertyTruthValue(direct_edit_string))
1123       return true;
1124     else
1125       return false;
1126   }
1127 
setGeneticCode(String table)1128   public void setGeneticCode(String table)
1129   {
1130     put("translation_table",table);
1131     fireChangeEvent("translation_table");
1132   }
1133 
setDisplayNameQualifiers(final String display_name_qualifiers)1134   public void setDisplayNameQualifiers(final String display_name_qualifiers)
1135   {
1136     display_gene_qualifier_names = null;
1137     put("display_name_qualifiers", display_name_qualifiers);
1138     fireChangeEvent("display_name_qualifiers");
1139   }
1140 
1141   /**
1142    *  Names of qualifiers to search when attempting to find the systematic
1143    *  name of a gene.
1144    **/
setSystematicQualifierNames(final String systematic_name_qualifiers)1145   public void setSystematicQualifierNames(final String systematic_name_qualifiers)
1146   {
1147     systematic_gene_qualifier_names = null;
1148     put("systematic_name_qualifiers", systematic_name_qualifiers);
1149     fireChangeEvent("systematic_name_qualifiers");
1150   }
1151 
1152   /**
1153    *  Set the organism type to eukaryotic if and only if the argument is
1154    *  true.  The other alternative is prokaryotic.
1155    **/
setEukaryoticMode(final boolean eukaryotic_mode)1156   public void setEukaryoticMode(final boolean eukaryotic_mode)
1157   {
1158     if(eukaryotic_mode)
1159     {
1160       if(!isEukaryoticMode())
1161       {
1162         put(eukaryotic_mode_string, "eukaryotic");
1163         fireChangeEvent(eukaryotic_mode_string);
1164       }
1165     }
1166     else
1167     {
1168       if(isEukaryoticMode())
1169       {
1170         put(eukaryotic_mode_string, "prokaryotic");
1171         fireChangeEvent(eukaryotic_mode_string);
1172       }
1173     }
1174   }
1175 
1176   /**
1177    *  Returns true if and only if we should be using the eukaryotic settings.
1178    *  This is only the default.
1179    **/
isEukaryoticMode()1180   public boolean isEukaryoticMode()
1181   {
1182     final String organism_type_prop = getProperty("organism_type");
1183 
1184     if(organism_type_prop == null ||
1185         organism_type_prop.equals("eukaryotic") ||
1186         organism_type_prop.equals("eukaryote") ||
1187         organism_type_prop.equals("euk"))
1188       return true;
1189     else
1190       return false;
1191   }
1192 
1193   /**
1194    *  Set whether or not to highlight the active entry.
1195    **/
setHighlightActiveEntryFlag(final boolean highlight_active)1196   public void setHighlightActiveEntryFlag(final boolean highlight_active)
1197   {
1198     if(highlight_active)
1199     {
1200       if(!highlightActiveEntryFlag())
1201       {
1202         put(highlight_active_entry_string, "true");
1203         fireChangeEvent(highlight_active_entry_string);
1204       }
1205     }
1206     else
1207     {
1208       if(highlightActiveEntryFlag())
1209       {
1210         put(highlight_active_entry_string, "false");
1211         fireChangeEvent(highlight_active_entry_string);
1212       }
1213     }
1214   }
1215 
1216   /**
1217    *  Returns true if and only if we should highlight the active entry in the
1218    *  display.
1219    **/
highlightActiveEntryFlag()1220   public boolean highlightActiveEntryFlag()
1221   {
1222     if(getPropertyTruthValue(highlight_active_entry_string))
1223       return true;
1224     else
1225       return false;
1226   }
1227 
1228   /**
1229    *  Returns true if this version of Java is running on a GNU/Linux machine
1230    *  and is version 1.2.0 or later.
1231    **/
isBuggyLinuxVM()1232   public boolean isBuggyLinuxVM()
1233   {
1234     if(!readWritePossible())
1235       // Java in a browser has problems, but not this problem
1236       return false;
1237 
1238     final String os_name = (String) System.getProperties().get("os.name");
1239 
1240     if(os_name.equals("Linux"))
1241     {
1242       final String java_version =
1243         (String) System.getProperties().get("java.version");
1244 
1245       if(java_version.startsWith("1.1."))
1246         return false;
1247       else
1248         return true;
1249     }
1250     else
1251       return false;
1252   }
1253 
1254 
1255   /**
1256    *  Return the EntryInformation object to use for EMBL and GENBANK entries.
1257    **/
getDBEntryInformation()1258   public static EntryInformation getDBEntryInformation()
1259   {
1260     return db_entry_information;
1261   }
1262 
1263   /**
1264    *  The EntryInformation object that has all the information that
1265    *  getDBEntryInformation () would return, but which has the non-standard
1266    *  qualifiers and keys added.
1267    **/
getArtemisEntryInformation()1268   public static EntryInformation getArtemisEntryInformation()
1269   {
1270     return extended_entry_information;
1271   }
1272 
1273   /**
1274    *  Return an EntryInformation object that is suitable for EMBL and GENBANK
1275    *  entries.
1276    **/
makeEntryInformation()1277   private static EntryInformation makeEntryInformation()
1278       throws IOException, QualifierInfoException
1279   {
1280     final InputStream feature_keys_stream =
1281       Options.class.getResourceAsStream("/etc/feature_keys");
1282 
1283     final InputStream qualifier_types_stream =
1284       Options.class.getResourceAsStream("/etc/qualifier_types");
1285 
1286     QualifierInfoVector qualifier_info_vector =
1287       readQualifierInfo(qualifier_types_stream, feature_keys_stream);
1288 
1289     final EntryInformation entry_information = new SimpleEntryInformation();
1290 
1291     for(int i = 0 ; i < qualifier_info_vector.size() ; ++i)
1292     {
1293       final QualifierInfo qualifier_info =
1294         qualifier_info_vector.elementAt(i);
1295 
1296       entry_information.addQualifierInfo(qualifier_info);
1297     }
1298 
1299     entry_information.setEMBLFormat(true);
1300 
1301     return entry_information;
1302   }
1303 
1304   /**
1305    *  Return an EntryInformation object that is suitable for EMBL and GENBANK
1306    *  entries, and has some useful non-standard additions (specified by the
1307    *  options file).
1308    **/
1309   private static EntryInformation
makeArtemisEntryInformation(final EntryInformation standard_entry_info)1310     makeArtemisEntryInformation(final EntryInformation standard_entry_info)
1311       throws QualifierInfoException
1312   {
1313 
1314     final StringVector extra_keys =
1315       getOptions().getOptionValues("extra_keys");
1316 
1317     final QualifierInfoVector extra_qualifiers =
1318       getOptions().getExtraQualifiers();
1319 
1320     final EntryInformation return_entry_information =
1321       new SimpleEntryInformation(standard_entry_info);
1322 
1323     for(int i = 0 ; i < extra_keys.size() ; ++i)
1324     {
1325       final Key new_key = new Key((String)extra_keys.elementAt(i));
1326       return_entry_information.addKey(new_key);
1327     }
1328 
1329     for(int i = 0 ; i < extra_qualifiers.size() ; ++i)
1330     {
1331       final QualifierInfo new_qualifier_info = extra_qualifiers.elementAt(i);
1332       return_entry_information.addQualifierInfo(new_qualifier_info);
1333     }
1334 
1335     // make a add qualifier info for each search program.  eg. add blastp_file
1336     // for blastp
1337     final ExternalProgramVector external_programs =
1338       getOptions().getExternalPrograms();
1339 
1340     for(int i = 0 ; i < external_programs.size() ; ++i)
1341     {
1342       final ExternalProgram external_program = external_programs.elementAt(i);
1343 
1344       if(external_program.getType() == ExternalProgram.AA_PROGRAM ||
1345           external_program.getType() == ExternalProgram.DNA_PROGRAM)
1346       {
1347         final QualifierInfo new_qualifier_info =
1348           new QualifierInfo(external_program.getName() + "_file",
1349                              QualifierInfo.QUOTED_TEXT,
1350                              null,
1351                              null,
1352                              true);
1353 
1354         return_entry_information.addQualifierInfo(new_qualifier_info);
1355       }
1356     }
1357 
1358     return_entry_information.setEMBLFormat(false);
1359 
1360     return return_entry_information;
1361   }
1362 
1363   /**
1364    *  Read the possible feature key and qualifier names and types from the two
1365    *  given streams (see etc/feature_keys and etc/qualifier_types for details
1366    *  on the formats).
1367    **/
1368   private static QualifierInfoVector
readQualifierInfo(final InputStream qualifier_types_stream, final InputStream feature_keys_stream)1369     readQualifierInfo(final InputStream qualifier_types_stream,
1370                        final InputStream feature_keys_stream)
1371       throws IOException
1372   {
1373 
1374     final QualifierInfoVector return_vector = new QualifierInfoVector();
1375 
1376     Properties feature_properties = new Properties();
1377     final Properties qualifier_properties = new Properties();
1378 
1379     feature_properties.load(feature_keys_stream);
1380     qualifier_properties.load(qualifier_types_stream);
1381 
1382     // parse the feature_properties
1383 
1384     {
1385       final Properties new_feature_properties = new Properties();
1386 
1387       final Enumeration feature_enum = feature_properties.propertyNames();
1388 
1389       while(feature_enum.hasMoreElements()) {
1390         String current_feature_name = (String) feature_enum.nextElement();
1391 
1392         final StringVector property_values =
1393           Options.getPropertyValues(feature_properties, current_feature_name);
1394 
1395         new_feature_properties.put(current_feature_name, property_values);
1396       }
1397 
1398       feature_properties = new_feature_properties;
1399     }
1400 
1401     final Enumeration qualifier_enum = qualifier_properties.propertyNames();
1402 
1403     while(qualifier_enum.hasMoreElements())
1404     {
1405       String current_qualifier_name = (String) qualifier_enum.nextElement();
1406 
1407       final StringVector current_qualifier_values =
1408         Options.getPropertyValues(qualifier_properties,
1409                                    current_qualifier_name);
1410 
1411       final boolean once_only =
1412         current_qualifier_values.elementAt(0).equals("yes");
1413 
1414       final String type_string = (String)current_qualifier_values.elementAt(1);
1415 
1416       // find the keys for which this qualifier name is valid or required
1417 
1418       final KeyVector valid_keys = new KeyVector();
1419       final KeyVector required_keys = new KeyVector();
1420 
1421       final Enumeration features_enum = feature_properties.propertyNames();
1422 
1423       while(features_enum.hasMoreElements())
1424       {
1425         final String current_key_string = (String)features_enum.nextElement();
1426 
1427         final Key current_key = new Key(current_key_string);
1428 
1429         final StringVector current_feature_qualifiers =
1430           (StringVector) feature_properties.get(current_key_string);
1431 
1432         if(current_feature_qualifiers.contains(current_qualifier_name))
1433           valid_keys.add(current_key);
1434         else
1435           if(current_feature_qualifiers.contains("@" +
1436                                                    current_qualifier_name))
1437           {
1438             valid_keys.add(current_key);
1439             required_keys.add(current_key);
1440           }
1441       }
1442 
1443       final int type = QualifierInfo.getQualifierTypeID(type_string);
1444 
1445       final QualifierInfo qualifier_info =
1446         new QualifierInfo(current_qualifier_name, type, valid_keys,
1447                            required_keys, once_only);
1448 
1449       return_vector.add(qualifier_info);
1450     }
1451 
1452     return return_vector;
1453   }
1454 
1455   /**
1456    *  Send a change event to all the listeners.
1457    *  @param option_name The name of the option that changed.
1458    **/
fireChangeEvent(final String option_name)1459   private void fireChangeEvent(final String option_name)
1460   {
1461     for(final Enumeration e = option_listener_hash.keys() ;
1462          e.hasMoreElements() ;)
1463     {
1464       final OptionChangeEvent event =
1465         new OptionChangeEvent(this, option_name);
1466 
1467       final OptionChangeListener target =
1468         (OptionChangeListener) e.nextElement();
1469 
1470       target.optionChanged(event);
1471     }
1472   }
1473 
1474   static
1475   {
1476     try
1477     {
1478       db_entry_information = makeEntryInformation();
1479       extended_entry_information =
1480         makeArtemisEntryInformation(db_entry_information);
1481     }
1482     catch(QualifierInfoException e)
1483     {
1484       System.err.println("could not initialise the embl package: " +
1485                           e.getMessage());
1486       System.exit(1);
1487     }
1488     catch(IOException e)
1489     {
1490       System.err.println("could not initialise the embl package: " +
1491                           e.getMessage());
1492       System.exit(1);
1493     }
1494   }
1495 
1496 }
1497