package com.netifera.platform.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.netifera.platform.api.iterables.IndexedIterable;
import com.netifera.platform.api.iterables.SequentialIterator;
public class PortSet implements IndexedIterable<Integer> {
private static final long serialVersionUID = -4951196327354642059L;
public static final int PORT_MAX = 0xFFFF; // FIXME restrict to UDP/TCP ?
private final List<PortRange> ports;
public PortSet() throws IllegalArgumentException {
ports = new ArrayList<PortRange>();
}
public PortSet(final String ports) throws IllegalArgumentException {
this.ports = new ArrayList<PortRange>();
fromString(ports);
}
@Override
public boolean equals(final Object obj) {
if (this == obj){
return true;
}
if (obj == null || !(obj instanceof PortSet)){
return false;
}
PortSet portset = (PortSet)obj;
if (itemCount() != portset.itemCount()) {
return false;
}
if (ports.size() != portset.ports.size()) {
return false;
}
//XXX
return ports.equals(((PortSet)obj).ports);
}
@Override
public int hashCode() {
//FIXME
return ports.hashCode();
}
public void clear() {
ports.clear();
}
public PortSet addPort(final int port) throws IllegalArgumentException {
verifyPort(port);
for(PortRange r : ports) {
if(r.contains(port)) {
return this;
}
}
ports.add(new PortRange(port));
simplify();
return this;
}
public PortSet addPortSet(final PortSet portSet) throws IllegalArgumentException {
return addPortSet(portSet.toString());
}
public PortSet addPortSet(final String portSet) throws IllegalArgumentException {
fromString(portSet);
simplify();
return this;
}
public boolean contains(final int port) {
verifyPort(port);
for (PortRange range: ports) {
if (range.contains(port)) {
return true;
}
}
return false;
}
private void fromString(final String string) throws IllegalArgumentException {
if (string.length() == 0) {
return;
}
final String[] parts = string.split(",");
for(String s : parts) {
if (s.contains("-")) {
String[] rangeParts = s.split("-");
ports.add(new PortRange(Integer.parseInt(rangeParts[0]),Integer.parseInt(rangeParts[1])));
} else {
ports.add(new PortRange(Integer.parseInt(s)));
}
}
simplify();
}
@Override
public String toString() {
return getLabel();
}
public String getLabel() {
final StringBuffer buffer = new StringBuffer();
for(PortRange r : ports) {
if(buffer.length() > 0) {
buffer.append(',');
}
buffer.append(r.toString());
}
return buffer.toString();
}
/**
* Sort the ranges of ports by start port and merge overlapping
* ranges. This method should be called after every change which
* modifies the set of ports.
*/
private void simplify() {
Collections.sort(ports);
final Iterator<PortRange> iter = ports.iterator();
PortRange last = null;
while(iter.hasNext()) {
final PortRange current = iter.next();
if (last != null) {
if (last.end + 1 >= current.start) {
last.end = current.end;
iter.remove();
continue;
}
}
last = current;
}
}
public static int verifyPort(final int port) throws IllegalArgumentException {
if (port < 0 || port > PORT_MAX) {
throw new IllegalArgumentException("Invalid port: " + port);
}
return port;
}
public Integer itemAt(final int index) {
if (index < 0) {
throw new IndexOutOfBoundsException();
}
int idx = index;
for (PortRange range: ports) {
if (idx < range.itemCount()) {
return range.itemAt(idx);
}
idx -= range.itemCount();
}
throw new IndexOutOfBoundsException();
}
public int itemCount() { // Cardinal
int answer = 0;
for (PortRange range: ports) {
answer += range.itemCount();
}
return answer;
}
public Iterator<Integer> iterator() {
return new SequentialIterator<Integer>(this);
}
}