// -*- c++ -*- /* * Jakelib2 - General purpose C++ library * Copyright (C) 2001 Florian Wolff (florian@donuz.de) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: SimpleDateFormat.jlc,v 1.9 2003/10/12 13:16:11 heiko Exp $ */ #include "jakelib2.h" #include "jakelib2/text/SimpleDateFormat.h" #include "jakelib2/text/DateFormat.h" #include "jakelib2/text/FieldPosition.h" #include "jakelib2/util/all.h" #include "jakelib2/lang/all.h" using namespace jakelib::lang; using namespace jakelib::text; using namespace jakelib::util; JAKELIB_IMPLEMENT_CLASS("jakelib.text.SimpleDateFormat", SimpleDateFormat, DateFormat); JAKELIB_IMPLEMENT_ARRAY(SimpleDateFormat); #pragma javasyntax /*****************************************************************************\ * SimpleDateFormat | *****************************************************************************/ SimpleDateFormat::SimpleDateFormat() { this->locale = Locale::getDefault(); this->syms = new DateFormatSymbols(this->locale); this->pattern = syms->getDateTimeFormats()->get(DateFormat::SHORT); compilePattern(); } SimpleDateFormat::SimpleDateFormat(jakelib::lang::String *pattern) { this->locale = Locale::getDefault(); this->syms = new DateFormatSymbols(this->locale); this->pattern = pattern; compilePattern(); } SimpleDateFormat::SimpleDateFormat(jakelib::lang::String *pattern, DateFormatSymbols *syms) { this->locale = Locale::getDefault(); this->syms = syms; this->pattern = pattern; compilePattern(); } SimpleDateFormat::SimpleDateFormat(jakelib::lang::String *pattern, Locale *locale) { this->locale = locale; this->syms = new DateFormatSymbols(locale); this->pattern = pattern; compilePattern(); } /*****************************************************************************\ * format | *****************************************************************************/ String *SimpleDateFormat::format(Date *date) { return DateFormat::format(date); } StringBuffer *SimpleDateFormat::format(jakelib::util::Date *date, jakelib::lang::StringBuffer *sb, jakelib::text::FieldPosition *fp) { if (date == null) { throw new NullPointerException(); } if (compiledPattern == null) { throw new NullPointerException(); } GregorianCalendar * calendar = new GregorianCalendar(); calendar->setTime(date); for (int idx=0; idx < compiledPattern->size(); idx++) { ParseToken * pt = (ParseToken *) compiledPattern->get(idx); int value; String * valString; switch (pt->element) { case ParseToken::STRING : sb->append(pt->strVal); break; case ParseToken::ERA : value = calendar->get(Calendar::ERA); sb->append("(ERA NOT LOCALIZED!)"); break; case ParseToken::YEAR : value = calendar->get(Calendar::YEAR); valString = String::valueOf(value); if (pt->length <= 2) sb->append(valString->substring(2)); else sb->append(valString); break; case ParseToken::MONTH_IN_YEAR : value = calendar->get(Calendar::MONTH); if (pt->length == 1) { sb->append(value+1); } else if(pt->length == 2) { if (value < 9) { sb->append("0"); } sb->append(value+1); } else if (pt->length == 3) { sb->append(this->syms->getShortMonths()->get(value)); } else { sb->append(this->syms->getMonths()->get(value)); } break; case ParseToken::WEEK_IN_YEAR : value = calendar->get(Calendar::WEEK_OF_YEAR); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::WEEK_IN_MONTH : value = calendar->get(Calendar::WEEK_OF_MONTH); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::DAY_IN_YEAR : value = calendar->get(Calendar::DAY_OF_YEAR); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::DAY_IN_MONTH : value = calendar->get(Calendar::DAY_OF_MONTH); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::DAY_OF_WEEK_IN_MONTH : value = calendar->get(Calendar::DAY_OF_WEEK_IN_MONTH); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::DAY_IN_WEEK : value = calendar->get(Calendar::DAY_OF_WEEK); if (pt->length < 4) sb->append(this->syms->getShortWeekdays()->get(value)); else sb->append(this->syms->getWeekdays()->get(value)); break; case ParseToken::AM_PM_MARKER : value = calendar->get(Calendar::AM_PM); sb->append(syms->getAmPmStrings()->get(value)); break; case ParseToken::HOUR_0_23 : value = calendar->get(Calendar::HOUR_OF_DAY); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::HOUR_1_24 : value = calendar->get(Calendar::HOUR_OF_DAY) + 1; valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::HOUR_0_11 : value = calendar->get(Calendar::HOUR)-1; valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::HOUR_1_12 : value = calendar->get(Calendar::HOUR); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::MINUTE : value = calendar->get(Calendar::MINUTE); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::SECOND : value = calendar->get(Calendar::SECOND); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::MILLISECOND : value = calendar->get(Calendar::MILLISECOND); valString = String::valueOf(value); if (pt->length > valString->length()) { while (valString->length() < pt->length) { valString = "0" .. valString; } } sb->append(valString); break; case ParseToken::TIMEZONE : // FIXME: TIMEZONE not available in Calendar! //TimeZone * tz = calendar->getTimeZone(); //value = calendar->get(Calendar::ZONE_OFFSET); sb->append("[NOT IMPLEMENTED]"); break; case ParseToken::TIMEZONE_OFFSET : sb->append("[NOT IMPLEMENTED]"); break; default: sb->append("[NOT FORMATABLE KEY]"); break; } } return sb; } /*****************************************************************************\ * compilePattern | *****************************************************************************/ void SimpleDateFormat::compilePattern() { if (this->pattern == null) { this->compiledPattern = null; throw new NullPointerException(); } else { this->compiledPattern = new ArrayList(); jboolean isQuoted = false; StringBuffer text; for (jint idx=0; idx < this->pattern->length(); idx++) { jchar currentChar = pattern->charAt(idx); if (!isQuoted) { jint amount = countEqualChars(pattern, idx, currentChar); switch (currentChar) { case 'G' : // ERA designator. compiledPattern->add(new ParseToken(ParseToken::ERA, amount, null)); idx = idx + amount - 1; break; case 'y' : // Year. compiledPattern->add(new ParseToken(ParseToken::YEAR, amount, null)); idx = idx + amount - 1; break; case 'M' : // Month. compiledPattern->add(new ParseToken(ParseToken::MONTH_IN_YEAR, amount, null)); idx = idx + amount - 1; break; case 'w' : // week in year. compiledPattern->add(new ParseToken(ParseToken::WEEK_IN_YEAR, amount, null)); idx = idx + amount - 1; break; case 'W' : // week in month. compiledPattern->add(new ParseToken(ParseToken::WEEK_IN_MONTH, amount, null)); idx = idx + amount - 1; break; case 'D' : // day in year. compiledPattern->add(new ParseToken(ParseToken::DAY_IN_YEAR, amount, null)); idx = idx + amount - 1; break; case 'd' : // day in month. compiledPattern->add(new ParseToken(ParseToken::DAY_IN_MONTH, amount, null)); idx = idx + amount - 1; break; case 'F' : // day of week in month. compiledPattern->add(new ParseToken(ParseToken::DAY_OF_WEEK_IN_MONTH, amount, null)); idx = idx + amount - 1; break; case 'E' : // day in week. compiledPattern->add(new ParseToken(ParseToken::DAY_IN_WEEK, amount, null)); idx = idx + amount - 1; break; case 'a' : // am/pm compiledPattern->add(new ParseToken(ParseToken::AM_PM_MARKER, amount, null)); idx = idx + amount - 1; break; case 'H' : // hour (0-23) compiledPattern->add(new ParseToken(ParseToken::HOUR_0_23, amount, null)); idx = idx + amount - 1; break; case 'k' : // hour (1-24) compiledPattern->add(new ParseToken(ParseToken::HOUR_1_24, amount, null)); idx = idx + amount - 1; break; case 'K' : // hour (0-11) compiledPattern->add(new ParseToken(ParseToken::HOUR_0_11, amount, null)); idx = idx + amount - 1; break; case 'h' : // hour (1-12). compiledPattern->add(new ParseToken(ParseToken::HOUR_1_12, amount, null)); idx = idx + amount - 1; break; case 'm' : // minute compiledPattern->add(new ParseToken(ParseToken::MINUTE, amount, null)); idx = idx + amount - 1; break; case 's' : // second compiledPattern->add(new ParseToken(ParseToken::SECOND, amount, null)); idx = idx + amount - 1; break; case 'S' : // millis compiledPattern->add(new ParseToken(ParseToken::MILLISECOND, amount, null)); idx = idx + amount - 1; break; case 'z' : // timzone string compiledPattern->add(new ParseToken(ParseToken::TIMEZONE, amount, null)); idx = idx + amount - 1; break; case 'Z' : // timezone offset compiledPattern->add(new ParseToken(ParseToken::TIMEZONE_OFFSET, amount, null)); idx = idx + amount - 1; break; case '\'' : // Quote! isQuoted = true; break; default: //STRING! compiledPattern->add(new ParseToken(ParseToken::STRING, 1, String::valueOf(currentChar))); break; } } else { if (currentChar == '\'') { if (idx <= pattern->length()-1 && pattern->charAt(idx+1) != '\'') { isQuoted = false; compiledPattern->add(new ParseToken(ParseToken::STRING, text.length(), text.toString())); text.setLength(0); } else { // FIXME: Geht am Ende nicht text.append("\'"); idx++; } } else { text.append(currentChar); } } } } } /*****************************************************************************\ * parse | *****************************************************************************/ jakelib::util::Date * SimpleDateFormat::parse(jakelib::lang::String *text, jakelib::text::ParsePosition *pos) { // set calendar to default value. // FIXME: constructor with current locale needed! GregorianCalendar * calendar = new GregorianCalendar(); calendar->setTimeInMillis(0l); jint position = 0; jboolean am = true; jint amPmHour = -1; // iterate through all parsed tokens and try to extract values from String for (int idx=0; idx < compiledPattern->size(); idx++) { ParseToken * pt = (ParseToken *) compiledPattern->get(idx); //System::out->println("idx: " .. idx); String * textElem; switch (pt->element) { case ParseToken::STRING : textElem = text->substring(position, position+(pt->length)); //System::out->println("TextElem:" .. textElem .. " strVal:" .. pt->strVal .. "length:" .. pt->length); if (textElem != null && textElem->equals(pt->strVal)) { position = position + (pt->length); } else { // break, error while parsing. System::out->println("Parse error, aborting!"); return calendar->getTime(); } break; case ParseToken::ERA : position = position + (pt->length); break; case ParseToken::YEAR : // FIXME: Behaviour doesnt match exactly Javadoc. jint yearValue; if (pt->length <= 2) { textElem = text->substring(position, position+2); position += 2; yearValue = Integer::parseInt(textElem, 10); if (yearValue+1900 < ((new GregorianCalendar)->get(Calendar::YEAR)-80)) { yearValue += 2000; } else { yearValue += 1900; } } else { textElem = text->substring(position, position+4); position += 4; yearValue = Integer::parseInt(textElem, 10); } //System::out->println("Year extracted: " .. yearValue); calendar->set(Calendar::YEAR, yearValue); break; case ParseToken::MONTH_IN_YEAR : jint month; if (pt->length <= 2) { // treat month as Number: textElem = text->substring(position, position+(pt->length)); month = Integer::parseInt(textElem, 10) - 1; position = position + (pt->length); } else if (pt->length == 3) { // match against abbreviated form month = matchArrayElement(this->syms->getShortMonths(), text->substring(position)); position = position + (this->syms->getShortMonths()->get(month)->length()); } else if (pt->length >= 4) { // match long form month = matchArrayElement(this->syms->getMonths(), text->substring(position)); position = position + (this->syms->getMonths()->get(month)->length()); } //System::out->println("Month extracted: " .. month); calendar->set(Calendar::MONTH, month); break; case ParseToken::WEEK_IN_YEAR : textElem = text->substring(position, position + (pt->length)); position += (pt->length); //System::out->println("Week in Year: " .. textElem); calendar->set(Calendar::WEEK_OF_YEAR, Integer::parseInt(textElem, 10)); break; case ParseToken::WEEK_IN_MONTH : textElem = text->substring(position, position + (pt->length)); position += (pt->length); //System::out->println("Week in Month:" .. textElem); calendar->set(Calendar::WEEK_OF_MONTH, Integer::parseInt(textElem, 10)); break; case ParseToken::DAY_IN_YEAR : textElem = text->substring(position, position + (pt->length)); position += (pt->length); //System::out->println("Day in Year:" .. textElem); calendar->set(Calendar::DAY_OF_YEAR, Integer::parseInt(textElem, 10)); break; case ParseToken::DAY_IN_MONTH : textElem = text->substring(position, position + (pt->length)); position += (pt->length); //System::out->println("Day in Month:" .. textElem); calendar->set(Calendar::DAY_OF_MONTH, Integer::parseInt(textElem, 10)); break; case ParseToken::DAY_OF_WEEK_IN_MONTH : textElem = text->substring(position, position + (pt->length)); position += (pt->length); //System::out->println("Day of Week in Month:" .. textElem); calendar->set(Calendar::DAY_OF_WEEK_IN_MONTH, Integer::parseInt(textElem, 10)); break; case ParseToken::DAY_IN_WEEK : // day in week not reallay needed, just get the right offset. jint day; if (pt->length < 4) { // match against abbreviated form day = matchArrayElement(this->syms->getShortWeekdays(), text->substring(position)); position = position + (this->syms->getShortWeekdays()->get(day)->length()); } else if (pt->length >= 4) { // match long form day = matchArrayElement(this->syms->getWeekdays(), text->substring(position)); position = position + (this->syms->getWeekdays()->get(day)->length()); } break; case ParseToken::AM_PM_MARKER : jint ampm; ampm = matchArrayElement(this->syms->getAmPmStrings(), text->substring(position)); if (ampm == 0) { am = true; //System::out->println("Time will be AM"); } else { am = false; //System::out->println("Time will be PM"); } position = position + (this->syms->getAmPmStrings()->get(ampm)->length()); break; case ParseToken::HOUR_0_23 : textElem = text->substring(position, position + (pt->length)); position += (pt->length); //System::out->println("Hour (0-23):" .. textElem); calendar->set(Calendar::HOUR_OF_DAY, Integer::parseInt(textElem, 10)); break; case ParseToken::HOUR_1_24 : textElem = text->substring(position, position + (pt->length)); position += (pt->length); //System::out->println("Hour (1-24):" .. textElem); calendar->set(Calendar::HOUR_OF_DAY, Integer::parseInt(textElem, 10) - 1); break; case ParseToken::HOUR_0_11 : textElem = text->substring(position, position + (pt->length)); position = position + (pt->length); amPmHour = Integer::parseInt(textElem, 10); //System::out->println("HOUR_0_11:" .. amPmHour); break; case ParseToken::HOUR_1_12 : textElem = text->substring(position, position + (pt->length)); position = position + (pt->length); amPmHour = Integer::parseInt(textElem, 10) - 1; //System::out->println("HOUR_1_12 (-1):" .. amPmHour); break; case ParseToken::MINUTE : textElem = text->substring(position, position + (pt->length)); position = position + (pt->length); //System::out->println("Minute:" .. textElem); calendar->set(Calendar::MINUTE, Integer::parseInt(textElem, 10)); break; case ParseToken::SECOND : textElem = text->substring(position, position + (pt->length)); position = position + (pt->length); //System::out->println("Second:" .. textElem); calendar->set(Calendar::SECOND, Integer::parseInt(textElem, 10)); break; case ParseToken::MILLISECOND : textElem = text->substring(position, position + (pt->length)); position = position + (pt->length); //System::out->println("Millis:" .. textElem); calendar->set(Calendar::MILLISECOND, Integer::parseInt(textElem, 10)); break; case ParseToken::TIMEZONE : // FIXME: TIMEZONE not available! position = position + (pt->length); break; case ParseToken::TIMEZONE_OFFSET : // FIXME: TIMEZONE_OFFSET not available! position = position + (pt->length); break; default: position = position + (pt->length); break; } } // set Hour with AM/PM if (amPmHour != -1) { if (am) { calendar->set(Calendar::HOUR_OF_DAY, amPmHour); } else { calendar->set(Calendar::HOUR_OF_DAY, amPmHour + 12); } } return calendar->getTime(); } /*****************************************************************************\ * countEqualChars | *****************************************************************************/ jint SimpleDateFormat::countEqualChars(jakelib::lang::String * str, jint startIndex, jchar myChar) { jint idx = startIndex; while (idx <= pattern->length()-2 && pattern->charAt(idx+1) == myChar) { idx++; } return idx - startIndex + 1; } /*****************************************************************************\ * parseObject | *****************************************************************************/ jakelib::lang::Object * SimpleDateFormat::parseObject(jakelib::lang::String * str, jakelib::text::ParsePosition * pos) { System::out->println("NOT YET IMPLEMETED!"); return null; } /*****************************************************************************\ * parseMonth | *****************************************************************************/ jint SimpleDateFormat::matchArrayElement(jakelib::lang::Strings * values, jakelib::lang::String * text) { jint max = values->length(); jint idx = 0; while (idx < max && !text->startsWith(values->get(idx))) { idx++; } if (idx == 12) { // default to zero. return 0; } else { // otherwise return extracted value. return idx; } } /*****************************************************************************\ * inner class: ParseTokens | *****************************************************************************/ SimpleDateFormat::ParseToken::ParseToken(PatternElement pe, int length, String * str) { this->element = pe; this->length = length; this->strVal = str; }