/protocols/smpp/src/main/java/org/mobicents/protocols/smpp/util/SMPPDateFormat.java

http://mobicents.googlecode.com/ · Java · 319 lines · 182 code · 17 blank · 120 comment · 28 complexity · 52ae452794e0854a5e642ded793fb8bd MD5 · raw file

  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.mobicents.protocols.smpp.util;
  23. import java.text.FieldPosition;
  24. import java.text.Format;
  25. import java.text.MessageFormat;
  26. import java.text.ParsePosition;
  27. import java.util.Calendar;
  28. import java.util.SimpleTimeZone;
  29. import java.util.TimeZone;
  30. import java.util.regex.Pattern;
  31. import org.mobicents.protocols.smpp.SMPPRuntimeException;
  32. /**
  33. * Parse a String to an SMPPDate object.
  34. * @version $Id: SMPPDateFormat.java 463 2009-06-16 12:07:19Z orank $
  35. */
  36. public class SMPPDateFormat extends Format {
  37. private static final long serialVersionUID = 2L;
  38. /**
  39. * "Local" time specification, no timezone info included.
  40. */
  41. private static final String ABS_FORMAT_12 =
  42. "{0,number,00}{1,number,00}{2,number,00}{3,number,00}{4,number,00}"
  43. + "{5,number,00}";
  44. /**
  45. * Absolute format, specifying timezone information.
  46. */
  47. private static final String ABS_FORMAT_16 =
  48. ABS_FORMAT_12 + "{6,number,0}{7,number,00}{8}";
  49. /**
  50. * Relative format.
  51. */
  52. private static final String REL_FORMAT_16 = ABS_FORMAT_12 + "000R";
  53. private static final Pattern ABS_PATTERN_16 = Pattern.compile("^\\d{15}[+-]");
  54. private static final Pattern REL_PATTERN_16 = Pattern.compile("^\\d{15}R");
  55. private static final Pattern ABS_PATTERN_12 = Pattern.compile("^\\d{12}");
  56. /**
  57. * The default year modifier. The year modifier is the value used
  58. * to convert 2-digit years into their full version. Year modifier is
  59. * simply added to (or subtracted from) one form to get the other.
  60. * <br />
  61. * <code>
  62. * int twoDigitYear = fullYear - getYearModifier();<br />
  63. * int fullYear = twoDigitYear + getYearModifier();<br />
  64. * </code>
  65. * The default value for this is <code>2000</code>, meaning the allowed
  66. * values for a 2-digit year (0 to 99) represent the years 2000 to 2099.
  67. */
  68. public static final int DEFAULT_YEAR_MODIFIER = 2000;
  69. private int yearModifier = DEFAULT_YEAR_MODIFIER;
  70. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  71. if (toAppendTo == null || pos == null) {
  72. throw new NullPointerException();
  73. }
  74. if (obj == null) {
  75. return toAppendTo;
  76. }
  77. if (!(obj instanceof SMPPDate)) {
  78. throw new IllegalArgumentException("Cannot format an object of type "
  79. + obj.getClass().getName());
  80. }
  81. SMPPDate date = (SMPPDate) obj;
  82. int year = date.getYear();
  83. String format = ABS_FORMAT_16;
  84. if (date.isAbsolute()) {
  85. year -= yearModifier;
  86. if (!date.hasTimezone()) {
  87. format = ABS_FORMAT_12;
  88. }
  89. } else if (date.isRelative()) {
  90. format = REL_FORMAT_16;
  91. }
  92. Object[] args = new Object[] {
  93. Integer.valueOf(year),
  94. Integer.valueOf(date.getMonth()),
  95. Integer.valueOf(date.getDay()),
  96. Integer.valueOf(date.getHour()),
  97. Integer.valueOf(date.getMinute()),
  98. Integer.valueOf(date.getSecond()),
  99. Integer.valueOf(date.getTenth()),
  100. Integer.valueOf(date.getUtcOffset()),
  101. Character.valueOf(date.getSign()),
  102. };
  103. toAppendTo.append(MessageFormat.format(format, args));
  104. return toAppendTo;
  105. }
  106. /**
  107. * Parse a string into an {@link SMPPDate}.
  108. * @param source The string to parse.
  109. * @param pos The position to begin parsing from.
  110. * @throws NullPointerException if <code>source</code> is <code>null</code>.
  111. * @throws IllegalArgumentException if any of the parsed values exceed
  112. * their allowed range.
  113. */
  114. public Object parseObject(String source, ParsePosition pos) {
  115. String s = source;
  116. if (pos.getIndex() > 0) {
  117. s = source.substring(pos.getIndex());
  118. }
  119. int updatePos = 0;
  120. boolean absolute = false;
  121. boolean hasTz = true;
  122. if (ABS_PATTERN_16.matcher(s).find()) {
  123. absolute = true;
  124. updatePos = 16;
  125. } else if (REL_PATTERN_16.matcher(s).find()) {
  126. absolute = false;
  127. updatePos = 16;
  128. } else if (ABS_PATTERN_12.matcher(s).find()) {
  129. absolute = true;
  130. hasTz = false;
  131. updatePos = 12;
  132. } else {
  133. return null;
  134. }
  135. SMPPDate date = null;
  136. try {
  137. if (absolute) {
  138. date = parseAbsoluteDate(s, pos, hasTz);
  139. } else {
  140. date = parseRelativeDate(s, pos);
  141. }
  142. pos.setIndex(pos.getIndex() + updatePos);
  143. } catch (SMPPRuntimeException x) {
  144. // Fall-through...ParsePosition will not get updated, and
  145. // error index should have been set.
  146. }
  147. return date;
  148. }
  149. /**
  150. * Get the value for the year modifier.
  151. * @return The current value of the year modifier.
  152. * @see #DEFAULT_YEAR_MODIFIER
  153. */
  154. public int getYearModifier() {
  155. return yearModifier;
  156. }
  157. /**
  158. * Set the value for the year modifier.
  159. * @param yearModifier The new value for year modifier.
  160. * @see #DEFAULT_YEAR_MODIFIER
  161. * @see #getYearModifier()
  162. */
  163. public void setYearModifier(int yearModifier) {
  164. this.yearModifier = yearModifier;
  165. }
  166. /**
  167. * Parse an absolute date from the string.
  168. * @param s The string to parse from.
  169. * @param pos The parse position.
  170. * @param hasTz Parse timezone information if <code>true</code>.
  171. * @return The parsed SMPP date.
  172. * @throws SMPPRuntimeException If the date cannot be parsed.
  173. */
  174. private SMPPDate parseAbsoluteDate(String s, ParsePosition pos, boolean hasTz) {
  175. int index = pos.getIndex();
  176. int year = parseAndCheck(s, pos, index, index + 2, 0, 99);
  177. int month = parseAndCheck(s, pos, index + 2, index + 4, 1, 12);
  178. int day = parseAndCheck(s, pos, index + 4, index + 6, 1, 31);
  179. int hour = parseAndCheck(s, pos, index + 6, index + 8, 0, 23);
  180. int minute = parseAndCheck(s, pos, index + 8, index + 10, 0, 59);
  181. int second = parseAndCheck(s, pos, index + 10, index + 12, 0, 59);
  182. Calendar cal;
  183. if (hasTz) {
  184. char sign = s.charAt(index + 15);
  185. int tenth = parseAndCheck(s, pos, index + 12, index + 13, 0, 9);
  186. int utcOffset = parseAndCheck(
  187. s, pos, index + 13, index + 15, 0, 48);
  188. cal = getCalendar(year, month, day, hour, minute, second,
  189. tenth, utcOffset, sign);
  190. } else {
  191. cal = getCalendar(
  192. year, month, day, hour, minute, second, 0, 0, (char) 0);
  193. }
  194. return SMPPDate.getAbsoluteInstance(cal, hasTz);
  195. }
  196. /**
  197. * Parse a relative date from the string.
  198. * @param s The string to parse the date from.
  199. * @param pos The parse position.
  200. * @return The parsed SMPP date.
  201. * @throws SMPPRuntimeException If the date cannot be parsed.
  202. */
  203. private SMPPDate parseRelativeDate(String s, ParsePosition pos) {
  204. int index = pos.getIndex();
  205. int year = parseAndCheck(s, pos, index, index + 2, 0, 99);
  206. int month = parseAndCheck(s, pos, index + 2, index + 4, 0, 99);
  207. int day = parseAndCheck(s, pos, index + 4, index + 6, 0, 99);
  208. int hour = parseAndCheck(s, pos, index + 6, index + 8, 0, 99);
  209. int minute = parseAndCheck(s, pos, index + 8, index + 10, 0, 99);
  210. int second = parseAndCheck(s, pos, 10, 12, 0, 99);
  211. return SMPPDate.getRelativeInstance(
  212. year, month, day, hour, minute, second);
  213. }
  214. /**
  215. * Get a {@link java.util.Calendar} object, initialising its fields
  216. * to the supplied values. The values should be specified in their
  217. * SMPP form (that is, year is 0-99, month is 1-12) and this method will
  218. * convert them appropriately before placing them in the calendar.
  219. * @param year The year.
  220. * @param month The month.
  221. * @param day The day.
  222. * @param hour The hour.
  223. * @param minute The minute.
  224. * @param second The second.
  225. * @param tenths Tenths of a second.
  226. * @param utcOffset The offset from UTC.
  227. * @param sign The direction of the UTC offset ('+' or '-').
  228. * @return An initialised Calendar object.
  229. */
  230. private Calendar getCalendar(int year,
  231. int month,
  232. int day,
  233. int hour,
  234. int minute,
  235. int second,
  236. int tenths,
  237. int utcOffset,
  238. char sign) {
  239. Calendar calendar = Calendar.getInstance();
  240. if (sign != (char) 0) {
  241. calendar.setTimeZone(getTimeZoneForOffset(utcOffset, sign));
  242. }
  243. calendar.set(Calendar.YEAR, year + yearModifier);
  244. calendar.set(Calendar.MONTH, month - 1);
  245. calendar.set(Calendar.DAY_OF_MONTH, day);
  246. calendar.set(Calendar.HOUR_OF_DAY, hour);
  247. calendar.set(Calendar.MINUTE, minute);
  248. calendar.set(Calendar.SECOND, second);
  249. calendar.set(Calendar.MILLISECOND, tenths * 100);
  250. return calendar;
  251. }
  252. /**
  253. * Get a timezone for the specified UTC offset.
  254. * A new {@link SimpleTimeZone} will be created and given the
  255. * specified offset from UTC.
  256. * @param utcOffset The offset, in quarter hours, from UTC.
  257. * @param sign Whether the offset is ahead ('+') or behind ('-') UTC.
  258. * @return A timezone object.
  259. */
  260. private TimeZone getTimeZoneForOffset(int utcOffset, char sign) {
  261. int rawOffset = utcOffset * 900000;
  262. if (sign == '-') {
  263. rawOffset = -rawOffset;
  264. }
  265. int hours = utcOffset / 4;
  266. int minutes = (utcOffset - (hours * 4)) * 15;
  267. String id = String.format("UTC%c%02d:%02d", sign, hours, minutes);
  268. TimeZone tz = new SimpleTimeZone(rawOffset, id);
  269. return tz;
  270. }
  271. /**
  272. * Parse an integer from a string and verify the value lies within
  273. * a maximum and minimum range. If it does
  274. * not, update the <code>ParsePosition&apos;s</code> error index to point
  275. * to <code>start</code> and throw an <code>SMPPRuntimeException</code>.
  276. * <code>SMPPRuntimeException</code> is also thrown in the case of a
  277. * <code>NumberFormatException</code>.
  278. * @param s The string to parse the integer from.
  279. * @param pos The parse position to update in case of error.
  280. * @param start The start of the substring containing the integer.
  281. * @param end The end of the substring containing the integer.
  282. * @param min The minimum allowed value.
  283. * @param max The maximum allowed value.
  284. */
  285. private int parseAndCheck(String s,
  286. ParsePosition pos,
  287. int start,
  288. int end,
  289. int min,
  290. int max) {
  291. try {
  292. int n = Integer.parseInt(s.substring(start, end));
  293. if (n < min || n > max) {
  294. throw new NumberFormatException();
  295. }
  296. return n;
  297. } catch (NumberFormatException x) {
  298. pos.setErrorIndex(start);
  299. throw new SMPPRuntimeException();
  300. }
  301. }
  302. }