Update to session creation procedure
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / event / TmfTimestampFormat.java
CommitLineData
f8177ba2
FC
1/*******************************************************************************
2 * Copyright (c) 2012 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
11 *******************************************************************************/
12
13package org.eclipse.linuxtools.tmf.core.event;
14
15import java.text.DecimalFormat;
16import java.text.ParseException;
17import java.text.SimpleDateFormat;
18import java.util.ArrayList;
19import java.util.Calendar;
20import java.util.Date;
21import java.util.List;
f8177ba2
FC
22import java.util.regex.Matcher;
23import java.util.regex.Pattern;
24
25import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager;
26import org.eclipse.linuxtools.tmf.core.signal.TmfTimestampFormatUpdateSignal;
27
28/**
29 * A formatting and parsing facility that can handle timestamps that span the
30 * epoch with a precision down to the nanosecond. It can be understood as a
31 * simplified and more constrained version of SimpleDateFormat as it limits the
32 * number of allowed pattern characters and the acceptable timestamp formats.
33 * <p>
34 * The timestamp representation is broken down into a number of optional
35 * components that can be assembled into a fairly simple way.
36 *
37 * <h4>Date Pattern</h4>
38 * <blockquote>
39 * <table border=0 cellspacing=3 cellpadding=0 >
40 * <tr bgcolor="#ccccff">
41 * <th align=left>Format
42 * <th align=left>Description
43 * <th align=left>Value Range
44 * <th align=left>Example
45 * <tr>
46 * <td><code>yyyy</code>
47 * <td>Year
48 * <td><code>1970-...</code>
49 * <td><code>2012</code>
50 * <tr bgcolor="#eeeeff">
51 * <td><code>MM</code>
52 * <td>Month in year
53 * <td><code>01-12</code>
54 * <td><code>09</code>
55 * <tr>
56 * <td><code>dd</code>
57 * <td>Day in month
58 * <td><code>01-31</code>
59 * <td><code>22</code>
60 * </table>
61 * </blockquote>
62 *
63 * <h4>Time Pattern</h4>
64 * <blockquote>
65 * <table border=0 cellspacing=3 cellpadding=0 >
66 * <tr bgcolor="#ccccff">
67 * <th align=left>Format
68 * <th align=left>Description
69 * <th align=left>Value Range
70 * <th align=left>Example
71 * <tr>
72 * <td><code>HH</code>
73 * <td>Hour in day
74 * <td><code>00-23</code>
75 * <td><code>07</code>
76 * <tr bgcolor="#eeeeff">
77 * <td><code>mm</code>
78 * <td>Minute in hour
79 * <td><code>00-59</code>
80 * <td><code>35</code>
81 * <tr>
82 * <td><code>ss</code>
83 * <td>Second in minute
84 * <td><code>00-59</code>
85 * <td><code>41</code>
86 * <tr bgcolor="#eeeeff">
87 * <td><code>T</code>
88 * <td>The seconds since the epoch
89 * <td><code>00-...</code>
90 * <td><code>1332170682</code>
91 * </table>
92 * </blockquote>
93 *
94 * <h4>Sub-Seconds Pattern</h4>
95 * <blockquote>
96 * <table border=0 cellspacing=3 cellpadding=0 >
97 * <tr bgcolor="#ccccff">
98 * <th align=left>Format
99 * <th align=left>Description
100 * <th align=left>Value Range
101 * <th align=left>Example
102 * <tr>
103 * <td><code>SSS</code>
104 * <td>Millisecond in second
105 * <td><code>000-999</code>
106 * <td><code>123</code>
107 * <tr bgcolor="#eeeeff">
108 * <td><code>CCC</code>
109 * <td>Microseconds in ms
110 * <td><code>000-999</code>
111 * <td><code>456</code>
112 * <tr>
113 * <td><code>NNN</code>
114 * <td>Nanosecond in &#181s
115 * <td><code>000-999</code>
116 * <td><code>789</code>
117 * </table>
118 * </blockquote>
119 *
120 * <strong>Note: </strong>If "T" is used, no other Date or Time pattern
121 * can be used. Also, "T" should be used for time intervals.
122 * <p>
123 * <strong>Note: </strong>Each sub-field can be separated by a single,
124 * optional character delimiter. However, the between Date/Time and the
125 * Sub-seconds pattern is mandatory (if there is a fractional part) and
126 * has to be separated from Date/time by "." (period).
127 * <p>
128 * The recognized delimiters are:
129 * <ul>
130 * <li>Space ("<code> </code>")
131 * <li>Period (<code>".</code>")
132 * <li>Comma ("<code>,</code>")
133 * <li>Dash ("<code>-</code>")
134 * <li>Underline ("<code>_</code>")
135 * <li>Colon ("<code>:</code>")
136 * <li>Semicolon ("<code>;</code>")
137 * <li>Slash ("<code>/</code>")
138 * <li>Double-quote ("<code>"</code>")
139 * </ul>
140 *
141 * <h4>Examples</h4>
142 * The following examples show how timestamp patterns are interpreted in
143 * the U.S. locale. The given timestamp is 1332170682539677389L, the number
144 * of nanoseconds since 1970/01/01.
145 *
146 * <blockquote>
147 * <table border=0 cellspacing=3 cellpadding=0>
148 * <tr bgcolor="#ccccff">
149 * <th align=left>Date and Time Pattern
150 * <th align=left>Result
151 * <tr>
152 * <td><code>"yyyy-MM-dd HH:mm:ss.SSS.CCC.NNN"</code>
153 * <td><code>2012-03-19 11:24:42.539.677.389</code>
154 * <tr bgcolor="#eeeeff">
155 * <td><code>"yyyy-MM-dd HH:mm:ss.SSS.CCC"</code>
156 * <td><code>2012-03-19 11:24:42.539.677</code>
157 * <tr>
158 * <td><code>"yyyy-D HH:mm:ss.SSS.CCC"</code>
159 * <td><code>2012-79 11:24:42.539.677</code>
160 * <tr bgcolor="#eeeeff">
161 * <td><code>"ss.SSSCCCNNN"</code>
162 * <td><code>42.539677389</code>
163 * <tr>
164 * <td><code>"T.SSS CCC NNN"</code>
165 * <td><code>1332170682.539 677 389</code>
166 * <tr bgcolor="#eeeeff">
167 * <td><code>"T"</code>
168 * <td><code>1332170682</code>
169 * </table>
170 * </blockquote>
171 * <p>
172 * @version 1.0
173 * @since 2.0
174 * @author Francois Chouinard
175 */
176public class TmfTimestampFormat extends SimpleDateFormat {
177
178 // ------------------------------------------------------------------------
179 // Constants
180 // ------------------------------------------------------------------------
181
182 /**
183 * This class' serialization ID
184 */
185 private static final long serialVersionUID = 2835829763122454020L;
186
187 /**
188 * The default timestamp pattern
189 */
d96e9054 190 public static final String DEFAULT_TIME_PATTERN = "HH:mm:ss.SSS CCC NNN"; //$NON-NLS-1$
f8177ba2
FC
191
192 /**
193 * The LTTng 0.x legacy timestamp format
194 */
d96e9054 195 public static final String DEFAULT_INTERVAL_PATTERN = "TTT.SSS CCC NNN"; //$NON-NLS-1$
f8177ba2
FC
196
197 // Fractions of seconds supported patterns
198 private static final String DOT_RE = "\\."; //$NON-NLS-1$
199 private static final String SEP_RE = "[ \\.,-_:;/\\\"]?"; //$NON-NLS-1$
200 private static final String DGTS_3_RE = "(\\d{3})"; //$NON-NLS-1$
201 private static final String DGTS_13_RE = "(\\d{1,3})"; //$NON-NLS-1$
202
203 private static final String MILLISEC_RE = DOT_RE + DGTS_13_RE;
204 private static final String MICROSEC_RE = DOT_RE + DGTS_3_RE + SEP_RE + DGTS_13_RE;
205 private static final String NANOSEC_RE = DOT_RE + DGTS_3_RE + SEP_RE + DGTS_3_RE + SEP_RE + DGTS_13_RE;
206
207 private static final Pattern MILLISEC_PAT = Pattern.compile(MILLISEC_RE);
208 private static final Pattern MICROSEC_PAT = Pattern.compile(MICROSEC_RE);
209 private static final Pattern NANOSEC_PAT = Pattern.compile(NANOSEC_RE);
210
211 // ------------------------------------------------------------------------
212 // Attributes
213 // ------------------------------------------------------------------------
214
215 // The default timestamp pattern
216 private static String fDefaultTimePattern = null;
217 private static TmfTimestampFormat fDefaultTimeFormat = null;
218
219 // The default time interval format
220 private static String fDefaultIntervalPattern = null;
221 private static TmfTimestampFormat fDefaultIntervalFormat = null;
222
223 // The timestamp pattern
224 private String fPattern;
225
226 // The timestamp pattern
227 private List<String> fSupplPatterns = new ArrayList<String>();
228
229 /**
230 * The supplementary pattern letters. Can be redefined by sub-classes
231 * to either override existing letters or augment the letter set.
232 * If so, the format() method must provide the (re-)implementation of the
233 * pattern.
234 */
235 protected String fSupplPatternLetters = "TSCN"; //$NON-NLS-1$
236
6f4e8ec0 237 /*
f8177ba2
FC
238 * The bracketing symbols used to mitigate the risk of a format string
239 * that contains escaped sequences that would conflict with our format
240 * extension.
241 */
6f4e8ec0 242 /** The open bracket symbol */
f8177ba2 243 protected String fOpenBracket = "[&"; //$NON-NLS-1$
6f4e8ec0
AM
244
245 /** The closing bracket symbol */
f8177ba2
FC
246 protected String fCloseBracket = "&]"; //$NON-NLS-1$
247
248 // ------------------------------------------------------------------------
249 // Constructors
250 // ------------------------------------------------------------------------
251
252 /**
253 * The default constructor (uses the default pattern)
254 */
255 public TmfTimestampFormat() {
256 this(fDefaultTimePattern);
257 }
258
259 /**
260 * The normal constructor
261 *
262 * @param pattern the format pattern
263 */
264 public TmfTimestampFormat(String pattern) {
f8177ba2
FC
265 applyPattern(pattern);
266 }
267
268 /**
269 * The copy constructor
270 *
271 * @param other the other format pattern
272 */
273 public TmfTimestampFormat(TmfTimestampFormat other) {
274 this(other.fPattern);
275 }
276
277 // ------------------------------------------------------------------------
278 // Getters/setters
279 // ------------------------------------------------------------------------
280
281 /**
282 * @param pattern the new default time pattern
283 */
5419a136 284 public static void setDefaultTimeFormat(final String pattern) {
f8177ba2
FC
285 fDefaultTimePattern = pattern;
286 fDefaultTimeFormat = new TmfTimestampFormat(fDefaultTimePattern);
287 TmfSignalManager.dispatchSignal(new TmfTimestampFormatUpdateSignal(null));
288 }
289
290 /**
291 * @return the default time format pattern
292 */
5419a136 293 public static TmfTimestampFormat getDefaulTimeFormat() {
f8177ba2
FC
294 if (fDefaultTimeFormat == null) {
295 fDefaultTimeFormat = new TmfTimestampFormat(DEFAULT_TIME_PATTERN);
296 }
297 return fDefaultTimeFormat;
298 }
299
300 /**
301 * @param pattern the new default interval pattern
302 */
5419a136 303 public static void setDefaultIntervalFormat(final String pattern) {
f8177ba2
FC
304 fDefaultIntervalPattern = pattern;
305 fDefaultIntervalFormat = new TmfTimestampFormat(fDefaultIntervalPattern);
306 TmfSignalManager.dispatchSignal(new TmfTimestampFormatUpdateSignal(null));
307 }
308
309 /**
310 * @return the default interval format pattern
311 */
5419a136 312 public static TmfTimestampFormat getDefaulIntervalFormat() {
f8177ba2
FC
313 if (fDefaultIntervalFormat == null) {
314 fDefaultIntervalFormat = new TmfTimestampFormat(DEFAULT_INTERVAL_PATTERN);
315 }
316 return fDefaultIntervalFormat;
317 }
318
319 /* (non-Javadoc)
320 * @see java.text.SimpleDateFormat#applyPattern(java.lang.String)
321 */
322 @Override
323 public void applyPattern(String pattern) {
324 fPattern = pattern;
325 String quotedPattern = quoteSpecificTags(pattern);
326 super.applyPattern(quotedPattern);
327 }
328
329 /* (non-Javadoc)
330 * @see java.text.SimpleDateFormat#toPattern()
331 */
332 @Override
333 public String toPattern() {
334 return fPattern;
335 }
336
337 // ------------------------------------------------------------------------
338 // Operations
339 // ------------------------------------------------------------------------
340
341 /**
342 * Format the timestamp according to its pattern.
343 *
344 * @param value the timestamp value to format (in ns)
345 * @return the formatted timestamp
346 */
1e10705b 347 public synchronized String format(long value) {
f8177ba2
FC
348
349 // Split the timestamp value into its sub-components
350 long sec = value / 1000000000; // seconds
351 long ms = value % 1000000000 / 1000000; // milliseconds
352 long cs = value % 1000000 / 1000; // microseconds
353 long ns = value % 1000; // nanoseconds
354
355 // Let the base class fill the stuff it knows about
356 StringBuffer result = new StringBuffer(super.format(sec * 1000 + ms));
357
358 // In the case where there is no separation between 2 supplementary
359 // fields, the pattern will have the form "..'[pat-1]''[pat-2]'.." and
360 // the base class format() will interpret the 2 adjacent quotes as a
361 // wanted character in the result string as ("..[pat-1]'[pat-2]..").
362 // Remove these extra quotes before filling the supplementary fields.
363 int loc = result.indexOf(fCloseBracket + "'" + fOpenBracket); //$NON-NLS-1$
364 while (loc != -1) {
365 result.deleteCharAt(loc + fCloseBracket.length());
366 loc = result.indexOf(fCloseBracket + "'" + fOpenBracket); //$NON-NLS-1$
367 }
368
369 // Fill in our extensions
370 for (String pattern : fSupplPatterns) {
371 int length = pattern.length();
372
373 // Prepare the format buffer
374 StringBuffer fmt = new StringBuffer(length);
375 for (int i = 0; i < length; i++) {
376 fmt.append("0"); //$NON-NLS-1$
377 }
378 DecimalFormat dfmt = new DecimalFormat(fmt.toString());
379 String fmtVal = ""; //$NON-NLS-1$;
380
381 // Format the proper value as per the pattern
382 switch (pattern.charAt(0)) {
383 case 'T':
384 fmtVal = dfmt.format(sec);
385 break;
386 case 'S':
387 fmtVal = dfmt.format(ms);
388 break;
389 case 'C':
390 fmtVal = dfmt.format(cs);
391 break;
392 case 'N':
393 fmtVal = dfmt.format(ns);
394 break;
395 default:
396 break;
397 }
398
399 // Substitute the placeholder with the formatted value
400 String ph = new StringBuffer(fOpenBracket + pattern + fCloseBracket).toString();
401 loc = result.indexOf(ph);
402 result.replace(loc, loc + length + fOpenBracket.length() + fCloseBracket.length(), fmtVal);
403 }
404
405 return result.toString();
406 }
407
408 /**
409 * Parse a string according to the format pattern
410 *
411 * @param string the source string
412 * @param ref the reference (base) time
413 * @return the parsed value
414 * @throws ParseException if the string has an invalid format
415 */
1e10705b 416 public synchronized long parseValue(final String string, final long ref) throws ParseException {
f8177ba2
FC
417
418 // Trivial case
419 if (string == null || string.length() == 0) {
420 return 0;
421 }
422
423 // The timestamp sub-components
424 long seconds = -1;
425 long millisec = 0;
426 long microsec = 0;
427 long nanosec = 0;
428
429 // Since we are processing the fractional part, substitute it with
430 // its pattern so the base parser doesn't complain
431 StringBuilder sb = new StringBuilder(string);
432 int dot = string.indexOf('.');
433 if (dot == -1) {
434 sb.append('.');
435 dot = string.length();
436 }
437 sb = new StringBuilder(string.substring(0, dot));
438 String basePattern = super.toPattern();
439 int dot2 = basePattern.indexOf('.');
440 if (dot2 != -1) {
441 sb.append(basePattern.substring(dot2));
442 }
443
444 // Fill in our extensions
445 for (String pattern : fSupplPatterns) {
446 String pat = fOpenBracket + pattern + fCloseBracket;
447 Matcher matcher;
448
449 // Extract the substring corresponding to the extra pattern letters
450 // and replace with the pattern so the base parser can do its job.
451 switch (pattern.charAt(0)) {
452 case 'T':
453 // Remove everything up to the first "." and compute the
454 // number of seconds since the epoch. If there is no period,
455 // assume an integer value and return immediately
456 if (dot < 1) {
457 return new DecimalFormat("0").parse(string).longValue() * 1000000000; //$NON-NLS-1$
458 }
459 seconds = new DecimalFormat("0").parse(string.substring(0, dot)).longValue(); //$NON-NLS-1$
460 sb.delete(0, dot);
461 sb.insert(0, pat);
462 break;
463 case 'S':
464 matcher = MILLISEC_PAT.matcher(string.substring(dot));
465 if (matcher.find()) {
466 millisec = new Long(matcher.group(1));
467 for (int l = matcher.group(1).length(); l < 3; l++) {
468 millisec *= 10;
469 }
470 }
471 stripQuotes(sb, pattern);
472 break;
473 case 'C':
474 matcher = MICROSEC_PAT.matcher(string.substring(dot));
475 if (matcher.find()) {
476 microsec = new Long(matcher.group(2));
477 for (int l = matcher.group(2).length(); l < 3; l++) {
478 microsec *= 10;
479 }
480 }
481 stripQuotes(sb, pattern);
482 break;
483 case 'N':
484 matcher = NANOSEC_PAT.matcher(string.substring(dot));
485 if (matcher.find()) {
486 nanosec = new Long(matcher.group(3));
487 for (int l = matcher.group(3).length(); l < 3; l++) {
488 nanosec *= 10;
489 }
490 }
491 stripQuotes(sb, pattern);
492 break;
493 default:
494 break;
495 }
496 }
497
498 // If there was no "T" (thus not an interval), parse as a date
499 if (seconds == -1) {
500 Date baseDate = super.parse(sb.toString());
501
06441eb5 502 Calendar refTime = Calendar.getInstance(getTimeZone());
f8177ba2 503 refTime.setTimeInMillis(ref / 1000000);
06441eb5 504 Calendar newTime = Calendar.getInstance(getTimeZone());
f8177ba2
FC
505 newTime.setTimeInMillis(baseDate.getTime());
506
06441eb5 507 int[] fields = new int[] { Calendar.YEAR, Calendar.MONTH, Calendar.DATE, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND };
f8177ba2
FC
508 for (int field : fields) {
509 int value = newTime.get(field);
510 // Do some adjustments...
511 if (field == Calendar.YEAR) {
512 value -= 1970;
06441eb5 513 } else if (field == Calendar.DATE) {
f8177ba2
FC
514 value -= 1;
515 }
516 // ... and fill-in the empty fields
517 if (value == 0) {
518 newTime.set(field, refTime.get(field));
f8177ba2
FC
519 } else {
520 break; // Get out as soon as we have a significant value
521 }
522 }
523 seconds = newTime.getTimeInMillis() / 1000;
524 }
525
526 // Compute the value in ns
527 return seconds * 1000000000 + millisec * 1000000 + microsec * 1000 + nanosec;
528 }
529
530 /**
531 * Parse a string according to the format pattern
532 *
533 * @param string the source string
534 * @return the parsed value
535 * @throws ParseException if the string has an invalid format
536 */
537 public long parseValue(final String string) throws ParseException {
538 long result = parseValue(string, 0);
539 return result;
540
541 }
542
543 // ------------------------------------------------------------------------
544 // Helper functions
545 // ------------------------------------------------------------------------
546
547 /**
548 * Copy the pattern but quote (bracket with "[&" and "&]") the
549 * TmfTimestampFormat specific tags so these fields are treated as
550 * comments by the base class.
551 *
552 * It also keeps track of the corresponding quoted fields so they can be
553 * properly populated later on (by format()).
554 *
555 * @param pattern the 'extended' pattern
556 * @return the quoted and bracketed pattern
557 */
558 private String quoteSpecificTags(final String pattern) {
559
560 StringBuffer result = new StringBuffer();
561
562 int length = pattern.length();
563 boolean inQuote = false;
564
565 for (int i = 0; i < length; i++) {
566 char c = pattern.charAt(i);
567 result.append(c);
568 if (c == '\'') {
569 // '' is treated as a single quote regardless of being
570 // in a quoted section.
571 if ((i + 1) < length) {
572 c = pattern.charAt(i + 1);
573 if (c == '\'') {
574 i++;
575 result.append(c);
576 continue;
577 }
578 }
579 inQuote = !inQuote;
580 continue;
581 }
582 if (!inQuote) {
583 if (fSupplPatternLetters.indexOf(c) != -1) {
584 StringBuilder pat = new StringBuilder();
585 pat.append(c);
586 result.insert(result.length() - 1, "'" + fOpenBracket); //$NON-NLS-1$
587 while ((i + 1) < length && pattern.charAt(i + 1) == c) {
588 result.append(c);
589 pat.append(c);
590 i++;
591 }
592 result.append(fCloseBracket + "'"); //$NON-NLS-1$
593 fSupplPatterns.add(pat.toString());
594 }
595 }
596 }
597 return result.toString();
598 }
599
600 /**
601 * Remove the quotes from the pattern
602 *
603 * @param sb
604 * @param pattern
605 */
606 private void stripQuotes(StringBuilder sb, String pattern) {
607 String pt = "'" + fOpenBracket + pattern + fCloseBracket + "'"; //$NON-NLS-1$//$NON-NLS-2$
608 int l = sb.indexOf(pt);
609 if (l != -1) {
610 sb.delete(l + pt.length() - 1, l + pt.length());
611 sb.delete(l, l + 1);
612 }
613 }
614
615}
This page took 0.05055 seconds and 5 git commands to generate.