JAL-2446 added delete/contains to pseudo-random tests, fail fixes
[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> implements 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   @Override
73   public int getBegin()
74   {
75     return region.getBegin();
76   }
77
78   @Override
79   public int getEnd()
80   {
81     return region.getEnd();
82   }
83
84   /**
85    * Formats the node as a bracketed list e.g.
86    * 
87    * <pre>
88    * [1-100 [10-30 [10-20]], 15-30 [20-20]]
89    * </pre>
90    */
91   @Override
92   public String toString() {
93     StringBuilder sb = new StringBuilder(10 * size);
94     sb.append(region.getBegin()).append("-").append(region.getEnd());
95     if (subregions != null)
96     {
97       sb.append(" ").append(subregions.toString());
98     }
99     return sb.toString();
100   }
101
102   void prettyPrint(StringBuilder sb, int offset, int indent) {
103     for (int i = 0 ; i < offset ; i++) {
104       sb.append(" ");
105     }
106     sb.append(region.getBegin()).append("-").append(region.getEnd());
107     if (subregions != null)
108     {
109       sb.append(System.lineSeparator());
110       subregions.prettyPrint(sb, offset + 2, indent);
111     }
112   }
113   /**
114    * Add any ranges that overlap the from-to range to the result list
115    * 
116    * @param from
117    * @param to
118    * @param result
119    */
120   void findOverlaps(long from, long to, List<V> result)
121   {
122     if (region.getBegin() <= to && region.getEnd() >= from)
123     {
124       result.add(region);
125     }
126     if (subregions != null)
127     {
128       subregions.findOverlaps(from, to, result);
129     }
130   }
131
132   /**
133    * Add one range to this subrange
134    * 
135    * @param entry
136    */
137   synchronized void add(V entry)
138   {
139     if (entry.getBegin() < region.getBegin() || entry.getEnd() > region.getEnd()) {
140       throw new IllegalArgumentException(String.format(
141               "adding improper subrange %d-%d to range %d-%d",
142               entry.getBegin(), entry.getEnd(), region.getBegin(),
143               region.getEnd()));
144     }
145     if (subregions == null)
146     {
147       subregions = new NCList<V>(entry);
148     }
149     else
150     {
151       subregions.add(entry);
152     }
153   }
154
155   /**
156    * Answers true if the data held satisfy the rules of construction of an
157    * NCList, else false.
158    * 
159    * @return
160    */
161   boolean isValid()
162   {
163     /*
164      * we don't handle reverse ranges
165      */
166     if (region != null && region.getBegin() > region.getEnd())
167     {
168       return false;
169     }
170     if (subregions == null)
171     {
172       return true;
173     }
174     return subregions.isValid(getBegin(), getEnd());
175   }
176
177   /**
178    * Adds all contained entries to the given list
179    * 
180    * @param entries
181    */
182   void getEntries(List<V> entries)
183   {
184     entries.add(region);
185     if (subregions != null)
186     {
187       subregions.getEntries(entries);
188     }
189   }
190
191   /**
192    * Answers true if this object contains the given entry (by object equals
193    * test), else false
194    * 
195    * @param entry
196    * @return
197    */
198   boolean contains(V entry)
199   {
200     if (entry == null)
201     {
202       return false;
203     }
204     if (entry.equals(region))
205     {
206       return true;
207     }
208     return subregions == null ? false : subregions.contains(entry);
209   }
210
211   /**
212    * Answers the 'root' region modelled by this object
213    * 
214    * @return
215    */
216   V getRegion()
217   {
218     return region;
219   }
220
221   /**
222    * Answers the (possibly null) contained regions within this object
223    * 
224    * @return
225    */
226   NCList<V> getSubRegions()
227   {
228     return subregions;
229   }
230 }