android: Implement Iterable interface and addAll() for range set
[strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / utils / IPRangeSet.java
1 /*
2 * Copyright (C) 2012-2017 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 package org.strongswan.android.utils;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.TreeSet;
23
24 /**
25 * Class that represents a set of IP address ranges (not necessarily proper subnets) and allows
26 * modifying the set and enumerating the resulting subnets.
27 */
28 public class IPRangeSet implements Iterable<IPRange>
29 {
30 private TreeSet<IPRange> mRanges = new TreeSet<>();
31
32 /**
33 * Parse the given string (space separated ranges in CIDR or range notation) and return the
34 * resulting set or {@code null} if the string was invalid. An empty set is returned if the given string
35 * is {@code null}.
36 */
37 public static IPRangeSet fromString(String ranges)
38 {
39 IPRangeSet set = new IPRangeSet();
40 if (ranges != null)
41 {
42 for (String range : ranges.split("\\s+"))
43 {
44 try
45 {
46 set.add(new IPRange(range));
47 }
48 catch (Exception unused)
49 { /* besides due to invalid strings exceptions might get thrown if the string
50 * contains a hostname (NetworkOnMainThreadException) */
51 return null;
52 }
53 }
54 }
55 return set;
56 }
57
58 /**
59 * Add a range to this set. Automatically gets merged with existing ranges.
60 */
61 public void add(IPRange range)
62 {
63 if (mRanges.contains(range))
64 {
65 return;
66 }
67 reinsert:
68 while (true)
69 {
70 Iterator<IPRange> iterator = mRanges.iterator();
71 while (iterator.hasNext())
72 {
73 IPRange existing = iterator.next();
74 IPRange replacement = existing.merge(range);
75 if (replacement != null)
76 {
77 iterator.remove();
78 range = replacement;
79 continue reinsert;
80 }
81 }
82 mRanges.add(range);
83 break;
84 }
85 }
86
87 /**
88 * Add all ranges from the given collection to this set.
89 */
90 public void addAll(Collection<? extends IPRange> coll)
91 {
92 for (IPRange range : coll)
93 {
94 add(range);
95 }
96 }
97
98 /**
99 * Remove the given range from this set. Existing ranges are automatically adjusted.
100 */
101 public void remove(IPRange range)
102 {
103 ArrayList <IPRange> additions = new ArrayList<>();
104 Iterator<IPRange> iterator = mRanges.iterator();
105 while (iterator.hasNext())
106 {
107 IPRange existing = iterator.next();
108 List<IPRange> result = existing.remove(range);
109 if (result.size() == 0)
110 {
111 iterator.remove();
112 }
113 else if (!result.get(0).equals(existing))
114 {
115 iterator.remove();
116 additions.addAll(result);
117 }
118 }
119 mRanges.addAll(additions);
120 }
121
122 /**
123 * Remove the given ranges from ranges in this set.
124 */
125 public void remove(IPRangeSet ranges)
126 {
127 if (ranges == this)
128 {
129 mRanges.clear();
130 return;
131 }
132 for (IPRange range : ranges.mRanges)
133 {
134 remove(range);
135 }
136 }
137
138 /**
139 * Get all the subnets derived from all the ranges in this set.
140 */
141 public Iterable<IPRange> subnets()
142 {
143 return new Iterable<IPRange>()
144 {
145 @Override
146 public Iterator<IPRange> iterator()
147 {
148 return new Iterator<IPRange>()
149 {
150 private Iterator<IPRange> mIterator = mRanges.iterator();
151 private List<IPRange> mSubnets;
152
153 @Override
154 public boolean hasNext()
155 {
156 return (mSubnets != null && mSubnets.size() > 0) || mIterator.hasNext();
157 }
158
159 @Override
160 public IPRange next()
161 {
162 if (mSubnets == null || mSubnets.size() == 0)
163 {
164 IPRange range = mIterator.next();
165 mSubnets = range.toSubnets();
166 }
167 return mSubnets.remove(0);
168 }
169
170 @Override
171 public void remove()
172 {
173 throw new UnsupportedOperationException();
174 }
175 };
176 }
177 };
178 }
179
180 @Override
181 public Iterator<IPRange> iterator()
182 {
183 return mRanges.iterator();
184 }
185
186 /**
187 * Returns the number of ranges, not subnets.
188 */
189 public int size()
190 {
191 return mRanges.size();
192 }
193 }