JAL-2808 refine FeatureMatcher interface and matcher toString methods
[jalview.git] / src / jalview / datamodel / features / FeatureMatcher.java
1 package jalview.datamodel.features;
2
3 import jalview.datamodel.SequenceFeature;
4 import jalview.util.MessageManager;
5 import jalview.util.matcher.Condition;
6 import jalview.util.matcher.Matcher;
7 import jalview.util.matcher.MatcherI;
8
9 /**
10  * An immutable class that models one or more match conditions, each of which is
11  * applied to the value obtained by lookup given the match key.
12  * <p>
13  * For example, the value provider could be a SequenceFeature's attributes map,
14  * and the conditions might be
15  * <ul>
16  * <li>CSQ contains "pathological"</li>
17  * <li>AND</li>
18  * <li>AF <= 1.0e-5</li>
19  * </ul>
20  * 
21  * @author gmcarstairs
22  *
23  */
24 public class FeatureMatcher implements FeatureMatcherI
25 {
26   /*
27    * a dummy matcher that comes in useful for the 'add a filter' gui row
28    */
29   public static final FeatureMatcherI NULL_MATCHER = FeatureMatcher
30           .byLabel(Condition.values()[0], "");
31
32   private static final String COLON = ":";
33
34   /*
35    * if true, match is against feature description
36    */
37   final private boolean byLabel;
38
39   /*
40    * if true, match is against feature score
41    */
42   final private boolean byScore;
43
44   /*
45    * if not null, match is against feature attribute [sub-attribute]
46    */
47   final private String[] key;
48
49   final private MatcherI matcher;
50
51   /**
52    * A factory constructor method for a matcher that applies its match condition
53    * to the feature label (description)
54    * 
55    * @param cond
56    * @param pattern
57    * @return
58    */
59   public static FeatureMatcher byLabel(Condition cond, String pattern)
60   {
61     return new FeatureMatcher(new Matcher(cond, pattern), true, false,
62             null);
63   }
64
65   /**
66    * A factory constructor method for a matcher that applies its match condition
67    * to the feature score
68    * 
69    * @param cond
70    * @param pattern
71    * @return
72    */
73   public static FeatureMatcher byScore(Condition cond, String pattern)
74   {
75     return new FeatureMatcher(new Matcher(cond, pattern), false, true,
76             null);
77   }
78
79   /**
80    * A factory constructor method for a matcher that applies its match condition
81    * to the named feature attribute [and optional sub-attribute]
82    * 
83    * @param cond
84    * @param pattern
85    * @param attName
86    * @return
87    */
88   public static FeatureMatcher byAttribute(Condition cond, String pattern,
89           String... attName)
90   {
91     return new FeatureMatcher(new Matcher(cond, pattern), false, false,
92             attName);
93   }
94
95   private FeatureMatcher(Matcher m, boolean forLabel, boolean forScore,
96           String[] theKey)
97   {
98     key = theKey;
99     matcher = m;
100     byLabel = forLabel;
101     byScore = forScore;
102   }
103   @Override
104   public boolean matches(SequenceFeature feature)
105   {
106     String value = byLabel ? feature.getDescription()
107             : (byScore ? String.valueOf(feature.getScore())
108                     : feature.getValueAsString(key));
109     return matcher.matches(value);
110   }
111
112   @Override
113   public String[] getAttribute()
114   {
115     return key;
116   }
117
118   @Override
119   public MatcherI getMatcher()
120   {
121     return matcher;
122   }
123
124   /**
125    * Answers a string description of this matcher, suitable for display, debugging
126    * or logging. The format may change in future.
127    */
128   @Override
129   public String toString()
130   {
131     StringBuilder sb = new StringBuilder();
132     if (byLabel)
133     {
134       sb.append(MessageManager.getString("label.label"));
135     }
136     else if (byScore)
137     {
138       sb.append(MessageManager.getString("label.score"));
139     }
140     else
141     {
142       sb.append(String.join(COLON, key));
143     }
144
145     Condition condition = matcher.getCondition();
146     sb.append(" ").append(condition.toString().toLowerCase());
147     if (condition.isNumeric())
148     {
149       sb.append(" ").append(matcher.getPattern());
150     }
151     else if (condition.needsAPattern())
152     {
153       sb.append(" '").append(matcher.getPattern()).append("'");
154     }
155
156     return sb.toString();
157   }
158
159   @Override
160   public boolean isByLabel()
161   {
162     return byLabel;
163   }
164
165   @Override
166   public boolean isByScore()
167   {
168     return byScore;
169   }
170 }