Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
UserColourScheme | 39 | 100 | 46 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.schemes; | |
22 | ||
23 | import java.util.Locale; | |
24 | ||
25 | import jalview.api.AlignViewportI; | |
26 | import jalview.datamodel.AnnotatedCollectionI; | |
27 | import jalview.util.ColorUtils; | |
28 | import jalview.util.StringUtils; | |
29 | ||
30 | import java.awt.Color; | |
31 | import java.util.ArrayList; | |
32 | import java.util.Collections; | |
33 | import java.util.HashMap; | |
34 | import java.util.List; | |
35 | import java.util.Map; | |
36 | import java.util.Map.Entry; | |
37 | import java.util.StringTokenizer; | |
38 | ||
39 | public class UserColourScheme extends ResidueColourScheme | |
40 | { | |
41 | /* | |
42 | * lookup (by symbol index) of lower case colours (if configured) | |
43 | */ | |
44 | Color[] lowerCaseColours; | |
45 | ||
46 | protected String schemeName; | |
47 | ||
48 | 5 | public UserColourScheme() |
49 | { | |
50 | 5 | super(ResidueProperties.aaIndex); |
51 | } | |
52 | ||
53 | 8 | public UserColourScheme(Color[] newColors) |
54 | { | |
55 | 8 | super(ResidueProperties.aaIndex); |
56 | 8 | colors = newColors; |
57 | } | |
58 | ||
59 | 0 | @Override |
60 | public ColourSchemeI getInstance(AlignViewportI view, | |
61 | AnnotatedCollectionI sg) | |
62 | { | |
63 | 0 | return new UserColourScheme(this); |
64 | } | |
65 | ||
66 | /** | |
67 | * Copy constructor | |
68 | * | |
69 | * @return | |
70 | */ | |
71 | 0 | protected UserColourScheme(UserColourScheme from) |
72 | { | |
73 | 0 | this(from.colors); |
74 | 0 | schemeName = from.schemeName; |
75 | 0 | if (from.lowerCaseColours != null) |
76 | { | |
77 | 0 | lowerCaseColours = new Color[from.lowerCaseColours.length]; |
78 | 0 | System.arraycopy(from.lowerCaseColours, 0, lowerCaseColours, 0, |
79 | from.lowerCaseColours.length); | |
80 | } | |
81 | } | |
82 | ||
83 | /** | |
84 | * Constructor for an animino acid colour scheme. The colour specification may | |
85 | * be one of | |
86 | * <ul> | |
87 | * <li>an AWT colour name e.g. red</li> | |
88 | * <li>an AWT hex rgb colour e.g. ff2288</li> | |
89 | * <li>residue colours list e.g. D,E=red;K,R,H=0022FF;c=yellow</li> | |
90 | * </ul> | |
91 | * | |
92 | * @param colour | |
93 | */ | |
94 | 5 | public UserColourScheme(String colour) |
95 | { | |
96 | 5 | super(ResidueProperties.aaIndex); |
97 | ||
98 | 5 | if (colour.contains("=")) |
99 | { | |
100 | /* | |
101 | * a list of colours per residue(s) | |
102 | */ | |
103 | 2 | parseAppletParameter(colour); |
104 | 2 | return; |
105 | } | |
106 | ||
107 | 3 | Color col = ColorUtils.parseColourString(colour); |
108 | ||
109 | 3 | if (col == null) |
110 | { | |
111 | 1 | jalview.bin.Console.outPrintln("Making colour from name: " + colour); |
112 | 1 | col = ColorUtils.createColourFromName(colour); |
113 | } | |
114 | ||
115 | 3 | setAll(col); |
116 | 3 | schemeName = colour; |
117 | } | |
118 | ||
119 | /** | |
120 | * Sets all symbols to the specified colour | |
121 | * | |
122 | * @param col | |
123 | */ | |
124 | 7 | protected void setAll(Color col) |
125 | { | |
126 | 7 | if (symbolIndex == null) |
127 | { | |
128 | 0 | return; |
129 | } | |
130 | 7 | int max = 0; |
131 | 7 | for (int index : symbolIndex) |
132 | { | |
133 | 1785 | max = Math.max(max, index); |
134 | } | |
135 | 7 | colors = new Color[max + 1]; |
136 | 175 | for (int i = 0; i <= max; i++) |
137 | { | |
138 | 168 | colors[i] = col; |
139 | } | |
140 | } | |
141 | ||
142 | 0 | public Color[] getColours() |
143 | { | |
144 | 0 | return colors; |
145 | } | |
146 | ||
147 | 0 | public Color[] getLowerCaseColours() |
148 | { | |
149 | 0 | return lowerCaseColours; |
150 | } | |
151 | ||
152 | 3 | public void setName(String name) |
153 | { | |
154 | 3 | schemeName = name; |
155 | } | |
156 | ||
157 | 0 | public String getName() |
158 | { | |
159 | 0 | return schemeName; |
160 | } | |
161 | ||
162 | /** | |
163 | * Parse and save residue colours specified as (for example) | |
164 | * | |
165 | * <pre> | |
166 | * D,E=red; K,R,H=0022FF; c=100,50,75 | |
167 | * </pre> | |
168 | * | |
169 | * This should be a semi-colon separated list of colours, which may be defined | |
170 | * by colour name, hex value or comma-separated RGB triple. Each colour is | |
171 | * defined for a comma-separated list of amino acid single letter codes. (Note | |
172 | * that this also allows a colour scheme to be defined for ACGT, but not for | |
173 | * U.) | |
174 | * | |
175 | * @param paramValue | |
176 | */ | |
177 | 4 | void parseAppletParameter(String paramValue) |
178 | { | |
179 | 4 | setAll(Color.white); |
180 | ||
181 | 4 | StringTokenizer st = new StringTokenizer(paramValue, ";"); |
182 | 4 | StringTokenizer st2; |
183 | 4 | String token = null, colour, residues; |
184 | 4 | try |
185 | { | |
186 | 21 | while (st.hasMoreElements()) |
187 | { | |
188 | 17 | token = st.nextToken().trim(); |
189 | 17 | residues = token.substring(0, token.indexOf("=")); |
190 | 17 | colour = token.substring(token.indexOf("=") + 1); |
191 | ||
192 | 17 | st2 = new StringTokenizer(residues, " ,"); |
193 | 45 | while (st2.hasMoreTokens()) |
194 | { | |
195 | 28 | String residue = st2.nextToken(); |
196 | ||
197 | 28 | int colIndex = ResidueProperties.aaIndex[residue.charAt(0)]; |
198 | 28 | if (colIndex == -1) |
199 | { | |
200 | 0 | continue; |
201 | } | |
202 | ||
203 | 28 | if (residue.equalsIgnoreCase("lowerCase")) |
204 | { | |
205 | 1 | if (lowerCaseColours == null) |
206 | { | |
207 | 0 | lowerCaseColours = new Color[colors.length]; |
208 | } | |
209 | 25 | for (int i = 0; i < lowerCaseColours.length; i++) |
210 | { | |
211 | 24 | if (lowerCaseColours[i] == null) |
212 | { | |
213 | 22 | lowerCaseColours[i] = ColorUtils.parseColourString(colour); |
214 | } | |
215 | } | |
216 | ||
217 | 1 | continue; |
218 | } | |
219 | ||
220 | 27 | if (residue.equals(residue.toLowerCase(Locale.ROOT))) |
221 | { | |
222 | 6 | if (lowerCaseColours == null) |
223 | { | |
224 | 4 | lowerCaseColours = new Color[colors.length]; |
225 | } | |
226 | 6 | lowerCaseColours[colIndex] = ColorUtils |
227 | .parseColourString(colour); | |
228 | } | |
229 | else | |
230 | { | |
231 | 21 | colors[colIndex] = ColorUtils.parseColourString(colour); |
232 | } | |
233 | } | |
234 | } | |
235 | } catch (Exception ex) | |
236 | { | |
237 | 0 | jalview.bin.Console.outPrintln( |
238 | "Error parsing userDefinedColours:\n" + token + "\n" + ex); | |
239 | } | |
240 | ||
241 | } | |
242 | ||
243 | 0 | public void setLowerCaseColours(Color[] lcolours) |
244 | { | |
245 | 0 | lowerCaseColours = lcolours; |
246 | } | |
247 | ||
248 | /** | |
249 | * Returns the colour for the given residue character. If the residue is | |
250 | * lower-case, and there is a specific colour defined for lower case, that | |
251 | * colour is returned, else the colour for the upper case residue. | |
252 | */ | |
253 | 58 | @Override |
254 | public Color findColour(char c) | |
255 | { | |
256 | 58 | if ('a' <= c && c <= 'z' && lowerCaseColours != null) |
257 | { | |
258 | 14 | Color colour = lowerCaseColours[symbolIndex[c]]; |
259 | 14 | if (colour != null) |
260 | { | |
261 | 10 | return colour; |
262 | } | |
263 | } | |
264 | 48 | return super.findColour(c); |
265 | } | |
266 | ||
267 | /** | |
268 | * Answers the customised name of the colour scheme, if it has one, else "User | |
269 | * Defined" | |
270 | */ | |
271 | 5 | @Override |
272 | public String getSchemeName() | |
273 | { | |
274 | 5 | if (schemeName != null && schemeName.length() > 0) |
275 | { | |
276 | 1 | return schemeName; |
277 | } | |
278 | 4 | return ResidueColourScheme.USER_DEFINED; |
279 | } | |
280 | ||
281 | /** | |
282 | * Generate an applet colour parameter like A,C,D=12ffe9;Q,W=2393fd;w=9178dd | |
283 | * | |
284 | * @return | |
285 | */ | |
286 | 1 | public String toAppletParameter() |
287 | { | |
288 | /* | |
289 | * step 1: build a map from colours to the symbol(s) that have the colour | |
290 | */ | |
291 | 1 | Map<Color, List<String>> colours = new HashMap<>(); |
292 | ||
293 | 27 | for (char symbol = 'A'; symbol <= 'Z'; symbol++) |
294 | { | |
295 | 26 | String residue = String.valueOf(symbol); |
296 | 26 | int index = symbolIndex[symbol]; |
297 | 26 | Color c = colors[index]; |
298 | 26 | if (c != null && !c.equals(Color.white)) |
299 | { | |
300 | 5 | if (colours.get(c) == null) |
301 | { | |
302 | 2 | colours.put(c, new ArrayList<String>()); |
303 | } | |
304 | 5 | colours.get(c).add(residue); |
305 | } | |
306 | 26 | if (lowerCaseColours != null) |
307 | { | |
308 | 26 | c = lowerCaseColours[index]; |
309 | 26 | if (c != null && !c.equals(Color.white)) |
310 | { | |
311 | 1 | residue = residue.toLowerCase(Locale.ROOT); |
312 | 1 | if (colours.get(c) == null) |
313 | { | |
314 | 1 | colours.put(c, new ArrayList<String>()); |
315 | } | |
316 | 1 | colours.get(c).add(residue); |
317 | } | |
318 | } | |
319 | } | |
320 | ||
321 | /* | |
322 | * step 2: make a list of { A,G,R=12f9d6 } residues/colour specs | |
323 | */ | |
324 | 1 | List<String> residueColours = new ArrayList<>(); |
325 | 1 | for (Entry<Color, List<String>> cols : colours.entrySet()) |
326 | { | |
327 | 3 | boolean first = true; |
328 | 3 | StringBuilder sb = new StringBuilder(); |
329 | 3 | for (String residue : cols.getValue()) |
330 | { | |
331 | 6 | if (!first) |
332 | { | |
333 | 3 | sb.append(","); |
334 | } | |
335 | 6 | sb.append(residue); |
336 | 6 | first = false; |
337 | } | |
338 | 3 | sb.append("="); |
339 | /* | |
340 | * get color as hex value, dropping the alpha (ff) part | |
341 | */ | |
342 | 3 | String hexString = Integer.toHexString(cols.getKey().getRGB()) |
343 | .substring(2); | |
344 | 3 | sb.append(hexString); |
345 | 3 | residueColours.add(sb.toString()); |
346 | } | |
347 | ||
348 | /* | |
349 | * sort and output | |
350 | */ | |
351 | 1 | Collections.sort(residueColours); |
352 | 1 | return StringUtils.listToDelimitedString(residueColours, ";"); |
353 | } | |
354 | ||
355 | 4 | @Override |
356 | public boolean hasGapColour() | |
357 | { | |
358 | 4 | return (findColour(' ') != null); |
359 | } | |
360 | } |