• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 12870114886

20 Jan 2025 02:26PM UTC coverage: 63.806% (+0.006%) from 63.8%
12870114886

push

github

dkayiwa
Fix failing CustomDatatypeUtilTest

1 of 1 new or added line in 1 file covered. (100.0%)

22 existing lines in 4 files now uncovered.

22017 of 34506 relevant lines covered (63.81%)

0.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.75
/api/src/main/java/org/openmrs/customdatatype/CustomDatatypeUtil.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.customdatatype;
11

12
import java.util.ArrayList;
13
import java.util.Collections;
14
import java.util.HashMap;
15
import java.util.List;
16
import java.util.Map;
17

18
import org.apache.commons.lang3.StringUtils;
19
import org.openmrs.ConceptDatatype;
20
import org.openmrs.api.APIException;
21
import org.openmrs.api.context.Context;
22
import org.openmrs.attribute.Attribute;
23
import org.openmrs.attribute.AttributeType;
24
import org.openmrs.serialization.SerializationException;
25
import org.openmrs.util.OpenmrsConstants;
26
import org.slf4j.Logger;
27
import org.slf4j.LoggerFactory;
28

29
/**
30
 * Helper methods for dealing with custom datatypes and their handlers
31
 * @since 1.9
32
 */
33
public class CustomDatatypeUtil {
34

35
        private CustomDatatypeUtil() {
36
        }
37
        
38
        private static final Logger log = LoggerFactory.getLogger(CustomDatatypeUtil.class);
1✔
39
        
40
        /**
41
         * @param descriptor
42
         * @return a configured datatype appropriate for descriptor
43
         */
44
        public static CustomDatatype<?> getDatatype(CustomValueDescriptor descriptor) {
45
                return getDatatype(descriptor.getDatatypeClassname(), descriptor.getDatatypeConfig());
1✔
46
        }
47
        
48
        /**
49
         * @param datatypeClassname
50
         * @param datatypeConfig
51
         * @return a configured datatype with the given classname and configuration
52
         */
53
        public static CustomDatatype<?> getDatatype(String datatypeClassname, String datatypeConfig) {
54
                try {
55
                        Class dtClass = Context.loadClass(datatypeClassname);
1✔
56
                        CustomDatatype<?> ret = (CustomDatatype<?>) Context.getDatatypeService().getDatatype(dtClass, datatypeConfig);
1✔
57
                        if (ret == null) {
1✔
UNCOV
58
                                throw new CustomDatatypeException("Can't find datatype: " + datatypeClassname);
×
59
                        }
60
                        return ret;
1✔
61
                }
62
                catch (Exception ex) {
1✔
63
                        throw new CustomDatatypeException("Error loading " + datatypeClassname + " and configuring it with "
1✔
64
                                + datatypeConfig, ex);
65
                }
66
        }
67
        
68
        /**
69
         * @param descriptor
70
         * @return a configured datatype appropriate for descriptor
71
         */
72
        public static CustomDatatype<?> getDatatypeOrDefault(CustomValueDescriptor descriptor) {
73
                try {
74
                        return getDatatype(descriptor);
1✔
75
                }
76
                catch (CustomDatatypeException ex) {
×
UNCOV
77
                        return getDatatype(OpenmrsConstants.DEFAULT_CUSTOM_DATATYPE, null);
×
78
                }
79
        }
80
        
81
        /**
82
         * @param descriptor
83
         * @return a configured datatype handler appropriate for descriptor
84
         */
85
        public static CustomDatatypeHandler getHandler(CustomValueDescriptor descriptor) {
86
                return getHandler(getDatatypeOrDefault(descriptor), descriptor.getPreferredHandlerClassname(), descriptor
1✔
87
                        .getHandlerConfig());
1✔
88
        }
89
        
90
        /**
91
         * @param dt the datatype that this handler should be for
92
         * @param preferredHandlerClassname
93
         * @param handlerConfig
94
         * @return a configured datatype handler with the given classname and configuration
95
         */
96
        public static CustomDatatypeHandler getHandler(CustomDatatype<?> dt, String preferredHandlerClassname,
97
                String handlerConfig) {
98
                if (preferredHandlerClassname != null) {
1✔
99
                        try {
100
                                Class<? extends CustomDatatypeHandler> clazz = (Class<? extends CustomDatatypeHandler>) Context
1✔
101
                                        .loadClass(preferredHandlerClassname);
1✔
102
                                CustomDatatypeHandler handler = clazz.newInstance();
1✔
103
                                if (handlerConfig != null) {
1✔
UNCOV
104
                                        handler.setHandlerConfiguration(handlerConfig);
×
105
                                }
106
                                return handler;
1✔
107
                        }
108
                        catch (Exception ex) {
1✔
109
                                log.warn("Failed to instantiate and configure preferred handler with class " + preferredHandlerClassname
1✔
110
                                        + " and config " + handlerConfig, ex);
111
                        }
112
                }
113
                
114
                // if we couldn't get the preferred handler (or none was specified) we get the default one by datatype
115
                return Context.getDatatypeService().getHandler(dt, handlerConfig);
1✔
116
        }
117
        
118
        /**
119
         * Converts a simple String-based configuration to a serialized form.
120
         * Utility method for property-style configuration implementations.
121
         *
122
         * @param simpleConfig
123
         * @return serialized form
124
         */
125
        public static String serializeSimpleConfiguration(Map<String, String> simpleConfig) {
126
                if (simpleConfig == null || simpleConfig.size() == 0) {
1✔
UNCOV
127
                        return "";
×
128
                }
129
                try {
130
                        return Context.getSerializationService().getDefaultSerializer().serialize(simpleConfig);
1✔
131
                }
132
                catch (SerializationException ex) {
×
UNCOV
133
                        throw new APIException(ex);
×
134
                }
135
        }
136
        
137
        /**
138
         * Deserializes a simple String-based configuration from the serialized form used by
139
         * {@link #serializeSimpleConfiguration(Map)}
140
         * Utility method for property-style configuration implementations.
141
         *
142
         * @param serializedConfig
143
         * @return deserialized configuration
144
         * <strong>Should</strong> deserialize a configuration serialized by the corresponding serialize method
145
         */
146
        @SuppressWarnings("unchecked")
147
        public static Map<String, String> deserializeSimpleConfiguration(String serializedConfig) {
148
                if (StringUtils.isBlank(serializedConfig)) {
1✔
UNCOV
149
                        return Collections.emptyMap();
×
150
                }
151
                try {
152
                        return Context.getSerializationService().getDefaultSerializer().deserialize(serializedConfig, Map.class);
1✔
153
                }
154
                catch (SerializationException ex) {
×
UNCOV
155
                        throw new APIException(ex);
×
156
                }
157
        }
158
        
159
        /**
160
         * Uses the appropriate datatypes to convert all values in the input map to their valueReference equivalents.
161
         * This is a convenience method for calling XyzService.getXyz(..., attributeValues, ...).
162
         *
163
         * @param datatypeValues
164
         * @return a map similar to the input parameter, but with typed values converted to their reference equivalents
165
         */
166
        public static <T extends AttributeType<?>, U> Map<T, String> getValueReferences(Map<T, U> datatypeValues) {
167
                Map<T, String> serializedAttributeValues = null;
1✔
168
                if (datatypeValues != null) {
1✔
169
                        serializedAttributeValues = new HashMap<>();
1✔
170
                        for (Map.Entry<T, U> e : datatypeValues.entrySet()) {
1✔
171
                                T vat = e.getKey();
1✔
172
                                CustomDatatype<U> customDatatype = (CustomDatatype<U>) getDatatype(vat);
1✔
173
                                String valueReference;
174
                                try {
175
                                        valueReference = customDatatype.getReferenceStringForValue(e.getValue());
1✔
176
                                }
177
                                catch (UnsupportedOperationException ex) {
×
UNCOV
178
                                        throw new APIException("CustomDatatype.error.cannot.search", new Object[] { customDatatype.getClass() });
×
179
                                }
1✔
180
                                serializedAttributeValues.put(vat, valueReference);
1✔
181
                        }
1✔
182
                }
183
                return serializedAttributeValues;
1✔
184
        }
185
        
186
        /**
187
         * @return fully-qualified classnames of all registered datatypes
188
         */
189
        public static List<String> getDatatypeClassnames() {
190
                List<String> ret = new ArrayList<>();
×
191
                for (Class<?> c : Context.getDatatypeService().getAllDatatypeClasses()) {
×
192
                        ret.add(c.getName());
×
193
                }
×
UNCOV
194
                return ret;
×
195
        }
196
        
197
        /**
198
         * @return full-qualified classnames of all registered handlers
199
         */
200
        public static List<String> getHandlerClassnames() {
201
                List<String> ret = new ArrayList<>();
×
202
                for (Class<?> c : Context.getDatatypeService().getAllHandlerClasses()) {
×
203
                        ret.add(c.getName());
×
204
                }
×
UNCOV
205
                return ret;
×
206
        }
207
        
208
        /**
209
         * @param handler
210
         * @param datatype
211
         * @return whether or not handler is compatible with datatype
212
         */
213
        public static boolean isCompatibleHandler(CustomDatatypeHandler handler, CustomDatatype<?> datatype) {
214
                List<Class<? extends CustomDatatypeHandler>> handlerClasses = Context.getDatatypeService().getHandlerClasses(
1✔
215
                    (Class<? extends CustomDatatype<?>>) datatype.getClass());
1✔
216
                return handlerClasses.contains(handler.getClass());
1✔
217
        }
218
        
219
        /**
220
         * To be called by service save methods for customizable implementations.
221
         * Iterates over all attributes and calls save on the {@link ConceptDatatype} for any dirty ones.
222
         *
223
         * @param customizable
224
         */
225
        public static void saveAttributesIfNecessary(Customizable<?> customizable) {
226
                // TODO decide whether we can move this into a SingleCustomValueSaveHandler instead of leaving it here to be called by each Customizable service's save method
227
                for (Attribute attr : customizable.getAttributes()) {
1✔
228
                        saveIfDirty(attr);
1✔
229
                }
1✔
230
        }
1✔
231
        
232
        /**
233
         * Calls the save method on value's {@link ConceptDatatype} if necessary
234
         *
235
         * @param value
236
         */
237
        public static void saveIfDirty(SingleCustomValue<?> value) {
238
                if (value.isDirty()) {
1✔
239
                        CustomDatatype datatype = CustomDatatypeUtil.getDatatype(value.getDescriptor());
1✔
240
                        if (value.getValue() == null) {
1✔
UNCOV
241
                                throw new InvalidCustomValueException(value.getClass() + " with type=" + value.getDescriptor()
×
242
                                        + " cannot be null");
243
                        }
244
                        String existingValueReference = null;
1✔
245
                        try {
246
                                existingValueReference = value.getValueReference();
1✔
247
                        }
248
                        catch (NotYetPersistedException ex) {
1✔
249
                                // this is expected
250
                        }
1✔
251
                        String newValueReference = datatype.save(value.getValue(), existingValueReference);
1✔
252
                        value.setValueReferenceInternal(newValueReference);
1✔
253
                }
254
                
255
        }
1✔
256
        
257
        /**
258
         * Validates a {@link SingleCustomValue}
259
         *
260
         * @param value
261
         * @return true is value is valid, according to its configured datatype
262
         */
263
        @SuppressWarnings("unchecked")
264
        public static <T, D extends CustomValueDescriptor> boolean validate(SingleCustomValue<D> value) {
265
                try {
266
                        CustomDatatype<T> datatype = (CustomDatatype<T>) getDatatype(value.getDescriptor());
1✔
267
                        datatype.validate((T) value.getValue());
1✔
268
                        return true;
1✔
269
                }
270
                catch (Exception ex) {
×
UNCOV
271
                        return false;
×
272
                }
273
        }
274
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc