View Javadoc

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.util.CompositeFormat;
28  
29  /**
30   * Formats a 3D vector in components list format "{x; y; z}".
31   * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by
32   * any user-defined strings. The number format for components can be configured.</p>
33   * <p>White space is ignored at parse time, even if it is in the prefix, suffix
34   * or separator specifications. So even if the default separator does include a space
35   * character that is used at format time, both input string "{1;1;1}" and
36   * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be
37   * returned. In the second case, however, the parse position after parsing will be
38   * just after the closing curly brace, i.e. just before the trailing space.</p>
39   *
40   * @version $Revision: 772119 $ $Date: 2009-05-06 05:43:28 -0400 (Wed, 06 May 2009) $
41   */
42  public class Vector3DFormat extends CompositeFormat {
43  
44      /** Serializable version identifier */
45      private static final long serialVersionUID = -5447606608652576301L;
46  
47      /** The default prefix: "{". */
48      private static final String DEFAULT_PREFIX = "{";
49  
50      /** The default suffix: "}". */
51      private static final String DEFAULT_SUFFIX = "}";
52  
53      /** The default separator: ", ". */
54      private static final String DEFAULT_SEPARATOR = "; ";
55  
56      /** Prefix. */
57      private final String prefix;
58  
59      /** Suffix. */
60      private final String suffix;
61  
62      /** Separator. */
63      private final String separator;
64  
65      /** Trimmed prefix. */
66      private final String trimmedPrefix;
67  
68      /** Trimmed suffix. */
69      private final String trimmedSuffix;
70  
71      /** Trimmed separator. */
72      private final String trimmedSeparator;
73  
74      /** The format used for components. */
75      private NumberFormat format;
76  
77      /**
78       * Create an instance with default settings.
79       * <p>The instance uses the default prefix, suffix and separator:
80       * "{", "}", and "; " and the default number format for components.</p>
81       */
82      public Vector3DFormat() {
83          this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, getDefaultNumberFormat());
84      }
85  
86      /**
87       * Create an instance with a custom number format for components.
88       * @param format the custom format for components.
89       */
90      public Vector3DFormat(final NumberFormat format) {
91          this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
92      }
93  
94      /**
95       * Create an instance with custom prefix, suffix and separator.
96       * @param prefix prefix to use instead of the default "{"
97       * @param suffix suffix to use instead of the default "}"
98       * @param separator separator to use instead of the default "; "
99       */
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 }