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    *      https://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  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.geometry;
24  
25  import java.text.FieldPosition;
26  import java.text.NumberFormat;
27  import java.text.ParsePosition;
28  import java.util.Locale;
29  
30  import org.hipparchus.exception.MathIllegalStateException;
31  import org.hipparchus.util.CompositeFormat;
32  
33  /**
34   * Formats a vector in components list format "{x; y; ...}".
35   * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by
36   * any user-defined strings. The number format for components can be configured.</p>
37   * <p>White space is ignored at parse time, even if it is in the prefix, suffix
38   * or separator specifications. So even if the default separator does include a space
39   * character that is used at format time, both input string "{1;1;1}" and
40   * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be
41   * returned. In the second case, however, the parse position after parsing will be
42   * just after the closing curly brace, i.e. just before the trailing space.</p>
43   * <p><b>Note:</b> using "," as a separator may interfere with the grouping separator
44   * of the default {@link NumberFormat} for the current locale. Thus it is advised
45   * to use a {@link NumberFormat} instance with disabled grouping in such a case.</p>
46   *
47   * @param <S> Type of the space.
48   * @param <V> Type of vector implementing Vector interface.
49   */
50  public abstract class VectorFormat<S extends Space, V extends Vector<S,V>> {
51  
52      /** The default prefix: "{". */
53      public static final String DEFAULT_PREFIX = "{";
54  
55      /** The default suffix: "}". */
56      public static final String DEFAULT_SUFFIX = "}";
57  
58      /** The default separator: ", ". */
59      public static final String DEFAULT_SEPARATOR = "; ";
60  
61      /** Prefix. */
62      private final String prefix;
63  
64      /** Suffix. */
65      private final String suffix;
66  
67      /** Separator. */
68      private final String separator;
69  
70      /** Trimmed prefix. */
71      private final String trimmedPrefix;
72  
73      /** Trimmed suffix. */
74      private final String trimmedSuffix;
75  
76      /** Trimmed separator. */
77      private final String trimmedSeparator;
78  
79      /** The format used for components. */
80      private final NumberFormat format;
81  
82      /**
83       * Create an instance with default settings.
84       * <p>The instance uses the default prefix, suffix and separator:
85       * "{", "}", and "; " and the default number format for components.</p>
86       */
87      protected VectorFormat() {
88          this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR,
89               CompositeFormat.getDefaultNumberFormat());
90      }
91  
92      /**
93       * Create an instance with a custom number format for components.
94       * @param format the custom format for components.
95       */
96      protected VectorFormat(final NumberFormat format) {
97          this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
98      }
99  
100     /**
101      * Create an instance with custom prefix, suffix and separator.
102      * @param prefix prefix to use instead of the default "{"
103      * @param suffix suffix to use instead of the default "}"
104      * @param separator separator to use instead of the default "; "
105      */
106     protected VectorFormat(final String prefix, final String suffix,
107                           final String separator) {
108         this(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat());
109     }
110 
111     /**
112      * Create an instance with custom prefix, suffix, separator and format
113      * for components.
114      * @param prefix prefix to use instead of the default "{"
115      * @param suffix suffix to use instead of the default "}"
116      * @param separator separator to use instead of the default "; "
117      * @param format the custom format for components.
118      */
119     protected VectorFormat(final String prefix, final String suffix,
120                           final String separator, final NumberFormat format) {
121         this.prefix      = prefix;
122         this.suffix      = suffix;
123         this.separator   = separator;
124         trimmedPrefix    = prefix.trim();
125         trimmedSuffix    = suffix.trim();
126         trimmedSeparator = separator.trim();
127         this.format      = format;
128     }
129 
130     /**
131      * Get the set of locales for which point/vector formats are available.
132      * <p>This is the same set as the {@link NumberFormat} set.</p>
133      * @return available point/vector format locales.
134      */
135     public static Locale[] getAvailableLocales() {
136         return NumberFormat.getAvailableLocales();
137     }
138 
139     /**
140      * Get the format prefix.
141      * @return format prefix.
142      */
143     public String getPrefix() {
144         return prefix;
145     }
146 
147     /**
148      * Get the format suffix.
149      * @return format suffix.
150      */
151     public String getSuffix() {
152         return suffix;
153     }
154 
155     /**
156      * Get the format separator between components.
157      * @return format separator.
158      */
159     public String getSeparator() {
160         return separator;
161     }
162 
163     /**
164      * Get the components format.
165      * @return components format.
166      */
167     public NumberFormat getFormat() {
168         return format;
169     }
170 
171     /**
172      * Formats a {@link Vector} object to produce a string.
173      * @param vector the object to format.
174      * @return a formatted string.
175      */
176     public String format(Vector<S, V> vector) {
177         return format(vector, new StringBuffer(), new FieldPosition(0)).toString();
178     }
179 
180     /**
181      * Formats a {@link Vector} object to produce a string.
182      * @param vector the object to format.
183      * @param toAppendTo where the text is to be appended
184      * @param pos On input: an alignment field, if desired. On output: the
185      *            offsets of the alignment field
186      * @return the value passed in as toAppendTo.
187      */
188     public abstract StringBuffer format(Vector<S, V> vector,
189                                         StringBuffer toAppendTo, FieldPosition pos);
190 
191     /**
192      * Formats the coordinates of a {@link Vector} to produce a string.
193      * @param toAppendTo where the text is to be appended
194      * @param pos On input: an alignment field, if desired. On output: the
195      *            offsets of the alignment field
196      * @param coordinates coordinates of the object to format.
197      * @return the value passed in as toAppendTo.
198      */
199     protected StringBuffer format(StringBuffer toAppendTo, FieldPosition pos,
200                                   double ... coordinates) {
201 
202         pos.setBeginIndex(0);
203         pos.setEndIndex(0);
204 
205         // format prefix
206         toAppendTo.append(prefix);
207 
208         // format components
209         for (int i = 0; i < coordinates.length; ++i) {
210             if (i > 0) {
211                 toAppendTo.append(separator);
212             }
213             CompositeFormat.formatDouble(coordinates[i], format, toAppendTo, pos);
214         }
215 
216         // format suffix
217         toAppendTo.append(suffix);
218 
219         return toAppendTo;
220 
221     }
222 
223     /**
224      * Parses a string to produce a {@link Vector} object.
225      * @param source the string to parse
226      * @return the parsed {@link Vector} object.
227      * @throws MathIllegalStateException if the beginning of the specified string
228      * cannot be parsed.
229      */
230     public abstract Vector<S, V> parse(String source) throws MathIllegalStateException;
231 
232     /**
233      * Parses a string to produce a {@link Vector} object.
234      * @param source the string to parse
235      * @param pos input/output parsing parameter.
236      * @return the parsed {@link Vector} object.
237      */
238     public abstract Vector<S, V> parse(String source, ParsePosition pos);
239 
240     /**
241      * Parses a string to produce an array of coordinates.
242      * @param dimension dimension of the space
243      * @param source the string to parse
244      * @param pos input/output parsing parameter.
245      * @return coordinates array.
246      */
247     protected double[] parseCoordinates(int dimension, String source, ParsePosition pos) {
248 
249         int initialIndex = pos.getIndex();
250         double[] coordinates = new double[dimension];
251 
252         // parse prefix
253         CompositeFormat.parseAndIgnoreWhitespace(source, pos);
254         if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) {
255             return null; // NOPMD
256         }
257 
258         for (int i = 0; i < dimension; ++i) {
259 
260             // skip whitespace
261             CompositeFormat.parseAndIgnoreWhitespace(source, pos);
262 
263             // parse separator
264             if (i > 0 && !CompositeFormat.parseFixedstring(source, trimmedSeparator, pos)) {
265                 return null; // NOPMD
266             }
267 
268             // skip whitespace
269             CompositeFormat.parseAndIgnoreWhitespace(source, pos);
270 
271             // parse coordinate
272             Number c = CompositeFormat.parseNumber(source, format, pos);
273             if (c == null) {
274                 // invalid coordinate
275                 // set index back to initial, error index should already be set
276                 pos.setIndex(initialIndex);
277                 return null; // NOPMD
278             }
279 
280             // store coordinate
281             coordinates[i] = c.doubleValue();
282 
283         }
284 
285         // parse suffix
286         CompositeFormat.parseAndIgnoreWhitespace(source, pos);
287         if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) {
288             return null; // NOPMD
289         }
290 
291         return coordinates;
292 
293     }
294 
295 }