1// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.io.*;
6import java.text.*;
7
8/**
9 * Location - describes the physical location of hosts, networks, subnets.
10 *
11 * @author Brian Wellington
12 */
13
14public class LOCRecord extends Record {
15
16private static final long serialVersionUID = 9058224788126750409L;
17
18private static NumberFormat w2, w3;
19
20private long size, hPrecision, vPrecision;
21private long latitude, longitude, altitude;
22
23static {
24	w2 = new DecimalFormat();
25	w2.setMinimumIntegerDigits(2);
26
27	w3 = new DecimalFormat();
28	w3.setMinimumIntegerDigits(3);
29}
30
31LOCRecord() {}
32
33Record
34getObject() {
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*/
47public
48LOCRecord(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
60void
61rrFromWire(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
76private double
77parseFixedPoint(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
93private long
94parsePosition(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
134private long
135parseDouble(Tokenizer st, String type, boolean required, long min, long max,
136	    long defaultValue)
137throws 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
160void
161rdataFromString(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
173private void
174renderFixedPoint(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
185private String
186positionToString(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 */
215String
216rrToString() {
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 */
247public double
248getLatitude() {
249	return ((double)(latitude - (1L << 31))) / (3600 * 1000);
250}
251
252/** Returns the longitude */
253public double
254getLongitude() {
255	return ((double)(longitude - (1L << 31))) / (3600 * 1000);
256}
257
258/** Returns the altitude */
259public double
260getAltitude() {
261	return ((double)(altitude - 10000000)) / 100;
262}
263
264/** Returns the diameter of the enclosing sphere */
265public double
266getSize() {
267	return ((double)size) / 100;
268}
269
270/** Returns the horizontal precision */
271public double
272getHPrecision() {
273	return ((double)hPrecision) / 100;
274}
275
276/** Returns the horizontal precision */
277public double
278getVPrecision() {
279	return ((double)vPrecision) / 100;
280}
281
282void
283rrToWire(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
293private static long
294parseLOCformat(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
304private int
305toLOCformat(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