1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.math.geometry; 19 20 import java.text.FieldPosition; 21 import java.text.NumberFormat; 22 import java.text.ParseException; 23 import java.text.ParsePosition; 24 import java.util.Locale; 25 26 import org.apache.commons.math.MathRuntimeException; 27 import org.apache.commons.math.exception.util.LocalizedFormats; 28 import org.apache.commons.math.util.CompositeFormat; 29 30 /** 31 * Formats a 3D vector in components list format "{x; y; z}". 32 * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by 33 * any user-defined strings. The number format for components can be configured.</p> 34 * <p>White space is ignored at parse time, even if it is in the prefix, suffix 35 * or separator specifications. So even if the default separator does include a space 36 * character that is used at format time, both input string "{1;1;1}" and 37 * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be 38 * returned. In the second case, however, the parse position after parsing will be 39 * just after the closing curly brace, i.e. just before the trailing space.</p> 40 * 41 * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $ 42 */ 43 public class Vector3DFormat extends CompositeFormat { 44 45 /** Serializable version identifier */ 46 private static final long serialVersionUID = -5447606608652576301L; 47 48 /** The default prefix: "{". */ 49 private static final String DEFAULT_PREFIX = "{"; 50 51 /** The default suffix: "}". */ 52 private static final String DEFAULT_SUFFIX = "}"; 53 54 /** The default separator: ", ". */ 55 private static final String DEFAULT_SEPARATOR = "; "; 56 57 /** Prefix. */ 58 private final String prefix; 59 60 /** Suffix. */ 61 private final String suffix; 62 63 /** Separator. */ 64 private final String separator; 65 66 /** Trimmed prefix. */ 67 private final String trimmedPrefix; 68 69 /** Trimmed suffix. */ 70 private final String trimmedSuffix; 71 72 /** Trimmed separator. */ 73 private final String trimmedSeparator; 74 75 /** The format used for components. */ 76 private final NumberFormat format; 77 78 /** 79 * Create an instance with default settings. 80 * <p>The instance uses the default prefix, suffix and separator: 81 * "{", "}", and "; " and the default number format for components.</p> 82 */ 83 public Vector3DFormat() { 84 this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat()); 85 } 86 87 /** 88 * Create an instance with a custom number format for components. 89 * @param format the custom format for components. 90 */ 91 public Vector3DFormat(final NumberFormat format) { 92 this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format); 93 } 94 95 /** 96 * Create an instance with custom prefix, suffix and separator. 97 * @param prefix prefix to use instead of the default "{" 98 * @param suffix suffix to use instead of the default "}" 99 * @param separator separator to use instead of the default "; " 100 */ 101 public Vector3DFormat(final String prefix, final String suffix, 102 final String separator) { 103 this(prefix, suffix, separator, getDefaultNumberFormat()); 104 } 105 106 /** 107 * Create an instance with custom prefix, suffix, separator and format 108 * for components. 109 * @param prefix prefix to use instead of the default "{" 110 * @param suffix suffix to use instead of the default "}" 111 * @param separator separator to use instead of the default "; " 112 * @param format the custom format for components. 113 */ 114 public Vector3DFormat(final String prefix, final String suffix, 115 final String separator, final NumberFormat format) { 116 this.prefix = prefix; 117 this.suffix = suffix; 118 this.separator = separator; 119 trimmedPrefix = prefix.trim(); 120 trimmedSuffix = suffix.trim(); 121 trimmedSeparator = separator.trim(); 122 this.format = format; 123 } 124 125 /** 126 * Get the set of locales for which 3D vectors formats are available. 127 * <p>This is the same set as the {@link NumberFormat} set.</p> 128 * @return available 3D vector format locales. 129 */ 130 public static Locale[] getAvailableLocales() { 131 return NumberFormat.getAvailableLocales(); 132 } 133 134 /** 135 * Get the format prefix. 136 * @return format prefix. 137 */ 138 public String getPrefix() { 139 return prefix; 140 } 141 142 /** 143 * Get the format suffix. 144 * @return format suffix. 145 */ 146 public String getSuffix() { 147 return suffix; 148 } 149 150 /** 151 * Get the format separator between components. 152 * @return format separator. 153 */ 154 public String getSeparator() { 155 return separator; 156 } 157 158 /** 159 * Get the components format. 160 * @return components format. 161 */ 162 public NumberFormat getFormat() { 163 return format; 164 } 165 166 /** 167 * Returns the default 3D vector format for the current locale. 168 * @return the default 3D vector format. 169 */ 170 public static Vector3DFormat getInstance() { 171 return getInstance(Locale.getDefault()); 172 } 173 174 /** 175 * Returns the default 3D vector format for the given locale. 176 * @param locale the specific locale used by the format. 177 * @return the 3D vector format specific to the given locale. 178 */ 179 public static Vector3DFormat getInstance(final Locale locale) { 180 return new Vector3DFormat(getDefaultNumberFormat(locale)); 181 } 182 183 /** 184 * This static method calls {@link #format(Object)} on a default instance of 185 * Vector3DFormat. 186 * 187 * @param v Vector3D object to format 188 * @return A formatted vector 189 */ 190 public static String formatVector3D(Vector3D v) { 191 return getInstance().format(v); 192 } 193 194 /** 195 * Formats a {@link Vector3D} object to produce a string. 196 * @param vector the object to format. 197 * @param toAppendTo where the text is to be appended 198 * @param pos On input: an alignment field, if desired. On output: the 199 * offsets of the alignment field 200 * @return the value passed in as toAppendTo. 201 */ 202 public StringBuffer format(Vector3D vector, StringBuffer toAppendTo, 203 FieldPosition pos) { 204 205 pos.setBeginIndex(0); 206 pos.setEndIndex(0); 207 208 // format prefix 209 toAppendTo.append(prefix); 210 211 // format components 212 formatDouble(vector.getX(), format, toAppendTo, pos); 213 toAppendTo.append(separator); 214 formatDouble(vector.getY(), format, toAppendTo, pos); 215 toAppendTo.append(separator); 216 formatDouble(vector.getZ(), format, toAppendTo, pos); 217 218 // format suffix 219 toAppendTo.append(suffix); 220 221 return toAppendTo; 222 223 } 224 225 /** 226 * Formats a object to produce a string. 227 * <p><code>obj</code> must be a {@link Vector3D} object. Any other type of 228 * object will result in an {@link IllegalArgumentException} being thrown.</p> 229 * @param obj the object to format. 230 * @param toAppendTo where the text is to be appended 231 * @param pos On input: an alignment field, if desired. On output: the 232 * offsets of the alignment field 233 * @return the value passed in as toAppendTo. 234 * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition) 235 * @throws IllegalArgumentException is <code>obj</code> is not a valid type. 236 */ 237 @Override 238 public StringBuffer format(Object obj, StringBuffer toAppendTo, 239 FieldPosition pos) { 240 241 if (obj instanceof Vector3D) { 242 return format( (Vector3D)obj, toAppendTo, pos); 243 } 244 245 throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.CANNOT_FORMAT_INSTANCE_AS_3D_VECTOR, 246 obj.getClass().getName()); 247 248 } 249 250 /** 251 * Parses a string to produce a {@link Vector3D} object. 252 * @param source the string to parse 253 * @return the parsed {@link Vector3D} object. 254 * @exception ParseException if the beginning of the specified string 255 * cannot be parsed. 256 */ 257 public Vector3D parse(String source) throws ParseException { 258 ParsePosition parsePosition = new ParsePosition(0); 259 Vector3D result = parse(source, parsePosition); 260 if (parsePosition.getIndex() == 0) { 261 throw MathRuntimeException.createParseException( 262 parsePosition.getErrorIndex(), 263 LocalizedFormats.UNPARSEABLE_3D_VECTOR, source); 264 } 265 return result; 266 } 267 268 /** 269 * Parses a string to produce a {@link Vector3D} object. 270 * @param source the string to parse 271 * @param pos input/ouput parsing parameter. 272 * @return the parsed {@link Vector3D} object. 273 */ 274 public Vector3D parse(String source, ParsePosition pos) { 275 int initialIndex = pos.getIndex(); 276 277 // parse prefix 278 parseAndIgnoreWhitespace(source, pos); 279 if (!parseFixedstring(source, trimmedPrefix, pos)) { 280 return null; 281 } 282 283 // parse X component 284 parseAndIgnoreWhitespace(source, pos); 285 Number x = parseNumber(source, format, pos); 286 if (x == null) { 287 // invalid abscissa 288 // set index back to initial, error index should already be set 289 pos.setIndex(initialIndex); 290 return null; 291 } 292 293 // parse Y component 294 parseAndIgnoreWhitespace(source, pos); 295 if (!parseFixedstring(source, trimmedSeparator, pos)) { 296 return null; 297 } 298 parseAndIgnoreWhitespace(source, pos); 299 Number y = parseNumber(source, format, pos); 300 if (y == null) { 301 // invalid ordinate 302 // set index back to initial, error index should already be set 303 pos.setIndex(initialIndex); 304 return null; 305 } 306 307 // parse Z component 308 parseAndIgnoreWhitespace(source, pos); 309 if (!parseFixedstring(source, trimmedSeparator, pos)) { 310 return null; 311 } 312 parseAndIgnoreWhitespace(source, pos); 313 Number z = parseNumber(source, format, pos); 314 if (z == null) { 315 // invalid height 316 // set index back to initial, error index should already be set 317 pos.setIndex(initialIndex); 318 return null; 319 } 320 321 // parse suffix 322 parseAndIgnoreWhitespace(source, pos); 323 if (!parseFixedstring(source, trimmedSuffix, pos)) { 324 return null; 325 } 326 327 return new Vector3D(x.doubleValue(), y.doubleValue(), z.doubleValue()); 328 329 } 330 331 /** 332 * Parses a string to produce a object. 333 * @param source the string to parse 334 * @param pos input/ouput parsing parameter. 335 * @return the parsed object. 336 * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) 337 */ 338 @Override 339 public Object parseObject(String source, ParsePosition pos) { 340 return parse(source, pos); 341 } 342 343 } 344