android: Change format of address ranges and print sets
[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 set.
89 */
90 public void add(IPRangeSet ranges)
91 {
92 if (ranges == this)
93 {
94 return;
95 }
96 for (IPRange range : ranges.mRanges)
97 {
98 add(range);
99 }
100 }
101
102 /**
103 * Add all ranges from the given collection to this set.
104 */
105 public void addAll(Collection<? extends IPRange> coll)
106 {
107 for (IPRange range : coll)
108 {
109 add(range);
110 }
111 }
112
113 /**
114 * Remove the given range from this set. Existing ranges are automatically adjusted.
115 */
116 public void remove(IPRange range)
117 {
118 ArrayList <IPRange> additions = new ArrayList<>();
119 Iterator<IPRange> iterator = mRanges.iterator();
120 while (iterator.hasNext())
121 {
122 IPRange existing = iterator.next();
123 List<IPRange> result = existing.remove(range);
124 if (result.size() == 0)
125 {
126 iterator.remove();
127 }
128 else if (!result.get(0).equals(existing))
129 {
130 iterator.remove();
131 additions.addAll(result);
132 }
133 }
134 mRanges.addAll(additions);
135 }
136
137 /**
138 * Remove the given ranges from ranges in this set.
139 */
140 public void remove(IPRangeSet ranges)
141 {
142 if (ranges == this)
143 {
144 mRanges.clear();
145 return;
146 }
147 for (IPRange range : ranges.mRanges)
148 {
149 remove(range);
150 }
151 }
152
153 /**
154 * Get all the subnets derived from all the ranges in this set.
155 */
156 public Iterable<IPRange> subnets()
157 {
158 return new Iterable<IPRange>()
159 {
160 @Override
161 public Iterator<IPRange> iterator()
162 {
163 return new Iterator<IPRange>()
164 {
165 private Iterator<IPRange> mIterator = mRanges.iterator();
166 private List<IPRange> mSubnets;
167
168 @Override
169 public boolean hasNext()
170 {
171 return (mSubnets != null && mSubnets.size() > 0) || mIterator.hasNext();
172 }
173
174 @Override
175 public IPRange next()
176 {
177 if (mSubnets == null || mSubnets.size() == 0)
178 {
179 IPRange range = mIterator.next();
180 mSubnets = range.toSubnets();
181 }
182 return mSubnets.remove(0);
183 }
184
185 @Override
186 public void remove()
187 {
188 throw new UnsupportedOperationException();
189 }
190 };
191 }
192 };
193 }
194
195 @Override
196 public Iterator<IPRange> iterator()
197 {
198 return mRanges.iterator();
199 }
200
201 /**
202 * Returns the number of ranges, not subnets.
203 */
204 public int size()
205 {
206 return mRanges.size();
207 }
208
209 @Override
210 public String toString()
211 { /* we could use TextUtils, but that causes the unit tests to fail */
212 StringBuilder sb = new StringBuilder();
213 for (IPRange range : mRanges)
214 {
215 if (sb.length() > 0)
216 {
217 sb.append(" ");
218 }
219 sb.append(range.toString());
220 }
221 return sb.toString();
222 }
223 }