• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.health.connect.datatypes.validation;
18 
19 import android.health.connect.datatypes.TimeInterval;
20 
21 import java.time.Instant;
22 import java.util.Comparator;
23 import java.util.List;
24 import java.util.Set;
25 
26 /**
27  * An util class for HC datatype validation
28  *
29  * @hide
30  */
31 public final class ValidationUtils {
32     public static final String INTDEF_VALIDATION_ERROR_PREFIX = "Unknown Intdef value";
33 
34     /** Requires long value to be within the range. */
requireInRange(long value, long lowerBound, long upperBound, String name)35     public static void requireInRange(long value, long lowerBound, long upperBound, String name) {
36         if (value < lowerBound) {
37             throw new IllegalArgumentException(
38                     name + " must not be less than " + lowerBound + ", currently " + value);
39         }
40 
41         if (value > upperBound) {
42             throw new IllegalArgumentException(
43                     name + " must not be more than " + upperBound + ", currently " + value);
44         }
45     }
46 
47     /** Requires double value to be non negative. */
requireNonNegative(double value, String name)48     public static void requireNonNegative(double value, String name) {
49         if (value < 0.0) {
50             throw new IllegalArgumentException(name + "must be non-negative, currently " + value);
51         }
52     }
53 
54     /** Requires double value to be within the range. */
requireInRange( double value, double lowerBound, double upperBound, String name)55     public static void requireInRange(
56             double value, double lowerBound, double upperBound, String name) {
57         if (value < lowerBound) {
58             throw new IllegalArgumentException(
59                     name + " must not be less than " + lowerBound + ", currently " + value);
60         }
61 
62         if (value > upperBound) {
63             throw new IllegalArgumentException(
64                     name + " must not be more than " + upperBound + ", currently " + value);
65         }
66     }
67 
68     /** Requires an integer value to be among the set of allowed values. */
validateIntDefValue(int value, Set<Integer> allowedValues, String name)69     public static void validateIntDefValue(int value, Set<Integer> allowedValues, String name) {
70         if (!allowedValues.contains(value)) {
71             throw new IllegalArgumentException(
72                     INTDEF_VALIDATION_ERROR_PREFIX + ": " + value + " for Intdef: " + name + ".");
73         }
74     }
75 
76     /** Requires list of times to be within the range. */
validateSampleStartAndEndTime( Instant sessionStartTime, Instant sessionEndTime, List<Instant> timeInstants)77     public static void validateSampleStartAndEndTime(
78             Instant sessionStartTime, Instant sessionEndTime, List<Instant> timeInstants) {
79         if (timeInstants.size() > 0) {
80             Instant minTime = timeInstants.get(0);
81             Instant maxTime = timeInstants.get(0);
82             for (Instant instant : timeInstants) {
83                 if (instant.isBefore(minTime)) {
84                     minTime = instant;
85                 }
86                 if (instant.isAfter(maxTime)) {
87                     maxTime = instant;
88                 }
89             }
90             if (minTime.isBefore(sessionStartTime) || maxTime.isAfter(sessionEndTime)) {
91                 throw new IllegalArgumentException(
92                         "Time instant values must be within session interval");
93             }
94         }
95     }
96 
97     /** Requires comparable class to be within the range. */
requireInRangeIfExists( Comparable<T> value, T threshold, T limit, String name)98     public static <T extends Comparable<T>> void requireInRangeIfExists(
99             Comparable<T> value, T threshold, T limit, String name) {
100         if (value != null && value.compareTo(threshold) < 0) {
101             throw new IllegalArgumentException(
102                     name + " must not be less than " + threshold + ", currently " + value);
103         }
104 
105         if (value != null && value.compareTo(limit) > 0) {
106             throw new IllegalArgumentException(
107                     name + " must not be more than " + limit + ", currently " + value);
108         }
109     }
110 
111     /**
112      * Sorts time interval holders by time intervals. Validates that time intervals do not overlap
113      * and within parent start and end times.
114      */
115     public static List<? extends TimeInterval.TimeIntervalHolder>
sortAndValidateTimeIntervalHolders( Instant parentStartTime, Instant parentEndTime, List<? extends TimeInterval.TimeIntervalHolder> intervalHolders)116             sortAndValidateTimeIntervalHolders(
117                     Instant parentStartTime,
118                     Instant parentEndTime,
119                     List<? extends TimeInterval.TimeIntervalHolder> intervalHolders) {
120         if (intervalHolders.isEmpty()) {
121             return intervalHolders;
122         }
123 
124         String intervalsName = intervalHolders.get(0).getClass().getSimpleName();
125         intervalHolders.sort(Comparator.comparing(TimeInterval.TimeIntervalHolder::getInterval));
126         TimeInterval currentInterval, previousInterval;
127         for (int i = 0; i < intervalHolders.size(); i++) {
128             currentInterval = intervalHolders.get(i).getInterval();
129             if (currentInterval.getStartTime().isBefore(parentStartTime)
130                     || currentInterval.getEndTime().isAfter(parentEndTime)) {
131                 throw new IllegalArgumentException(
132                         intervalsName
133                                 + ": time intervals must be within parent session time interval.");
134             }
135 
136             if (i != 0) {
137                 previousInterval = intervalHolders.get(i - 1).getInterval();
138                 if (previousInterval.getEndTime().isAfter(currentInterval.getStartTime())) {
139                     throw new IllegalArgumentException(
140                             intervalsName + ": time intervals must not overlap.");
141                 }
142             }
143         }
144         return intervalHolders;
145     }
146 }
147