All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jodd.datetime.format.AbstractFormatter Maven / Gradle / Ivy

There is a newer version: 5.3.0
Show newest version
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package jodd.datetime.format;

import jodd.datetime.DateTimeStamp;
import jodd.datetime.JDateTime;
import jodd.util.CharUtil;

/**
 * Abstract formatter for easier {@link JdtFormatter} implementations.
 * 

* For setting date and time, default formatter parses input String against * specified format. It extracts parts of input string upon patterns * and then each part is converted to a number for a date/time information. * It doesn't ignore any non-number character. If conversion fails, * null is returned. * *

* Getting date time is also user friendly. Specified format may not only * contains patterns but also any text. To remove errors in decoding when * text may be recognize as one of patterns, format text may be quoted * with the special escape sign. Double quote in the text will be decoded * as a single quote, of course. *

* * It is not necessary to have parsers for all patterns. */ public abstract class AbstractFormatter implements JdtFormatter { /** * Available patterns list. Used by {@link #findPattern(char[], int)} * when parsing date time format. Each formatter will have its own set of * patterns, in strictly defined order. */ protected char[][] patterns; /** * Escape character. */ protected char escapeChar = '\''; /** * Converts String array of patterns to char arrays. */ protected void preparePatterns(String[] spat) { patterns = new char[spat.length][]; for (int i = 0; i < spat.length; i++) { patterns[i] = spat[i].toCharArray(); } } /** * Finds the longest pattern in provided format starting from specified position. * All available patterns are stored in {@link #patterns}. * * @param format date time format to examine * @param i starting index * * @return 0-based index of founded pattern, or -1 if pattern not found */ protected int findPattern(char[] format, int i) { int frmtc_len = format.length; boolean match; int n, lastn = -1; int maxLen = 0; for (n = 0; n < patterns.length; n++) { char[] curr = patterns[n]; // current pattern from the pattern list if (i > frmtc_len - curr.length) { continue; } match = true; int delta = 0; while (delta < curr.length) { // match given pattern if (curr[delta] != format[i + delta]) { match = false; // no match, go to next break; } delta++; } if (match) { // match if (patterns[n].length > maxLen) { // find longest match lastn = n; maxLen = patterns[n].length; } } } return lastn; } // ---------------------------------------------------------------- convert /** * Creates a date-time string for founded pattern. Founded patterns * is identified by its {@link #patterns} index. * * @param patternIndex index of founded pattern * @param jdt date time information */ protected abstract String convertPattern(int patternIndex, JDateTime jdt); /** * {@inheritDoc} * @see JdtFormatter#convert(JDateTime, String) */ public String convert(JDateTime jdt, String format) { char[] fmtc = format.toCharArray(); int fmtc_len = fmtc.length; StringBuilder result = new StringBuilder(fmtc_len); int i = 0; while (i < fmtc_len) { if (fmtc[i] == escapeChar) { // quote founded int end = i + 1; while (end < fmtc_len) { if (fmtc[end] == escapeChar) { // second quote founded if (end + 1 < fmtc_len) { end++; if (fmtc[end] == escapeChar) { // skip double quotes result.append(escapeChar); // and continue } else { break; } } } else { result.append(fmtc[end]); } end++; } i = end; continue; // end of quoted string, continue the main loop } int n = findPattern(fmtc, i); if (n != -1) { // pattern founded result.append(convertPattern(n, jdt)); i += patterns[n].length; } else { result.append(fmtc[i]); i++; } } return result.toString(); } // ---------------------------------------------------------------- parse /** * Parses value for matched pattern. Founded patterns * is identified by its {@link #patterns} index. * Note that value may represent both integer and decimals. * May throw {@link NumberFormatException}. * * @param patternIndex index of founded pattern * @param value value to parse, no spaces or tabs * @param destination destination to modify */ protected abstract void parseValue(int patternIndex, String value, DateTimeStamp destination); /** * {@inheritDoc} * @see JdtFormatter#parse(String, String) */ public DateTimeStamp parse(String value, String format) { char[] valueChars = value.toCharArray(); char[] formatChars = format.toCharArray(); int i = 0, j = 0; int valueLen = valueChars.length; int formatLen = formatChars.length; // detect if separators are used boolean useSeparators = true; if (valueLen == formatLen) { useSeparators = false; for (char valueChar : valueChars) { if (!CharUtil.isDigit(valueChar)) { useSeparators = true; break; } } } DateTimeStamp time = new DateTimeStamp(); StringBuilder sb = new StringBuilder(); while (true) { int n = findPattern(formatChars, i); if (n != -1) { // pattern founded int patternLen = patterns[n].length; i += patternLen; sb.setLength(0); if (!useSeparators) { for (int k = 0; k < patternLen; k++) { sb.append(valueChars[j++]); } } else { char next = 0xFFFF; if (i < formatLen) { next = formatChars[i]; // next = delimiter } while ((j < valueLen) && (valueChars[j] != next)) { char scj = valueChars[j]; if ((scj != ' ') && (scj != '\t')) { // ignore surrounding whitespaces sb.append(valueChars[j]); } j++; } } parseValue(n, sb.toString(), time); } else { if (!useSeparators) { throw new IllegalArgumentException("Invalid value: " + value); } if (formatChars[i] == valueChars[j]) { j++; } i++; } if ((i == formatLen) || (j == valueLen)) { break; } } return time; } // ---------------------------------------------------------------- util /** * Prints values 00 - 99. */ protected String print2(int value) { if (value < 0) { throw new IllegalArgumentException("Value must be positive: " + value); } if (value < 10) { return '0' + Integer.toString(value); } if (value < 100) { return Integer.toString(value); } throw new IllegalArgumentException("Value too big: " + value); } /** * Prints values 00 - 999. */ protected String print3(int value) { if (value < 0) { throw new IllegalArgumentException("Value must be positive: " + value); } if (value < 10) { return "00" + Integer.toString(value); } if (value < 100) { return '0' + Integer.toString(value); } if (value < 1000) { return Integer.toString(value); } throw new IllegalArgumentException("Value too big: " + value); } /** * Prints 4 digits and optional minus sign. */ protected String printPad4(int value) { char[] result = new char[4]; int count = 0; if (value < 0) { result[count++] = '-'; value = -value; } String str = Integer.toString(value); if (value < 10) { result[count++] = '0'; result[count++] = '0'; result[count++] = '0'; result[count++] = str.charAt(0); } else if (value < 100) { result[count++] = '0'; result[count++] = '0'; result[count++] = str.charAt(0); result[count++] = str.charAt(1); } else if (value < 1000) { result[count++] = '0'; result[count++] = str.charAt(0); result[count++] = str.charAt(1); result[count++] = str.charAt(2); } else { if (count > 0) { return '-' + str; } return str; } return new String(result, 0, count); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy