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