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
18package org.apache.commons.math.geometry;
19
20import java.text.FieldPosition;
21import java.text.NumberFormat;
22import java.text.ParseException;
23import java.text.ParsePosition;
24import java.util.Locale;
25
26import org.apache.commons.math.MathRuntimeException;
27import org.apache.commons.math.exception.util.LocalizedFormats;
28import 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 */
43public 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