Add android smack source.

Change-Id: I49ce97136c17173c4ae3965c694af6e7bc49897d
diff --git a/src/org/xbill/DNS/Generator.java b/src/org/xbill/DNS/Generator.java
new file mode 100644
index 0000000..a08d343
--- /dev/null
+++ b/src/org/xbill/DNS/Generator.java
@@ -0,0 +1,264 @@
+// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
+
+package org.xbill.DNS;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * A representation of a $GENERATE statement in a master file.
+ *
+ * @author Brian Wellington
+ */
+
+public class Generator {
+
+/** The start of the range. */
+public long start;
+
+/** The end of the range. */
+public long end;
+
+/** The step value of the range. */
+public long step;
+
+/** The pattern to use for generating record names. */
+public final String namePattern;
+
+/** The type of the generated records. */
+public final int type;
+
+/** The class of the generated records. */
+public final int dclass;
+
+/** The ttl of the generated records. */
+public final long ttl;
+
+/** The pattern to use for generating record data. */
+public final String rdataPattern;
+
+/** The origin to append to relative names. */
+public final Name origin;
+
+private long current;
+
+/**
+ * Indicates whether generation is supported for this type.
+ * @throws InvalidTypeException The type is out of range.
+ */
+public static boolean
+supportedType(int type) {
+	Type.check(type);
+	return (type == Type.PTR || type == Type.CNAME || type == Type.DNAME ||
+		type == Type.A || type == Type.AAAA || type == Type.NS);
+}
+
+/**
+ * Creates a specification for generating records, as a $GENERATE
+ * statement in a master file.
+ * @param start The start of the range.
+ * @param end The end of the range.
+ * @param step The step value of the range.
+ * @param namePattern The pattern to use for generating record names.
+ * @param type The type of the generated records.  The supported types are
+ * PTR, CNAME, DNAME, A, AAAA, and NS.
+ * @param dclass The class of the generated records.
+ * @param ttl The ttl of the generated records.
+ * @param rdataPattern The pattern to use for generating record data.
+ * @param origin The origin to append to relative names.
+ * @throws IllegalArgumentException The range is invalid.
+ * @throws IllegalArgumentException The type does not support generation.
+ * @throws IllegalArgumentException The dclass is not a valid class.
+ */
+public
+Generator(long start, long end, long step, String namePattern,
+	  int type, int dclass, long ttl, String rdataPattern, Name origin)
+{
+	if (start < 0 || end < 0 || start > end || step <= 0)
+		throw new IllegalArgumentException
+				("invalid range specification");
+	if (!supportedType(type))
+		throw new IllegalArgumentException("unsupported type");
+	DClass.check(dclass);
+
+	this.start = start;
+	this.end = end;
+	this.step = step;
+	this.namePattern = namePattern;
+	this.type = type;
+	this.dclass = dclass;
+	this.ttl = ttl;
+	this.rdataPattern = rdataPattern;
+	this.origin = origin;
+	this.current = start;
+}
+
+private String
+substitute(String spec, long n) throws IOException {
+	boolean escaped = false;
+	byte [] str = spec.getBytes();
+	StringBuffer sb = new StringBuffer();
+
+	for (int i = 0; i < str.length; i++) {
+		char c = (char)(str[i] & 0xFF);
+		if (escaped) {
+			sb.append(c);
+			escaped = false;
+		} else if (c == '\\') {
+			if (i + 1 == str.length)
+				throw new TextParseException
+						("invalid escape character");
+			escaped = true;
+		} else if (c == '$') {
+			boolean negative = false;
+			long offset = 0;
+			long width = 0;
+			long base = 10;
+			boolean wantUpperCase = false;
+			if (i + 1 < str.length && str[i + 1] == '$') {
+				// '$$' == literal '$' for backwards
+				// compatibility with old versions of BIND.
+				c = (char)(str[++i] & 0xFF);
+				sb.append(c);
+				continue;
+			} else if (i + 1 < str.length && str[i + 1] == '{') {
+				// It's a substitution with modifiers.
+				i++;
+				if (i + 1 < str.length && str[i + 1] == '-') {
+					negative = true;
+					i++;
+				}
+				while (i + 1 < str.length) {
+					c = (char)(str[++i] & 0xFF);
+					if (c == ',' || c == '}')
+						break;
+					if (c < '0' || c > '9')
+						throw new TextParseException(
+							"invalid offset");
+					c -= '0';
+					offset *= 10;
+					offset += c;
+				}
+				if (negative)
+					offset = -offset;
+
+				if (c == ',') {
+					while (i + 1 < str.length) {
+						c = (char)(str[++i] & 0xFF);
+						if (c == ',' || c == '}')
+							break;
+						if (c < '0' || c > '9')
+							throw new
+							   TextParseException(
+							   "invalid width");
+						c -= '0';
+						width *= 10;
+						width += c;
+					}
+				}
+
+				if (c == ',') {
+					if  (i + 1 == str.length)
+						throw new TextParseException(
+							   "invalid base");
+					c = (char)(str[++i] & 0xFF);
+					if (c == 'o')
+						base = 8;
+					else if (c == 'x')
+						base = 16;
+					else if (c == 'X') {
+						base = 16;
+						wantUpperCase = true;
+					}
+					else if (c != 'd')
+						throw new TextParseException(
+							   "invalid base");
+				}
+
+				if (i + 1 == str.length || str[i + 1] != '}')
+					throw new TextParseException
+						("invalid modifiers");
+				i++;
+			}
+			long v = n + offset;
+			if (v < 0)
+				throw new TextParseException
+						("invalid offset expansion");
+			String number;
+			if (base == 8)
+				number = Long.toOctalString(v);
+			else if (base == 16)
+				number = Long.toHexString(v);
+			else
+				number = Long.toString(v);
+			if (wantUpperCase)
+				number = number.toUpperCase();
+			if (width != 0 && width > number.length()) {
+				int zeros = (int)width - number.length();
+				while (zeros-- > 0)
+					sb.append('0');
+			}
+			sb.append(number);
+		} else {
+			sb.append(c);
+		}
+	}
+	return sb.toString();
+}
+
+/**
+ * Constructs and returns the next record in the expansion.
+ * @throws IOException The name or rdata was invalid after substitutions were
+ * performed.
+ */
+public Record
+nextRecord() throws IOException {
+	if (current > end)
+		return null;
+	String namestr = substitute(namePattern, current);
+	Name name = Name.fromString(namestr, origin);
+	String rdata = substitute(rdataPattern, current);
+	current += step;
+	return Record.fromString(name, type, dclass, ttl, rdata, origin);
+}
+
+/**
+ * Constructs and returns all records in the expansion.
+ * @throws IOException The name or rdata of a record was invalid after
+ * substitutions were performed.
+ */
+public Record []
+expand() throws IOException {
+	List list = new ArrayList();
+	for (long i = start; i < end; i += step) {
+		String namestr = substitute(namePattern, current);
+		Name name = Name.fromString(namestr, origin);
+		String rdata = substitute(rdataPattern, current);
+		list.add(Record.fromString(name, type, dclass, ttl,
+					   rdata, origin));
+	}
+	return (Record []) list.toArray(new Record[list.size()]);
+}
+
+/**
+ * Converts the generate specification to a string containing the corresponding
+ * $GENERATE statement.
+ */
+public String
+toString() {
+	StringBuffer sb = new StringBuffer();
+	sb.append("$GENERATE ");
+	sb.append(start + "-" + end);
+	if (step > 1)
+		sb.append("/" + step);
+	sb.append(" ");
+	sb.append(namePattern + " ");
+	sb.append(ttl + " ");
+	if (dclass != DClass.IN || !Options.check("noPrintIN"))
+		sb.append(DClass.string(dclass) + " ");
+	sb.append(Type.string(type) + " ");
+	sb.append(rdataPattern + " ");
+	return sb.toString();
+}
+
+}