| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.ode.utils; |
| |
| import java.text.ParseException; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.GregorianCalendar; |
| import java.util.StringTokenizer; |
| |
| public class RelativeDateParser { |
| private static final char YEAR_TEMPLATE_MODIFIER = 'y'; |
| |
| private static final char MONTH_TEMPLATE_MODIFIER = 'M'; |
| |
| private static final char WEEK_TEMPLATE_MODIFIER = 'w'; |
| |
| private static final char DAY_TEMPLATE_MODIFIER = 'd'; |
| |
| private static final char HOUR_TEMPLATE_MODIFIER = 'h'; |
| |
| private static final char MINUTE_TEMPLATE_MODIFIER = 'm'; |
| |
| private static final char SECOND_TEMPLATE_MODIFIER = 's'; |
| |
| /** |
| * This array of strings contains all supported "modifiers" for method <code>parseRelativeDate</code>. |
| * NOTE: An order of elements in this array is important and used in algorithm of above method! |
| */ |
| private static final char[] TEMPLATE_MODIFIERS_LIST = new char[] { |
| YEAR_TEMPLATE_MODIFIER, MONTH_TEMPLATE_MODIFIER, |
| WEEK_TEMPLATE_MODIFIER, DAY_TEMPLATE_MODIFIER, |
| HOUR_TEMPLATE_MODIFIER, MINUTE_TEMPLATE_MODIFIER, |
| SECOND_TEMPLATE_MODIFIER, }; |
| |
| /** |
| * Returns a date which is less than current system date/time on a specified number of days, years, minutes, weeks |
| * etc.<p/> |
| * A common template which is valid for this method is like that:<br/> |
| * <strong>XXXXy XXXXM XXXXw XXXXd XXXXh XXXXm XXXXs</strong>, where: |
| * <pre> |
| * XXXX - any positive number composed of four or less digits, but not less that one digit |
| * y - this symbol represents how many years must be subtracted from current system date |
| * M - this symbol represents how many months must be subtracted from current system date |
| * w - this symbol represents how many weeks must be subtracted rom current system date |
| * d - this symbol represents how many days must be subtracted from current system date |
| * h - this symbol represents how many hours must be subtracted from current system date |
| * m - this symbol represents how many minutes must be subtracted from current system date |
| * s - this symbol represents how many seconds must be subtracted from current system date |
| * </pre> |
| * Above symbols are called "modifiers". |
| * <p><em>Modifiers are case sensitive</em>. |
| * Modifiers separated by single space character or sequence of spaces. |
| * Each modifier is optional, but an order of modifiers is matter and must be exactly the same |
| * how its specified in template.</p> |
| * <p/> |
| * Here is a few examples of date templates and its meanings:<br/> |
| * <code>"4h 3m 23s"</code> - means 4 hours, 3 minutes and 23 seconds ago from current system date<br/> |
| * <code>"1y 2m 3w"</code> - means 1 year, 2 months and 3 weeks ago from current system date<br/> |
| * <code>"1m 1w 13h 5m"</code> - means 1 month, 1 week, 13 hours and 5 minutes ago from current system date<br/> |
| * |
| * @param dateTemplate a template of a date we want to be calculated |
| * @return a resulted <code>Date</code> object that is applies specified template |
| * @throws java.text.ParseException if some problems were occurred while parsing specified date template |
| */ |
| public static Date parseRelativeDate(String dateTemplate) |
| throws java.text.ParseException { |
| int[] agoValues = new int[TEMPLATE_MODIFIERS_LIST.length]; |
| int currentModifierPointer = 0; |
| |
| StringTokenizer tokens = new StringTokenizer(dateTemplate.trim()); |
| while (tokens.hasMoreTokens()) { |
| String token = tokens.nextToken(); |
| if (token.length() < 2 || token.length() > 5) { |
| throw new ParseException("Invalid token length. Token: " |
| + token, dateTemplate.indexOf(token)); |
| } |
| |
| int modValue; |
| try { |
| modValue = Integer.parseInt(token.substring(0, |
| token.length() - 1)); |
| if (modValue <= 0) { |
| throw new ParseException( |
| "Modifier value must be a positive number. Token: " |
| + token, dateTemplate.indexOf(token)); |
| } |
| } catch (NumberFormatException nfe) { |
| throw new ParseException("Can't parse integer value. Token: " |
| + token, dateTemplate.indexOf(token)); |
| } |
| |
| char mod = token.charAt(token.length() - 1); |
| while (true) { |
| if (currentModifierPointer >= TEMPLATE_MODIFIERS_LIST.length) { |
| throw new ParseException( |
| "Incorrect modifier at this position. Token: " |
| + token, dateTemplate.indexOf(token) |
| + token.length() - 1); |
| } else if (mod != TEMPLATE_MODIFIERS_LIST[currentModifierPointer]) { |
| currentModifierPointer++; |
| //continue; |
| } else if (agoValues[currentModifierPointer] != 0) { |
| throw new ParseException( |
| "Dublicated modifier found. Token: " + token, |
| dateTemplate.indexOf(token) + token.length() - 1); |
| } else { |
| agoValues[currentModifierPointer++] = modValue; |
| break; |
| } |
| } |
| }//while (tokens) |
| |
| final Calendar calendar = GregorianCalendar.getInstance(); |
| for (int i = 0; i < agoValues.length; i++) { |
| if (agoValues[i] == 0) { |
| continue; |
| } |
| int calendarField = templateModifierIndexToCalendarField(i); |
| calendar.add(calendarField, 0 - agoValues[i]); |
| } |
| |
| return calendar.getTime(); |
| } |
| |
| private static int templateModifierIndexToCalendarField(int modifierIndex) |
| throws IllegalArgumentException { |
| switch (modifierIndex) { |
| case 0: |
| return Calendar.YEAR; |
| case 1: |
| return Calendar.MONTH; |
| case 2: |
| return Calendar.WEEK_OF_MONTH; |
| case 3: |
| return Calendar.DAY_OF_MONTH; |
| case 4: |
| return Calendar.HOUR_OF_DAY; |
| case 5: |
| return Calendar.MINUTE; |
| case 6: |
| return Calendar.SECOND; |
| } |
| throw new IllegalArgumentException("Invalid template modifier index: " |
| + modifierIndex); |
| } |
| } |