001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.math.geometry;
019
020 import java.text.FieldPosition;
021 import java.text.NumberFormat;
022 import java.text.ParseException;
023 import java.text.ParsePosition;
024 import java.util.Locale;
025
026 import org.apache.commons.math.MathRuntimeException;
027 import org.apache.commons.math.util.CompositeFormat;
028
029 /**
030 * Formats a 3D vector in components list format "{x; y; z}".
031 * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by
032 * any user-defined strings. The number format for components can be configured.</p>
033 * <p>White space is ignored at parse time, even if it is in the prefix, suffix
034 * or separator specifications. So even if the default separator does include a space
035 * character that is used at format time, both input string "{1;1;1}" and
036 * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be
037 * returned. In the second case, however, the parse position after parsing will be
038 * just after the closing curly brace, i.e. just before the trailing space.</p>
039 *
040 * @version $Revision: 772119 $ $Date: 2009-05-06 05:43:28 -0400 (Wed, 06 May 2009) $
041 */
042 public class Vector3DFormat extends CompositeFormat {
043
044 /** Serializable version identifier */
045 private static final long serialVersionUID = -5447606608652576301L;
046
047 /** The default prefix: "{". */
048 private static final String DEFAULT_PREFIX = "{";
049
050 /** The default suffix: "}". */
051 private static final String DEFAULT_SUFFIX = "}";
052
053 /** The default separator: ", ". */
054 private static final String DEFAULT_SEPARATOR = "; ";
055
056 /** Prefix. */
057 private final String prefix;
058
059 /** Suffix. */
060 private final String suffix;
061
062 /** Separator. */
063 private final String separator;
064
065 /** Trimmed prefix. */
066 private final String trimmedPrefix;
067
068 /** Trimmed suffix. */
069 private final String trimmedSuffix;
070
071 /** Trimmed separator. */
072 private final String trimmedSeparator;
073
074 /** The format used for components. */
075 private NumberFormat format;
076
077 /**
078 * Create an instance with default settings.
079 * <p>The instance uses the default prefix, suffix and separator:
080 * "{", "}", and "; " and the default number format for components.</p>
081 */
082 public Vector3DFormat() {
083 this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat());
084 }
085
086 /**
087 * Create an instance with a custom number format for components.
088 * @param format the custom format for components.
089 */
090 public Vector3DFormat(final NumberFormat format) {
091 this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
092 }
093
094 /**
095 * Create an instance with custom prefix, suffix and separator.
096 * @param prefix prefix to use instead of the default "{"
097 * @param suffix suffix to use instead of the default "}"
098 * @param separator separator to use instead of the default "; "
099 */
100 public Vector3DFormat(final String prefix, final String suffix,
101 final String separator) {
102 this(prefix, suffix, separator, getDefaultNumberFormat());
103 }
104
105 /**
106 * Create an instance with custom prefix, suffix, separator and format
107 * for components.
108 * @param prefix prefix to use instead of the default "{"
109 * @param suffix suffix to use instead of the default "}"
110 * @param separator separator to use instead of the default "; "
111 * @param format the custom format for components.
112 */
113 public Vector3DFormat(final String prefix, final String suffix,
114 final String separator, final NumberFormat format) {
115 this.prefix = prefix;
116 this.suffix = suffix;
117 this.separator = separator;
118 trimmedPrefix = prefix.trim();
119 trimmedSuffix = suffix.trim();
120 trimmedSeparator = separator.trim();
121 this.format = format;
122 }
123
124 /**
125 * Get the set of locales for which 3D vectors formats are available.
126 * <p>This is the same set as the {@link NumberFormat} set.</p>
127 * @return available 3D vector format locales.
128 */
129 public static Locale[] getAvailableLocales() {
130 return NumberFormat.getAvailableLocales();
131 }
132
133 /**
134 * Get the format prefix.
135 * @return format prefix.
136 */
137 public String getPrefix() {
138 return prefix;
139 }
140
141 /**
142 * Get the format suffix.
143 * @return format suffix.
144 */
145 public String getSuffix() {
146 return suffix;
147 }
148
149 /**
150 * Get the format separator between components.
151 * @return format separator.
152 */
153 public String getSeparator() {
154 return separator;
155 }
156
157 /**
158 * Get the components format.
159 * @return components format.
160 */
161 public NumberFormat getFormat() {
162 return format;
163 }
164
165 /**
166 * Returns the default 3D vector format for the current locale.
167 * @return the default 3D vector format.
168 */
169 public static Vector3DFormat getInstance() {
170 return getInstance(Locale.getDefault());
171 }
172
173 /**
174 * Returns the default 3D vector format for the given locale.
175 * @param locale the specific locale used by the format.
176 * @return the 3D vector format specific to the given locale.
177 */
178 public static Vector3DFormat getInstance(final Locale locale) {
179 return new Vector3DFormat(getDefaultNumberFormat(locale));
180 }
181
182 /**
183 * This static method calls {@link #format(Object)} on a default instance of
184 * Vector3DFormat.
185 *
186 * @param v Vector3D object to format
187 * @return A formatted vector
188 */
189 public static String formatVector3D(Vector3D v) {
190 return getInstance().format(v);
191 }
192
193 /**
194 * Formats a {@link Vector3D} object to produce a string.
195 * @param vector the object to format.
196 * @param toAppendTo where the text is to be appended
197 * @param pos On input: an alignment field, if desired. On output: the
198 * offsets of the alignment field
199 * @return the value passed in as toAppendTo.
200 */
201 public StringBuffer format(Vector3D vector, StringBuffer toAppendTo,
202 FieldPosition pos) {
203
204 pos.setBeginIndex(0);
205 pos.setEndIndex(0);
206
207 // format prefix
208 toAppendTo.append(prefix);
209
210 // format components
211 formatDouble(vector.getX(), format, toAppendTo, pos);
212 toAppendTo.append(separator);
213 formatDouble(vector.getY(), format, toAppendTo, pos);
214 toAppendTo.append(separator);
215 formatDouble(vector.getZ(), format, toAppendTo, pos);
216
217 // format suffix
218 toAppendTo.append(suffix);
219
220 return toAppendTo;
221
222 }
223
224 /**
225 * Formats a object to produce a string.
226 * <p><code>obj</code> must be a {@link Vector3D} object. Any other type of
227 * object will result in an {@link IllegalArgumentException} being thrown.</p>
228 * @param obj the object to format.
229 * @param toAppendTo where the text is to be appended
230 * @param pos On input: an alignment field, if desired. On output: the
231 * offsets of the alignment field
232 * @return the value passed in as toAppendTo.
233 * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
234 * @throws IllegalArgumentException is <code>obj</code> is not a valid type.
235 */
236 @Override
237 public StringBuffer format(Object obj, StringBuffer toAppendTo,
238 FieldPosition pos) {
239
240 if (obj instanceof Vector3D) {
241 return format( (Vector3D)obj, toAppendTo, pos);
242 }
243
244 throw MathRuntimeException.createIllegalArgumentException("cannot format a {0} instance as a 3D vector",
245 obj.getClass().getName());
246
247 }
248
249 /**
250 * Parses a string to produce a {@link Vector3D} object.
251 * @param source the string to parse
252 * @return the parsed {@link Vector3D} object.
253 * @exception ParseException if the beginning of the specified string
254 * cannot be parsed.
255 */
256 public Vector3D parse(String source) throws ParseException {
257 ParsePosition parsePosition = new ParsePosition(0);
258 Vector3D result = parse(source, parsePosition);
259 if (parsePosition.getIndex() == 0) {
260 throw MathRuntimeException.createParseException(
261 parsePosition.getErrorIndex(),
262 "unparseable 3D vector: \"{0}\"", source);
263 }
264 return result;
265 }
266
267 /**
268 * Parses a string to produce a {@link Vector3D} object.
269 * @param source the string to parse
270 * @param pos input/ouput parsing parameter.
271 * @return the parsed {@link Vector3D} object.
272 */
273 public Vector3D parse(String source, ParsePosition pos) {
274 int initialIndex = pos.getIndex();
275
276 // parse prefix
277 parseAndIgnoreWhitespace(source, pos);
278 if (!parseFixedstring(source, trimmedPrefix, pos)) {
279 return null;
280 }
281
282 // parse X component
283 parseAndIgnoreWhitespace(source, pos);
284 Number x = parseNumber(source, format, pos);
285 if (x == null) {
286 // invalid abscissa
287 // set index back to initial, error index should already be set
288 pos.setIndex(initialIndex);
289 return null;
290 }
291
292 // parse Y component
293 parseAndIgnoreWhitespace(source, pos);
294 if (!parseFixedstring(source, trimmedSeparator, pos)) {
295 return null;
296 }
297 parseAndIgnoreWhitespace(source, pos);
298 Number y = parseNumber(source, format, pos);
299 if (y == null) {
300 // invalid ordinate
301 // set index back to initial, error index should already be set
302 pos.setIndex(initialIndex);
303 return null;
304 }
305
306 // parse Z component
307 parseAndIgnoreWhitespace(source, pos);
308 if (!parseFixedstring(source, trimmedSeparator, pos)) {
309 return null;
310 }
311 parseAndIgnoreWhitespace(source, pos);
312 Number z = parseNumber(source, format, pos);
313 if (z == null) {
314 // invalid height
315 // set index back to initial, error index should already be set
316 pos.setIndex(initialIndex);
317 return null;
318 }
319
320 // parse suffix
321 parseAndIgnoreWhitespace(source, pos);
322 if (!parseFixedstring(source, trimmedSuffix, pos)) {
323 return null;
324 }
325
326 return new Vector3D(x.doubleValue(), y.doubleValue(), z.doubleValue());
327
328 }
329
330 /**
331 * Parses a string to produce a object.
332 * @param source the string to parse
333 * @param pos input/ouput parsing parameter.
334 * @return the parsed object.
335 * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
336 */
337 @Override
338 public Object parseObject(String source, ParsePosition pos) {
339 return parse(source, pos);
340 }
341
342 }