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.math3.linear;
19  
20  import java.text.FieldPosition;
21  import java.text.NumberFormat;
22  import java.text.ParsePosition;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Locale;
26  
27  import org.apache.commons.math3.exception.MathParseException;
28  import org.apache.commons.math3.util.CompositeFormat;
29  
30  /**
31   * Formats a vector in components list format "{v0; v1; ...; vk-1}".
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 $Id: RealVectorFormat.java 1416643 2012-12-03 19:37:14Z tn $
42   * @since 2.0
43   */
44  public class RealVectorFormat {
45  
46      /** The default prefix: "{". */
47      private static final String DEFAULT_PREFIX = "{";
48      /** The default suffix: "}". */
49      private static final String DEFAULT_SUFFIX = "}";
50      /** The default separator: ", ". */
51      private static final String DEFAULT_SEPARATOR = "; ";
52      /** Prefix. */
53      private final String prefix;
54      /** Suffix. */
55      private final String suffix;
56      /** Separator. */
57      private final String separator;
58      /** Trimmed prefix. */
59      private final String trimmedPrefix;
60      /** Trimmed suffix. */
61      private final String trimmedSuffix;
62      /** Trimmed separator. */
63      private final String trimmedSeparator;
64      /** The format used for components. */
65      private final NumberFormat format;
66  
67      /**
68       * Create an instance with default settings.
69       * <p>The instance uses the default prefix, suffix and separator:
70       * "{", "}", and "; " and the default number format for components.</p>
71       */
72      public RealVectorFormat() {
73          this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR,
74               CompositeFormat.getDefaultNumberFormat());
75      }
76  
77      /**
78       * Create an instance with a custom number format for components.
79       * @param format the custom format for components.
80       */
81      public RealVectorFormat(final NumberFormat format) {
82          this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
83      }
84  
85      /**
86       * Create an instance with custom prefix, suffix and separator.
87       * @param prefix prefix to use instead of the default "{"
88       * @param suffix suffix to use instead of the default "}"
89       * @param separator separator to use instead of the default "; "
90       */
91      public RealVectorFormat(final String prefix, final String suffix,
92                              final String separator) {
93          this(prefix, suffix, separator,
94               CompositeFormat.getDefaultNumberFormat());
95      }
96  
97      /**
98       * Create an instance with custom prefix, suffix, separator and format
99       * for components.
100      * @param prefix prefix to use instead of the default "{"
101      * @param suffix suffix to use instead of the default "}"
102      * @param separator separator to use instead of the default "; "
103      * @param format the custom format for components.
104      */
105     public RealVectorFormat(final String prefix, final String suffix,
106                             final String separator, final NumberFormat format) {
107         this.prefix      = prefix;
108         this.suffix      = suffix;
109         this.separator   = separator;
110         trimmedPrefix    = prefix.trim();
111         trimmedSuffix    = suffix.trim();
112         trimmedSeparator = separator.trim();
113         this.format      = format;
114     }
115 
116     /**
117      * Get the set of locales for which real vectors formats are available.
118      * <p>This is the same set as the {@link NumberFormat} set.</p>
119      * @return available real vector format locales.
120      */
121     public static Locale[] getAvailableLocales() {
122         return NumberFormat.getAvailableLocales();
123     }
124 
125     /**
126      * Get the format prefix.
127      * @return format prefix.
128      */
129     public String getPrefix() {
130         return prefix;
131     }
132 
133     /**
134      * Get the format suffix.
135      * @return format suffix.
136      */
137     public String getSuffix() {
138         return suffix;
139     }
140 
141     /**
142      * Get the format separator between components.
143      * @return format separator.
144      */
145     public String getSeparator() {
146         return separator;
147     }
148 
149     /**
150      * Get the components format.
151      * @return components format.
152      */
153     public NumberFormat getFormat() {
154         return format;
155     }
156 
157     /**
158      * Returns the default real vector format for the current locale.
159      * @return the default real vector format.
160      */
161     public static RealVectorFormat getInstance() {
162         return getInstance(Locale.getDefault());
163     }
164 
165     /**
166      * Returns the default real vector format for the given locale.
167      * @param locale the specific locale used by the format.
168      * @return the real vector format specific to the given locale.
169      */
170     public static RealVectorFormat getInstance(final Locale locale) {
171         return new RealVectorFormat(CompositeFormat.getDefaultNumberFormat(locale));
172     }
173 
174     /**
175      * This method calls {@link #format(RealVector,StringBuffer,FieldPosition)}.
176      *
177      * @param v RealVector object to format.
178      * @return a formatted vector.
179      */
180     public String format(RealVector v) {
181         return format(v, new StringBuffer(), new FieldPosition(0)).toString();
182     }
183 
184     /**
185      * Formats a {@link RealVector} object to produce a string.
186      * @param vector the object to format.
187      * @param toAppendTo where the text is to be appended
188      * @param pos On input: an alignment field, if desired. On output: the
189      *            offsets of the alignment field
190      * @return the value passed in as toAppendTo.
191      */
192     public StringBuffer format(RealVector vector, StringBuffer toAppendTo,
193                                FieldPosition pos) {
194 
195         pos.setBeginIndex(0);
196         pos.setEndIndex(0);
197 
198         // format prefix
199         toAppendTo.append(prefix);
200 
201         // format components
202         for (int i = 0; i < vector.getDimension(); ++i) {
203             if (i > 0) {
204                 toAppendTo.append(separator);
205             }
206             CompositeFormat.formatDouble(vector.getEntry(i), format, toAppendTo, pos);
207         }
208 
209         // format suffix
210         toAppendTo.append(suffix);
211 
212         return toAppendTo;
213     }
214 
215     /**
216      * Parse a string to produce a {@link RealVector} object.
217      *
218      * @param source String to parse.
219      * @return the parsed {@link RealVector} object.
220      * @throws MathParseException if the beginning of the specified string
221      * cannot be parsed.
222      */
223     public ArrayRealVector parse(String source) {
224         final ParsePosition parsePosition = new ParsePosition(0);
225         final ArrayRealVector result = parse(source, parsePosition);
226         if (parsePosition.getIndex() == 0) {
227             throw new MathParseException(source,
228                                          parsePosition.getErrorIndex(),
229                                          ArrayRealVector.class);
230         }
231         return result;
232     }
233 
234     /**
235      * Parse a string to produce a {@link RealVector} object.
236      *
237      * @param source String to parse.
238      * @param pos input/ouput parsing parameter.
239      * @return the parsed {@link RealVector} object.
240      */
241     public ArrayRealVector parse(String source, ParsePosition pos) {
242         int initialIndex = pos.getIndex();
243 
244         // parse prefix
245         CompositeFormat.parseAndIgnoreWhitespace(source, pos);
246         if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) {
247             return null;
248         }
249 
250         // parse components
251         List<Number> components = new ArrayList<Number>();
252         for (boolean loop = true; loop;){
253 
254             if (!components.isEmpty()) {
255                 CompositeFormat.parseAndIgnoreWhitespace(source, pos);
256                 if (!CompositeFormat.parseFixedstring(source, trimmedSeparator, pos)) {
257                     loop = false;
258                 }
259             }
260 
261             if (loop) {
262                 CompositeFormat.parseAndIgnoreWhitespace(source, pos);
263                 Number component = CompositeFormat.parseNumber(source, format, pos);
264                 if (component != null) {
265                     components.add(component);
266                 } else {
267                     // invalid component
268                     // set index back to initial, error index should already be set
269                     pos.setIndex(initialIndex);
270                     return null;
271                 }
272             }
273 
274         }
275 
276         // parse suffix
277         CompositeFormat.parseAndIgnoreWhitespace(source, pos);
278         if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) {
279             return null;
280         }
281 
282         // build vector
283         double[] data = new double[components.size()];
284         for (int i = 0; i < data.length; ++i) {
285             data[i] = components.get(i).doubleValue();
286         }
287         return new ArrayRealVector(data, false);
288     }
289 }