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