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