diff options
author | Patrick Tasse | 2014-01-13 19:14:20 +0000 |
---|---|---|
committer | Patrick Tasse | 2014-01-13 19:14:20 +0000 |
commit | 93d7a3e2ea85ca152c18db1c015c6ad871eb9d85 (patch) | |
tree | 356d64b6244407c7078312e7cbac8782feba0647 | |
parent | 3ed1a084a76c5d28a936d279491f6aead0461cc6 (diff) | |
download | org.eclipse.linuxtools-93d7a3e2ea85ca152c18db1c015c6ad871eb9d85.tar.gz org.eclipse.linuxtools-93d7a3e2ea85ca152c18db1c015c6ad871eb9d85.tar.xz org.eclipse.linuxtools-93d7a3e2ea85ca152c18db1c015c6ad871eb9d85.zip |
tmf: Fix parsing bugs in TmfTimestampFormat
- Fix bug 425199 when parsing a string with no sub-second delimiter
- Fix bug when parsing when the pattern has no sub-second delimiter
- Fix bug when parsing a string starting with a period without seconds
- Fix bug when formatting Long.MIN_VALUE
- Simplify code by letting base class only format the date/time pattern
- Improve and correct Javadoc comments
Change-Id: I2d5bcde62cc8f173c1340e84decbd9d770fc723f
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-on: https://git.eclipse.org/r/20520
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
IP-Clean: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Hudson CI
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
-rw-r--r-- | lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/timestamp/TmfTimestampFormat.java | 131 |
1 files changed, 55 insertions, 76 deletions
diff --git a/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/timestamp/TmfTimestampFormat.java b/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/timestamp/TmfTimestampFormat.java index bbf2d34290..e98e038d54 100644 --- a/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/timestamp/TmfTimestampFormat.java +++ b/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/timestamp/TmfTimestampFormat.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2013 Ericsson + * Copyright (c) 2012, 2014 Ericsson * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which @@ -27,7 +27,7 @@ import java.util.regex.Pattern; /** * A formatting and parsing facility that can handle timestamps that span the - * epoch with a precision down to the nanosecond. It can be understood as a + * epoch with a precision down to the nanosecond. It can be understood as a * simplified and more constrained version of SimpleDateFormat as it limits the * number of allowed pattern characters and the acceptable timestamp formats. * <p> @@ -86,7 +86,7 @@ import java.util.regex.Pattern; * <tr bgcolor="#eeeeff"> * <td><code>T</code> * <td>The seconds since the epoch - * <td><code>00-...</code> + * <td><code>0-...</code> * <td><code>1332170682</code> * </table> * </blockquote> @@ -111,7 +111,7 @@ import java.util.regex.Pattern; * <td><code>456</code> * <tr> * <td><code>NNN</code> - * <td>Nanosecond in µs + * <td>Nanoseconds in µs * <td><code>000-999</code> * <td><code>789</code> * </table> @@ -120,12 +120,13 @@ import java.util.regex.Pattern; * <strong>Note: </strong>If "T" is used, no other Date or Time pattern * can be used. Also, "T" should be used for time intervals. * <p> - * <strong>Note: </strong>Each sub-field can be separated by a single, - * optional character delimiter. However, the between Date/Time and the - * Sub-seconds pattern is mandatory (if there is a fractional part) and - * has to be separated from Date/time by "." (period). + * <strong>Note: </strong>When parsing, the Sub-Seconds sub-field digits can be + * separated by a single, optional character delimiter. However, the delimiter + * between the Date/Time pattern and the Sub-Seconds pattern is mandatory (if + * there is a fractional part) and must be a "." (period). The Date and Time + * patterns must not contain any "." (period). * <p> - * The recognized delimiters are: + * The recognized Sub-Seconds delimiters are: * <ul> * <li>Space ("<code> </code>") * <li>Period (<code>".</code>") @@ -190,13 +191,13 @@ public class TmfTimestampFormat extends SimpleDateFormat { public static final String DEFAULT_TIME_PATTERN = "HH:mm:ss.SSS CCC NNN"; //$NON-NLS-1$ /** - * The LTTng 0.x legacy timestamp format + * The default interval pattern */ public static final String DEFAULT_INTERVAL_PATTERN = "TTT.SSS CCC NNN"; //$NON-NLS-1$ // Fractions of seconds supported patterns private static final String DOT_RE = "\\."; //$NON-NLS-1$ - private static final String SEP_RE = "[ \\.,-_:;/\\\"]?"; //$NON-NLS-1$ + private static final String SEP_RE = "[ \\.,\\-_:;/\\\"]?"; //$NON-NLS-1$ private static final String DGTS_3_RE = "(\\d{3})"; //$NON-NLS-1$ private static final String DGTS_13_RE = "(\\d{1,3})"; //$NON-NLS-1$ @@ -221,7 +222,10 @@ public class TmfTimestampFormat extends SimpleDateFormat { // The timestamp pattern private String fPattern; - // The timestamp pattern + // The sub-seconds pattern + private String fSubSecPattern; + + // The list of supplementary patterns private List<String> fSupplPatterns = new ArrayList<String>(); /** @@ -340,8 +344,14 @@ public class TmfTimestampFormat extends SimpleDateFormat { @Override public void applyPattern(String pattern) { fPattern = pattern; - String quotedPattern = quoteSpecificTags(pattern); - super.applyPattern(quotedPattern); + int dot = pattern.indexOf('.'); + if (dot == -1) { + dot = pattern.length(); + } + // The super pattern is the date/time pattern, quoted and bracketed + super.applyPattern(quoteSpecificTags(pattern.substring(0, dot), true)); + // The sub-seconds pattern is bracketed (but not quoted) + fSubSecPattern = quoteSpecificTags(pattern.substring(dot), false); } @Override @@ -364,32 +374,23 @@ public class TmfTimestampFormat extends SimpleDateFormat { // Split the timestamp value into its sub-components long date = value / 1000000; // milliseconds since January 1, 1970, 00:00:00 GMT long sec = value / 1000000000; // seconds since January 1, 1970, 00:00:00 GMT - long ms = Math.abs(value) % 1000000000 / 1000000; // milliseconds - long cs = Math.abs(value) % 1000000 / 1000; // microseconds - long ns = Math.abs(value) % 1000; // nanoseconds + long ms = Math.abs((value % 1000000000) / 1000000); // milliseconds + long cs = Math.abs((value % 1000000) / 1000); // microseconds + long ns = Math.abs(value % 1000); // nanoseconds // Adjust for negative value when formatted as a date - if (value < 0 && ms + cs + ns > 0 && !fPattern.contains("T")) { //$NON-NLS-1$ + if (value < 0 && ms + cs + ns > 0 && !super.toPattern().contains(fOpenBracket + "T")) { //$NON-NLS-1$ date -= 1; long nanosec = 1000000000 - (1000000 * ms + 1000 * cs + ns); ms = nanosec / 1000000; - cs = nanosec % 1000000 / 1000; + cs = (nanosec % 1000000) / 1000; ns = nanosec % 1000; } - // Let the base class fill the stuff it knows about + // Let the base class format the date/time pattern StringBuffer result = new StringBuffer(super.format(date)); - - // In the case where there is no separation between 2 supplementary - // fields, the pattern will have the form "..'[pat-1]''[pat-2]'.." and - // the base class format() will interpret the 2 adjacent quotes as a - // wanted character in the result string as ("..[pat-1]'[pat-2].."). - // Remove these extra quotes before filling the supplementary fields. - int loc = result.indexOf(fCloseBracket + "'" + fOpenBracket); //$NON-NLS-1$ - while (loc != -1) { - result.deleteCharAt(loc + fCloseBracket.length()); - loc = result.indexOf(fCloseBracket + "'" + fOpenBracket); //$NON-NLS-1$ - } + // Append the sub-second pattern + result.append(fSubSecPattern); // Fill in our extensions for (String pattern : fSupplPatterns) { @@ -424,9 +425,9 @@ public class TmfTimestampFormat extends SimpleDateFormat { break; } - // Substitute the placeholder with the formatted value + // Substitute the placeholder pattern with the formatted value String ph = new StringBuffer(fOpenBracket + pattern + fCloseBracket).toString(); - loc = result.indexOf(ph); + int loc = result.indexOf(ph); result.replace(loc, loc + length + fOpenBracket.length() + fCloseBracket.length(), fmtVal); } @@ -437,8 +438,8 @@ public class TmfTimestampFormat extends SimpleDateFormat { * Parse a string according to the format pattern * * @param string the source string - * @param ref the reference (base) time - * @return the parsed value + * @param ref the reference (base) time (in ns) + * @return the parsed value (in ns) * @throws ParseException if the string has an invalid format */ public synchronized long parseValue(final String string, final long ref) throws ParseException { @@ -454,39 +455,28 @@ public class TmfTimestampFormat extends SimpleDateFormat { long microsec = 0; long nanosec = 0; - // Since we are processing the fractional part, substitute it with - // its pattern so the base parser doesn't complain - StringBuilder sb = new StringBuilder(string); int dot = string.indexOf('.'); if (dot == -1) { - sb.append('.'); dot = string.length(); } - sb = new StringBuilder(string.substring(0, dot)); - String basePattern = super.toPattern(); - int dot2 = basePattern.indexOf('.'); - if (dot2 != -1) { - sb.append(basePattern.substring(dot2)); - } // Fill in our extensions for (String pattern : fSupplPatterns) { - String pat = fOpenBracket + pattern + fCloseBracket; Matcher matcher; // Extract the substring corresponding to the extra pattern letters - // and replace with the pattern so the base parser can do its job. switch (pattern.charAt(0)) { case 'T': // Remove everything up to the first "." and compute the // number of seconds since the epoch. If there is no period, // assume an integer value and return immediately - if (dot < 1) { + if (dot < 0) { return new DecimalFormat("0").parse(string).longValue() * 1000000000; //$NON-NLS-1$ + } else if (dot == 0) { + seconds = 0; + } else { + seconds = new DecimalFormat("0").parse(string.substring(0, dot)).longValue(); //$NON-NLS-1$ } - seconds = new DecimalFormat("0").parse(string.substring(0, dot)).longValue(); //$NON-NLS-1$ - sb.delete(0, dot); - sb.insert(0, pat); break; case 'S': matcher = MILLISEC_PAT.matcher(string.substring(dot)); @@ -496,7 +486,6 @@ public class TmfTimestampFormat extends SimpleDateFormat { millisec *= 10; } } - stripQuotes(sb, pattern); break; case 'C': matcher = MICROSEC_PAT.matcher(string.substring(dot)); @@ -506,7 +495,6 @@ public class TmfTimestampFormat extends SimpleDateFormat { microsec *= 10; } } - stripQuotes(sb, pattern); break; case 'N': matcher = NANOSEC_PAT.matcher(string.substring(dot)); @@ -516,7 +504,6 @@ public class TmfTimestampFormat extends SimpleDateFormat { nanosec *= 10; } } - stripQuotes(sb, pattern); break; default: break; @@ -525,7 +512,7 @@ public class TmfTimestampFormat extends SimpleDateFormat { // If there was no "T" (thus not an interval), parse as a date if (seconds == -1) { - Date baseDate = super.parse(sb.toString()); + Date baseDate = super.parse(string.substring(0, dot)); Calendar refTime = Calendar.getInstance(getTimeZone()); refTime.setTimeInMillis(ref / 1000000); @@ -559,7 +546,7 @@ public class TmfTimestampFormat extends SimpleDateFormat { * Parse a string according to the format pattern * * @param string the source string - * @return the parsed value + * @return the parsed value (in ns) * @throws ParseException if the string has an invalid format */ public long parseValue(final String string) throws ParseException { @@ -574,16 +561,17 @@ public class TmfTimestampFormat extends SimpleDateFormat { /** * Copy the pattern but quote (bracket with "[&" and "&]") the - * TmfTimestampFormat specific tags so these fields are treated as - * comments by the base class. + * TmfTimestampFormat specific tags. Optionally surround tags with single + * quotes so these fields are treated as comments by the base class. * * It also keeps track of the corresponding quoted fields so they can be * properly populated later on (by format()). * * @param pattern the 'extended' pattern + * @param addQuotes true to add single quotes around tags * @return the quoted and bracketed pattern */ - private String quoteSpecificTags(final String pattern) { + private String quoteSpecificTags(final String pattern, boolean addQuotes) { StringBuffer result = new StringBuffer(); @@ -611,13 +599,19 @@ public class TmfTimestampFormat extends SimpleDateFormat { if (fSupplPatternLetters.indexOf(c) != -1) { StringBuilder pat = new StringBuilder(); pat.append(c); - result.insert(result.length() - 1, "'" + fOpenBracket); //$NON-NLS-1$ + if (addQuotes) { + result.insert(result.length() - 1, "'"); //$NON-NLS-1$ + } + result.insert(result.length() - 1, fOpenBracket); while ((i + 1) < length && pattern.charAt(i + 1) == c) { result.append(c); pat.append(c); i++; } - result.append(fCloseBracket + "'"); //$NON-NLS-1$ + result.append(fCloseBracket); + if (addQuotes) { + result.append("'"); //$NON-NLS-1$ + } fSupplPatterns.add(pat.toString()); } } @@ -625,19 +619,4 @@ public class TmfTimestampFormat extends SimpleDateFormat { return result.toString(); } - /** - * Remove the quotes from the pattern - * - * @param sb - * @param pattern - */ - private void stripQuotes(StringBuilder sb, String pattern) { - String pt = "'" + fOpenBracket + pattern + fCloseBracket + "'"; //$NON-NLS-1$//$NON-NLS-2$ - int l = sb.indexOf(pt); - if (l != -1) { - sb.delete(l + pt.length() - 1, l + pt.length()); - sb.delete(l, l + 1); - } - } - } |