blob: 4eddc1574c6bedb9fa85f4a5c5ab182125fd3619 [file] [log] [blame]
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package org.xbill.DNS;
import java.io.*;
import java.text.*;
/**
* Location - describes the physical location of hosts, networks, subnets.
*
* @author Brian Wellington
*/
public class LOCRecord extends Record {
private static final long serialVersionUID = 9058224788126750409L;
private static NumberFormat w2, w3;
private long size, hPrecision, vPrecision;
private long latitude, longitude, altitude;
static {
w2 = new DecimalFormat();
w2.setMinimumIntegerDigits(2);
w3 = new DecimalFormat();
w3.setMinimumIntegerDigits(3);
}
LOCRecord() {}
Record
getObject() {
return new LOCRecord();
}
/**
* Creates an LOC Record from the given data
* @param latitude The latitude of the center of the sphere
* @param longitude The longitude of the center of the sphere
* @param altitude The altitude of the center of the sphere, in m
* @param size The diameter of a sphere enclosing the described entity, in m.
* @param hPrecision The horizontal precision of the data, in m.
* @param vPrecision The vertical precision of the data, in m.
*/
public
LOCRecord(Name name, int dclass, long ttl, double latitude, double longitude,
double altitude, double size, double hPrecision, double vPrecision)
{
super(name, Type.LOC, dclass, ttl);
this.latitude = (long)(latitude * 3600 * 1000 + (1L << 31));
this.longitude = (long)(longitude * 3600 * 1000 + (1L << 31));
this.altitude = (long)((altitude + 100000) * 100);
this.size = (long)(size * 100);
this.hPrecision = (long)(hPrecision * 100);
this.vPrecision = (long)(vPrecision * 100);
}
void
rrFromWire(DNSInput in) throws IOException {
int version;
version = in.readU8();
if (version != 0)
throw new WireParseException("Invalid LOC version");
size = parseLOCformat(in.readU8());
hPrecision = parseLOCformat(in.readU8());
vPrecision = parseLOCformat(in.readU8());
latitude = in.readU32();
longitude = in.readU32();
altitude = in.readU32();
}
private double
parseFixedPoint(String s)
{
if (s.matches("^-?\\d+$"))
return Integer.parseInt(s);
else if (s.matches("^-?\\d+\\.\\d*$")) {
String [] parts = s.split("\\.");
double value = Integer.parseInt(parts[0]);
double fraction = Integer.parseInt(parts[1]);
if (value < 0)
fraction *= -1;
int digits = parts[1].length();
return value + (fraction / Math.pow(10, digits));
} else
throw new NumberFormatException();
}
private long
parsePosition(Tokenizer st, String type) throws IOException {
boolean isLatitude = type.equals("latitude");
int deg = 0, min = 0;
double sec = 0;
long value;
String s;
deg = st.getUInt16();
if (deg > 180 || (deg > 90 && isLatitude))
throw st.exception("Invalid LOC " + type + " degrees");
s = st.getString();
try {
min = Integer.parseInt(s);
if (min < 0 || min > 59)
throw st.exception("Invalid LOC " + type + " minutes");
s = st.getString();
sec = parseFixedPoint(s);
if (sec < 0 || sec >= 60)
throw st.exception("Invalid LOC " + type + " seconds");
s = st.getString();
} catch (NumberFormatException e) {
}
if (s.length() != 1)
throw st.exception("Invalid LOC " + type);
value = (long) (1000 * (sec + 60L * (min + 60L * deg)));
char c = Character.toUpperCase(s.charAt(0));
if ((isLatitude && c == 'S') || (!isLatitude && c == 'W'))
value = -value;
else if ((isLatitude && c != 'N') || (!isLatitude && c != 'E'))
throw st.exception("Invalid LOC " + type);
value += (1L << 31);
return value;
}
private long
parseDouble(Tokenizer st, String type, boolean required, long min, long max,
long defaultValue)
throws IOException
{
Tokenizer.Token token = st.get();
if (token.isEOL()) {
if (required)
throw st.exception("Invalid LOC " + type);
st.unget();
return defaultValue;
}
String s = token.value;
if (s.length() > 1 && s.charAt(s.length() - 1) == 'm')
s = s.substring(0, s.length() - 1);
try {
long value = (long)(100 * parseFixedPoint(s));
if (value < min || value > max)
throw st.exception("Invalid LOC " + type);
return value;
}
catch (NumberFormatException e) {
throw st.exception("Invalid LOC " + type);
}
}
void
rdataFromString(Tokenizer st, Name origin) throws IOException {
latitude = parsePosition(st, "latitude");
longitude = parsePosition(st, "longitude");
altitude = parseDouble(st, "altitude", true,
-10000000, 4284967295L, 0) + 10000000;
size = parseDouble(st, "size", false, 0, 9000000000L, 100);
hPrecision = parseDouble(st, "horizontal precision", false,
0, 9000000000L, 1000000);
vPrecision = parseDouble(st, "vertical precision", false,
0, 9000000000L, 1000);
}
private void
renderFixedPoint(StringBuffer sb, NumberFormat formatter, long value,
long divisor)
{
sb.append(value / divisor);
value %= divisor;
if (value != 0) {
sb.append(".");
sb.append(formatter.format(value));
}
}
private String
positionToString(long value, char pos, char neg) {
StringBuffer sb = new StringBuffer();
char direction;
long temp = value - (1L << 31);
if (temp < 0) {
temp = -temp;
direction = neg;
} else
direction = pos;
sb.append(temp / (3600 * 1000)); /* degrees */
temp = temp % (3600 * 1000);
sb.append(" ");
sb.append(temp / (60 * 1000)); /* minutes */
temp = temp % (60 * 1000);
sb.append(" ");
renderFixedPoint(sb, w3, temp, 1000); /* seconds */
sb.append(" ");
sb.append(direction);
return sb.toString();
}
/** Convert to a String */
String
rrToString() {
StringBuffer sb = new StringBuffer();
/* Latitude */
sb.append(positionToString(latitude, 'N', 'S'));
sb.append(" ");
/* Latitude */
sb.append(positionToString(longitude, 'E', 'W'));
sb.append(" ");
/* Altitude */
renderFixedPoint(sb, w2, altitude - 10000000, 100);
sb.append("m ");
/* Size */
renderFixedPoint(sb, w2, size, 100);
sb.append("m ");
/* Horizontal precision */
renderFixedPoint(sb, w2, hPrecision, 100);
sb.append("m ");
/* Vertical precision */
renderFixedPoint(sb, w2, vPrecision, 100);
sb.append("m");
return sb.toString();
}
/** Returns the latitude */
public double
getLatitude() {
return ((double)(latitude - (1L << 31))) / (3600 * 1000);
}
/** Returns the longitude */
public double
getLongitude() {
return ((double)(longitude - (1L << 31))) / (3600 * 1000);
}
/** Returns the altitude */
public double
getAltitude() {
return ((double)(altitude - 10000000)) / 100;
}
/** Returns the diameter of the enclosing sphere */
public double
getSize() {
return ((double)size) / 100;
}
/** Returns the horizontal precision */
public double
getHPrecision() {
return ((double)hPrecision) / 100;
}
/** Returns the horizontal precision */
public double
getVPrecision() {
return ((double)vPrecision) / 100;
}
void
rrToWire(DNSOutput out, Compression c, boolean canonical) {
out.writeU8(0); /* version */
out.writeU8(toLOCformat(size));
out.writeU8(toLOCformat(hPrecision));
out.writeU8(toLOCformat(vPrecision));
out.writeU32(latitude);
out.writeU32(longitude);
out.writeU32(altitude);
}
private static long
parseLOCformat(int b) throws WireParseException {
long out = b >> 4;
int exp = b & 0xF;
if (out > 9 || exp > 9)
throw new WireParseException("Invalid LOC Encoding");
while (exp-- > 0)
out *= 10;
return (out);
}
private int
toLOCformat(long l) {
byte exp = 0;
while (l > 9) {
exp++;
l /= 10;
}
return (int)((l << 4) + exp);
}
}