JAL-2069 spike updated with latest (FeatureTypeSettings)
[jalview.git] / src / jalview / datamodel / features / NCNode.java
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.datamodel.features;
22
23 import jalview.datamodel.ContiguousI;
24
25 import java.util.ArrayList;
26 import java.util.List;
27
28 /**
29  * Each node of the NCList tree consists of a range, and (optionally) the NCList
30  * of ranges it encloses
31  *
32  * @param <V>
33  */
34 class NCNode<V extends ContiguousI> implements ContiguousI
35 {
36   /*
37    * deep size (number of ranges included)
38    */
39   private int size;
40
41   private V region;
42
43   /*
44    * null, or an object holding contained subregions of this nodes region
45    */
46   private NCList<V> subregions;
47
48   /**
49    * Constructor given a list of ranges
50    * 
51    * @param ranges
52    */
53   NCNode(List<V> ranges)
54   {
55     build(ranges);
56   }
57
58   /**
59    * Constructor given a single range
60    * 
61    * @param range
62    */
63   NCNode(V range)
64   {
65     List<V> ranges = new ArrayList<>();
66     ranges.add(range);
67     build(ranges);
68   }
69
70   NCNode(V entry, NCList<V> newNCList)
71   {
72     region = entry;
73     subregions = newNCList;
74     size = 1 + newNCList.size();
75   }
76
77   /**
78    * @param ranges
79    */
80   protected void build(List<V> ranges)
81   {
82     size = ranges.size();
83
84     if (!ranges.isEmpty())
85     {
86       region = ranges.get(0);
87     }
88     if (ranges.size() > 1)
89     {
90       subregions = new NCList<V>(ranges.subList(1, ranges.size()));
91     }
92   }
93
94   @Override
95   public int getBegin()
96   {
97     return region.getBegin();
98   }
99
100   @Override
101   public int getEnd()
102   {
103     return region.getEnd();
104   }
105
106   /**
107    * Formats the node as a bracketed list e.g.
108    * 
109    * <pre>
110    * [1-100 [10-30 [10-20]], 15-30 [20-20]]
111    * </pre>
112    */
113   @Override
114   public String toString() {
115     StringBuilder sb = new StringBuilder(10 * size);
116     sb.append(region.getBegin()).append("-").append(region.getEnd());
117     if (subregions != null)
118     {
119       sb.append(" ").append(subregions.toString());
120     }
121     return sb.toString();
122   }
123
124   void prettyPrint(StringBuilder sb, int offset, int indent) {
125     for (int i = 0 ; i < offset ; i++) {
126       sb.append(" ");
127     }
128     sb.append(region.getBegin()).append("-").append(region.getEnd());
129     if (subregions != null)
130     {
131       sb.append(System.lineSeparator());
132       subregions.prettyPrint(sb, offset + 2, indent);
133     }
134   }
135   /**
136    * Add any ranges that overlap the from-to range to the result list
137    * 
138    * @param from
139    * @param to
140    * @param result
141    */
142   void findOverlaps(long from, long to, List<V> result)
143   {
144     if (region.getBegin() <= to && region.getEnd() >= from)
145     {
146       result.add(region);
147     }
148     if (subregions != null)
149     {
150       subregions.findOverlaps(from, to, result);
151     }
152   }
153
154   /**
155    * Add one range to this subrange
156    * 
157    * @param entry
158    */
159   synchronized void add(V entry)
160   {
161     if (entry.getBegin() < region.getBegin() || entry.getEnd() > region.getEnd()) {
162       throw new IllegalArgumentException(String.format(
163               "adding improper subrange %d-%d to range %d-%d",
164               entry.getBegin(), entry.getEnd(), region.getBegin(),
165               region.getEnd()));
166     }
167     if (subregions == null)
168     {
169       subregions = new NCList<V>(entry);
170     }
171     else
172     {
173       subregions.add(entry);
174     }
175     size++;
176   }
177
178   /**
179    * Answers true if the data held satisfy the rules of construction of an
180    * NCList, else false.
181    * 
182    * @return
183    */
184   boolean isValid()
185   {
186     /*
187      * we don't handle reverse ranges
188      */
189     if (region != null && region.getBegin() > region.getEnd())
190     {
191       return false;
192     }
193     if (subregions == null)
194     {
195       return true;
196     }
197     return subregions.isValid(getBegin(), getEnd());
198   }
199
200   /**
201    * Adds all contained entries to the given list
202    * 
203    * @param entries
204    */
205   void getEntries(List<V> entries)
206   {
207     entries.add(region);
208     if (subregions != null)
209     {
210       subregions.getEntries(entries);
211     }
212   }
213
214   /**
215    * Answers true if this object contains the given entry (by object equals
216    * test), else false
217    * 
218    * @param entry
219    * @return
220    */
221   boolean contains(V entry)
222   {
223     if (entry == null)
224     {
225       return false;
226     }
227     if (entry.equals(region))
228     {
229       return true;
230     }
231     return subregions == null ? false : subregions.contains(entry);
232   }
233
234   /**
235    * Answers the 'root' region modelled by this object
236    * 
237    * @return
238    */
239   V getRegion()
240   {
241     return region;
242   }
243
244   /**
245    * Answers the (possibly null) contained regions within this object
246    * 
247    * @return
248    */
249   NCList<V> getSubRegions()
250   {
251     return subregions;
252   }
253
254   /**
255    * Nulls the subregion reference if it is empty (after a delete entry
256    * operation)
257    */
258   void deleteSubRegionsIfEmpty()
259   {
260     if (subregions != null && subregions.size() == 0)
261     {
262       subregions = null;
263     }
264   }
265
266   /**
267    * Answers the (deep) size of this node i.e. the number of ranges it models
268    * 
269    * @return
270    */
271   int size()
272   {
273     return size;
274   }
275 }