View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.provisioning.api.utils;
20  
21  import java.text.DecimalFormat;
22  import java.text.DecimalFormatSymbols;
23  import java.text.ParseException;
24  import java.time.LocalDate;
25  import java.time.LocalDateTime;
26  import java.time.OffsetDateTime;
27  import java.time.ZoneOffset;
28  import java.time.format.DateTimeFormatter;
29  import java.time.format.DateTimeParseException;
30  import java.time.temporal.ChronoUnit;
31  import java.time.temporal.TemporalAccessor;
32  import java.util.Arrays;
33  import java.util.Locale;
34  import org.apache.commons.lang3.StringUtils;
35  import org.apache.syncope.common.lib.SyncopeConstants;
36  
37  /**
38   * Utility class for parsing / formatting dates and numbers.
39   */
40  public final class FormatUtils {
41  
42      private static final String NO_CONVERSION_PATTERN = "#,##0.###";
43  
44      private static final ThreadLocal<DecimalFormat> DECIMAL_FORMAT = ThreadLocal.withInitial(() -> {
45          DecimalFormat df = new DecimalFormat(NO_CONVERSION_PATTERN);
46          df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH));
47          return df;
48      });
49  
50      public static final ZoneOffset DEFAULT_OFFSET = OffsetDateTime.now().getOffset();
51  
52      public static String format(final TemporalAccessor temporal) {
53          return OffsetDateTime.from(temporal).
54                  truncatedTo(ChronoUnit.SECONDS).
55                  format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
56      }
57  
58      public static String format(final TemporalAccessor temporal, final String conversionPattern) {
59          return OffsetDateTime.from(temporal).format(DateTimeFormatter.ofPattern(conversionPattern));
60      }
61  
62      public static String format(final long number) {
63          return format(number, NO_CONVERSION_PATTERN);
64      }
65  
66      public static String format(final long number, final String conversionPattern) {
67          DecimalFormat df = DECIMAL_FORMAT.get();
68  
69          String previous = df.toPattern();
70          if (!previous.equals(conversionPattern)) {
71              df.applyPattern(conversionPattern);
72          }
73  
74          String formatted = df.format(number);
75  
76          if (!previous.equals(conversionPattern)) {
77              df.applyPattern(previous);
78          }
79  
80          return formatted;
81      }
82  
83      public static String format(final double number) {
84          return format(number, NO_CONVERSION_PATTERN);
85      }
86  
87      public static String format(final double number, final String conversionPattern) {
88          DecimalFormat df = DECIMAL_FORMAT.get();
89  
90          String previous = df.toPattern();
91          if (!previous.equals(conversionPattern)) {
92              df.applyPattern(conversionPattern);
93          }
94  
95          String formatted = df.format(number);
96  
97          if (!previous.equals(conversionPattern)) {
98              df.applyPattern(previous);
99          }
100 
101         return formatted;
102     }
103 
104     public static OffsetDateTime parseDate(final String source)
105             throws DateTimeParseException {
106 
107         for (String pattern : SyncopeConstants.DATE_PATTERNS) {
108             try {
109                 return parseDate(source, pattern);
110             } catch (DateTimeParseException e) {
111                 // ignore
112             }
113         }
114 
115         throw new DateTimeParseException(
116                 "Could not parse with any of " + Arrays.asList(SyncopeConstants.DATE_PATTERNS), source, 0);
117     }
118 
119     public static OffsetDateTime parseDate(final String source, final String conversionPattern)
120             throws DateTimeParseException {
121 
122         DateTimeParseException dtpe;
123 
124         DateTimeFormatter dtf = DateTimeFormatter.ofPattern(conversionPattern);
125         try {
126             if (StringUtils.containsIgnoreCase(conversionPattern, "Z")) {
127                 return OffsetDateTime.parse(source, dtf);
128             } else {
129                 return LocalDateTime.parse(source, dtf).atZone(DEFAULT_OFFSET).toOffsetDateTime();
130             }
131         } catch (DateTimeParseException e) {
132             dtpe = e;
133         }
134         try {
135             return LocalDate.parse(source, dtf).atStartOfDay(ZoneOffset.UTC).toOffsetDateTime();
136         } catch (DateTimeParseException e) {
137             dtpe = e;
138         }
139 
140         throw dtpe;
141     }
142 
143     public static Number parseNumber(final String source, final String conversionPattern) throws ParseException {
144         DecimalFormat df = DECIMAL_FORMAT.get();
145         df.applyPattern(conversionPattern);
146         return df.parse(source);
147     }
148 
149     public static void clear() {
150         DECIMAL_FORMAT.remove();
151     }
152 
153     private FormatUtils() {
154         // private empty constructor
155     }
156 }