JAL-2416 order score models by order of addition rather than name
[jalview.git] / src / jalview / schemes / ColourSchemes.java
1 package jalview.schemes;
2
3 import jalview.binding.JalviewUserColours;
4 import jalview.datamodel.AnnotatedCollectionI;
5 import jalview.datamodel.SequenceCollectionI;
6 import jalview.datamodel.SequenceI;
7
8 import java.awt.Color;
9 import java.io.File;
10 import java.io.FileInputStream;
11 import java.io.InputStreamReader;
12 import java.util.LinkedHashMap;
13 import java.util.Map;
14
15 public class ColourSchemes
16 {
17   /*
18    * singleton instance of this class
19    */
20   private static ColourSchemes instance = new ColourSchemes();
21
22   /*
23    * a map from scheme name to an instance of it
24    */
25   private Map<String, ColourSchemeI> schemes;
26
27   /**
28    * Returns the singleton instance of this class
29    * 
30    * @return
31    */
32   public static ColourSchemes getInstance()
33   {
34     return instance;
35   }
36
37   private ColourSchemes()
38   {
39     loadColourSchemes();
40   }
41
42   /**
43    * Loads an instance of each standard or user-defined colour scheme
44    * 
45    * @return
46    */
47   void loadColourSchemes()
48   {
49     /*
50      * store in an order-preserving map, so items can be added to menus 
51      * in the order in which they are 'discovered'
52      */
53     schemes = new LinkedHashMap<String, ColourSchemeI>();
54
55     for (JalviewColourScheme cs : JalviewColourScheme.values())
56     {
57       try
58       {
59         registerColourScheme(cs.getSchemeClass().newInstance());
60       } catch (InstantiationException | IllegalAccessException e)
61       {
62         System.err.println("Error instantiating colour scheme for "
63                 + cs.toString() + " " + e.getMessage());
64       }
65     }
66   }
67
68   /**
69    * Registers a colour scheme
70    * 
71    * @param cs
72    */
73   public void registerColourScheme(ColourSchemeI cs)
74   {
75     String name = cs.getSchemeName();
76     if (name == null)
77     {
78       System.err.println("ColourScheme name may not be null");
79       return;
80     }
81
82     /*
83      * name is lower-case for non-case-sensitive lookup
84      * (name in the colour keeps its true case)
85      */
86     String lower = name.toLowerCase();
87     if (schemes.containsKey(lower))
88     {
89       System.err
90               .println("Warning: overwriting colour scheme named " + name);
91     }
92     schemes.put(lower, cs);
93   }
94
95   /**
96    * Removes a colour scheme by name
97    * 
98    * @param name
99    */
100   public void removeColourScheme(String name)
101   {
102     schemes.remove(name);
103   }
104   
105   /**
106    * Returns an instance of the colour scheme with which the given view may be
107    * coloured
108    * 
109    * @param name
110    *          name of the colour scheme
111    * @param forData
112    *          the data to be coloured
113    * @param optional
114    *          map from hidden representative sequences to the sequences they
115    *          represent
116    * @return
117    */
118   public ColourSchemeI getColourScheme(String name,
119           AnnotatedCollectionI forData,
120           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
121   {
122     if (name == null)
123     {
124       return null;
125     }
126     ColourSchemeI cs = schemes.get(name.toLowerCase());
127     return cs == null ? null : cs.getInstance(forData, hiddenRepSequences);
128   }
129
130   /**
131    * Returns an instance of the colour scheme with which the given view may be
132    * coloured
133    * 
134    * @param name
135    *          name of the colour scheme
136    * @param forData
137    *          the data to be coloured
138    * @return
139    */
140   public ColourSchemeI getColourScheme(String name,
141           AnnotatedCollectionI forData)
142   {
143     return getColourScheme(name, forData, null);
144   }
145
146   /**
147    * Returns an iterable set of the colour schemes, in the order in which they
148    * were added
149    * 
150    * @return
151    */
152   public Iterable<ColourSchemeI> getColourSchemes()
153   {
154     return schemes.values();
155   }
156
157   /**
158    * Answers true if there is a scheme with the given name, else false. The test
159    * is not case-sensitive.
160    * 
161    * @param name
162    * @return
163    */
164   public boolean nameExists(String name)
165   {
166     if (name == null)
167     {
168       return false;
169     }
170     name = name.toLowerCase();
171     for (ColourSchemeI scheme : getColourSchemes())
172     {
173       if (name.equals(scheme.getSchemeName().toLowerCase()))
174       {
175         return true;
176       }
177     }
178     return false;
179   }
180
181   /**
182    * Loads a user defined colour scheme from file. The file should contain a
183    * definition of residue colours in XML format as defined in
184    * JalviewUserColours.xsd.
185    * 
186    * @param filePath
187    * 
188    * @return
189    */
190   public static UserColourScheme loadColourScheme(String filePath)
191   {
192     UserColourScheme ucs = null;
193     Color[] newColours = null;
194     File file = new File(filePath);
195     try
196     {
197       InputStreamReader in = new InputStreamReader(
198               new FileInputStream(file), "UTF-8");
199   
200       jalview.schemabinding.version2.JalviewUserColours jucs = new jalview.schemabinding.version2.JalviewUserColours();
201   
202       org.exolab.castor.xml.Unmarshaller unmar = new org.exolab.castor.xml.Unmarshaller(
203               jucs);
204       jucs = (jalview.schemabinding.version2.JalviewUserColours) unmar
205               .unmarshal(in);
206   
207       /*
208        * non-case-sensitive colours are for 20 amino acid codes,
209        * B, Z, X and Gap
210        * optionally, lower-case alternatives for all except Gap
211        */
212       newColours = new Color[24];
213       Color[] lowerCase = new Color[23];
214       boolean caseSensitive = false;
215   
216       String name;
217       int index;
218       for (int i = 0; i < jucs.getColourCount(); i++)
219       {
220         name = jucs.getColour(i).getName();
221         if (ResidueProperties.aa3Hash.containsKey(name))
222         {
223           index = ResidueProperties.aa3Hash.get(name).intValue();
224         }
225         else
226         {
227           index = ResidueProperties.aaIndex[name.charAt(0)];
228         }
229         if (index == -1)
230         {
231           continue;
232         }
233   
234         Color color = new Color(Integer.parseInt(jucs.getColour(i)
235                 .getRGB(), 16));
236         if (name.toLowerCase().equals(name))
237         {
238           caseSensitive = true;
239           lowerCase[index] = color;
240         }
241         else
242         {
243           newColours[index] = color;
244         }
245       }
246   
247       /*
248        * instantiate the colour scheme
249        */
250       ucs = new UserColourScheme(newColours);
251       ucs.setName(jucs.getSchemeName());
252       if (caseSensitive)
253       {
254         ucs.setLowerCaseColours(lowerCase);
255       }
256     } catch (Exception ex)
257     {
258       // Could be old Jalview Archive format
259       try
260       {
261         InputStreamReader in = new InputStreamReader(new FileInputStream(
262                 file), "UTF-8");
263   
264         jalview.binding.JalviewUserColours jucs = new jalview.binding.JalviewUserColours();
265   
266         jucs = JalviewUserColours.unmarshal(in);
267   
268         newColours = new Color[jucs.getColourCount()];
269   
270         for (int i = 0; i < 24; i++)
271         {
272           newColours[i] = new Color(Integer.parseInt(jucs.getColour(i)
273                   .getRGB(), 16));
274         }
275         ucs = new UserColourScheme(newColours);
276         ucs.setName(jucs.getSchemeName());
277       } catch (Exception ex2)
278       {
279         ex2.printStackTrace();
280       }
281   
282       if (newColours == null)
283       {
284         System.out.println("Error loading User ColourFile\n" + ex);
285       }
286     }
287   
288     return ucs;
289   }
290 }