JAL-2446 added contains, delete, checks for inclusion on add + tests
[jalview.git] / src / jalview / datamodel / features / NCNode.java
1 package jalview.datamodel.features;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 /**
7  * Each node of the NCList tree consists of a range, and (optionally) the NCList
8  * of ranges it encloses
9  *
10  * @param <V>
11  */
12 class NCNode<V extends ContiguousI>
13 {
14   /*
15    * deep size (number of ranges included)
16    */
17   private int size;
18
19   private V region;
20
21   /*
22    * null, or an object holding contained subregions of this nodes region
23    */
24   private NCList<V> subregions;
25
26   /**
27    * Constructor given a list of ranges
28    * 
29    * @param ranges
30    */
31   NCNode(List<V> ranges)
32   {
33     build(ranges);
34   }
35
36   /**
37    * Constructor given a single range
38    * 
39    * @param range
40    */
41   NCNode(V range)
42   {
43     List<V> ranges = new ArrayList<V>();
44     ranges.add(range);
45     build(ranges);
46   }
47
48   NCNode(V entry, NCList<V> newNCList)
49   {
50     region = entry;
51     subregions = newNCList;
52     size = 1 + newNCList.getSize();
53   }
54
55   /**
56    * @param ranges
57    */
58   protected void build(List<V> ranges)
59   {
60     size = ranges.size();
61
62     if (!ranges.isEmpty())
63     {
64       region = ranges.get(0);
65     }
66     if (ranges.size() > 1)
67     {
68       subregions = new NCList<V>(ranges.subList(1, ranges.size()));
69     }
70   }
71
72   int getStart()
73   {
74     return region.getBegin();
75   }
76
77   int getEnd()
78   {
79     return region.getEnd();
80   }
81
82   /**
83    * Formats the node as a bracketed list e.g.
84    * 
85    * <pre>
86    * [1-100 [10-30 [10-20]], 15-30 [20-20]]
87    * </pre>
88    */
89   @Override
90   public String toString() {
91     StringBuilder sb = new StringBuilder(10 * size);
92     sb.append(region.getBegin()).append("-").append(region.getEnd());
93     if (subregions != null)
94     {
95       sb.append(" ").append(subregions.toString());
96     }
97     return sb.toString();
98   }
99
100   void prettyPrint(StringBuilder sb, int offset, int indent) {
101     for (int i = 0 ; i < offset ; i++) {
102       sb.append(" ");
103     }
104     sb.append(region.getBegin()).append("-").append(region.getEnd());
105     if (subregions != null)
106     {
107       sb.append(System.lineSeparator());
108       subregions.prettyPrint(sb, offset + 2, indent);
109     }
110   }
111   /**
112    * Add any ranges that overlap the from-to range to the result list
113    * 
114    * @param from
115    * @param to
116    * @param result
117    */
118   void findOverlaps(long from, long to, List<V> result)
119   {
120     if (region.getBegin() <= to && region.getEnd() >= from)
121     {
122       result.add(region);
123     }
124     if (subregions != null)
125     {
126       subregions.findOverlaps(from, to, result);
127     }
128   }
129
130   /**
131    * Add one range to this subrange
132    * 
133    * @param entry
134    */
135   synchronized void add(V entry)
136   {
137     if (entry.getBegin() < region.getBegin() || entry.getEnd() > region.getEnd()) {
138       throw new IllegalArgumentException(String.format(
139               "adding improper subrange %d-%d to range %d-%d",
140               entry.getBegin(), entry.getEnd(), region.getBegin(),
141               region.getEnd()));
142     }
143     if (subregions == null)
144     {
145       subregions = new NCList<V>(entry);
146     }
147     else
148     {
149       subregions.add(entry);
150     }
151   }
152
153   /**
154    * Answers true if the data held satisfy the rules of construction of an
155    * NCList, else false.
156    * 
157    * @return
158    */
159   boolean isValid()
160   {
161     if (subregions == null)
162     {
163       return true;
164     }
165     return subregions.isValid(getStart(), getEnd());
166   }
167
168   /**
169    * Adds all contained entries to the given list
170    * 
171    * @param entries
172    */
173   void getEntries(List<V> entries)
174   {
175     entries.add(region);
176     if (subregions != null)
177     {
178       subregions.getEntries(entries);
179     }
180   }
181
182   /**
183    * Answers true if this object contains the given entry (by object equals
184    * test), else false
185    * 
186    * @param entry
187    * @return
188    */
189   boolean contains(V entry)
190   {
191     if (entry == null)
192     {
193       return false;
194     }
195     if (entry.equals(region))
196     {
197       return true;
198     }
199     return subregions == null ? false : subregions.contains(entry);
200   }
201
202   /**
203    * Answers the 'root' region modelled by this object
204    * 
205    * @return
206    */
207   V getRegion()
208   {
209     return region;
210   }
211
212   /**
213    * Answers the (possibly null) contained regions within this object
214    * 
215    * @return
216    */
217   NCList<V> getSubRegions()
218   {
219     return subregions;
220   }
221 }