001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/pt/AngleFormat.java $ 002 /* 003 * Geotools 2 - OpenSource mapping toolkit 004 * (C) 2003, Geotools Project Managment Committee (PMC) 005 * (C) 2001, Institut de Recherche pour le D�veloppement 006 * (C) 1999, Fisheries and Oceans Canada 007 * 008 * This library is free software; you can redistribute it and/or 009 * modify it under the terms of the GNU Lesser General Public 010 * License as published by the Free Software Foundation; either 011 * version 2.1 of the License, or (at your option) any later version. 012 * 013 * This library is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 016 * Lesser General Public License for more details. 017 * 018 * You should have received a copy of the GNU Lesser General Public 019 * License along with this library; if not, write to the Free Software 020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 021 * 022 * 023 * Contacts: 024 * UNITED KINGDOM: James Macgill 025 * mailto:j.macgill@geog.leeds.ac.uk 026 * 027 * FRANCE: Surveillance de l'Environnement Assist�e par Satellite 028 * Institut de Recherche pour le D�veloppement / US-Espace 029 * mailto:seasnet@teledetection.fr 030 * 031 * CANADA: Observatoire du Saint-Laurent 032 * Institut Maurice-Lamontagne 033 * mailto:osl@osl.gc.ca 034 */ 035 package org.deegree.model.csct.pt; 036 037 // Input/output 038 import java.io.IOException; 039 import java.io.ObjectInputStream; 040 import java.text.DecimalFormat; 041 import java.text.DecimalFormatSymbols; 042 import java.text.FieldPosition; 043 import java.text.Format; 044 import java.text.ParseException; 045 import java.text.ParsePosition; 046 import java.util.Locale; 047 048 import org.deegree.model.csct.resources.Utilities; 049 import org.deegree.model.csct.resources.XMath; 050 import org.deegree.model.csct.resources.css.ResourceKeys; 051 import org.deegree.model.csct.resources.css.Resources; 052 053 054 /** 055 * Parse and format angle according a specified pattern. The pattern is a string 056 * containing any characters, with a special meaning for the following characters: 057 * 058 * <blockquote><table cellpadding="3"> 059 * <tr><td><code>D</code></td><td> The integer part of degrees</td></tr> 060 * <tr><td><code>d</code></td><td> The fractional part of degrees</td></tr> 061 * <tr><td><code>M</code></td><td> The integer part of minutes</td></tr> 062 * <tr><td><code>m</code></td><td> The fractional part of minutes</td></tr> 063 * <tr><td><code>S</code></td><td> The integer part of seconds</td></tr> 064 * <tr><td><code>s</code></td><td> The fractional part of seconds</td></tr> 065 * <tr><td><code>.</code></td><td> The decimal separator</td></tr> 066 * </table></blockquote> 067 * <br> 068 * Upper-case letters <code>D</code>, <code>M</code> and <code>S</code> are for the integer 069 * parts of degrees, minutes and seconds respectively. They must appear in this order (e.g. 070 * "<code>M'D</code>" is illegal because "M" and "S" are inverted; "<code>D�S</code>" is 071 * illegal too because there is no "M" between "D" and "S"). Lower-case letters <code>d</code>, 072 * <code>m</code> and <code>s</code> are for fractional parts of degrees, minutes and seconds 073 * respectively. Only one of those may appears in a pattern, and it must be the last special 074 * symbol (e.g. "<code>D.dd�MM'</code>" is illegal because "d" is followed by "M"; 075 * "<code>D.mm</code>" is illegal because "m" is not the fractional part of "D"). 076 * <br><br> 077 * The number of occurrence of <code>D</code>, <code>M</code>, <code>S</code> and their 078 * lower-case counterpart is the number of digits to format. For example, "DD.ddd" will 079 * format angle with two digits for the integer part and three digits for the fractional 080 * part (e.g. 4.4578 will be formatted as "04.458"). Separator characters like <code>�</code>, 081 * <code>'</code> and <code>"</code> and inserted "as-is" in the formatted string (except the 082 * decimal separator dot ("<code>.</code>"), which is replaced by the local-dependent decimal 083 * separator). Separator characters may be completely omitted; <code>AngleFormat</code> will 084 * still differentiate degrees, minutes and seconds fields according the pattern. For example, 085 * "<code>0480439</code>" with the pattern "<code>DDDMMmm</code>" will be parsed as 48�04.39'. 086 * <br><br> 087 * The following table gives some examples of legal patterns. 088 * 089 * <blockquote><table cellpadding="3"> 090 * <tr><th>Pattern </th> <th>Example </th></tr> 091 * <tr><td><code>DD�MM'SS" </code></td> <td>48�30'00" </td></tr> 092 * <tr><td><code>DD�MM' </code></td> <td>48�30' </td></tr> 093 * <tr><td><code>DD.ddd </code></td> <td>48.500 </td></tr> 094 * <tr><td><code>DDMM </code></td> <td>4830 </td></tr> 095 * <tr><td><code>DDMMSS </code></td> <td>483000 </td></tr> 096 * </table></blockquote> 097 * 098 * @see Angle 099 * @see Latitude 100 * @see Longitude 101 * 102 * @version 1.0 103 * @author Martin Desruisseaux 104 */ 105 public class AngleFormat extends Format { 106 /** 107 * Caract�re repr�sentant l'h�misph�re nord. 108 * Il doit obligatoirement �tre en majuscule. 109 */ 110 private static final char NORTH='N'; 111 112 /** 113 * Caract�re repr�sentant l'h�misph�re sud. 114 * Il doit obligatoirement �tre en majuscule. 115 */ 116 private static final char SOUTH='S'; 117 118 /** 119 * Caract�re repr�sentant l'h�misph�re est. 120 * Il doit obligatoirement �tre en majuscule. 121 */ 122 private static final char EAST='E'; 123 124 /** 125 * Caract�re repr�sentant l'h�misph�re ouest. 126 * Il doit obligatoirement �tre en majuscule. 127 */ 128 private static final char WEST='W'; 129 130 /** 131 * Constante indique que l'angle 132 * � formater est une longitude. 133 */ 134 static final int LONGITUDE=0; 135 136 /** 137 * Constante indique que l'angle 138 * � formater est une latitude. 139 */ 140 static final int LATITUDE=1; 141 142 /** 143 * Constante indique que le nombre 144 * � formater est une altitude. 145 */ 146 static final int ALTITUDE=2; 147 148 /** 149 * A constant for the symbol to appears before the degrees fields. 150 * Fields PREFIX, DEGREES, MINUTES and SECONDS <strong>must</strong> 151 * have increasing values (-1, 0, +1, +2, +3). 152 */ 153 private static final int PREFIX_FIELD = -1; 154 155 /** 156 * Constant for degrees field. When formatting a string, this value may be 157 * specified to the {@link java.text.FieldPosition} constructor in order to 158 * get the bounding index where degrees have been written. 159 */ 160 public static final int DEGREES_FIELD = 0; 161 162 /** 163 * Constant for minutes field. When formatting a string, this value may be 164 * specified to the {@link java.text.FieldPosition} constructor in order to 165 * get the bounding index where minutes have been written. 166 */ 167 public static final int MINUTES_FIELD = 1; 168 169 /** 170 * Constant for seconds field. When formatting a string, this value may be 171 * specified to the {@link java.text.FieldPosition} constructor in order to 172 * get the bounding index where seconds have been written. 173 */ 174 public static final int SECONDS_FIELD = 2; 175 176 /** 177 * Constant for hemisphere field. When formatting a string, this value may be 178 * specified to the {@link java.text.FieldPosition} constructor in order to 179 * get the bounding index where the hemisphere synbol has been written. 180 */ 181 public static final int HEMISPHERE_FIELD = 3; 182 183 /** 184 * Symboles repr�sentant les degr�s (0), 185 * minutes (1) et les secondes (2). 186 */ 187 private static final char[] SYMBOLS = {'D', 'M', 'S'}; 188 189 /** 190 * Nombre minimal d'espaces que doivent occuper les parties 191 * enti�res des degr�s (0), minutes (1) et secondes (2). Le 192 * champs <code>widthDecimal</code> indique la largeur fixe 193 * que doit avoir la partie d�cimale. Il s'appliquera au 194 * dernier champs non-zero dans <code>width0..2</code>. 195 */ 196 private int width0=1, width1=2, width2=0, widthDecimal=0; 197 198 /** 199 * Caract�res � ins�rer au d�but (<code>prefix</code>) et � la 200 * suite des degr�s, minutes et secondes (<code>suffix0..2</code>). 201 * Ces champs doivent �tre <code>null</code> s'il n'y a rien � ins�rer. 202 */ 203 private String prefix=null, suffix0="�", suffix1="'", suffix2="\""; 204 205 /** 206 * Indique s'il faut utiliser le s�parateur d�cimal pour s�parer la partie 207 * enti�re de la partie fractionnaire. La valeur <code>false</code> indique 208 * que les parties enti�res et fractionnaires doivent �tre �crites ensembles 209 * (par exemple 34867 pour 34.867). La valeur par d�faut est <code>true</code>. 210 */ 211 private boolean decimalSeparator=true; 212 213 /** 214 * Format � utiliser pour �crire les nombres 215 * (degr�s, minutes ou secondes) � l'int�rieur 216 * de l'�criture d'un angle. 217 */ 218 private final DecimalFormat numberFormat; 219 220 /** 221 * Objet � transmetre aux m�thodes <code>DecimalFormat.format</code>. 222 * Ce param�tre existe simplement pour �viter de cr�er cet objet trop 223 * souvent, alors qu'on ne s'y int�resse pas. 224 */ 225 private transient FieldPosition dummy = new FieldPosition(0); 226 227 /** 228 * Restore fields after deserialization. 229 */ 230 private void readObject(final ObjectInputStream in) 231 throws IOException, ClassNotFoundException 232 { 233 in.defaultReadObject(); 234 dummy = new FieldPosition(0); 235 } 236 237 /** 238 * Returns the width of the specified field. 239 */ 240 private int getWidth(final int index) { 241 switch (index) { 242 case DEGREES_FIELD: return width0; 243 case MINUTES_FIELD: return width1; 244 case SECONDS_FIELD: return width2; 245 default: return 0; // Must be 0 (important!) 246 } 247 } 248 249 /** 250 * Set the width for the specified field. 251 * All folowing fields will be set to 0. 252 */ 253 private void setWidth(final int index, int width) { 254 switch (index) { 255 case DEGREES_FIELD: width0=width; width=0; // fall through 256 case MINUTES_FIELD: width1=width; width=0; // fall through 257 case SECONDS_FIELD: width2=width; // fall through 258 } 259 } 260 261 /** 262 * Returns the suffix for the specified field. 263 */ 264 private String getSuffix(final int index) { 265 switch (index) { 266 case PREFIX_FIELD: return prefix; 267 case DEGREES_FIELD: return suffix0; 268 case MINUTES_FIELD: return suffix1; 269 case SECONDS_FIELD: return suffix2; 270 default: return null; 271 } 272 } 273 274 /** 275 * Set the suffix for the specified field. Suffix 276 * for all following fields will be set to their 277 * default value. 278 */ 279 private void setSuffix(final int index, String s) { 280 switch (index) { 281 case PREFIX_FIELD: prefix=s; s="�"; // fall through 282 case DEGREES_FIELD: suffix0=s; s="'"; // fall through 283 case MINUTES_FIELD: suffix1=s; s="\""; // fall through 284 case SECONDS_FIELD: suffix2=s; // fall through 285 } 286 } 287 288 /** 289 * Construct a new <code>AngleFormat</code> using 290 * the current default locale and a default pattern. 291 */ 292 public AngleFormat() { 293 this("D�MM.m'"); 294 } 295 296 /** 297 * Construct a new <code>AngleFormat</code> using the 298 * current default locale and the specified pattern. 299 * 300 * @param pattern Pattern to use for parsing and formatting angle. 301 * See class description for an explanation of how this pattern work. 302 * @throws IllegalArgumentException If the specified pattern is not legal. 303 */ 304 public AngleFormat(final String pattern) throws IllegalArgumentException { 305 this(pattern, new DecimalFormatSymbols()); 306 } 307 308 /** 309 * Construct a new <code>AngleFormat</code> 310 * using the specified pattern and locale. 311 * 312 * @param pattern Pattern to use for parsing and formatting angle. 313 * See class description for an explanation of how this pattern work. 314 * @param locale Locale to use. 315 * @throws IllegalArgumentException If the specified pattern is not legal. 316 */ 317 public AngleFormat(final String pattern, final Locale locale) throws IllegalArgumentException { 318 this(pattern, new DecimalFormatSymbols(locale)); 319 } 320 321 /** 322 * Construct a new <code>AngleFormat</code> 323 * using the specified pattern and decimal symbols. 324 * 325 * @param pattern Pattern to use for parsing and formatting angle. 326 * See class description for an explanation of how this pattern work. 327 * @param symbols The symbols to use for parsing and formatting numbers. 328 * @throws IllegalArgumentException If the specified pattern is not legal. 329 */ 330 public AngleFormat(final String pattern, final DecimalFormatSymbols symbols) { 331 // NOTE: pour cette routine, il ne faut PAS que DecimalFormat 332 // reconnaisse la notation exponentielle, parce que �a 333 // risquerait d'�tre confondu avec le "E" de "Est". 334 numberFormat=new DecimalFormat("#0", symbols); 335 applyPattern(pattern); 336 } 337 338 /** 339 * Set the pattern to use for parsing and formatting angle. 340 * See class description for an explanation of how patterns work. 341 * 342 * @param pattern Pattern to use for parsing and formatting angle. 343 * @throws IllegalArgumentException If the specified pattern is not legal. 344 */ 345 public synchronized void applyPattern(final String pattern) throws IllegalArgumentException { 346 widthDecimal = 0; 347 decimalSeparator = true; 348 int startPrefix = 0; 349 int symbolIndex = 0; 350 boolean parseFinished = false; 351 final int length = pattern.length(); 352 for (int i=0; i<length; i++) { 353 /* 354 * On examine un � un tous les caract�res du patron en 355 * sautant ceux qui ne sont pas r�serv�s ("D", "M", "S" 356 * et leur �quivalents en minuscules). Les caract�res 357 * non-reserv�s seront m�moris�s comme suffix plus tard. 358 */ 359 final char c = pattern.charAt(i); 360 final char upperCaseC = Character.toUpperCase(c); 361 for (int field=DEGREES_FIELD; field<SYMBOLS.length; field++) { 362 if (upperCaseC == SYMBOLS[field]) { 363 /* 364 * Un caract�re r�serv� a �t� trouv�. V�rifie maintenant 365 * s'il est valide. Par exemple il serait illegal d'avoir 366 * comme patron "MM.mm" sans qu'il soit pr�c�d� des degr�s. 367 * On attend les lettres "D", "M" et "S" dans l'ordre. Si 368 * le caract�re est en lettres minuscules, il doit �tre le 369 * m�me que le dernier code (par exemple "DD.mm" est illegal). 370 */ 371 if (c==upperCaseC) { 372 symbolIndex++; 373 } 374 if (field!=symbolIndex-1 || parseFinished) { 375 setWidth(DEGREES_FIELD, 1); 376 setSuffix(PREFIX_FIELD, null); 377 widthDecimal=0; 378 decimalSeparator=true; 379 throw new IllegalArgumentException(Resources.format(ResourceKeys.ERROR_ILLEGAL_ANGLE_PATTERN_$1, pattern)); 380 } 381 if (c==upperCaseC) { 382 /* 383 * M�morise les caract�res qui pr�c�daient ce code comme suffix 384 * du champs pr�c�dent. Puis on comptera le nombre de fois que le 385 * code se r�p�te, en m�morisant cette information comme largeur 386 * de ce champ. 387 */ 388 setSuffix(field-1, (i>startPrefix) ? pattern.substring(startPrefix, i) : null); 389 int w=1; while (++i<length && pattern.charAt(i)==c) w++; 390 setWidth(field, w); 391 } else { 392 /* 393 * Si le caract�re est une minuscule, ce qui le pr�c�dait sera le 394 * s�parateur d�cimal plut�t qu'un suffix. On comptera le nombre 395 * d'occurences du caract�res pour obtenir la pr�cision. 396 */ 397 switch (i-startPrefix) { 398 case 0: decimalSeparator=false; break; 399 case 1: if (pattern.charAt(startPrefix)=='.') { 400 decimalSeparator=true; 401 break; 402 } 403 default: throw new IllegalArgumentException(Resources.format( 404 ResourceKeys.ERROR_ILLEGAL_ANGLE_PATTERN_$1, pattern)); 405 } 406 int w=1; while (++i<length && pattern.charAt(i)==c) w++; 407 widthDecimal=w; 408 parseFinished=true; 409 } 410 startPrefix = i--; 411 break; // Break 'j' and continue 'i'. 412 } 413 } 414 } 415 setSuffix(symbolIndex-1, (startPrefix<length) ? pattern.substring(startPrefix) : null); 416 } 417 418 /** 419 * Returns the pattern used for parsing and formatting angles. 420 * See class description for an explanation of how patterns work. 421 */ 422 public synchronized String toPattern() { 423 char symbol='#'; 424 final StringBuffer buffer=new StringBuffer(); 425 for (int field=DEGREES_FIELD; field<=SYMBOLS.length; field++) { 426 final String previousSuffix=getSuffix(field-1); 427 int w=getWidth(field); 428 if (w>0) { 429 /* 430 * Proc�de � l'�criture de la partie enti�re des degr�s, 431 * minutes ou secondes. Le suffix du champs pr�c�dent 432 * sera �crit avant les degr�s, minutes ou secondes. 433 */ 434 if (previousSuffix!=null) { 435 buffer.append(previousSuffix); 436 } 437 symbol=SYMBOLS[field]; 438 do buffer.append(symbol); 439 while (--w>0); 440 } else { 441 /* 442 * Proc�de � l'�criture de la partie d�cimale des 443 * degr�s, minutes ou secondes. Le suffix du ce 444 * champs sera �crit apr�s cette partie fractionnaire. 445 */ 446 w=widthDecimal; 447 if (w>0) { 448 if (decimalSeparator) buffer.append('.'); 449 symbol=Character.toLowerCase(symbol); 450 do buffer.append(symbol); 451 while (--w>0); 452 } 453 if (previousSuffix!=null) { 454 buffer.append(previousSuffix); 455 } 456 break; 457 } 458 } 459 return buffer.toString(); 460 } 461 462 /** 463 * Format an angle. The string will be formatted according 464 * the pattern set in the last call to {@link #applyPattern}. 465 * 466 * @param angle Angle to format, in degrees. 467 * @return The formatted string. 468 */ 469 public final String format(final double angle) { 470 return format(angle, new StringBuffer(), null).toString(); 471 } 472 473 /** 474 * Formats an angle and appends the resulting text to a given string buffer. 475 * The string will be formatted according the pattern set in the last call 476 * to {@link #applyPattern}. 477 * 478 * @param angle Angle to format, in degrees. 479 * @param toAppendTo Where the text is to be appended. 480 * @param pos An optional {@link FieldPosition} identifying a field 481 * in the formatted text, or <code>null</code> if this 482 * information is not wanted. This field position shall 483 * be constructed with one of the following constants: 484 * {@link #DEGREES_FIELD}, 485 * {@link #MINUTES_FIELD}, 486 * {@link #SECONDS_FIELD} or 487 * {@link #HEMISPHERE_FIELD}. 488 * 489 * @return The string buffer passed in as <code>toAppendTo</code>, with formatted text appended. 490 */ 491 public synchronized StringBuffer format(final double angle, 492 StringBuffer toAppendTo, 493 final FieldPosition pos) 494 { 495 double degrees = angle; 496 /* 497 * Calcule � l'avance les minutes et les secondes. Si les minutes et secondes 498 * ne doivent pas �tre �crits, on m�morisera NaN. Notez que pour extraire les 499 * parties enti�res, on utilise (int) au lieu de 'Math.floor' car (int) arrondie 500 * vers 0 (ce qui est le comportement souhait�) alors que 'floor' arrondie vers 501 * l'entier inf�rieur. 502 */ 503 double minutes = Double.NaN; 504 double secondes = Double.NaN; 505 if (width1!=0 && !Double.isNaN(angle)) { 506 int tmp = (int) degrees; // Arrondie vers 0 m�me si n�gatif. 507 minutes = Math.abs(degrees-tmp)*60; 508 degrees = tmp; 509 if (minutes<0 || minutes>60) { 510 // Erreur d'arrondissement (parce que l'angle est trop �lev�) 511 throw new IllegalArgumentException(Resources.format(ResourceKeys.ERROR_ANGLE_OVERFLOW_$1, new Double(angle))); 512 } 513 if (width2 != 0) { 514 tmp = (int) minutes; // Arrondie vers 0 m�me si n�gatif. 515 secondes = (minutes-tmp)*60; 516 minutes = tmp; 517 if (secondes<0 || secondes>60) { 518 // Erreur d'arrondissement (parce que l'angle est trop �lev�) 519 throw new IllegalArgumentException(Resources.format(ResourceKeys.ERROR_ANGLE_OVERFLOW_$1, new Double(angle))); 520 } 521 /* 522 * On applique maintenant une correction qui tiendra 523 * compte des probl�mes d'arrondissements. 524 */ 525 final double puissance=XMath.pow10(widthDecimal); 526 secondes=Math.rint(secondes*puissance)/puissance; 527 tmp = (int) (secondes/60); 528 secondes -= 60*tmp; 529 minutes += tmp; 530 } else { 531 final double puissance=XMath.pow10(widthDecimal); 532 minutes = Math.rint(minutes*puissance)/puissance; 533 } 534 tmp = (int) (minutes/60); // Arrondie vers 0 m�me si n�gatif. 535 minutes -= 60*tmp; 536 degrees += tmp; 537 } 538 /* 539 * Les variables 'degr�s', 'minutes' et 'secondes' contiennent 540 * maintenant les valeurs des champs � �crire, en principe �pur�s 541 * des probl�mes d'arrondissements. Proc�de maintenant � l'�criture 542 * de l'angle. 543 */ 544 if (prefix != null) { 545 toAppendTo.append(prefix); 546 } 547 final int field; 548 if (pos != null) { 549 field = pos.getField(); 550 pos.setBeginIndex(0); 551 pos.setEndIndex(0); 552 } else { 553 field=PREFIX_FIELD; 554 } 555 toAppendTo = formatField(degrees, toAppendTo, 556 field==DEGREES_FIELD ? pos : null, 557 width0, width1==0, suffix0); 558 if (!Double.isNaN(minutes)) { 559 toAppendTo=formatField(minutes, toAppendTo, 560 field==MINUTES_FIELD ? pos : null, 561 width1, width2==0, suffix1); 562 } 563 if (!Double.isNaN(secondes)) { 564 toAppendTo=formatField(secondes, toAppendTo, 565 field==SECONDS_FIELD ? pos : null, 566 width2, true, suffix2); 567 } 568 return toAppendTo; 569 } 570 571 /** 572 * Proc�de � l'�criture d'un champ de l'angle. 573 * 574 * @param value Valeur � �crire. 575 * @param toAppendTo Buffer dans lequel �crire le champs. 576 * @param pos Objet dans lequel m�moriser les index des premiers 577 * et derniers caract�res �crits, ou <code>null</code> 578 * pour ne pas m�moriser ces index. 579 * @param w Nombre de minimal caract�res de la partie enti�re. 580 * @param last <code>true</code> si ce champs est le dernier, 581 * et qu'il faut donc �crire la partie d�cimale. 582 * @param s Suffix � �crire apr�s le nombre (peut �tre nul). 583 */ 584 private StringBuffer formatField(double value, 585 StringBuffer toAppendTo, final FieldPosition pos, 586 final int w, final boolean last, final String s) 587 { 588 final int startPosition=toAppendTo.length(); 589 if (!last) { 590 numberFormat.setMinimumIntegerDigits(w); 591 numberFormat.setMaximumFractionDigits(0); 592 toAppendTo = numberFormat.format(value, toAppendTo, dummy); 593 } else if (decimalSeparator) { 594 numberFormat.setMinimumIntegerDigits(w); 595 numberFormat.setMinimumFractionDigits(widthDecimal); 596 numberFormat.setMaximumFractionDigits(widthDecimal); 597 toAppendTo = numberFormat.format(value, toAppendTo, dummy); 598 } else { 599 value *= XMath.pow10(widthDecimal); 600 numberFormat.setMaximumFractionDigits(0); 601 numberFormat.setMinimumIntegerDigits(w+widthDecimal); 602 toAppendTo = numberFormat.format(value, toAppendTo, dummy); 603 } 604 if (s!=null) { 605 toAppendTo.append(s); 606 } 607 if (pos!=null) { 608 pos.setBeginIndex(startPosition); 609 pos.setEndIndex(toAppendTo.length()-1); 610 } 611 return toAppendTo; 612 } 613 614 /** 615 * Formats an angle, a latitude or a longitude and appends the resulting text 616 * to a given string buffer. The string will be formatted according the pattern 617 * set in the last call to {@link #applyPattern}. The argument <code>obj</code> 618 * shall be an {@link Angle} object or one of its derived class ({@link Latitude}, 619 * {@link Longitude}). If <code>obj</code> is a {@link Latitude} object, then a 620 * symbol "N" or "S" will be appended to the end of the string (the symbol will 621 * be choosen according the angle's sign). Otherwise, if <code>obj</code> is a 622 * {@link Longitude} object, then a symbol "E" or "W" will be appended to the 623 * end of the string. Otherwise, no hemisphere symbol will be appended. 624 * <br><br> 625 * Strictly speaking, formatting ordinary numbers is not the 626 * <code>AngleFormat</code>'s job. Nevertheless, this method 627 * accept {@link Number} objects. This capability is provided 628 * only as a convenient way to format altitude numbers together 629 * with longitude and latitude angles. 630 * 631 * @param obj {@link Angle} or {@link Number} object to format. 632 * @param toAppendTo Where the text is to be appended. 633 * @param pos An optional {@link FieldPosition} identifying a field 634 * in the formatted text, or <code>null</code> if this 635 * information is not wanted. This field position shall 636 * be constructed with one of the following constants: 637 * {@link #DEGREES_FIELD}, 638 * {@link #MINUTES_FIELD}, 639 * {@link #SECONDS_FIELD} or 640 * {@link #HEMISPHERE_FIELD}. 641 * 642 * @return The string buffer passed in as <code>toAppendTo</code>, with 643 * formatted text appended. 644 * @throws IllegalArgumentException if <code>obj</code> if not an object 645 * of class {@link Angle} or {@link Number}. 646 */ 647 public synchronized StringBuffer format(final Object obj, 648 StringBuffer toAppendTo, 649 final FieldPosition pos) 650 throws IllegalArgumentException 651 { 652 if (obj instanceof Latitude) { 653 return format(((Latitude) obj).degrees(), toAppendTo, pos, NORTH, SOUTH); 654 } 655 if (obj instanceof Longitude) { 656 return format(((Longitude) obj).degrees(), toAppendTo, pos, EAST, WEST); 657 } 658 if (obj instanceof Angle) { 659 return format(((Angle) obj).degrees(), toAppendTo, pos); 660 } 661 if (obj instanceof Number) { 662 numberFormat.setMinimumIntegerDigits(1); 663 numberFormat.setMinimumFractionDigits(0); 664 numberFormat.setMaximumFractionDigits(2); 665 return numberFormat.format(obj, toAppendTo, (pos!=null) ? pos : dummy); 666 } 667 throw new IllegalArgumentException(Resources.format( 668 ResourceKeys.ERROR_NOT_AN_ANGLE_OBJECT_$1, 669 Utilities.getShortClassName(obj))); 670 } 671 672 /** 673 * Proc�de � l'�criture d'un angle, d'une latitude ou d'une longitude. 674 * 675 * @param type Type de l'angle ou du nombre: 676 * {@link #LONGITUDE}, 677 * {@link #LATITUDE} ou 678 * {@link #ALTITUDE}. 679 * @param toAppendTo Buffer dans lequel �crire l'angle. 680 * @param pos En entr�, le code du champs dont on d�sire les index 681 * ({@link #DEGREES_FIELD}, 682 * {@link #MINUTES_FIELD}, 683 * {@link #SECONDS_FIELD} ou 684 * {@link #HEMISPHERE_FIELD}). 685 * En sortie, les index du champs demand�. Ce param�tre 686 * peut �tre nul si cette information n'est pas d�sir�e. 687 * 688 * @return Le buffer <code>toAppendTo</code> par commodit�. 689 */ 690 synchronized StringBuffer format(final double number, final int type, 691 StringBuffer toAppendTo, 692 final FieldPosition pos) 693 { 694 switch (type) { 695 default: throw new IllegalArgumentException(Integer.toString(type)); // Should not happen. 696 case LATITUDE: return format(number, toAppendTo, pos, NORTH, SOUTH); 697 case LONGITUDE: return format(number, toAppendTo, pos, EAST, WEST ); 698 case ALTITUDE: { 699 numberFormat.setMinimumIntegerDigits(1); 700 numberFormat.setMinimumFractionDigits(0); 701 numberFormat.setMaximumFractionDigits(2); 702 return numberFormat.format(number, toAppendTo, (pos!=null) ? pos : dummy); 703 } 704 } 705 } 706 707 /** 708 * Proc�de � l'�criture d'un angle suivit d'un suffix 'N','S','E' ou 'W'. 709 * L'angle sera format� en utilisant comme mod�le le patron sp�cifi� lors 710 * du dernier appel de la m�thode {@link #applyPattern}. 711 * 712 * @param angle Angle � �crire, en degr�s. 713 * @param toAppendTo Buffer dans lequel �crire l'angle. 714 * @param pos En entr�, le code du champs dont on d�sire les index 715 * ({@link #DEGREES_FIELD}, 716 * {@link #MINUTES_FIELD}, 717 * {@link #SECONDS_FIELD} ou 718 * {@link #HEMISPHERE_FIELD}). 719 * En sortie, les index du champs demand�. Ce param�tre 720 * peut �tre nul si cette information n'est pas d�sir�e. 721 * @param north Caract�res � �crire si l'angle est positif ou nul. 722 * @param south Caract�res � �crire si l'angle est n�gatif. 723 * 724 * @return Le buffer <code>toAppendTo</code> par commodit�. 725 */ 726 private StringBuffer format(final double angle, 727 StringBuffer toAppendTo, 728 final FieldPosition pos, 729 final char north, final char south) 730 { 731 toAppendTo = format(Math.abs(angle), toAppendTo, pos); 732 final int start = toAppendTo.length(); 733 toAppendTo.append(angle<0 ? south : north); 734 if (pos!=null && pos.getField()==HEMISPHERE_FIELD) { 735 pos.setBeginIndex(start); 736 pos.setEndIndex(toAppendTo.length()-1); 737 } 738 return toAppendTo; 739 } 740 741 /** 742 * Ignore le suffix d'un nombre. Cette m�thode est appell�e par la m�thode 743 * {@link #parse} pour savoir quel champs il vient de lire. Par exemple si 744 * l'on vient de lire les degr�s dans "48�12'", alors cette m�thode extraira 745 * le "�" et retournera 0 pour indiquer que l'on vient de lire des degr�s. 746 * 747 * Cette m�thode se chargera d'ignorer les espaces qui pr�c�dent le suffix. 748 * Elle tentera ensuite de d'abord interpr�ter le suffix selon les symboles 749 * du patron (sp�cifi� avec {@link #applyPattern}. Si le suffix n'a pas �t� 750 * reconnus, elle tentera ensuite de le comparer aux symboles standards 751 * (� ' "). 752 * 753 * @param source Cha�ne dans laquelle on doit sauter le suffix. 754 * @param pos En entr�, l'index du premier caract�re � consid�rer dans la 755 * cha�ne <code>pos</code>. En sortie, l'index du premier caract�re 756 * suivant le suffix (c'est-�-dire index � partir d'o� continuer la 757 * lecture apr�s l'appel de cette m�thode). Si le suffix n'a pas �t� 758 * reconnu, alors cette m�thode retourne par convention <code>SYMBOLS.length</code>. 759 * @param field Champs � v�rifier de pr�f�rences. Par exemple la valeur 1 signifie que les 760 * suffix des minutes et des secondes devront �tre v�rifi�s avant celui des degr�s. 761 * @return Le num�ro du champs correspondant au suffix qui vient d'�tre extrait: 762 * -1 pour le pr�fix de l'angle, 0 pour le suffix des degr�s, 1 pour le 763 * suffix des minutes et 2 pour le suffix des secondes. Si le texte n'a 764 * pas �t� reconnu, retourne <code>SYMBOLS.length</code>. 765 */ 766 private int skipSuffix(final String source, final ParsePosition pos, int field) { 767 /* 768 * Essaie d'abord de sauter les suffix qui 769 * avaient �t� sp�cifi�s dans le patron. 770 */ 771 final int length=source.length(); 772 int start=pos.getIndex(); 773 for (int j=SYMBOLS.length; j>=0; j--) { // C'est bien j>=0 et non j>0. 774 int index=start; 775 final String toSkip=getSuffix(field); 776 if (toSkip!=null) { 777 final int toSkipLength=toSkip.length(); 778 do { 779 if (source.regionMatches(index, toSkip, 0, toSkipLength)) { 780 pos.setIndex(index+toSkipLength); 781 return field; 782 } 783 } 784 while (index<length && Character.isSpaceChar(source.charAt(index++))); 785 } 786 if (++field >= SYMBOLS.length) field=-1; 787 } 788 /* 789 * Le texte trouv� ne correspondant � aucun suffix du patron, 790 * essaie maintenant de sauter un des suffix standards (apr�s 791 * avoir ignor� les espaces qui le pr�c�daient). 792 */ 793 char c; 794 do { 795 if (start>=length) { 796 return SYMBOLS.length; 797 } 798 } 799 while (Character.isSpaceChar(c=source.charAt(start++))); 800 switch (c) { 801 case '�' : pos.setIndex(start); return DEGREES_FIELD; 802 case '\'': pos.setIndex(start); return MINUTES_FIELD; 803 case '"' : pos.setIndex(start); return SECONDS_FIELD; 804 default : return SYMBOLS.length; // Unknow field. 805 } 806 } 807 808 /** 809 * Parse a string as an angle. This method can parse an angle even if it 810 * doesn't comply exactly to the expected pattern. For example, this method 811 * will parse correctly string "<code>48�12.34'</code>" even if the expected 812 * pattern was "<code>DDMM.mm</code>" (i.e. the string should have been 813 * "<code>4812.34</code>"). Spaces between degrees, minutes and secondes 814 * are ignored. If the string ends with an hemisphere symbol "N" or "S", 815 * then this method returns an object of class {@link Latitude}. Otherwise, 816 * if the string ends with an hemisphere symbol "E" or "W", then this method 817 * returns an object of class {@link Longitude}. Otherwise, this method 818 * returns an object of class {@link Angle}. 819 * 820 * @param source A String whose beginning should be parsed. 821 * @param pos Position where to start parsing. 822 * @return The parsed string as an {@link Angle}, {@link Latitude} 823 * or {@link Longitude} object. 824 */ 825 public Angle parse(final String source, final ParsePosition pos) { 826 return parse(source, pos, false); 827 } 828 829 /** 830 * Interpr�te une cha�ne de caract�res repr�sentant un angle. Les r�gles 831 * d'interpr�tation de cette m�thode sont assez souples. Par exemple cettte 832 * m�thode interpr�tera correctement la cha�ne "48�12.34'" m�me si le patron 833 * attendu �tait "DDMM.mm" (c'est-�-dire que la cha�ne aurait du �tre "4812.34"). 834 * Les espaces entre les degr�s, minutes et secondes sont accept�s. Si l'angle 835 * est suivit d'un symbole "N" ou "S", alors l'objet retourn� sera de la classe 836 * {@link Latitude}. S'il est plutot suivit d'un symbole "E" ou "W", alors l'objet 837 * retourn� sera de la classe {@link Longitude}. Sinon, il sera de la classe 838 * {@link Angle}. 839 * 840 * @param source Cha�ne de caract�res � lire. 841 * @param pos Position � partir d'o� interpr�ter la cha�ne. 842 * @param spaceAsSeparator Indique si l'espace est accept� comme s�parateur 843 * � l'int�rieur d'un angle. La valeur <code>true</code> 844 * fait que l'angle "45 30" sera interpr�t� comme "45�30". 845 * @return L'angle lu. 846 */ 847 private synchronized Angle parse(final String source, 848 final ParsePosition pos, 849 final boolean spaceAsSeparator) 850 { 851 double degrees = Double.NaN; 852 double minutes = Double.NaN; 853 double secondes = Double.NaN; 854 final int length=source.length(); 855 /////////////////////////////////////////////////////////////////////////////// 856 // BLOC A: Analyse la cha�ne de caract�res 'source' et affecte aux variables // 857 // 'degr�s', 'minutes' et 'secondes' les valeurs appropri�es. // 858 // Les premi�res accolades ne servent qu'� garder locales // 859 // les variables sans int�r�t une fois la lecture termin�e. // 860 /////////////////////////////////////////////////////////////////////////////// 861 { 862 /* 863 * Extrait le pr�fix, s'il y en avait un. Si on tombe sur un symbole des 864 * degr�s, minutes ou secondes alors qu'on n'a pas encore lu de nombre, 865 * on consid�rera que la lecture a �chou�e. 866 */ 867 final int indexStart=pos.getIndex(); 868 int index=skipSuffix(source, pos, PREFIX_FIELD); 869 if (index>=0 && index<SYMBOLS.length) { 870 pos.setErrorIndex(indexStart); 871 pos.setIndex(indexStart); 872 return null; 873 } 874 /* 875 * Saute les espaces blancs qui 876 * pr�c�dent le champs des degr�s. 877 */ 878 index=pos.getIndex(); 879 while (index<length && Character.isSpaceChar(source.charAt(index))) index++; 880 pos.setIndex(index); 881 /* 882 * Lit les degr�s. Notez que si aucun s�parateur ne s�parait les degr�s 883 * des minutes des secondes, alors cette lecture pourra inclure plusieurs 884 * champs (exemple: "DDDMMmmm"). La s�paration sera faite plus tard. 885 */ 886 Number fieldObject=numberFormat.parse(source, pos); 887 if (fieldObject==null) { 888 pos.setIndex(indexStart); 889 if (pos.getErrorIndex()<indexStart) { 890 pos.setErrorIndex(index); 891 } 892 return null; 893 } 894 degrees=fieldObject.doubleValue(); 895 int indexEndField=pos.getIndex(); 896 boolean swapDM=true; 897 BigBoss: switch (skipSuffix(source, pos, DEGREES_FIELD)) { 898 /* ---------------------------------------------- 899 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�S DEGR�S 900 * ---------------------------------------------- 901 * Les degr�s �taient suivit du pr�fix d'un autre angle. Le pr�fix sera donc 902 * retourn� dans le buffer pour un �ventuel traitement par le prochain appel 903 * � la m�thode 'parse' et on n'ira pas plus loin dans l'analyse de la cha�ne. 904 */ 905 case PREFIX_FIELD: { 906 pos.setIndex(indexEndField); 907 break BigBoss; 908 } 909 /* ---------------------------------------------- 910 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�S DEGR�S 911 * ---------------------------------------------- 912 * On a trouv� le symbole des secondes au lieu de celui des degr�s. On fait 913 * la correction dans les variables 'degr�s' et 'secondes' et on consid�re 914 * que la lecture est termin�e. 915 */ 916 case SECONDS_FIELD: { 917 secondes = degrees; 918 degrees = Double.NaN; 919 break BigBoss; 920 } 921 /* ---------------------------------------------- 922 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�S DEGR�S 923 * ---------------------------------------------- 924 * Aucun symbole ne suit les degr�s. Des minutes sont-elles attendues? 925 * Si oui, on fera comme si le symbole des degr�s avait �t� l�. Sinon, 926 * on consid�rera que la lecture est termin�e. 927 */ 928 default: { 929 if (width1==0) break BigBoss; 930 if (!spaceAsSeparator) break BigBoss; 931 // fall through 932 } 933 /* ---------------------------------------------- 934 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�S DEGR�S 935 * ---------------------------------------------- 936 * Un symbole des degr�s a �t� explicitement trouv�. Les degr�s sont peut-�tre 937 * suivit des minutes. On proc�dera donc � la lecture du prochain nombre, puis 938 * � l'analyse du symbole qui le suit. 939 */ 940 case DEGREES_FIELD: { 941 final int indexStartField = index = pos.getIndex(); 942 while (index<length && Character.isSpaceChar(source.charAt(index))) { 943 index++; 944 } 945 if (!spaceAsSeparator && index!=indexStartField) { 946 break BigBoss; 947 } 948 pos.setIndex(index); 949 fieldObject=numberFormat.parse(source, pos); 950 if (fieldObject==null) { 951 pos.setIndex(indexStartField); 952 break BigBoss; 953 } 954 indexEndField = pos.getIndex(); 955 minutes = fieldObject.doubleValue(); 956 switch (skipSuffix(source, pos, (width1!=0) ? MINUTES_FIELD : PREFIX_FIELD)) { 957 /* ------------------------------------------------ 958 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES MINUTES 959 * ------------------------------------------------ 960 * Le symbole trouv� est bel et bien celui des minutes. 961 * On continuera le bloc pour tenter de lire les secondes. 962 */ 963 case MINUTES_FIELD: { 964 break; // continue outer switch 965 } 966 /* ------------------------------------------------ 967 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES MINUTES 968 * ------------------------------------------------ 969 * Un symbole des secondes a �t� trouv� au lieu du symbole des minutes 970 * attendu. On fera la modification dans les variables 'secondes' et 971 * 'minutes' et on consid�rera la lecture termin�e. 972 */ 973 case SECONDS_FIELD: { 974 secondes = minutes; 975 minutes = Double.NaN; 976 break BigBoss; 977 } 978 /* ------------------------------------------------ 979 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES MINUTES 980 * ------------------------------------------------ 981 * Aucun symbole n'a �t� trouv�. Les minutes �taient-elles attendues? 982 * Si oui, on les acceptera et on tentera de lire les secondes. Si non, 983 * on retourne le texte lu dans le buffer et on termine la lecture. 984 */ 985 default: { 986 if (width1!=0) break; // Continue outer switch 987 // fall through 988 } 989 /* ------------------------------------------------ 990 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES MINUTES 991 * ------------------------------------------------ 992 * Au lieu des minutes, le symbole lu est celui des degr�s. On consid�re 993 * qu'il appartient au prochain angle. On retournera donc le texte lu dans 994 * le buffer et on terminera la lecture. 995 */ 996 case DEGREES_FIELD: { 997 pos.setIndex(indexStartField); 998 minutes=Double.NaN; 999 break BigBoss; 1000 } 1001 /* ------------------------------------------------ 1002 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES MINUTES 1003 * ------------------------------------------------ 1004 * Apr�s les minutes (qu'on accepte), on a trouv� le pr�fix du prochain 1005 * angle � lire. On retourne ce pr�fix dans le buffer et on consid�re la 1006 * lecture termin�e. 1007 */ 1008 case PREFIX_FIELD: { 1009 pos.setIndex(indexEndField); 1010 break BigBoss; 1011 } 1012 } 1013 swapDM=false; 1014 // fall through 1015 } 1016 /* ---------------------------------------------- 1017 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�S DEGR�S 1018 * ---------------------------------------------- 1019 * Un symbole des minutes a �t� trouv� au lieu du symbole des degr�s attendu. 1020 * On fera donc la modification dans les variables 'degr�s' et 'minutes'. Ces 1021 * minutes sont peut-�tre suivies des secondes. On tentera donc de lire le 1022 * prochain nombre. 1023 */ 1024 case MINUTES_FIELD: { 1025 if (swapDM) { 1026 minutes = degrees; 1027 degrees = Double.NaN; 1028 } 1029 final int indexStartField = index = pos.getIndex(); 1030 while (index<length && Character.isSpaceChar(source.charAt(index))) { 1031 index++; 1032 } 1033 if (!spaceAsSeparator && index!=indexStartField) { 1034 break BigBoss; 1035 } 1036 pos.setIndex(index); 1037 fieldObject=numberFormat.parse(source, pos); 1038 if (fieldObject==null) { 1039 pos.setIndex(indexStartField); 1040 break; 1041 } 1042 indexEndField = pos.getIndex(); 1043 secondes = fieldObject.doubleValue(); 1044 switch (skipSuffix(source, pos, (width2!=0) ? MINUTES_FIELD : PREFIX_FIELD)) { 1045 /* ------------------------------------------------- 1046 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES SECONDES 1047 * ------------------------------------------------- 1048 * Un symbole des secondes explicite a �t� trouv�e. 1049 * La lecture est donc termin�e. 1050 */ 1051 case SECONDS_FIELD: { 1052 break; 1053 } 1054 /* ------------------------------------------------- 1055 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES SECONDES 1056 * ------------------------------------------------- 1057 * Aucun symbole n'a �t� trouv�e. Attendait-on des secondes? Si oui, les 1058 * secondes seront accept�es. Sinon, elles seront retourn�es au buffer. 1059 */ 1060 default: { 1061 if (width2!=0) break; 1062 // fall through 1063 } 1064 /* ------------------------------------------------- 1065 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES SECONDES 1066 * ------------------------------------------------- 1067 * Au lieu des degr�s, on a trouv� un symbole des minutes ou des 1068 * secondes. On renvoie donc le nombre et son symbole dans le buffer. 1069 */ 1070 case MINUTES_FIELD: 1071 case DEGREES_FIELD: { 1072 pos.setIndex(indexStartField); 1073 secondes=Double.NaN; 1074 break; 1075 } 1076 /* ------------------------------------------------- 1077 * ANALYSE DU SYMBOLE SUIVANT LES PR�SUM�ES SECONDES 1078 * ------------------------------------------------- 1079 * Apr�s les secondes (qu'on accepte), on a trouv� le pr�fix du prochain 1080 * angle � lire. On retourne ce pr�fix dans le buffer et on consid�re la 1081 * lecture termin�e. 1082 */ 1083 case PREFIX_FIELD: { 1084 pos.setIndex(indexEndField); 1085 break BigBoss; 1086 } 1087 } 1088 break; 1089 } 1090 } 1091 } 1092 //////////////////////////////////////////////////////////////////// 1093 // BLOC B: Prend en compte l'�ventualit� ou le s�parateur d�cimal // 1094 // aurrait �t� absent, puis calcule l'angle en degr�s. // 1095 //////////////////////////////////////////////////////////////////// 1096 if (minutes<0) { 1097 secondes = -secondes; 1098 } 1099 if (degrees<0) { 1100 minutes = -minutes; 1101 secondes = -secondes; 1102 } 1103 if (!decimalSeparator) { 1104 final double facteur=XMath.pow10(widthDecimal); 1105 if (width2!=0) { 1106 if (suffix1==null && Double.isNaN(secondes)) { 1107 if (suffix0==null && Double.isNaN(minutes)) { 1108 degrees /= facteur; 1109 } else { 1110 minutes /= facteur; 1111 } 1112 } else { 1113 secondes /= facteur; 1114 } 1115 } else if (Double.isNaN(secondes)) { 1116 if (width1!=0) { 1117 if (suffix0==null && Double.isNaN(minutes)) { 1118 degrees /= facteur; 1119 } else { 1120 minutes /= facteur; 1121 } 1122 } else if (Double.isNaN(minutes)) { 1123 degrees /= facteur; 1124 } 1125 } 1126 } 1127 /* 1128 * S'il n'y a rien qui permet de s�parer les degr�s des minutes (par exemple si 1129 * le patron est "DDDMMmmm"), alors la variable 'degr�s' englobe � la fois les 1130 * degr�s, les minutes et d'�ventuelles secondes. On applique une correction ici. 1131 */ 1132 if (suffix1==null && width2!=0 && Double.isNaN(secondes)) { 1133 double facteur = XMath.pow10(width2); 1134 if (suffix0==null && width1!=0 && Double.isNaN(minutes)) { 1135 /////////////////// 1136 //// DDDMMSS.s //// 1137 /////////////////// 1138 secondes = degrees; 1139 minutes = (int) (degrees/facteur); // Arrondie vers 0 1140 secondes -= minutes*facteur; 1141 facteur = XMath.pow10(width1); 1142 degrees = (int) (minutes/facteur); // Arrondie vers 0 1143 minutes -= degrees*facteur; 1144 } else { 1145 //////////////////// 1146 //// DDD�MMSS.s //// 1147 //////////////////// 1148 secondes = minutes; 1149 minutes = (int) (minutes/facteur); // Arrondie vers 0 1150 secondes -= minutes*facteur; 1151 } 1152 } else if (suffix0==null && width1!=0 && Double.isNaN(minutes)) { 1153 ///////////////// 1154 //// DDDMM.m //// 1155 ///////////////// 1156 final double facteur = XMath.pow10(width1); 1157 minutes = degrees; 1158 degrees = (int) (degrees/facteur); // Arrondie vers 0 1159 minutes -= degrees*facteur; 1160 } 1161 pos.setErrorIndex(-1); 1162 if ( Double.isNaN(degrees)) degrees=0; 1163 if (!Double.isNaN(minutes)) degrees += minutes/60; 1164 if (!Double.isNaN(secondes)) degrees += secondes/3600; 1165 ///////////////////////////////////////////////////// 1166 // BLOC C: V�rifie maintenant si l'angle ne serait // 1167 // pas suivit d'un symbole N, S, E ou W. // 1168 ///////////////////////////////////////////////////// 1169 for (int index=pos.getIndex(); index<length; index++) { 1170 final char c=source.charAt(index); 1171 switch (Character.toUpperCase(c)) { 1172 case NORTH: pos.setIndex(index+1); return new Latitude( degrees); 1173 case SOUTH: pos.setIndex(index+1); return new Latitude(-degrees); 1174 case EAST : pos.setIndex(index+1); return new Longitude( degrees); 1175 case WEST : pos.setIndex(index+1); return new Longitude(-degrees); 1176 } 1177 if (!Character.isSpaceChar(c)) { 1178 break; 1179 } 1180 } 1181 return new Angle(degrees); 1182 } 1183 1184 /** 1185 * Parse a string as an angle. 1186 * 1187 * @param source The string to parse. 1188 * @return The parsed string as an {@link Angle}, {@link Latitude} 1189 * or {@link Longitude} object. 1190 * @throws ParseException if the string has not been fully parsed. 1191 */ 1192 public Angle parse(final String source) throws ParseException { 1193 final ParsePosition pos = new ParsePosition(0); 1194 final Angle ang = parse(source, pos, true); 1195 checkComplete(source, pos, false); 1196 return ang; 1197 } 1198 1199 /** 1200 * Parse a substring as an angle. Default implementation invokes 1201 * {@link #parse(String, ParsePosition)}. 1202 * 1203 * @param source A String whose beginning should be parsed. 1204 * @param pos Position where to start parsing. 1205 * @return The parsed string as an {@link Angle}, 1206 * {@link Latitude} or {@link Longitude} object. 1207 */ 1208 public Object parseObject(final String source, final ParsePosition pos) { 1209 return parse(source, pos); 1210 } 1211 1212 /** 1213 * Parse a string as an object. Default implementation invokes 1214 * {@link #parse(String)}. 1215 * 1216 * @param source The string to parse. 1217 * @return The parsed string as an {@link Angle}, {@link Latitude} or 1218 * {@link Longitude} object. 1219 * @throws ParseException if the string has not been fully parsed. 1220 */ 1221 public Object parseObject(final String source) throws ParseException { 1222 return parse(source); 1223 } 1224 1225 /** 1226 * Interpr�te une cha�ne de caract�res qui devrait repr�senter un nombre. 1227 * Cette m�thode est utile pour lire une altitude apr�s les angles. 1228 * 1229 * @param source Cha�ne de caract�res � interpr�ter. 1230 * @param pos Position � partir d'o� commencer l'interpr�tation 1231 * de la cha�ne <code>source</code>. 1232 * @return Le nombre lu comme objet {@link Number}. 1233 */ 1234 final Number parseNumber(final String source, final ParsePosition pos) { 1235 return numberFormat.parse(source, pos); 1236 } 1237 1238 /** 1239 * V�rifie si l'interpr�tation d'une cha�ne de caract�res a �t� compl�te. 1240 * Si ce n'�tait pas le cas, lance une exception avec un message d'erreur 1241 * soulignant les caract�res probl�matiques. 1242 * 1243 * @param source Cha�ne de caract�res qui �tait � interpr�ter. 1244 * @param pos Position � laquelle s'est termin�e l'interpr�tation de la 1245 * cha�ne <code>source</code>. 1246 * @param isCoordinate <code>false</code> si on interpr�tait un angle, 1247 * ou <code>true</code> si on interpr�tait une coordonn�e. 1248 * @throws ParseException Si la cha�ne <code>source</code> n'a pas �t� 1249 * interpr�t�e dans sa totalit�. 1250 */ 1251 static void checkComplete(final String source, 1252 final ParsePosition pos, 1253 final boolean isCoordinate) 1254 throws ParseException 1255 { 1256 final int length=source.length(); 1257 final int origin=pos.getIndex(); 1258 for (int index=origin; index<length; index++) { 1259 if (!Character.isWhitespace(source.charAt(index))) { 1260 index=pos.getErrorIndex(); if (index<0) index=origin; 1261 int lower=index; 1262 while (lower<length && Character.isWhitespace(source.charAt(lower))) { 1263 lower++; 1264 } 1265 int upper=lower; 1266 while (upper<length && !Character.isWhitespace(source.charAt(upper))) { 1267 upper++; 1268 } 1269 throw new ParseException(Resources.format( 1270 ResourceKeys.ERROR_PARSE_ANGLE_EXCEPTION_$2, source, 1271 source.substring(lower, Math.min(lower+10, upper))), index); 1272 } 1273 } 1274 } 1275 1276 /** 1277 * Returns a "hash value" for this object. 1278 */ 1279 public synchronized int hashCode() { 1280 int c = 78236951; 1281 if (decimalSeparator) c^= 0xFF; 1282 if (prefix !=null) c^= prefix.hashCode(); 1283 if (suffix0 !=null) c = c*37 + suffix0.hashCode(); 1284 if (suffix1 !=null) c^= c*37 + suffix1.hashCode(); 1285 if (suffix2 !=null) c^= c*37 + suffix2.hashCode(); 1286 return c ^ (((((width0 << 8) ^ width1) << 8) ^ width2) << 8) ^ widthDecimal; 1287 } 1288 1289 /** 1290 * Compare this format with the specified object for equality. 1291 */ 1292 public synchronized boolean equals(final Object obj) { 1293 // On ne peut pas synchroniser "obj" si on ne veut 1294 // pas risquer un "deadlock". Voir RFE #4210659. 1295 if (obj==this) { 1296 return true; 1297 } 1298 if (obj!=null && getClass().equals(obj.getClass())) { 1299 final AngleFormat cast = (AngleFormat) obj; 1300 return width0 == cast.width0 && 1301 width1 == cast.width1 && 1302 width2 == cast.width2 && 1303 widthDecimal == cast.widthDecimal && 1304 decimalSeparator == cast.decimalSeparator && 1305 Utilities.equals(prefix, cast.prefix ) && 1306 Utilities.equals(suffix0, cast.suffix0) && 1307 Utilities.equals(suffix1, cast.suffix1) && 1308 Utilities.equals(suffix2, cast.suffix2) && 1309 Utilities.equals(numberFormat.getDecimalFormatSymbols(), 1310 cast.numberFormat.getDecimalFormatSymbols()); 1311 } 1312 return false; 1313 1314 } 1315 1316 /** 1317 * Returns a string representation of this object. 1318 */ 1319 public String toString() { 1320 return Utilities.getShortClassName(this)+'['+toPattern()+']'; 1321 } 1322 }