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 package org.apache.commons.math4.legacy.util; 18 19 import java.text.FieldPosition; 20 import java.text.NumberFormat; 21 import java.text.ParsePosition; 22 import java.util.Locale; 23 24 /** 25 * Base class for formatters of composite objects (complex numbers, vectors ...). 26 * 27 */ 28 public final class CompositeFormat { 29 30 /** 31 * Class contains only static methods. 32 */ 33 private CompositeFormat() {} 34 35 /** 36 * Create a default number format. The default number format is based on 37 * {@link NumberFormat#getInstance()} with the only customizing that the 38 * maximum number of fraction digits is set to 10. 39 * @return the default number format. 40 */ 41 public static NumberFormat getDefaultNumberFormat() { 42 return getDefaultNumberFormat(Locale.getDefault()); 43 } 44 45 /** 46 * Create a default number format. The default number format is based on 47 * {@link NumberFormat#getInstance(java.util.Locale)} with the only 48 * customizing that the maximum number of fraction digits is set to 10. 49 * @param locale the specific locale used by the format. 50 * @return the default number format specific to the given locale. 51 */ 52 public static NumberFormat getDefaultNumberFormat(final Locale locale) { 53 final NumberFormat nf = NumberFormat.getInstance(locale); 54 nf.setMaximumFractionDigits(10); 55 return nf; 56 } 57 58 /** 59 * Parses <code>source</code> until a non-whitespace character is found. 60 * 61 * @param source the string to parse 62 * @param pos input/output parsing parameter. On output, <code>pos</code> 63 * holds the index of the next non-whitespace character. 64 */ 65 public static void parseAndIgnoreWhitespace(final String source, 66 final ParsePosition pos) { 67 parseNextCharacter(source, pos); 68 pos.setIndex(pos.getIndex() - 1); 69 } 70 71 /** 72 * Parses <code>source</code> until a non-whitespace character is found. 73 * 74 * @param source the string to parse 75 * @param pos input/output parsing parameter. 76 * @return the first non-whitespace character. 77 */ 78 public static char parseNextCharacter(final String source, 79 final ParsePosition pos) { 80 int index = pos.getIndex(); 81 final int n = source.length(); 82 char ret = 0; 83 84 if (index < n) { 85 char c; 86 do { 87 c = source.charAt(index++); 88 } while (Character.isWhitespace(c) && index < n); 89 pos.setIndex(index); 90 91 if (index < n) { 92 ret = c; 93 } 94 } 95 96 return ret; 97 } 98 99 /** 100 * Parses <code>source</code> for special double values. These values 101 * include Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. 102 * 103 * @param source the string to parse 104 * @param value the special value to parse. 105 * @param pos input/output parsing parameter. 106 * @return the special number. 107 */ 108 private static Number parseNumber(final String source, final double value, 109 final ParsePosition pos) { 110 Number ret = null; 111 112 StringBuilder sb = new StringBuilder(); 113 sb.append('('); 114 sb.append(value); 115 sb.append(')'); 116 117 final int n = sb.length(); 118 final int startIndex = pos.getIndex(); 119 final int endIndex = startIndex + n; 120 if (endIndex < source.length() && 121 source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) { 122 ret = Double.valueOf(value); 123 pos.setIndex(endIndex); 124 } 125 126 return ret; 127 } 128 129 /** 130 * Parses <code>source</code> for a number. This method can parse normal, 131 * numeric values as well as special values. These special values include 132 * Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY. 133 * 134 * @param source the string to parse 135 * @param format the number format used to parse normal, numeric values. 136 * @param pos input/output parsing parameter. 137 * @return the parsed number. 138 */ 139 public static Number parseNumber(final String source, final NumberFormat format, 140 final ParsePosition pos) { 141 final int startIndex = pos.getIndex(); 142 Number number = format.parse(source, pos); 143 final int endIndex = pos.getIndex(); 144 145 // check for error parsing number 146 if (startIndex == endIndex) { 147 // try parsing special numbers 148 final double[] special = { 149 Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY 150 }; 151 for (int i = 0; i < special.length; ++i) { 152 number = parseNumber(source, special[i], pos); 153 if (number != null) { 154 break; 155 } 156 } 157 } 158 159 return number; 160 } 161 162 /** 163 * Parse <code>source</code> for an expected fixed string. 164 * @param source the string to parse 165 * @param expected expected string 166 * @param pos input/output parsing parameter. 167 * @return true if the expected string was there 168 */ 169 public static boolean parseFixedstring(final String source, 170 final String expected, 171 final ParsePosition pos) { 172 173 final int startIndex = pos.getIndex(); 174 final int endIndex = startIndex + expected.length(); 175 if (startIndex >= source.length() || 176 endIndex > source.length() || 177 source.substring(startIndex, endIndex).compareTo(expected) != 0) { 178 // set index back to start, error index should be the start index 179 pos.setIndex(startIndex); 180 pos.setErrorIndex(startIndex); 181 return false; 182 } 183 184 // the string was here 185 pos.setIndex(endIndex); 186 return true; 187 } 188 189 /** 190 * Formats a double value to produce a string. In general, the value is 191 * formatted using the formatting rules of <code>format</code>. There are 192 * three exceptions to this: 193 * <ol> 194 * <li>NaN is formatted as '(NaN)'</li> 195 * <li>Positive infinity is formatted as '(Infinity)'</li> 196 * <li>Negative infinity is formatted as '(-Infinity)'</li> 197 * </ol> 198 * 199 * @param value the double to format. 200 * @param format the format used. 201 * @param toAppendTo where the text is to be appended 202 * @param pos On input: an alignment field, if desired. On output: the 203 * offsets of the alignment field 204 * @return the value passed in as toAppendTo. 205 */ 206 public static StringBuffer formatDouble(final double value, final NumberFormat format, 207 final StringBuffer toAppendTo, 208 final FieldPosition pos) { 209 if( Double.isNaN(value) || Double.isInfinite(value) ) { 210 toAppendTo.append('('); 211 toAppendTo.append(value); 212 toAppendTo.append(')'); 213 } else { 214 format.format(value, toAppendTo, pos); 215 } 216 return toAppendTo; 217 } 218 }