Home | History | Annotate | Download | only in DNS
      1 // Copyright (c) 1999-2004 Brian Wellington (bwelling (at) xbill.org)
      2 
      3 package org.xbill.DNS;
      4 
      5 import java.io.*;
      6 import java.text.*;
      7 
      8 /**
      9  * Location - describes the physical location of hosts, networks, subnets.
     10  *
     11  * @author Brian Wellington
     12  */
     13 
     14 public class LOCRecord extends Record {
     15 
     16 private static final long serialVersionUID = 9058224788126750409L;
     17 
     18 private static NumberFormat w2, w3;
     19 
     20 private long size, hPrecision, vPrecision;
     21 private long latitude, longitude, altitude;
     22 
     23 static {
     24 	w2 = new DecimalFormat();
     25 	w2.setMinimumIntegerDigits(2);
     26 
     27 	w3 = new DecimalFormat();
     28 	w3.setMinimumIntegerDigits(3);
     29 }
     30 
     31 LOCRecord() {}
     32 
     33 Record
     34 getObject() {
     35 	return new LOCRecord();
     36 }
     37 
     38 /**
     39  * Creates an LOC Record from the given data
     40  * @param latitude The latitude of the center of the sphere
     41  * @param longitude The longitude of the center of the sphere
     42  * @param altitude The altitude of the center of the sphere, in m
     43  * @param size The diameter of a sphere enclosing the described entity, in m.
     44  * @param hPrecision The horizontal precision of the data, in m.
     45  * @param vPrecision The vertical precision of the data, in m.
     46 */
     47 public
     48 LOCRecord(Name name, int dclass, long ttl, double latitude, double longitude,
     49 	  double altitude, double size, double hPrecision, double vPrecision)
     50 {
     51 	super(name, Type.LOC, dclass, ttl);
     52 	this.latitude = (long)(latitude * 3600 * 1000 + (1L << 31));
     53 	this.longitude = (long)(longitude * 3600 * 1000 + (1L << 31));
     54 	this.altitude = (long)((altitude + 100000) * 100);
     55 	this.size = (long)(size * 100);
     56 	this.hPrecision = (long)(hPrecision * 100);
     57 	this.vPrecision = (long)(vPrecision * 100);
     58 }
     59 
     60 void
     61 rrFromWire(DNSInput in) throws IOException {
     62 	int version;
     63 
     64 	version = in.readU8();
     65 	if (version != 0)
     66 		throw new WireParseException("Invalid LOC version");
     67 
     68 	size = parseLOCformat(in.readU8());
     69 	hPrecision = parseLOCformat(in.readU8());
     70 	vPrecision = parseLOCformat(in.readU8());
     71 	latitude = in.readU32();
     72 	longitude = in.readU32();
     73 	altitude = in.readU32();
     74 }
     75 
     76 private double
     77 parseFixedPoint(String s)
     78 {
     79 	if (s.matches("^-?\\d+$"))
     80 		return Integer.parseInt(s);
     81 	else if (s.matches("^-?\\d+\\.\\d*$")) {
     82 		String [] parts = s.split("\\.");
     83 		double value = Integer.parseInt(parts[0]);
     84 		double fraction = Integer.parseInt(parts[1]);
     85 		if (value < 0)
     86 			fraction *= -1;
     87 		int digits = parts[1].length();
     88 		return value + (fraction / Math.pow(10, digits));
     89 	} else
     90 		throw new NumberFormatException();
     91 }
     92 
     93 private long
     94 parsePosition(Tokenizer st, String type) throws IOException {
     95 	boolean isLatitude = type.equals("latitude");
     96 	int deg = 0, min = 0;
     97 	double sec = 0;
     98 	long value;
     99 	String s;
    100 
    101 	deg = st.getUInt16();
    102 	if (deg > 180 || (deg > 90 && isLatitude))
    103 		throw st.exception("Invalid LOC " + type + " degrees");
    104 
    105 	s = st.getString();
    106 	try {
    107 		min = Integer.parseInt(s);
    108 		if (min < 0 || min > 59)
    109 			throw st.exception("Invalid LOC " + type + " minutes");
    110 		s = st.getString();
    111 		sec = parseFixedPoint(s);
    112 		if (sec < 0 || sec >= 60)
    113 			throw st.exception("Invalid LOC " + type + " seconds");
    114 		s = st.getString();
    115 	} catch (NumberFormatException e) {
    116 	}
    117 
    118 	if (s.length() != 1)
    119 		throw st.exception("Invalid LOC " + type);
    120 
    121 	value = (long) (1000 * (sec + 60L * (min + 60L * deg)));
    122 
    123 	char c = Character.toUpperCase(s.charAt(0));
    124 	if ((isLatitude && c == 'S') || (!isLatitude && c == 'W'))
    125 		value = -value;
    126 	else if ((isLatitude && c != 'N') || (!isLatitude && c != 'E'))
    127 		throw st.exception("Invalid LOC " + type);
    128 
    129 	value += (1L << 31);
    130 
    131 	return value;
    132 }
    133 
    134 private long
    135 parseDouble(Tokenizer st, String type, boolean required, long min, long max,
    136 	    long defaultValue)
    137 throws IOException
    138 {
    139 	Tokenizer.Token token = st.get();
    140 	if (token.isEOL()) {
    141 		if (required)
    142 			throw st.exception("Invalid LOC " + type);
    143 		st.unget();
    144 		return defaultValue;
    145 	}
    146 	String s = token.value;
    147 	if (s.length() > 1 && s.charAt(s.length() - 1) == 'm')
    148 		s = s.substring(0, s.length() - 1);
    149 	try {
    150 		long value = (long)(100 * parseFixedPoint(s));
    151 		if (value < min || value > max)
    152 			throw st.exception("Invalid LOC " + type);
    153 		return value;
    154 	}
    155 	catch (NumberFormatException e) {
    156 		throw st.exception("Invalid LOC " + type);
    157 	}
    158 }
    159 
    160 void
    161 rdataFromString(Tokenizer st, Name origin) throws IOException {
    162 	latitude = parsePosition(st, "latitude");
    163 	longitude = parsePosition(st, "longitude");
    164 	altitude = parseDouble(st, "altitude", true,
    165 			       -10000000, 4284967295L, 0) + 10000000;
    166 	size = parseDouble(st, "size", false, 0, 9000000000L, 100);
    167 	hPrecision = parseDouble(st, "horizontal precision", false,
    168 				 0, 9000000000L, 1000000);
    169 	vPrecision = parseDouble(st, "vertical precision", false,
    170 				 0, 9000000000L, 1000);
    171 }
    172 
    173 private void
    174 renderFixedPoint(StringBuffer sb, NumberFormat formatter, long value,
    175 		 long divisor)
    176 {
    177 	sb.append(value / divisor);
    178 	value %= divisor;
    179 	if (value != 0) {
    180 		sb.append(".");
    181 		sb.append(formatter.format(value));
    182 	}
    183 }
    184 
    185 private String
    186 positionToString(long value, char pos, char neg) {
    187 	StringBuffer sb = new StringBuffer();
    188 	char direction;
    189 
    190 	long temp = value - (1L << 31);
    191 	if (temp < 0) {
    192 		temp = -temp;
    193 		direction = neg;
    194 	} else
    195 		direction = pos;
    196 
    197 	sb.append(temp / (3600 * 1000)); /* degrees */
    198 	temp = temp % (3600 * 1000);
    199 	sb.append(" ");
    200 
    201 	sb.append(temp / (60 * 1000)); /* minutes */
    202 	temp = temp % (60 * 1000);
    203 	sb.append(" ");
    204 
    205 	renderFixedPoint(sb, w3, temp, 1000); /* seconds */
    206 	sb.append(" ");
    207 
    208 	sb.append(direction);
    209 
    210 	return sb.toString();
    211 }
    212 
    213 
    214 /** Convert to a String */
    215 String
    216 rrToString() {
    217 	StringBuffer sb = new StringBuffer();
    218 
    219 	/* Latitude */
    220 	sb.append(positionToString(latitude, 'N', 'S'));
    221 	sb.append(" ");
    222 
    223 	/* Latitude */
    224 	sb.append(positionToString(longitude, 'E', 'W'));
    225 	sb.append(" ");
    226 
    227 	/* Altitude */
    228 	renderFixedPoint(sb, w2, altitude - 10000000, 100);
    229 	sb.append("m ");
    230 
    231 	/* Size */
    232 	renderFixedPoint(sb, w2, size, 100);
    233 	sb.append("m ");
    234 
    235 	/* Horizontal precision */
    236 	renderFixedPoint(sb, w2, hPrecision, 100);
    237 	sb.append("m ");
    238 
    239 	/* Vertical precision */
    240 	renderFixedPoint(sb, w2, vPrecision, 100);
    241 	sb.append("m");
    242 
    243 	return sb.toString();
    244 }
    245 
    246 /** Returns the latitude */
    247 public double
    248 getLatitude() {
    249 	return ((double)(latitude - (1L << 31))) / (3600 * 1000);
    250 }
    251 
    252 /** Returns the longitude */
    253 public double
    254 getLongitude() {
    255 	return ((double)(longitude - (1L << 31))) / (3600 * 1000);
    256 }
    257 
    258 /** Returns the altitude */
    259 public double
    260 getAltitude() {
    261 	return ((double)(altitude - 10000000)) / 100;
    262 }
    263 
    264 /** Returns the diameter of the enclosing sphere */
    265 public double
    266 getSize() {
    267 	return ((double)size) / 100;
    268 }
    269 
    270 /** Returns the horizontal precision */
    271 public double
    272 getHPrecision() {
    273 	return ((double)hPrecision) / 100;
    274 }
    275 
    276 /** Returns the horizontal precision */
    277 public double
    278 getVPrecision() {
    279 	return ((double)vPrecision) / 100;
    280 }
    281 
    282 void
    283 rrToWire(DNSOutput out, Compression c, boolean canonical) {
    284 	out.writeU8(0); /* version */
    285 	out.writeU8(toLOCformat(size));
    286 	out.writeU8(toLOCformat(hPrecision));
    287 	out.writeU8(toLOCformat(vPrecision));
    288 	out.writeU32(latitude);
    289 	out.writeU32(longitude);
    290 	out.writeU32(altitude);
    291 }
    292 
    293 private static long
    294 parseLOCformat(int b) throws WireParseException {
    295 	long out = b >> 4;
    296 	int exp = b & 0xF;
    297 	if (out > 9 || exp > 9)
    298 		throw new WireParseException("Invalid LOC Encoding");
    299 	while (exp-- > 0)
    300 		out *= 10;
    301 	return (out);
    302 }
    303 
    304 private int
    305 toLOCformat(long l) {
    306 	byte exp = 0;
    307 	while (l > 9) {
    308 		exp++;
    309 		l /= 10;
    310 	}
    311 	return (int)((l << 4) + exp);
    312 }
    313 
    314 }
    315