// 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();
}
}