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

openmrs / openmrs-module-webservices.rest / 14894311399

07 May 2025 10:17PM UTC coverage: 48.295% (+0.06%) from 48.237%
14894311399

push

github

web-flow
RESTWS-979 - Custom representations are not parsed correctly (#655)

21 of 25 new or added lines in 1 file covered. (84.0%)

1 existing line in 1 file now uncovered.

6967 of 14426 relevant lines covered (48.29%)

0.48 hits per line

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

52.17
/omod-common/src/main/java/org/openmrs/module/webservices/rest/web/ConversionUtil.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.module.webservices.rest.web;
11

12
import org.apache.commons.beanutils.PropertyUtils;
13
import org.apache.commons.logging.Log;
14
import org.apache.commons.logging.LogFactory;
15
import org.joda.time.DateTime;
16
import org.joda.time.format.DateTimeFormat;
17
import org.openmrs.Auditable;
18
import org.openmrs.Retireable;
19
import org.openmrs.Voidable;
20
import org.openmrs.api.APIException;
21
import org.openmrs.api.context.Context;
22
import org.openmrs.module.webservices.rest.SimpleObject;
23
import org.openmrs.module.webservices.rest.web.api.RestService;
24
import org.openmrs.module.webservices.rest.web.representation.CustomRepresentation;
25
import org.openmrs.module.webservices.rest.web.representation.DefaultRepresentation;
26
import org.openmrs.module.webservices.rest.web.representation.Representation;
27
import org.openmrs.module.webservices.rest.web.resource.api.Converter;
28
import org.openmrs.module.webservices.rest.web.resource.api.Resource;
29
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription;
30
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription.Property;
31
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceHandler;
32
import org.openmrs.module.webservices.rest.web.response.ConversionException;
33
import org.openmrs.util.HandlerUtil;
34
import org.openmrs.util.LocaleUtility;
35

36
import java.lang.reflect.Array;
37
import java.lang.reflect.Method;
38
import java.lang.reflect.Modifier;
39
import java.lang.reflect.ParameterizedType;
40
import java.lang.reflect.Type;
41
import java.lang.reflect.TypeVariable;
42
import java.text.SimpleDateFormat;
43
import java.util.ArrayList;
44
import java.util.Collection;
45
import java.util.Date;
46
import java.util.HashSet;
47
import java.util.List;
48
import java.util.Locale;
49
import java.util.Map;
50
import java.util.Set;
51
import java.util.SortedSet;
52
import java.util.TreeSet;
53
import java.util.concurrent.ConcurrentHashMap;
54
import java.util.concurrent.ConcurrentMap;
55

56
import static org.openmrs.module.webservices.rest.web.representation.Representation.DEFAULT;
57
import static org.openmrs.module.webservices.rest.web.representation.Representation.FULL;
58
import static org.openmrs.module.webservices.rest.web.representation.Representation.REF;
59

UNCOV
60
public class ConversionUtil {
×
61
        
62
        static final Log log = LogFactory.getLog(ConversionUtil.class);
1✔
63
        
64
        public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
65
        
66
        // This would better be a Map<Pair<Class, String>, Type> but adding the dependency for
67
        //  org.apache.commons.lang3.tuple.Pair (through omrs-api) messed up other tests
68
        private static final Map<String, Type> typeVariableMap = new ConcurrentHashMap<String, Type>();
1✔
69
        
70
        private static ConcurrentMap<Class<?>, Converter> converterCache;
71
        
72
        private static final Converter nullConverter;
73
        
74
        static {
75
                converterCache = new ConcurrentHashMap<Class<?>, Converter>();
1✔
76
                nullConverter = new Converter() {
1✔
77
                        
78
                        @Override
79
                        public Object newInstance(String type) {
80
                                return null;
×
81
                        }
82
                        
83
                        @Override
84
                        public Object getByUniqueId(String string) {
85
                                return null;
×
86
                        }
87
                        
88
                        @Override
89
                        public SimpleObject asRepresentation(Object instance, Representation rep) throws ConversionException {
90
                                return null;
×
91
                        }
92
                        
93
                        @Override
94
                        public Object getProperty(Object instance, String propertyName) throws ConversionException {
95
                                return null;
×
96
                        }
97
                        
98
                        @Override
99
                        public void setProperty(Object instance, String propertyName, Object value) throws ConversionException {
100
                                
101
                        }
×
102
                };
103
        }
1✔
104
        
105
        public static void clearCache() {
106
                converterCache = new ConcurrentHashMap<Class<?>, Converter>();
×
107
        }
×
108
        
109
        @SuppressWarnings("unchecked")
110
        public static <T> Converter<T> getConverter(Class<T> clazz) {
111
                Converter<T> result = converterCache.get(clazz);
1✔
112
                if (result != null) {
1✔
113
                        return result == nullConverter ? null : result;
1✔
114
                }
115
                
116
                try {
117
                        try {
118
                                Resource resource = Context.getService(RestService.class).getResourceBySupportedClass(clazz);
×
119
                                
120
                                if (resource instanceof Converter) {
×
121
                                        result = (Converter<T>) resource;
×
122
                                }
123
                        }
124
                        catch (APIException e) {}
1✔
125
                        
126
                        if (result == null) {
1✔
127
                                result = HandlerUtil.getPreferredHandler(Converter.class, clazz);
×
128
                        }
129
                }
130
                catch (APIException ex) {
1✔
131
                        result = null;
1✔
132
                }
×
133
                
134
                // At this point, we don't really care if a result was found or not, we cache it regardless so that repeated
135
                // searches are not performed.
136
                if (result == null) {
1✔
137
                        converterCache.put(clazz, nullConverter);
1✔
138
                } else {
139
                        converterCache.put(clazz, result);
×
140
                }
141
                
142
                return result;
1✔
143
        }
144
        
145
        /**
146
         * Converts the given object to the given type
147
         * 
148
         * @param object The value to convert
149
         * @param toType The type to convert the value to
150
         * @param instance The source object instance
151
         * @return The specified object converted to the specified type
152
         * <strong>Should</strong> resolve TypeVariables to actual type
153
         */
154
        public static Object convert(Object object, Type toType, Object instance) throws ConversionException {
155
                if (instance != null && toType instanceof TypeVariable<?>) {
1✔
156
                        TypeVariable<?> temp = ((TypeVariable<?>) toType);
1✔
157
                        toType = getTypeVariableClass(instance.getClass(), temp);
1✔
158
                }
159
                
160
                return convert(object, toType);
1✔
161
        }
162
        
163
        /**
164
         * Converts the given object to the given type
165
         * 
166
         * @param object
167
         * @param toType a simple class or generic type
168
         * @return
169
         * @throws ConversionException
170
         * <strong>Should</strong> convert strings to locales
171
         * <strong>Should</strong> convert strings to enum values
172
         * <strong>Should</strong> convert to an array
173
         * <strong>Should</strong> convert to a class
174
         */
175
        @SuppressWarnings({ "rawtypes", "unchecked" })
176
        public static Object convert(Object object, Type toType) throws ConversionException {
177
                if (object == null) {
1✔
178
                        return null;
×
179
                }
180
                
181
                Class<?> toClass = toType instanceof Class ? ((Class<?>) toType) : (Class<?>) (((ParameterizedType) toType)
1✔
182
                        .getRawType());
1✔
183
                
184
                // if we're trying to convert _to_ a collection, handle it as a special case
185
                if (Collection.class.isAssignableFrom(toClass) || toClass.isArray()) {
1✔
186
                        if (!(object instanceof Collection))
1✔
187
                                throw new ConversionException("Can only convert a Collection to a Collection/Array. Not "
×
188
                                        + object.getClass() + " to " + toType, null);
×
189
                        
190
                        if (toClass.isArray()) {
1✔
191
                                Class<?> targetElementType = toClass.getComponentType();
1✔
192
                                Collection input = (Collection) object;
1✔
193
                                Object ret = Array.newInstance(targetElementType, input.size());
1✔
194
                                
195
                                int i = 0;
1✔
196
                                for (Object element : input) {
1✔
197
                                        Array.set(ret, i, convert(element, targetElementType));
1✔
198
                                        ++i;
1✔
199
                                }
1✔
200
                                return ret;
1✔
201
                        }
202
                        
203
                        Collection ret = null;
×
204
                        if (SortedSet.class.isAssignableFrom(toClass)) {
×
205
                                ret = new TreeSet();
×
206
                        } else if (Set.class.isAssignableFrom(toClass)) {
×
207
                                ret = new HashSet();
×
208
                        } else if (List.class.isAssignableFrom(toClass)) {
×
209
                                ret = new ArrayList();
×
210
                        } else {
211
                                throw new ConversionException("Don't know how to handle collection class: " + toClass, null);
×
212
                        }
213
                        
214
                        if (toType instanceof ParameterizedType) {
×
215
                                // if we have generic type information for the target collection, we can use it to do conversion
216
                                ParameterizedType toParameterizedType = (ParameterizedType) toType;
×
217
                                Type targetElementType = toParameterizedType.getActualTypeArguments()[0];
×
218
                                for (Object element : (Collection) object) {
×
219
                                        ret.add(convert(element, targetElementType));
×
220
                                }
×
221
                        } else {
×
222
                                // otherwise we must just add all items in a non-type-safe manner
223
                                ret.addAll((Collection) object);
×
224
                        }
225
                        return ret;
×
226
                }
227
                
228
                // otherwise we're converting _to_ a non-collection type
229
                
230
                if (toClass.isAssignableFrom(object.getClass())) {
1✔
231
                        return object;
×
232
                }
233
                
234
                // Numbers with a decimal are always assumed to be Double, so convert to Float, if necessary
235
                if (toClass.isAssignableFrom(Float.class) && object instanceof Double) {
1✔
236
                        return new Float((Double) object);
×
237
                }
238
                
239
                if (object instanceof String) {
1✔
240
                        String string = (String) object;
1✔
241
                        Converter<?> converter = getConverter(toClass);
1✔
242
                        if (converter != null)
1✔
243
                                return converter.getByUniqueId(string);
×
244
                        
245
                        if (toClass.isAssignableFrom(Date.class)) {
1✔
246
                                IllegalArgumentException pex = null;
1✔
247
                                String[] supportedFormats = { DATE_FORMAT, "yyyy-MM-dd'T'HH:mm:ss.SSS", "yyyy-MM-dd'T'HH:mm:ssZ",
1✔
248
                                        "yyyy-MM-dd'T'HH:mm:ssXXX", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd" };
249
                                for (int i = 0; i < supportedFormats.length; i++) {
1✔
250
                                        try {
251
                                                return DateTime.parse(string, DateTimeFormat.forPattern(supportedFormats[i])).toDate();
1✔
252
                                        }
253
                                        catch (IllegalArgumentException ex) {
1✔
254
                                                pex = ex;
1✔
255
                                        }
256
                                }
257
                                throw new ConversionException(
×
258
                                        "Error converting date - correct format (ISO8601 Long): yyyy-MM-dd'T'HH:mm:ss.SSSZ", pex);
259
                        } else if (toClass.isAssignableFrom(Locale.class)) {
1✔
260
                                return LocaleUtility.fromSpecification(object.toString());
1✔
261
                        } else if (toClass.isEnum()) {
1✔
262
                                return Enum.valueOf((Class<? extends Enum>) toClass, object.toString().toUpperCase());
1✔
263
                        } else if (toClass.isAssignableFrom(Class.class)) {
1✔
264
                                try {
265
                                        return Context.loadClass((String) object);
1✔
266
                                }
267
                                catch (ClassNotFoundException e) {
×
268
                                        throw new ConversionException("Could not convert from " + object.getClass() + " to " + toType, e);
×
269
                                }
270
                        }
271
                        // look for a static valueOf(String) method (e.g. Double, Integer, Boolean)
272
                        try {
273
                                Method method = toClass.getMethod("valueOf", String.class);
1✔
274
                                if (Modifier.isStatic(method.getModifiers()) && toClass.isAssignableFrom(method.getReturnType())) {
1✔
275
                                        return method.invoke(null, string);
1✔
276
                                }
277
                        }
278
                        catch (Exception ex) {}
×
279
                } else if (object instanceof Map) {
×
280
                        return convertMap((Map<String, ?>) object, toClass);
×
281
                }
282
                if (toClass.isAssignableFrom(Double.class) && object instanceof Number) {
×
283
                        return ((Number) object).doubleValue();
×
284
                } else if (toClass.isAssignableFrom(Integer.class) && object instanceof Number) {
×
285
                        return ((Number) object).intValue();
×
286
                }
287
                
288
                if (toClass.isAssignableFrom(String.class) && object instanceof Boolean) {
×
289
                        return String.valueOf(object);
×
290
                }
291
                
292
                throw new ConversionException("Don't know how to convert from " + object.getClass() + " to " + toType, null);
×
293
        }
294
        
295
        /**
296
         * Converts a map to the given type, using the registered converter
297
         * 
298
         * @param map the map (typically a SimpleObject submitted as json) to convert
299
         * @param toClass the class to convert map to
300
         * @return the result of using a converter to instantiate a new class and set map's properties
301
         *         on it
302
         * @throws ConversionException
303
         */
304
        @SuppressWarnings({ "rawtypes", "unchecked" })
305
        public static Object convertMap(Map<String, ?> map, Class<?> toClass) throws ConversionException {
306
                // TODO handle refs by fetching the object at their URI
307
                Converter converter = getConverter(toClass);
×
308
                
309
                Object ret = null;
×
310
                Object uuid = map.get(RestConstants.PROPERTY_UUID);
×
311
                if (uuid instanceof String) {
×
312
                        ret = converter.getByUniqueId(uuid.toString());
×
313
                }
314
                
315
                if (ret == null) {
×
316
                        String type = (String) map.get(RestConstants.PROPERTY_FOR_TYPE);
×
317
                        ret = converter.newInstance(type);
×
318
                }
319
                
320
                // If the converter is a resource handler use the order of properties of its default representation
321
                if (converter instanceof DelegatingResourceHandler) {
×
322
                        
323
                        DelegatingResourceHandler handler = (DelegatingResourceHandler) converter;
×
324
                        DelegatingResourceDescription resDesc = handler.getRepresentationDescription(new DefaultRepresentation());
×
325
                        
326
                        // Some resources do not have delegating resource description
327
                        if (resDesc != null) {
×
328
                                for (Map.Entry<String, Property> prop : resDesc.getProperties().entrySet()) {
×
329
                                        if (map.containsKey(prop.getKey()) && !RestConstants.PROPERTY_FOR_TYPE.equals(prop.getKey())) {
×
330
                                                converter.setProperty(ret, prop.getKey(), map.get(prop.getKey()));
×
331
                                        }
332
                                }
×
333
                        }
334
                }
335
                
336
                for (Map.Entry<String, ?> prop : map.entrySet()) {
×
337
                        if (RestConstants.PROPERTY_FOR_TYPE.equals(prop.getKey()))
×
338
                                continue;
×
339
                        converter.setProperty(ret, prop.getKey(), prop.getValue());
×
340
                }
×
341
                return ret;
×
342
        }
343
        
344
        /**
345
         * Gets a property from the delegate, with the given representation
346
         * 
347
         * @param propertyName
348
         * @param rep
349
         * @return
350
         * @throws ConversionException
351
         */
352
        public static Object getPropertyWithRepresentation(Object bean, String propertyName, Representation rep)
353
                throws ConversionException {
354
                Object o;
355
                try {
356
                        o = PropertyUtils.getProperty(bean, propertyName);
1✔
357
                }
358
                catch (Exception ex) {
×
359
                        throw new ConversionException(null, ex);
×
360
                }
1✔
361
                if (o instanceof Collection) {
1✔
362
                        List<Object> ret = new ArrayList<Object>();
×
363
                        for (Object element : (Collection<?>) o)
×
364
                                ret.add(convertToRepresentation(element, rep));
×
365
                        return ret;
×
366
                } else {
367
                        o = convertToRepresentation(o, rep);
1✔
368
                        return o;
1✔
369
                }
370
        }
371
        
372
        @SuppressWarnings("unchecked")
373
        public static <S> Object convertToRepresentation(S o, Representation rep) throws ConversionException {
374
                return convertToRepresentation(o, rep, (Converter) null);
1✔
375
        }
376
        
377
        @SuppressWarnings("unchecked")
378
        public static <S> Object convertToRepresentation(S o, Representation rep, Class<?> convertAs) throws ConversionException {
379
                Converter<?> converter = convertAs != null ? getConverter(convertAs) : null;
×
380
                return convertToRepresentation(o, rep, converter);
×
381
        }
382
        
383
        public static <S> Object convertToRepresentation(S o, Representation rep, Converter specificConverter)
384
                throws ConversionException {
385
                if (o == null)
1✔
386
                        return null;
×
387
                o = new HibernateLazyLoader().load(o);
1✔
388
                
389
                if (o instanceof Collection) {
1✔
390
                        List ret = new ArrayList();
×
391
                        for (Object item : ((Collection) o)) {
×
392
                                ret.add(convertToRepresentation(item, rep, specificConverter));
×
393
                        }
×
394
                        return ret;
×
395
                } else if (o instanceof Map) {
1✔
396
                        if (rep instanceof CustomRepresentation) {
1✔
397
                                return convertToCustomRepresentation(o, (CustomRepresentation) rep);
1✔
398
                        }
399
                        SimpleObject ret = new SimpleObject();
×
400
                        for (Map.Entry<?, ?> entry : ((Map<?, ?>) o).entrySet()) {
×
401
                                ret.put(entry.getKey().toString(),
×
402
                                    convertToRepresentation(entry.getValue(), Representation.REF, (Converter) null));
×
403
                        }
×
404
                        return ret;
×
405
                } else {
406
                        Converter<S> converter = specificConverter != null ? specificConverter : (Converter) getConverter(o.getClass());
1✔
407
                        if (converter == null) {
1✔
408
                                // try a few known datatypes
409
                                if (o instanceof Date) {
1✔
410
                                        return new SimpleDateFormat(DATE_FORMAT).format((Date) o);
1✔
411
                                }
412
                                // otherwise we have no choice but to return the plain object
413
                                return o;
1✔
414
                        }
415
                        try {
416
                                return converter.asRepresentation(o, rep);
×
417
                        }
418
                        catch (Exception ex) {
×
419
                                throw new ConversionException("converting " + o.getClass() + " to " + rep, ex);
×
420
                        }
421
                }
422
        }
423
        
424
        /**
425
         * Converts an object to its custom representation
426
         * This could be used to convert any domain objects that does not have any specific converter associated with them
427
         * such as SimpleObject's, Map's, etc
428
         */
429
        private static SimpleObject convertToCustomRepresentation(Object o, CustomRepresentation rep) {
430
                DelegatingResourceDescription drd = ConversionUtil.getCustomRepresentationDescription(rep);
1✔
431
                
432
                SimpleObject result = new SimpleObject();
1✔
433
                for (String propertyName : drd.getProperties().keySet()) {
1✔
434
                        DelegatingResourceDescription.Property property = drd.getProperties().get(propertyName);
1✔
435
                        Object propertyValue = ConversionUtil.getPropertyWithRepresentation(o, propertyName, property.getRep());
1✔
436
                        result.add(propertyName, propertyValue);
1✔
437
                }
1✔
438
                
439
                return result;
1✔
440
        }
441
        
442
        /**
443
         * Gets the type for the specified generic type variable.
444
         * 
445
         * @param instanceClass An instance of the class with the specified generic type variable.
446
         * @param typeVariable The generic type variable.
447
         * @return The actual type of the generic type variable or {@code null} if not found.
448
         * <strong>Should</strong> return the actual type if defined on the parent class
449
         * <strong>Should</strong> return the actual type if defined on the grand-parent class
450
         * <strong>Should</strong> return null when actual type cannot be found
451
         * <strong>Should</strong> return the correct actual type if there are multiple generic types
452
         * <strong>Should</strong> throw IllegalArgumentException when instance class is null
453
         * <strong>Should</strong> throw IllegalArgumentException when typeVariable is null
454
         */
455
        public static Type getTypeVariableClass(Class<?> instanceClass, TypeVariable<?> typeVariable) {
456
                if (instanceClass == null) {
1✔
457
                        throw new IllegalArgumentException("The instance class is required.");
1✔
458
                }
459
                if (typeVariable == null) {
1✔
460
                        throw new IllegalArgumentException("The type variable is required.");
1✔
461
                }
462
                
463
                String genericTypeName = typeVariable.getName();
1✔
464
                Type type = instanceClass;
1✔
465
                
466
                // Check to see if type variable has already been cached
467
                Type result = typeVariableMap.get(instanceClass.getName().concat(genericTypeName));
1✔
468
                
469
                // Walk the inheritance chain up and try to find the generic type with the specified name
470
                while (result == null && type != null && !type.equals(Object.class)) {
1✔
471
                        if (type instanceof Class) {
1✔
472
                                type = ((Class) type).getGenericSuperclass();
1✔
473
                        } else {
474
                                ParameterizedType parameterizedType = (ParameterizedType) type;
1✔
475
                                Class<?> rawType = (Class) parameterizedType.getRawType();
1✔
476
                                
477
                                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
1✔
478
                                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
1✔
479
                                for (int i = 0; i < actualTypeArguments.length; i++) {
1✔
480
                                        String name = typeParameters[i].getName();
1✔
481
                                        Type actualType = actualTypeArguments[i];
1✔
482
                                        
483
                                        // Cache each generic type's actual type
484
                                        typeVariableMap.put(instanceClass.getName().concat(name), actualType);
1✔
485
                                        
486
                                        if (name.equals(genericTypeName)) {
1✔
487
                                                // Found it
488
                                                result = actualType;
1✔
489
                                                break;
1✔
490
                                        }
491
                                }
492
                                
493
                                // Move up to the parent class
494
                                type = rawType.getGenericSuperclass();
1✔
495
                        }
1✔
496
                }
497
                
498
                return result;
1✔
499
        }
500
        
501
        /**
502
         * Gets extra book-keeping info, for the full representation
503
         * 
504
         * @param delegate
505
         * @return
506
         */
507
        public static SimpleObject getAuditInfo(Object delegate) {
508
                SimpleObject ret = new SimpleObject();
×
509
                
510
                if (delegate instanceof Auditable) {
×
511
                        Auditable auditable = (Auditable) delegate;
×
512
                        ret.put("creator", getPropertyWithRepresentation(auditable, "creator", Representation.REF));
×
513
                        ret.put("dateCreated", convertToRepresentation(auditable.getDateCreated(), Representation.DEFAULT));
×
514
                        ret.put("changedBy", getPropertyWithRepresentation(auditable, "changedBy", Representation.REF));
×
515
                        ret.put("dateChanged", convertToRepresentation(auditable.getDateChanged(), Representation.DEFAULT));
×
516
                }
517
                if (delegate instanceof Retireable) {
×
518
                        Retireable retireable = (Retireable) delegate;
×
519
                        if (retireable.isRetired()) {
×
520
                                ret.put("retiredBy", getPropertyWithRepresentation(retireable, "retiredBy", Representation.REF));
×
521
                                ret.put("dateRetired", convertToRepresentation(retireable.getDateRetired(), Representation.DEFAULT));
×
522
                                ret.put("retireReason", convertToRepresentation(retireable.getRetireReason(), Representation.DEFAULT));
×
523
                        }
524
                }
525
                if (delegate instanceof Voidable) {
×
526
                        Voidable voidable = (Voidable) delegate;
×
527
                        if (voidable.isVoided()) {
×
528
                                ret.put("voidedBy", getPropertyWithRepresentation(voidable, "voidedBy", Representation.REF));
×
529
                                ret.put("dateVoided", convertToRepresentation(voidable.getDateVoided(), Representation.DEFAULT));
×
530
                                ret.put("voidReason", convertToRepresentation(voidable.getVoidReason(), Representation.DEFAULT));
×
531
                        }
532
                }
533
                
534
                return ret;
×
535
        }
536

537
        /**
538
         * <strong>Should</strong> return delegating resource description
539
         */
540
        public static DelegatingResourceDescription getCustomRepresentationDescription(CustomRepresentation representation) {
541
                DelegatingResourceDescription desc = new DelegatingResourceDescription();
1✔
542

543
                String def = representation.getRepresentation();
1✔
544
                def = def.startsWith("(") ? def.substring(1) : def;
1✔
545
                def = def.endsWith(")") ? def.substring(0, def.length() - 1) : def;
1✔
546

547
                int startIndex = 0;
1✔
548
                List<String> properties = new ArrayList<String>();
1✔
549
                int nestingLevel = 0;
1✔
550
                for (int i=0; i < def.length(); i++) {
1✔
551
                        char c = def.charAt(i);
1✔
552
                        if (c == '(') {
1✔
553
                                nestingLevel++;
1✔
554
                        }
555
                        else if (c == ')') {
1✔
NEW
556
                                nestingLevel--;
×
557
                        }
558
                        else if (c == ',' && nestingLevel == 0) {
1✔
NEW
559
                                properties.add(def.substring(startIndex, i));
×
NEW
560
                                startIndex = i + 1;
×
561
                        }
562
                }
563
                properties.add(def.substring(startIndex));
1✔
564

565
                for (String propertyDefinition : properties) {
1✔
566
                        if (propertyDefinition.contains(":")) {
1✔
567
                                String[] propertyAndRepresentation = propertyDefinition.split(":", 2);
1✔
568
                                String property = propertyAndRepresentation[0];
1✔
569
                                String rep = propertyAndRepresentation[1];
1✔
570
                                Representation r;
571
                                if (rep.startsWith("(")) {
1✔
572
                                        r = new CustomRepresentation(rep);
1✔
573
                                }
574
                                else {
NEW
575
                                        r = rep.equalsIgnoreCase("REF") ? REF : rep.equalsIgnoreCase("FULL") ? FULL : DEFAULT;
×
576
                                }
577
                                desc.addProperty(property, r);
1✔
578
                        }
1✔
579
                        else {
580
                                if (propertyDefinition.equals("links")) {
1✔
581
                                        desc.addSelfLink();
×
582
                                        desc.addLink("default", ".?v=" + RestConstants.REPRESENTATION_DEFAULT);
×
583
                                }
584
                                else {
585
                                        desc.addProperty(propertyDefinition);
1✔
586
                                }
587
                        }
588
                }
1✔
589

590
                return desc;
1✔
591
        }
592
}
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