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.math3.linear;
019    
020    import java.text.FieldPosition;
021    import java.text.NumberFormat;
022    import java.text.ParsePosition;
023    import java.util.ArrayList;
024    import java.util.List;
025    import java.util.Locale;
026    
027    import org.apache.commons.math3.exception.MathParseException;
028    import org.apache.commons.math3.util.CompositeFormat;
029    
030    /**
031     * Formats a vector in components list format "{v0; v1; ...; vk-1}".
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 $Id: RealVectorFormat.java 1416643 2012-12-03 19:37:14Z tn $
042     * @since 2.0
043     */
044    public class RealVectorFormat {
045    
046        /** The default prefix: "{". */
047        private static final String DEFAULT_PREFIX = "{";
048        /** The default suffix: "}". */
049        private static final String DEFAULT_SUFFIX = "}";
050        /** The default separator: ", ". */
051        private static final String DEFAULT_SEPARATOR = "; ";
052        /** Prefix. */
053        private final String prefix;
054        /** Suffix. */
055        private final String suffix;
056        /** Separator. */
057        private final String separator;
058        /** Trimmed prefix. */
059        private final String trimmedPrefix;
060        /** Trimmed suffix. */
061        private final String trimmedSuffix;
062        /** Trimmed separator. */
063        private final String trimmedSeparator;
064        /** The format used for components. */
065        private final NumberFormat format;
066    
067        /**
068         * Create an instance with default settings.
069         * <p>The instance uses the default prefix, suffix and separator:
070         * "{", "}", and "; " and the default number format for components.</p>
071         */
072        public RealVectorFormat() {
073            this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR,
074                 CompositeFormat.getDefaultNumberFormat());
075        }
076    
077        /**
078         * Create an instance with a custom number format for components.
079         * @param format the custom format for components.
080         */
081        public RealVectorFormat(final NumberFormat format) {
082            this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
083        }
084    
085        /**
086         * Create an instance with custom prefix, suffix and separator.
087         * @param prefix prefix to use instead of the default "{"
088         * @param suffix suffix to use instead of the default "}"
089         * @param separator separator to use instead of the default "; "
090         */
091        public RealVectorFormat(final String prefix, final String suffix,
092                                final String separator) {
093            this(prefix, suffix, separator,
094                 CompositeFormat.getDefaultNumberFormat());
095        }
096    
097        /**
098         * Create an instance with custom prefix, suffix, separator and format
099         * 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    }