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.exception.util.LocalizedFormats;
028 import org.apache.commons.math.util.CompositeFormat;
029
030 /**
031 * Formats a 3D vector in components list format "{x; y; z}".
032 * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by
033 * any user-defined strings. The number format for components can be configured.</p>
034 * <p>White space is ignored at parse time, even if it is in the prefix, suffix
035 * or separator specifications. So even if the default separator does include a space
036 * character that is used at format time, both input string "{1;1;1}" and
037 * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be
038 * returned. In the second case, however, the parse position after parsing will be
039 * just after the closing curly brace, i.e. just before the trailing space.</p>
040 *
041 * @version $Revision: 1003886 $ $Date: 2010-10-02 23:04:44 +0200 (sam. 02 oct. 2010) $
042 */
043 public class Vector3DFormat extends CompositeFormat {
044
045 /** Serializable version identifier */
046 private static final long serialVersionUID = -5447606608652576301L;
047
048 /** The default prefix: "{". */
049 private static final String DEFAULT_PREFIX = "{";
050
051 /** The default suffix: "}". */
052 private static final String DEFAULT_SUFFIX = "}";
053
054 /** The default separator: ", ". */
055 private static final String DEFAULT_SEPARATOR = "; ";
056
057 /** Prefix. */
058 private final String prefix;
059
060 /** Suffix. */
061 private final String suffix;
062
063 /** Separator. */
064 private final String separator;
065
066 /** Trimmed prefix. */
067 private final String trimmedPrefix;
068
069 /** Trimmed suffix. */
070 private final String trimmedSuffix;
071
072 /** Trimmed separator. */
073 private final String trimmedSeparator;
074
075 /** The format used for components. */
076 private final NumberFormat format;
077
078 /**
079 * Create an instance with default settings.
080 * <p>The instance uses the default prefix, suffix and separator:
081 * "{", "}", and "; " and the default number format for components.</p>
082 */
083 public Vector3DFormat() {
084 this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat());
085 }
086
087 /**
088 * Create an instance with a custom number format for components.
089 * @param format the custom format for components.
090 */
091 public Vector3DFormat(final NumberFormat format) {
092 this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
093 }
094
095 /**
096 * Create an instance with custom prefix, suffix and separator.
097 * @param prefix prefix to use instead of the default "{"
098 * @param suffix suffix to use instead of the default "}"
099 * @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 }