001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.util.time; 033 034import java.text.DateFormatSymbols; 035import java.util.Calendar; 036import java.util.Locale; 037import java.util.regex.Pattern; 038 039/** 040 * Defines components of {@link ISODateIO}. 041 * 042 */ 043public abstract class ISODateComponent { 044 045 /** 046 * Directives shortcut of the component. This property can not be changed. 047 */ 048 protected final String directive; 049 /** 050 * Replacement of the directive. Could be a regular expression. The value 051 * catch will be sent to the component with 052 * <i>set(catched_value,Calendar)</i>. This property can not be changed. 053 */ 054 protected final String replace; 055 056 /** 057 * Build a new component composed of a directive name ("%.") and a 058 * replacement value. 059 * 060 * @param directive 061 * directive name, should start with a leading '%'. 062 * @param replace 063 * replace the directive with the value given here. 064 */ 065 public ISODateComponent(String directive, String replace) { 066 this.directive = directive; 067 this.replace = replace; 068 } 069 070 /** 071 * Access to the directive name of the component. 072 * 073 * @return directive of the component. 074 */ 075 public String getDirective() { 076 return directive; 077 } 078 079 /** 080 * Return true if this component is an alias. An alias can contain other 081 * directive name and its replacement should be parse again. 082 * 083 * @return true if component is an alias. 084 */ 085 public boolean isAlias() { 086 return false; 087 } 088 089 /** 090 * Get the replacement value of this component. 091 * 092 * @return replacement value 093 */ 094 public String getReplacement() { 095 return replace; 096 } 097 098 /** 099 * Handle the value catched with the replacement value. 100 * 101 * @param value 102 * value matching the replacement string 103 * @param calendar 104 * calendar we are working on 105 */ 106 public abstract void set(String value, Calendar calendar); 107 108 /** 109 * Get a string representation of this component for a given calendar. 110 * 111 * @param calendar 112 * the calendar 113 * @return string representation of this component. 114 */ 115 public abstract String get(Calendar calendar); 116 117 /** 118 * Defines an alias component. Such component does nothing else that replace 119 * them directive by another string. 120 */ 121 public static class AliasComponent extends ISODateComponent { 122 123 public AliasComponent(String shortcut, String replace) { 124 super(shortcut, replace); 125 } 126 127 public boolean isAlias() { 128 return true; 129 } 130 131 public void set(String value, Calendar calendar) { 132 // Nothing to do 133 } 134 135 public String get(Calendar calendar) { 136 return ""; 137 } 138 } 139 140 /** 141 * Defines a text component. Such component does nothing else that append 142 * text to the resulting regular expression. 143 */ 144 public static class TextComponent extends ISODateComponent { 145 String unquoted; 146 147 public TextComponent(String value) { 148 super(null, Pattern.quote(value)); 149 unquoted = value; 150 } 151 152 public void set(String value, Calendar calendar) { 153 // Nothing to do 154 } 155 156 public String get(Calendar calendar) { 157 return unquoted; 158 } 159 } 160 161 /** 162 * Defines a component associated with a field of a calendar. When a value 163 * is handled, component will try to set the associated field of the 164 * calendar. 165 */ 166 public static class FieldComponent extends ISODateComponent { 167 protected final int field; 168 protected final int offset; 169 protected final String format; 170 171 public FieldComponent(String shortcut, String replace, int field, 172 String format) { 173 this(shortcut, replace, field, 0, format); 174 } 175 176 public FieldComponent(String shortcut, String replace, int field, 177 int offset, String format) { 178 super(shortcut, replace); 179 this.field = field; 180 this.offset = offset; 181 this.format = format; 182 } 183 184 public void set(String value, Calendar calendar) { 185 while (value.charAt(0) == '0' && value.length() > 1) 186 value = value.substring(1); 187 int val = Integer.parseInt(value); 188 calendar.set(field, val + offset); 189 } 190 191 public String get(Calendar calendar) { 192 return String.format(format, calendar.get(field)); 193 } 194 } 195 196 /** 197 * Base for locale-dependent component. 198 */ 199 protected static abstract class LocaleDependentComponent extends 200 ISODateComponent { 201 protected Locale locale; 202 protected DateFormatSymbols symbols; 203 204 public LocaleDependentComponent(String shortcut, String replace) { 205 this(shortcut, replace, Locale.getDefault()); 206 } 207 208 public LocaleDependentComponent(String shortcut, String replace, 209 Locale locale) { 210 super(shortcut, replace); 211 this.locale = locale; 212 this.symbols = DateFormatSymbols.getInstance(locale); 213 } 214 } 215 216 /** 217 * Component handling AM/PM. 218 */ 219 public static class AMPMComponent extends LocaleDependentComponent { 220 public AMPMComponent() { 221 super("%p", "AM|PM|am|pm"); 222 } 223 224 public void set(String value, Calendar calendar) { 225 if (value.equalsIgnoreCase(symbols.getAmPmStrings()[Calendar.AM])) 226 calendar.set(Calendar.AM_PM, Calendar.AM); 227 else if (value 228 .equalsIgnoreCase(symbols.getAmPmStrings()[Calendar.PM])) 229 calendar.set(Calendar.AM_PM, Calendar.PM); 230 } 231 232 public String get(Calendar calendar) { 233 return symbols.getAmPmStrings()[calendar.get(Calendar.AM_PM)]; 234 } 235 } 236 237 /** 238 * Component handling utc offset (+/- 0000). 239 */ 240 public static class UTCOffsetComponent extends ISODateComponent { 241 public UTCOffsetComponent() { 242 super("%z", "(?:[-+]\\d{4}|Z)"); 243 } 244 245 public void set(String value, Calendar calendar) { 246 if (value.equals("Z")) { 247 calendar.getTimeZone().setRawOffset(0); 248 } else { 249 String hs = value.substring(1, 3); 250 String ms = value.substring(3, 5); 251 if (hs.charAt(0) == '0') 252 hs = hs.substring(1); 253 if (ms.charAt(0) == '0') 254 ms = ms.substring(1); 255 256 int i = value.charAt(0) == '+' ? 1 : -1; 257 int h = Integer.parseInt(hs); 258 int m = Integer.parseInt(ms); 259 260 calendar.getTimeZone().setRawOffset(i * (h * 60 + m) * 60000); 261 } 262 } 263 264 public String get(Calendar calendar) { 265 int offset = calendar.getTimeZone().getRawOffset(); 266 String sign = "+"; 267 268 if (offset < 0) { 269 sign = "-"; 270 offset = -offset; 271 } 272 273 offset /= 60000; 274 275 int h = offset / 60; 276 int m = offset % 60; 277 278 return String.format("%s%02d%02d", sign, h, m); 279 } 280 } 281 282 /** 283 * Component handling a number of milliseconds since the epoch (january, 1st 284 * 1970). 285 */ 286 public static class EpochComponent extends ISODateComponent { 287 public EpochComponent() { 288 super("%K", "\\d+"); 289 } 290 291 public void set(String value, Calendar calendar) { 292 long e = Long.parseLong(value); 293 calendar.setTimeInMillis(e); 294 } 295 296 public String get(Calendar calendar) { 297 return String.format("%d", calendar.getTimeInMillis()); 298 } 299 } 300 301 /** 302 * Defines a not implemented component. Such components throw an Error if 303 * used. 304 */ 305 public static class NotImplementedComponent extends ISODateComponent { 306 public NotImplementedComponent(String shortcut, String replace) { 307 super(shortcut, replace); 308 } 309 310 public void set(String value, Calendar cal) { 311 throw new Error("not implemented component"); 312 } 313 314 public String get(Calendar calendar) { 315 throw new Error("not implemented component"); 316 } 317 } 318 319 public static final ISODateComponent ABBREVIATED_WEEKDAY_NAME = new NotImplementedComponent( 320 "%a", "\\w+[.]"); 321 public static final ISODateComponent FULL_WEEKDAY_NAME = new NotImplementedComponent( 322 "%A", "\\w+"); 323 public static final ISODateComponent ABBREVIATED_MONTH_NAME = new NotImplementedComponent( 324 "%b", "\\w+[.]"); 325 public static final ISODateComponent FULL_MONTH_NAME = new NotImplementedComponent( 326 "%B", "\\w+"); 327 public static final ISODateComponent LOCALE_DATE_AND_TIME = new NotImplementedComponent( 328 "%c", null); 329 public static final ISODateComponent CENTURY = new NotImplementedComponent( 330 "%C", "\\d\\d"); 331 public static final ISODateComponent DAY_OF_MONTH_2_DIGITS = new FieldComponent( 332 "%d", "[012]\\d|3[01]", Calendar.DAY_OF_MONTH, "%02d"); 333 public static final ISODateComponent DATE = new AliasComponent("%D", 334 "%m/%d/%y"); 335 public static final ISODateComponent DAY_OF_MONTH = new FieldComponent( 336 "%e", "\\d|[12]\\d|3[01]", Calendar.DAY_OF_MONTH, "%2d"); 337 public static final ISODateComponent DATE_ISO8601 = new AliasComponent( 338 "%F", "%Y-%m-%d"); 339 public static final ISODateComponent WEEK_BASED_YEAR_2_DIGITS = new FieldComponent( 340 "%g", "\\d\\d", Calendar.YEAR, "%02d"); 341 public static final ISODateComponent WEEK_BASED_YEAR_4_DIGITS = new FieldComponent( 342 "%G", "\\d{4}", Calendar.YEAR, "%04d"); 343 public static final ISODateComponent ABBREVIATED_MONTH_NAME_ALIAS = new AliasComponent( 344 "%h", "%b"); 345 public static final ISODateComponent HOUR_OF_DAY = new FieldComponent("%H", 346 "[01]\\d|2[0123]", Calendar.HOUR_OF_DAY, "%02d"); 347 public static final ISODateComponent HOUR = new FieldComponent("%I", 348 "0\\d|1[012]", Calendar.HOUR, "%02d"); 349 public static final ISODateComponent DAY_OF_YEAR = new FieldComponent("%j", 350 "[012]\\d\\d|3[0-5]\\d|36[0-6]", Calendar.DAY_OF_YEAR, "%03d"); 351 public static final ISODateComponent MILLISECOND = new FieldComponent("%k", 352 "\\d{3}", Calendar.MILLISECOND, "%03d"); 353 public static final ISODateComponent EPOCH = new EpochComponent(); 354 public static final ISODateComponent MONTH = new FieldComponent("%m", 355 "0[1-9]|1[012]", Calendar.MONTH, -1, "%02d"); 356 public static final ISODateComponent MINUTE = new FieldComponent("%M", 357 "[0-5]\\d", Calendar.MINUTE, "%02d"); 358 public static final ISODateComponent NEW_LINE = new AliasComponent("%n", 359 "\n"); 360 public static final ISODateComponent AM_PM = new AMPMComponent(); 361 public static final ISODateComponent LOCALE_CLOCK_TIME_12_HOUR = new NotImplementedComponent( 362 "%r", ""); 363 public static final ISODateComponent HOUR_AND_MINUTE = new AliasComponent( 364 "%R", "%H:%M"); 365 public static final ISODateComponent SECOND = new FieldComponent("%S", 366 "[0-5]\\d|60", Calendar.SECOND, "%02d"); 367 public static final ISODateComponent TABULATION = new AliasComponent("%t", 368 "\t"); 369 public static final ISODateComponent TIME_ISO8601 = new AliasComponent( 370 "%T", "%H:%M:%S"); 371 public static final ISODateComponent DAY_OF_WEEK_1_7 = new FieldComponent( 372 "%u", "[1-7]", Calendar.DAY_OF_WEEK, -1, "%1d"); 373 public static final ISODateComponent WEEK_OF_YEAR_FROM_SUNDAY = new FieldComponent( 374 "%U", "[0-4]\\d|5[0123]", Calendar.WEEK_OF_YEAR, 1, "%2d"); 375 public static final ISODateComponent WEEK_NUMBER_ISO8601 = new NotImplementedComponent( 376 "%V", "0[1-9]|[2-4]\\d|5[0123]"); 377 public static final ISODateComponent DAY_OF_WEEK_0_6 = new FieldComponent( 378 "%w", "[0-6]", Calendar.DAY_OF_WEEK, "%01d"); 379 public static final ISODateComponent WEEK_OF_YEAR_FROM_MONDAY = new FieldComponent( 380 "%W", "[0-4]\\d|5[0123]", Calendar.WEEK_OF_YEAR, "%02d"); 381 public static final ISODateComponent LOCALE_DATE_REPRESENTATION = new NotImplementedComponent( 382 "%x", ""); 383 public static final ISODateComponent LOCALE_TIME_REPRESENTATION = new NotImplementedComponent( 384 "%X", ""); 385 public static final ISODateComponent YEAR_2_DIGITS = new FieldComponent( 386 "%y", "\\d\\d", Calendar.YEAR, "%02d"); 387 public static final ISODateComponent YEAR_4_DIGITS = new FieldComponent( 388 "%Y", "\\d{4}", Calendar.YEAR, "%04d"); 389 public static final ISODateComponent UTC_OFFSET = new UTCOffsetComponent(); 390 public static final ISODateComponent LOCALE_TIME_ZONE_NAME = new NotImplementedComponent( 391 "%Z", "\\w*"); 392 public static final ISODateComponent PERCENT = new AliasComponent("%%", "%"); 393}