001/*
002 *  Copyright 2001-2005 Stephen Colebourne
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.joda.time.format;
017
018import java.io.IOException;
019import java.io.Writer;
020
021/**
022 * Utility methods used by formatters.
023 * <p>
024 * FormatUtils is thread-safe and immutable.
025 *
026 * @author Brian S O'Neill
027 * @since 1.0
028 */
029public class FormatUtils {
030
031    private static final double LOG_10 = Math.log(10);
032
033    /**
034     * Restricted constructor.
035     */
036    private FormatUtils() {
037    }
038
039    /**
040     * Converts an integer to a string, prepended with a variable amount of '0'
041     * pad characters, and appends it to the given buffer.
042     *
043     * <p>This method is optimized for converting small values to strings.
044     *
045     * @param buf receives integer converted to a string
046     * @param value value to convert to a string
047     * @param size minumum amount of digits to append
048     */
049    public static void appendPaddedInteger(StringBuffer buf, int value, int size) {
050        if (value < 0) {
051            buf.append('-');
052            if (value != Integer.MIN_VALUE) {
053                value = -value;
054            } else {
055                for (; size > 10; size--) {
056                    buf.append('0');
057                }
058                buf.append("" + -(long)Integer.MIN_VALUE);
059                return;
060            }
061        }
062        if (value < 10) {
063            for (; size > 1; size--) {
064                buf.append('0');
065            }
066            buf.append((char)(value + '0'));
067        } else if (value < 100) {
068            for (; size > 2; size--) {
069                buf.append('0');
070            }
071            // Calculate value div/mod by 10 without using two expensive
072            // division operations. (2 ^ 27) / 10 = 13421772. Add one to
073            // value to correct rounding error.
074            int d = ((value + 1) * 13421772) >> 27;
075            buf.append((char) (d + '0'));
076            // Append remainder by calculating (value - d * 10).
077            buf.append((char) (value - (d << 3) - (d << 1) + '0'));
078        } else {
079            int digits;
080            if (value < 1000) {
081                digits = 3;
082            } else if (value < 10000) {
083                digits = 4;
084            } else {
085                digits = (int)(Math.log(value) / LOG_10) + 1;
086            }
087            for (; size > digits; size--) {
088                buf.append('0');
089            }
090            buf.append(Integer.toString(value));
091        }
092    }
093
094    /**
095     * Converts an integer to a string, prepended with a variable amount of '0'
096     * pad characters, and appends it to the given buffer.
097     *
098     * <p>This method is optimized for converting small values to strings.
099     *
100     * @param buf receives integer converted to a string
101     * @param value value to convert to a string
102     * @param size minumum amount of digits to append
103     */
104    public static void appendPaddedInteger(StringBuffer buf, long value, int size) {
105        int intValue = (int)value;
106        if (intValue == value) {
107            appendPaddedInteger(buf, intValue, size);
108        } else if (size <= 19) {
109            buf.append(Long.toString(value));
110        } else {
111            if (value < 0) {
112                buf.append('-');
113                if (value != Long.MIN_VALUE) {
114                    value = -value;
115                } else {
116                    for (; size > 19; size--) {
117                        buf.append('0');
118                    }
119                    buf.append("9223372036854775808");
120                    return;
121                }
122            }
123            int digits = (int)(Math.log(value) / LOG_10) + 1;
124            for (; size > digits; size--) {
125                buf.append('0');
126            }
127            buf.append(Long.toString(value));
128        }
129    }
130
131    /**
132     * Converts an integer to a string, prepended with a variable amount of '0'
133     * pad characters, and writes it to the given writer.
134     *
135     * <p>This method is optimized for converting small values to strings.
136     *
137     * @param out receives integer converted to a string
138     * @param value value to convert to a string
139     * @param size minumum amount of digits to append
140     */
141    public static void writePaddedInteger(Writer out, int value, int size)
142        throws IOException
143    {
144        if (value < 0) {
145            out.write('-');
146            if (value != Integer.MIN_VALUE) {
147                value = -value;
148            } else {
149                for (; size > 10; size--) {
150                    out.write('0');
151                }
152                out.write("" + -(long)Integer.MIN_VALUE);
153                return;
154            }
155        }
156        if (value < 10) {
157            for (; size > 1; size--) {
158                out.write('0');
159            }
160            out.write(value + '0');
161        } else if (value < 100) {
162            for (; size > 2; size--) {
163                out.write('0');
164            }
165            // Calculate value div/mod by 10 without using two expensive
166            // division operations. (2 ^ 27) / 10 = 13421772. Add one to
167            // value to correct rounding error.
168            int d = ((value + 1) * 13421772) >> 27;
169            out.write(d + '0');
170            // Append remainder by calculating (value - d * 10).
171            out.write(value - (d << 3) - (d << 1) + '0');
172        } else {
173            int digits;
174            if (value < 1000) {
175                digits = 3;
176            } else if (value < 10000) {
177                digits = 4;
178            } else {
179                digits = (int)(Math.log(value) / LOG_10) + 1;
180            }
181            for (; size > digits; size--) {
182                out.write('0');
183            }
184            out.write(Integer.toString(value));
185        }
186    }
187
188    /**
189     * Converts an integer to a string, prepended with a variable amount of '0'
190     * pad characters, and writes it to the given writer.
191     *
192     * <p>This method is optimized for converting small values to strings.
193     *
194     * @param out receives integer converted to a string
195     * @param value value to convert to a string
196     * @param size minumum amount of digits to append
197     */
198    public static void writePaddedInteger(Writer out, long value, int size)
199        throws IOException
200    {
201        int intValue = (int)value;
202        if (intValue == value) {
203            writePaddedInteger(out, intValue, size);
204        } else if (size <= 19) {
205            out.write(Long.toString(value));
206        } else {
207            if (value < 0) {
208                out.write('-');
209                if (value != Long.MIN_VALUE) {
210                    value = -value;
211                } else {
212                    for (; size > 19; size--) {
213                        out.write('0');
214                    }
215                    out.write("9223372036854775808");
216                    return;
217                }
218            }
219            int digits = (int)(Math.log(value) / LOG_10) + 1;
220            for (; size > digits; size--) {
221                out.write('0');
222            }
223            out.write(Long.toString(value));
224        }
225    }
226
227    /**
228     * Converts an integer to a string, and appends it to the given buffer.
229     *
230     * <p>This method is optimized for converting small values to strings.
231     *
232     * @param buf receives integer converted to a string
233     * @param value value to convert to a string
234     */
235    public static void appendUnpaddedInteger(StringBuffer buf, int value) {
236        if (value < 0) {
237            buf.append('-');
238            if (value != Integer.MIN_VALUE) {
239                value = -value;
240            } else {
241                buf.append("" + -(long)Integer.MIN_VALUE);
242                return;
243            }
244        }
245        if (value < 10) {
246            buf.append((char)(value + '0'));
247        } else if (value < 100) {
248            // Calculate value div/mod by 10 without using two expensive
249            // division operations. (2 ^ 27) / 10 = 13421772. Add one to
250            // value to correct rounding error.
251            int d = ((value + 1) * 13421772) >> 27;
252            buf.append((char) (d + '0'));
253            // Append remainder by calculating (value - d * 10).
254            buf.append((char) (value - (d << 3) - (d << 1) + '0'));
255        } else {
256            buf.append(Integer.toString(value));
257        }
258    }
259
260    /**
261     * Converts an integer to a string, and appends it to the given buffer.
262     *
263     * <p>This method is optimized for converting small values to strings.
264     *
265     * @param buf receives integer converted to a string
266     * @param value value to convert to a string
267     */
268    public static void appendUnpaddedInteger(StringBuffer buf, long value) {
269        int intValue = (int)value;
270        if (intValue == value) {
271            appendUnpaddedInteger(buf, intValue);
272        } else {
273            buf.append(Long.toString(value));
274        }
275    }
276
277    /**
278     * Converts an integer to a string, and writes it to the given writer.
279     *
280     * <p>This method is optimized for converting small values to strings.
281     *
282     * @param out receives integer converted to a string
283     * @param value value to convert to a string
284     */
285    public static void writeUnpaddedInteger(Writer out, int value)
286        throws IOException
287    {
288        if (value < 0) {
289            out.write('-');
290            if (value != Integer.MIN_VALUE) {
291                value = -value;
292            } else {
293                out.write("" + -(long)Integer.MIN_VALUE);
294                return;
295            }
296        }
297        if (value < 10) {
298            out.write(value + '0');
299        } else if (value < 100) {
300            // Calculate value div/mod by 10 without using two expensive
301            // division operations. (2 ^ 27) / 10 = 13421772. Add one to
302            // value to correct rounding error.
303            int d = ((value + 1) * 13421772) >> 27;
304            out.write(d + '0');
305            // Append remainder by calculating (value - d * 10).
306            out.write(value - (d << 3) - (d << 1) + '0');
307        } else {
308            out.write(Integer.toString(value));
309        }
310    }
311
312    /**
313     * Converts an integer to a string, and writes it to the given writer.
314     *
315     * <p>This method is optimized for converting small values to strings.
316     *
317     * @param out receives integer converted to a string
318     * @param value value to convert to a string
319     */
320    public static void writeUnpaddedInteger(Writer out, long value)
321        throws IOException
322    {
323        int intValue = (int)value;
324        if (intValue == value) {
325            writeUnpaddedInteger(out, intValue);
326        } else {
327            out.write(Long.toString(value));
328        }
329    }
330
331    /**
332     * Calculates the number of decimal digits for the given value,
333     * including the sign.
334     */
335    public static int calculateDigitCount(long value) {
336        if (value < 0) {
337            if (value != Long.MIN_VALUE) {
338                return calculateDigitCount(-value) + 1;
339            } else {
340                return 20;
341            }
342        }
343        return 
344            (value < 10 ? 1 :
345             (value < 100 ? 2 :
346              (value < 1000 ? 3 :
347               (value < 10000 ? 4 :
348                ((int)(Math.log(value) / LOG_10) + 1)))));
349    }
350
351    static int parseTwoDigits(String text, int position) {
352        int value = text.charAt(position) - '0';
353        return ((value << 3) + (value << 1)) + text.charAt(position + 1) - '0';
354    }
355
356    static String createErrorMessage(final String text, final int errorPos) {
357        int sampleLen = errorPos + 32;
358        String sampleText;
359        if (text.length() <= sampleLen + 3) {
360            sampleText = text;
361        } else {
362            sampleText = text.substring(0, sampleLen).concat("...");
363        }
364        
365        if (errorPos <= 0) {
366            return "Invalid format: \"" + sampleText + '"';
367        }
368        
369        if (errorPos >= text.length()) {
370            return "Invalid format: \"" + sampleText + "\" is too short";
371        }
372        
373        return "Invalid format: \"" + sampleText + "\" is malformed at \"" +
374            sampleText.substring(errorPos) + '"';
375    }
376
377}