Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.datamodel.features

File FeatureAttributes.java

 

Coverage histogram

../../../img/srcFileCovDistChart9.png
12% of files have more coverage

Code metrics

62
92
14
3
374
228
54
0.59
6.57
4.67
3.86

Classes

Class Line # Actions
FeatureAttributes 15 69 35 22
0.818181881.8%
FeatureAttributes.Datatype 17 0 0 0
-1.0 -
FeatureAttributes.AttributeData 64 23 19 2
0.957446895.7%
 

Contributing tests

This file is covered by 74 tests. .

Source view

1    package jalview.datamodel.features;
2   
3    import java.util.ArrayList;
4    import java.util.Collections;
5    import java.util.Comparator;
6    import java.util.HashMap;
7    import java.util.List;
8    import java.util.Map;
9    import java.util.Map.Entry;
10    import java.util.TreeMap;
11   
12    /**
13    * A singleton class to hold the set of attributes known for each feature type
14    */
 
15    public class FeatureAttributes
16    {
 
17    public enum Datatype
18    {
19    Character, Number, Mixed
20    }
21   
22    private static FeatureAttributes instance = new FeatureAttributes();
23   
24    /*
25    * map, by feature type, of a map, by attribute name, of
26    * attribute description and min-max range (if known)
27    */
28    private Map<String, Map<String[], AttributeData>> attributes;
29   
30    /*
31    * a case-insensitive comparator so that attributes are ordered e.g.
32    * AC
33    * af
34    * CSQ:AFR_MAF
35    * CSQ:Allele
36    */
37    private Comparator<String[]> comparator = new Comparator<String[]>()
38    {
 
39  18096 toggle @Override
40    public int compare(String[] o1, String[] o2)
41    {
42  18096 int i = 0;
43  35441 while (i < o1.length || i < o2.length)
44    {
45  18565 if (o2.length <= i)
46    {
47  3 return o1.length <= i ? 0 : 1;
48    }
49  18562 if (o1.length <= i)
50    {
51  1 return -1;
52    }
53  18561 int comp = String.CASE_INSENSITIVE_ORDER.compare(o1[i], o2[i]);
54  18561 if (comp != 0)
55    {
56  1216 return comp;
57    }
58  17345 i++;
59    }
60  16876 return 0; // same length and all matched
61    }
62    };
63   
 
64    private class AttributeData
65    {
66    /*
67    * description(s) for this attribute, if known
68    * (different feature source might have differing descriptions)
69    */
70    List<String> description;
71   
72    /*
73    * minimum value (of any numeric values recorded)
74    */
75    float min = 0f;
76   
77    /*
78    * maximum value (of any numeric values recorded)
79    */
80    float max = 0f;
81   
82    /*
83    * flag is set true if any numeric value is detected for this attribute
84    */
85    boolean hasValue = false;
86   
87    Datatype type;
88   
89    /**
90    * Note one instance of this attribute, recording unique, non-null
91    * descriptions, and the min/max of any numerical values
92    *
93    * @param desc
94    * @param value
95    */
 
96  16993 toggle void addInstance(String desc, String value)
97    {
98  16993 addDescription(desc);
99   
100  16993 if (value != null)
101    {
102  16993 value = value.trim();
103   
104    /*
105    * Parse numeric value unless we have previously
106    * seen text data for this attribute type
107    */
108  16993 if (type == null || type == Datatype.Number)
109    {
110  364 try
111    {
112  364 float f = Float.valueOf(value);
113  226 min = hasValue ? Math.min(min, f) : f;
114  226 max = hasValue ? Math.max(max, f) : f;
115  226 hasValue = true;
116  226 type = (type == null || type == Datatype.Number)
117    ? Datatype.Number
118    : Datatype.Mixed;
119    } catch (NumberFormatException e)
120    {
121    /*
122    * non-numeric data: treat attribute as Character (or Mixed)
123    */
124  138 type = (type == null || type == Datatype.Character)
125    ? Datatype.Character
126    : Datatype.Mixed;
127  138 min = 0f;
128  138 max = 0f;
129  138 hasValue = false;
130    }
131    }
132    }
133    }
134   
135    /**
136    * Answers the description of the attribute, if recorded and unique, or null if either no, or more than description is recorded
137    * @return
138    */
 
139  2 toggle public String getDescription()
140    {
141  2 if (description != null && description.size() == 1)
142    {
143  1 return description.get(0);
144    }
145  1 return null;
146    }
147   
 
148  3 toggle public Datatype getType()
149    {
150  3 return type;
151    }
152   
153    /**
154    * Adds the given description to the list of known descriptions (without
155    * duplication)
156    *
157    * @param desc
158    */
 
159  16995 toggle public void addDescription(String desc)
160    {
161  16995 if (desc != null)
162    {
163  204 if (description == null)
164    {
165  37 description = new ArrayList<>();
166    }
167  204 if (!description.contains(desc))
168    {
169  38 description.add(desc);
170    }
171    }
172    }
173    }
174   
175    /**
176    * Answers the singleton instance of this class
177    *
178    * @return
179    */
 
180  16891 toggle public static FeatureAttributes getInstance()
181    {
182  16891 return instance;
183    }
184   
 
185  1 toggle private FeatureAttributes()
186    {
187  1 attributes = new HashMap<>();
188    }
189   
190    /**
191    * Answers the attribute names known for the given feature type, in
192    * alphabetical order (not case sensitive), or an empty set if no attributes
193    * are known. An attribute name is typically 'simple' e.g. "AC", but may be
194    * 'compound' e.g. {"CSQ", "Allele"} where a feature has map-valued attributes
195    *
196    * @param featureType
197    * @return
198    */
 
199  0 toggle public List<String[]> getAttributes(String featureType)
200    {
201  0 if (!attributes.containsKey(featureType))
202    {
203  0 return Collections.<String[]> emptyList();
204    }
205   
206  0 return new ArrayList<>(attributes.get(featureType).keySet());
207    }
208   
209    /**
210    * Answers true if at least one attribute is known for the given feature type,
211    * else false
212    *
213    * @param featureType
214    * @return
215    */
 
216  0 toggle public boolean hasAttributes(String featureType)
217    {
218  0 if (attributes.containsKey(featureType))
219    {
220  0 if (!attributes.get(featureType).isEmpty())
221    {
222  0 return true;
223    }
224    }
225  0 return false;
226    }
227   
228    /**
229    * Records the given attribute name and description for the given feature
230    * type, and updates the min-max for any numeric value
231    *
232    * @param featureType
233    * @param description
234    * @param value
235    * @param attName
236    */
 
237  17051 toggle public void addAttribute(String featureType, String description,
238    Object value, String... attName)
239    {
240  17051 if (featureType == null || attName == null)
241    {
242  0 return;
243    }
244   
245    /*
246    * if attribute value is a map, drill down one more level to
247    * record its sub-fields
248    */
249  17051 if (value instanceof Map<?, ?>)
250    {
251  58 for (Entry<?, ?> entry : ((Map<?, ?>) value).entrySet())
252    {
253  169 String[] attNames = new String[attName.length + 1];
254  169 System.arraycopy(attName, 0, attNames, 0, attName.length);
255  169 attNames[attName.length] = entry.getKey().toString();
256  169 addAttribute(featureType, description, entry.getValue(), attNames);
257    }
258  58 return;
259    }
260   
261  16993 String valueAsString = value.toString();
262  16993 Map<String[], AttributeData> atts = attributes.get(featureType);
263  16993 if (atts == null)
264    {
265  44 atts = new TreeMap<>(comparator);
266  44 attributes.put(featureType, atts);
267    }
268  16993 AttributeData attData = atts.get(attName);
269  16993 if (attData == null)
270    {
271  174 attData = new AttributeData();
272  174 atts.put(attName, attData);
273    }
274  16993 attData.addInstance(description, valueAsString);
275    }
276   
277    /**
278    * Answers the description of the given attribute for the given feature type,
279    * if known and unique, else null
280    *
281    * @param featureType
282    * @param attName
283    * @return
284    */
 
285  3 toggle public String getDescription(String featureType, String... attName)
286    {
287  3 String desc = null;
288  3 Map<String[], AttributeData> atts = attributes.get(featureType);
289  3 if (atts != null)
290    {
291  2 AttributeData attData = atts.get(attName);
292  2 if (attData != null)
293    {
294  2 desc = attData.getDescription();
295    }
296    }
297  3 return desc;
298    }
299   
300    /**
301    * Answers the [min, max] value range of the given attribute for the given
302    * feature type, if known, else null. Attributes with a mixture of text and
303    * numeric values are considered text (do not return a min-max range).
304    *
305    * @param featureType
306    * @param attName
307    * @return
308    */
 
309  7 toggle public float[] getMinMax(String featureType, String... attName)
310    {
311  7 Map<String[], AttributeData> atts = attributes.get(featureType);
312  7 if (atts != null)
313    {
314  6 AttributeData attData = atts.get(attName);
315  6 if (attData != null && attData.hasValue)
316    {
317  4 return new float[] { attData.min, attData.max };
318    }
319    }
320  3 return null;
321    }
322   
323    /**
324    * Records the given attribute description for the given feature type
325    *
326    * @param featureType
327    * @param attName
328    * @param description
329    */
 
330  2 toggle public void addDescription(String featureType, String description,
331    String... attName)
332    {
333  2 if (featureType == null || attName == null)
334    {
335  0 return;
336    }
337   
338  2 Map<String[], AttributeData> atts = attributes.get(featureType);
339  2 if (atts == null)
340    {
341  1 atts = new TreeMap<>(comparator);
342  1 attributes.put(featureType, atts);
343    }
344  2 AttributeData attData = atts.get(attName);
345  2 if (attData == null)
346    {
347  1 attData = new AttributeData();
348  1 atts.put(attName, attData);
349    }
350  2 attData.addDescription(description);
351    }
352   
353    /**
354    * Answers the datatype of the feature, which is one of Character, Number or
355    * Mixed (or null if not known), as discovered from values recorded.
356    *
357    * @param featureType
358    * @param attName
359    * @return
360    */
 
361  4 toggle public Datatype getDatatype(String featureType, String... attName)
362    {
363  4 Map<String[], AttributeData> atts = attributes.get(featureType);
364  4 if (atts != null)
365    {
366  3 AttributeData attData = atts.get(attName);
367  3 if (attData != null)
368    {
369  3 return attData.getType();
370    }
371    }
372  1 return null;
373    }
374    }