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

openmrs / openmrs-core / 25932489856

15 May 2026 05:43PM UTC coverage: 63.411% (-0.008%) from 63.419%
25932489856

Pull #6089

github

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

51 of 55 new or added lines in 3 files covered. (92.73%)

24 existing lines in 4 files now uncovered.

23308 of 36757 relevant lines covered (63.41%)

0.63 hits per line

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

81.74
/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.util.OpenmrsConstants;
30
import org.openmrs.util.OpenmrsUtil;
31
import org.springframework.transaction.annotation.Transactional;
32
import org.springframework.util.StringUtils;
33

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

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

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

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

145
                return result;
1✔
146
        }
147

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

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

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

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