package denominator.common;
import java.io.IOException;
import java.io.Reader;
import java.net.InetAddress;
import java.nio.CharBuffer;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import denominator.model.rdata.AAAAData;
import denominator.model.rdata.AData;
import denominator.model.rdata.CERTData;
import denominator.model.rdata.CNAMEData;
import denominator.model.rdata.MXData;
import denominator.model.rdata.NAPTRData;
import denominator.model.rdata.NSData;
import denominator.model.rdata.PTRData;
import denominator.model.rdata.SOAData;
import denominator.model.rdata.SPFData;
import denominator.model.rdata.SRVData;
import denominator.model.rdata.SSHFPData;
import denominator.model.rdata.TXTData;
import static denominator.common.Preconditions.checkNotNull;
/**
* Utilities, inspired by or adapted from guava.
*/
public class Util {
private static final int BUF_SIZE = 0x800; // 2K chars (4K bytes)
private Util() { // no instances
}
public static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
/**
* returns the {@code reader} as a string without closing it.
*/
public static String slurp(Reader reader) throws IOException {
StringBuilder to = new StringBuilder();
CharBuffer buf = CharBuffer.allocate(BUF_SIZE);
while (reader.read(buf) != -1) {
buf.flip();
to.append(buf);
buf.clear();
}
return to.toString();
}
public static String join(char delim, Object... parts) {
if (parts == null || parts.length == 0) {
return "";
}
StringBuilder to = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
to.append(parts[i]);
if (i + 1 < parts.length) {
to.append(delim);
}
}
return to.toString();
}
/**
* empty fields will result in null elements in the result.
*/
public static List<String> split(char delim, String toSplit) {
checkNotNull(toSplit, "toSplit");
if (toSplit.indexOf(delim) == -1) {
return Arrays.asList(toSplit); // sortable in JRE 7 and 8
}
List<String> out = new LinkedList<String>();
StringBuilder currentString = new StringBuilder();
for (char c : toSplit.toCharArray()) {
if (c == delim) {
out.add(emptyToNull(currentString.toString()));
currentString.setLength(0);
} else {
currentString.append(c);
}
}
out.add(emptyToNull(currentString.toString()));
return out;
}
private static String emptyToNull(String field) {
return "".equals(field) ? null : field;
}
public static <T> T nextOrNull(Iterator<T> it) {
return it.hasNext() ? it.next() : null;
}
public static <T> Iterator<T> singletonIterator(final T nullableValue) {
if (nullableValue == null) {
return (Iterator<T>) EMPTY_ITERATOR;
}
return new Iterator<T>() {
boolean done;
@Override
public boolean hasNext() {
return !done;
}
@Override
public T next() {
if (done) {
throw new NoSuchElementException();
}
done = true;
return nullableValue;
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
}
private static final Iterator<Object> EMPTY_ITERATOR = new Iterator<Object>() {
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
public static <T> PeekingIterator<T> peekingIterator(final Iterator<T> iterator) {
checkNotNull(iterator, "iterator");
return new PeekingIterator<T>() {
protected T computeNext() {
if (iterator.hasNext()) {
return iterator.next();
}
return endOfData();
}
};
}
public static <T> Iterator<T> concat(final Iterator<T> first, final Iterator<T> second) {
checkNotNull(first, "first");
checkNotNull(second, "second");
return new PeekingIterator<T>() {
Iterator<? extends T> current = first;
protected T computeNext() {
if (!current.hasNext() && current != second) {
current = second;
}
while (current.hasNext()) {
T element = current.next();
if (element != null) {
return element;
}
}
return endOfData();
}
};
}
public static <T> Iterator<T> concat(final Iterable<? extends Iterable<? extends T>> i) {
final Iterator<? extends Iterable<? extends T>> inputs = checkNotNull(i, "inputs").iterator();
return new PeekingIterator<T>() {
Iterator<? extends T> current = Collections.<T>emptyList().iterator();
protected T computeNext() {
while (!current.hasNext() && inputs.hasNext()) {
current = inputs.next().iterator();
}
while (current.hasNext()) {
T element = current.next();
if (element != null) {
return element;
}
}
return endOfData();
}
};
}
public static <T> Iterator<T> filter(final Iterator<T> unfiltered,
final Filter<? super T> filter) {
checkNotNull(unfiltered, "unfiltered");
checkNotNull(filter, "filter");
return new PeekingIterator<T>() {
protected T computeNext() {
while (unfiltered.hasNext()) {
T element = unfiltered.next();
if (filter.apply(element)) {
return element;
}
}
return endOfData();
}
};
}
public static <T> Filter<T> and(final Filter<T> first, final Filter<? super T> second) {
checkNotNull(first, "first");
checkNotNull(second, "second");
return new Filter<T>() {
public boolean apply(T in) {
if (!first.apply(in)) {
return false;
}
return second.apply(in);
}
};
}
public static String flatten(Map<String, Object> input) {
Collection<Object> orderedRdataValues = input.values();
if (orderedRdataValues.size() == 1) {
Object rdata = orderedRdataValues.iterator().next();
return rdata instanceof InetAddress ? InetAddress.class.cast(rdata).getHostAddress()
: rdata.toString();
}
return join(' ', orderedRdataValues.toArray());
}
public static Map<String, Object> toMap(String type, String rdata) {
return "TXT".equals(type) ? TXTData.create(rdata) : toMap(type, Util.split(' ', rdata));
}
public static Map<String, Object> toMap(String type, List<String> parts) {
if ("A".equals(type)) {
return AData.create(parts.get(0));
} else if ("AAAA".equals(type)) {
return AAAAData.create(parts.get(0));
} else if ("CNAME".equals(type)) {
return CNAMEData.create(parts.get(0));
} else if ("MX".equals(type)) {
return MXData.create(Integer.valueOf(parts.get(0)), parts.get(1));
} else if ("NS".equals(type)) {
return NSData.create(parts.get(0));
} else if ("PTR".equals(type)) {
return PTRData.create(parts.get(0));
} else if ("SOA".equals(type)) {
return SOAData.builder().mname(parts.get(0)).rname(parts.get(1))
.serial(Integer.valueOf(parts.get(2)))
.refresh(Integer.valueOf(parts.get(3))).retry(Integer.valueOf(parts.get(4)))
.expire(Integer.valueOf(parts.get(5))).minimum(Integer.valueOf(parts.get(6))).build();
} else if ("SPF".equals(type)) {
return SPFData.create(parts.get(0));
} else if ("SRV".equals(type)) {
return SRVData.builder().priority(Integer.valueOf(parts.get(0)))
.weight(Integer.valueOf(parts.get(1)))
.port(Integer.valueOf(parts.get(2))).target(parts.get(3)).build();
} else if ("TXT".equals(type)) {
return TXTData.create(parts.get(0));
} else if ("CERT".equals(type)) {
return CERTData.builder().format(Integer.valueOf(parts.get(0)))
.tag(Integer.valueOf(parts.get(1)))
.algorithm(Integer.valueOf(parts.get(2)))
.certificate(parts.get(3))
.build();
} else if ("NAPTR".equals(type)) {
return NAPTRData.builder().order(Integer.valueOf(parts.get(0)))
.preference(Integer.valueOf(parts.get(1)))
.flags(parts.get(2))
.services(parts.get(3))
.regexp(parts.get(4))
.replacement(parts.get(5))
.build();
} else if ("SSHFP".equals(type)) {
return SSHFPData.builder().algorithm(Integer.valueOf(parts.get(0)))
.fptype(Integer.valueOf(parts.get(1)))
.fingerprint(parts.get(2))
.build();
} else {
throw new IllegalArgumentException("unsupported type: " + type);
}
}
}