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

openmrs / openmrs-core / 25880439654

14 May 2026 07:19PM UTC coverage: 63.424% (+0.005%) from 63.419%
25880439654

Pull #6089

github

web-flow
Merge 01b42c203 into 0dab4c409
Pull Request #6089: TRUNK-6635 Add search by LocationSearchCriteria, with option to find …

60 of 64 new or added lines in 5 files covered. (93.75%)

17 existing lines in 4 files now uncovered.

23314 of 36759 relevant lines covered (63.42%)

0.63 hits per line

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

83.93
/api/src/main/java/org/openmrs/api/impl/LocationServiceImpl.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.api.impl;
11

12
import java.util.ArrayList;
13
import java.util.Date;
14
import java.util.List;
15
import java.util.Map;
16

17
import org.apache.commons.collections.CollectionUtils;
18
import org.openmrs.Address;
19
import org.openmrs.Location;
20
import org.openmrs.LocationAttribute;
21
import org.openmrs.LocationAttributeType;
22
import org.openmrs.LocationTag;
23
import org.openmrs.api.APIException;
24
import org.openmrs.api.LocationService;
25
import org.openmrs.api.context.Context;
26
import org.openmrs.api.db.LocationDAO;
27
import org.openmrs.customdatatype.CustomDatatypeUtil;
28
import org.openmrs.parameter.LocationSearchCriteria;
29
import org.openmrs.parameter.LocationSearchCriteriaBuilder;
30
import org.openmrs.util.OpenmrsConstants;
31
import org.openmrs.util.OpenmrsUtil;
32
import org.springframework.transaction.annotation.Transactional;
33
import org.springframework.util.StringUtils;
34

35
/**
36
 * Default implementation of the {@link LocationService}
37
 * <p>
38
 * This class should not be instantiated alone, get a service class from the Context:
39
 * Context.getLocationService();
40
 *
41
 * @see org.openmrs.api.context.Context
42
 * @see org.openmrs.api.LocationService
43
 * @see org.openmrs.Location
44
 */
45
@Transactional
46
public class LocationServiceImpl extends BaseOpenmrsService implements LocationService {
1✔
47
        
48
        private LocationDAO dao;
49
        
50
        /**
51
         * @see org.openmrs.api.LocationService#setLocationDAO(org.openmrs.api.db.LocationDAO)
52
         */
53
        @Override
54
        public void setLocationDAO(LocationDAO dao) {
55
                this.dao = dao;
1✔
56
        }
1✔
57
        
58
        /**
59
         * @see org.openmrs.api.LocationService#saveLocation(org.openmrs.Location)
60
         */
61
        @Override
62
        public Location saveLocation(Location location) throws APIException {
63
                if (location.getName() == null) {
1✔
64
                        throw new APIException("Location.name.required", (Object[]) null);
×
65
                }
66
                
67
                // Check for transient tags. If found, try to match by name and overwrite, otherwise throw exception.
68
                if (location.getTags() != null) {
1✔
69
                        for (LocationTag tag : location.getTags()) {
1✔
70
                                
71
                                // only check transient (aka non-precreated) location tags
72
                                if (tag.getLocationTagId() == null) {
1✔
73
                                        if (!StringUtils.hasLength(tag.getName())) {
1✔
74
                                                throw new APIException("Location.tag.name.required", (Object[]) null);
1✔
75
                                        }
76
                                        
77
                                        LocationTag existing = Context.getLocationService().getLocationTagByName(tag.getName());
1✔
78
                                        if (existing != null) {
1✔
79
                                                location.removeTag(tag);
1✔
80
                                                location.addTag(existing);
1✔
81
                                        } else {
82
                                                throw new APIException("Location.cannot.add.transient.tags", (Object[]) null);
1✔
83
                                        }
84
                                }
85
                        }
1✔
86
                }
87
                
88
                CustomDatatypeUtil.saveAttributesIfNecessary(location);
1✔
89
                
90
                return dao.saveLocation(location);
1✔
91
        }
92
        
93
        /**
94
         * @see org.openmrs.api.LocationService#getLocation(java.lang.Integer)
95
         */
96
        @Override
97
        @Transactional(readOnly = true)
98
        public Location getLocation(Integer locationId) throws APIException {
99
                return dao.getLocation(locationId);
1✔
100
        }
101
        
102
        /**
103
         * @see org.openmrs.api.LocationService#getLocation(java.lang.String)
104
         */
105
        @Override
106
        @Transactional(readOnly = true)
107
        public Location getLocation(String name) throws APIException {
108
                return dao.getLocation(name);
1✔
109
        }
110
        
111
        /**
112
         * @see org.openmrs.api.LocationService#getDefaultLocation()
113
         */
114
        @Override
115
        @Transactional(readOnly = true)
116
        public Location getDefaultLocation() throws APIException {
117
                Location location = null;
1✔
118
                String locationGP = Context.getAdministrationService().getGlobalProperty(
1✔
119
                    OpenmrsConstants.GLOBAL_PROPERTY_DEFAULT_LOCATION_NAME);
120
                
121
                if (StringUtils.hasText(locationGP)) {
1✔
122
                        location = Context.getLocationService().getLocation(locationGP);
1✔
123
                }
124

125
                if (location == null) {
1✔
126
                        location = getDefaultLocation(null, locationGP);
1✔
127
                }
128
                
129
                // If neither exist, get the first available location
130
                if (location == null) {
1✔
131
                        location = Context.getLocationService().getLocation(1);
1✔
132
                }
133
                
134
                return location;
1✔
135
        }
136

137
        private Location getDefaultLocation(Location location, String locationGP) {
138
                //Try to look up 'Unknown Location' in case the global property is something else
139
                Location result = getDefaultLocationFromSting(location, locationGP, "Unknown Location");
1✔
140

141
                // If Unknown Location does not exist, try Unknown if the global property was different
142
                if (result == null) {
1✔
143
                        result = getDefaultLocationFromSting(location, locationGP, "Unknown");
1✔
144
                }
145

146
                return result;
1✔
147
        }
148

149
        private Location getDefaultLocationFromSting(Location location, String locationGP, String defaultLocation) {
150
                Location result = null;
1✔
151
                if (location == null && (!StringUtils.hasText(locationGP) || !defaultLocation.equalsIgnoreCase(locationGP))) {
1✔
152
                        result = Context.getLocationService().getLocation(defaultLocation);
1✔
153
                }
154

155
                return result;
1✔
156
        }
157
        
158
        /**
159
         * @see org.openmrs.api.LocationService#getLocationByUuid(java.lang.String)
160
         */
161
        @Override
162
        @Transactional(readOnly = true)
163
        public Location getLocationByUuid(String uuid) throws APIException {
164
                return dao.getLocationByUuid(uuid);
1✔
165
        }
166
        
167
        /**
168
         * @see org.openmrs.api.LocationService#getLocationTagByUuid(java.lang.String)
169
         */
170
        @Override
171
        @Transactional(readOnly = true)
172
        public LocationTag getLocationTagByUuid(String uuid) throws APIException {
173
                return dao.getLocationTagByUuid(uuid);
1✔
174
        }
175
        
176
        /**
177
         * @see org.openmrs.api.LocationService#getAllLocations()
178
         */
179
        @Override
180
        @Transactional(readOnly = true)
181
        public List<Location> getAllLocations() throws APIException {
182
                return dao.getAllLocations(true);
1✔
183
        }
184
        
185
        /**
186
         * @see org.openmrs.api.LocationService#getAllLocations(boolean)
187
         */
188
        @Override
189
        @Transactional(readOnly = true)
190
        public List<Location> getAllLocations(boolean includeRetired) throws APIException {
191
                return dao.getAllLocations(includeRetired);
1✔
192
        }
193
        
194
        /**
195
         * @see org.openmrs.api.LocationService#getLocations(java.lang.String)
196
         */
197
        @Override
198
        @Transactional(readOnly = true)
199
        public List<Location> getLocations(String nameFragment) throws APIException {
200
                return Context.getLocationService().getLocations(nameFragment, null, null, false, null, null);
1✔
201
        }
202
        
203
        /**
204
         * @see org.openmrs.api.LocationService#getLocationsByTag(LocationTag)
205
         */
206
        @Override
207
        @Transactional(readOnly = true)
208
        public List<Location> getLocationsByTag(LocationTag tag) throws APIException {
209
                List<Location> locations = new ArrayList<>();
1✔
210
                
211
                for (Location l : dao.getAllLocations(false)) {
1✔
212
                        if (l.getTags() != null && l.getTags().contains(tag)) {
1✔
213
                                locations.add(l);
1✔
214
                        }
215
                }
1✔
216
                
217
                return locations;
1✔
218
        }
219
        
220
        /**
221
         * @see org.openmrs.api.LocationService#getLocationsHavingAllTags(List)
222
         */
223
        @Override
224
        @Transactional(readOnly = true)
225
        public List<Location> getLocationsHavingAllTags(List<LocationTag> tags) throws APIException {
226
                return CollectionUtils.isEmpty(tags) ? getAllLocations(false) : dao.getLocationsHavingAllTags(tags);
1✔
227
        }
228
        
229
        /**
230
         * @see org.openmrs.api.LocationService#getLocationsHavingAnyTag(List)
231
         */
232
        @Override
233
        @Transactional(readOnly = true)
234
        public List<Location> getLocationsHavingAnyTag(List<LocationTag> tags) throws APIException {
235
                List<Location> locations = new ArrayList<>();
1✔
236
                
237
                for (Location loc : dao.getAllLocations(false)) {
1✔
238
                        for (LocationTag t : tags) {
1✔
239
                                if (loc.getTags().contains(t) && !locations.contains(loc)) {
1✔
240
                                        locations.add(loc);
1✔
241
                                }
242
                        }
1✔
243
                }
1✔
244
                
245
                return locations;
1✔
246
        }
247
        
248
        /**
249
         * @see org.openmrs.api.LocationService#retireLocation(Location, String)
250
         */
251
        @Override
252
        public Location retireLocation(Location location, String reason) throws APIException {
253
                location.setRetired(true);
1✔
254
                location.setRetireReason(reason);
1✔
255
                return Context.getLocationService().saveLocation(location);
1✔
256
        }
257
        
258
        /**
259
         * @see org.openmrs.api.LocationService#unretireLocation(org.openmrs.Location)
260
         */
261
        @Override
262
        public Location unretireLocation(Location location) throws APIException {
263
                location.setRetired(false);
1✔
264
                return Context.getLocationService().saveLocation(location);
1✔
265
        }
266
        
267
        /**
268
         * @see org.openmrs.api.LocationService#purgeLocation(org.openmrs.Location)
269
         */
270
        @Override
271
        public void purgeLocation(Location location) throws APIException {
272
                dao.deleteLocation(location);
1✔
273
        }
1✔
274
        
275
        /**
276
         * @see org.openmrs.api.LocationService#saveLocationTag(org.openmrs.LocationTag)
277
         */
278
        @Override
279
        public LocationTag saveLocationTag(LocationTag tag) throws APIException {
280
                return dao.saveLocationTag(tag);
1✔
281
        }
282
        
283
        /**
284
         * @see org.openmrs.api.LocationService#getLocationTag(java.lang.Integer)
285
         */
286
        @Override
287
        @Transactional(readOnly = true)
288
        public LocationTag getLocationTag(Integer locationTagId) throws APIException {
289
                return dao.getLocationTag(locationTagId);
1✔
290
        }
291
        
292
        /**
293
         * @see org.openmrs.api.LocationService#getLocationTagByName(java.lang.String)
294
         */
295
        @Override
296
        @Transactional(readOnly = true)
297
        public LocationTag getLocationTagByName(String tag) throws APIException {
298
                return dao.getLocationTagByName(tag);
1✔
299
        }
300
        
301
        /**
302
         * @see org.openmrs.api.LocationService#getAllLocationTags()
303
         */
304
        @Override
305
        @Transactional(readOnly = true)
306
        public List<LocationTag> getAllLocationTags() throws APIException {
307
                return dao.getAllLocationTags(true);
1✔
308
        }
309
        
310
        /**
311
         * @see org.openmrs.api.LocationService#getAllLocationTags(boolean)
312
         */
313
        @Override
314
        @Transactional(readOnly = true)
315
        public List<LocationTag> getAllLocationTags(boolean includeRetired) throws APIException {
316
                return dao.getAllLocationTags(includeRetired);
1✔
317
        }
318
        
319
        /**
320
         * @see org.openmrs.api.LocationService#getLocationTags(java.lang.String)
321
         */
322
        @Override
323
        @Transactional(readOnly = true)
324
        public List<LocationTag> getLocationTags(String search) throws APIException {
325
                if (StringUtils.isEmpty(search)) {
1✔
326
                        return Context.getLocationService().getAllLocationTags(true);
×
327
                }
328
                
329
                return dao.getLocationTags(search);
1✔
330
        }
331
        
332
        /**
333
         * @see org.openmrs.api.LocationService#retireLocationTag(LocationTag, String)
334
         */
335
        @Override
336
        public LocationTag retireLocationTag(LocationTag tag, String reason) throws APIException {
337
                if (tag.getRetired()) {
1✔
338
                        return tag;
1✔
339
                } else {
340
                        if (reason == null) {
×
341
                                throw new APIException("Location.retired.reason.required", (Object[]) null);
×
342
                        }
343
                        tag.setRetired(true);
×
344
                        tag.setRetireReason(reason);
×
345
                        tag.setRetiredBy(Context.getAuthenticatedUser());
×
346
                        tag.setDateRetired(new Date());
×
347
                        return Context.getLocationService().saveLocationTag(tag);
×
348
                }
349
        }
350
        
351
        /**
352
         * @see org.openmrs.api.LocationService#unretireLocationTag(org.openmrs.LocationTag)
353
         */
354
        @Override
355
        public LocationTag unretireLocationTag(LocationTag tag) throws APIException {
356
                tag.setRetired(false);
1✔
357
                tag.setRetireReason(null);
1✔
358
                tag.setRetiredBy(null);
1✔
359
                tag.setDateRetired(null);
1✔
360
                return Context.getLocationService().saveLocationTag(tag);
1✔
361
        }
362
        
363
        /**
364
         * @see org.openmrs.api.LocationService#purgeLocationTag(org.openmrs.LocationTag)
365
         */
366
        @Override
367
        public void purgeLocationTag(LocationTag tag) throws APIException {
368
                dao.deleteLocationTag(tag);
1✔
369
        }
1✔
370
        
371
        /**
372
         * @see org.openmrs.api.LocationService#getCountOfLocations(String, Boolean)
373
         */
374
        @Override
375
        @Transactional(readOnly = true)
376
        public Integer getCountOfLocations(String nameFragment, Boolean includeRetired) {
377
                return OpenmrsUtil.convertToInteger(dao.getCountOfLocations(nameFragment, includeRetired));
×
378
        }
379
        
380
        /**
381
         * @see LocationService#getLocations(String, org.openmrs.Location, java.util.Map, boolean,
382
         *      Integer, Integer)
383
         */
384
        @Override
385
        public List<Location> getLocations(String nameFragment, Location parent,
386
                Map<LocationAttributeType, Object> attributeValues, boolean includeRetired, Integer start, Integer length) {
387
                
388
                Map<LocationAttributeType, String> serializedAttributeValues = CustomDatatypeUtil
1✔
389
                        .getValueReferences(attributeValues);
1✔
390
                
391
                return dao.getLocations(nameFragment, parent, serializedAttributeValues, includeRetired, start, length);
1✔
392
        }
393
        
394
        /**
395
         * @see LocationService#getRootLocations(boolean)
396
         */
397
        @Override
398
        @Transactional(readOnly = true)
399
        public List<Location> getRootLocations(boolean includeRetired) throws APIException {
400
                return dao.getRootLocations(includeRetired);
1✔
401
        }
402
        
403
        /**
404
         * @see LocationService#getLocations(LocationSearchCriteria)
405
         */
406
        @Override
407
        @Transactional(readOnly = true)
408
        public List<Location> getLocations(LocationSearchCriteria criteria) {
409
                return dao.getLocations(criteria);
1✔
410
        }
411

412
        /**
413
         * @see org.openmrs.api.LocationService#getDescendantLocations(org.openmrs.Location, boolean)
414
         */
415
        @Override
416
        @Transactional(readOnly = true)
417
        public List<Location> getDescendantLocations(Location location, boolean includeRetired) {
NEW
418
                return getLocations(new LocationSearchCriteriaBuilder().setDescendantOfLocation(location).includeRetired(includeRetired).build());
×
419
        }
420

421
        /**
422
         * @see org.openmrs.api.LocationService#getPossibleAddressValues(Address, String)
423
         */
424
        @Override
425
        public List<String> getPossibleAddressValues(Address incomplete, String fieldName) throws APIException {
426
                // not implemented by default
427
                return null;
×
428
        }
429
        
430
        /**
431
         * @see org.openmrs.api.LocationService#getAllLocationAttributeTypes()
432
         */
433
        @Override
434
        @Transactional(readOnly = true)
435
        public List<LocationAttributeType> getAllLocationAttributeTypes() {
436
                return dao.getAllLocationAttributeTypes();
1✔
437
        }
438
        
439
        /**
440
         * @see org.openmrs.api.LocationService#getLocationAttributeType(java.lang.Integer)
441
         */
442
        @Override
443
        @Transactional(readOnly = true)
444
        public LocationAttributeType getLocationAttributeType(Integer id) {
445
                return dao.getLocationAttributeType(id);
1✔
446
        }
447
        
448
        /**
449
         * @see org.openmrs.api.LocationService#getLocationAttributeTypeByUuid(java.lang.String)
450
         */
451
        @Override
452
        @Transactional(readOnly = true)
453
        public LocationAttributeType getLocationAttributeTypeByUuid(String uuid) {
454
                return dao.getLocationAttributeTypeByUuid(uuid);
1✔
455
        }
456
        
457
        /**
458
         * @see org.openmrs.api.LocationService#saveLocationAttributeType(org.openmrs.LocationAttributeType)
459
         */
460
        @Override
461
        public LocationAttributeType saveLocationAttributeType(LocationAttributeType locationAttributeType) {
462
                return dao.saveLocationAttributeType(locationAttributeType);
1✔
463
        }
464
        
465
        /**
466
         * @see org.openmrs.api.LocationService#retireLocationAttributeType(org.openmrs.LocationAttributeType,
467
         *      java.lang.String)
468
         */
469
        @Override
470
        public LocationAttributeType retireLocationAttributeType(LocationAttributeType locationAttributeType, String reason) {
471
                return dao.saveLocationAttributeType(locationAttributeType);
1✔
472
        }
473
        
474
        /**
475
         * @see org.openmrs.api.LocationService#unretireLocationAttributeType(org.openmrs.LocationAttributeType)
476
         */
477
        @Override
478
        public LocationAttributeType unretireLocationAttributeType(LocationAttributeType locationAttributeType) {
479
                return Context.getLocationService().saveLocationAttributeType(locationAttributeType);
1✔
480
        }
481
        
482
        /**
483
         * @see org.openmrs.api.LocationService#purgeLocationAttributeType(org.openmrs.LocationAttributeType)
484
         */
485
        @Override
486
        public void purgeLocationAttributeType(LocationAttributeType locationAttributeType) {
487
                dao.deleteLocationAttributeType(locationAttributeType);
1✔
488
        }
1✔
489
        
490
        /**
491
         * @see org.openmrs.api.LocationService#getLocationAttributeByUuid(java.lang.String)
492
         */
493
        @Override
494
        @Transactional(readOnly = true)
495
        public LocationAttribute getLocationAttributeByUuid(String uuid) {
496
                return dao.getLocationAttributeByUuid(uuid);
1✔
497
        }
498
        
499
        /**
500
         * @see org.openmrs.api.LocationService#getAddressTemplate()
501
         */
502
        @Override
503
        @Transactional(readOnly = true)
504
        public String getAddressTemplate() throws APIException {
505
                String addressTemplate = Context.getAdministrationService().getGlobalProperty(
×
506
                    OpenmrsConstants.GLOBAL_PROPERTY_ADDRESS_TEMPLATE);
507
                if (!StringUtils.hasLength(addressTemplate)) {
×
508
                        addressTemplate = OpenmrsConstants.DEFAULT_ADDRESS_TEMPLATE;
×
509
                }
510
                
511
                return addressTemplate;
×
512
        }
513
        
514
        /**
515
         * @see org.openmrs.api.LocationService#saveAddressTemplate(String)
516
         */
517
        @Override
518
        public void saveAddressTemplate(String xml) throws APIException {
519
                Context.getAdministrationService().setGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_ADDRESS_TEMPLATE, xml);
×
520
                
521
        }
×
522
        
523
        /**
524
         * @see org.openmrs.api.LocationService#getLocationAttributeTypeByName(java.lang.String)
525
         */
526
        @Override
527
        @Transactional(readOnly = true)
528
        public LocationAttributeType getLocationAttributeTypeByName(String name) {
529
                return dao.getLocationAttributeTypeByName(name);
1✔
530
        }
531
}
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