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

openmrs / openmrs-core / 25736562614

12 May 2026 01:09PM UTC coverage: 63.46% (+0.04%) from 63.419%
25736562614

Pull #6089

github

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

61 of 65 new or added lines in 5 files covered. (93.85%)

5 existing lines in 4 files now uncovered.

23328 of 36760 relevant lines covered (63.46%)

0.63 hits per line

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

72.44
/api/src/main/java/org/openmrs/Location.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;
11

12
import org.hibernate.annotations.BatchSize;
13
import org.hibernate.annotations.Cache;
14
import org.hibernate.annotations.CacheConcurrencyStrategy;
15
import org.hibernate.envers.Audited;
16
import org.openmrs.annotation.Independent;
17
import org.openmrs.api.APIException;
18
import org.openmrs.api.context.Context;
19
import org.openmrs.parameter.LocationSearchCriteriaBuilder;
20

21
import javax.persistence.AttributeOverride;
22
import javax.persistence.Cacheable;
23
import javax.persistence.CascadeType;
24
import javax.persistence.Column;
25
import javax.persistence.Entity;
26
import javax.persistence.FetchType;
27
import javax.persistence.GeneratedValue;
28
import javax.persistence.GenerationType;
29
import javax.persistence.Id;
30
import javax.persistence.JoinColumn;
31
import javax.persistence.JoinTable;
32
import javax.persistence.ManyToMany;
33
import javax.persistence.ManyToOne;
34
import javax.persistence.OneToMany;
35
import javax.persistence.OrderBy;
36
import javax.persistence.Table;
37
import java.util.Collections;
38
import java.util.HashSet;
39
import java.util.List;
40
import java.util.Set;
41

42
/**
43
 * A Location is a physical place, such as a hospital, a room, a clinic, or a district. Locations
44
 * support a single hierarchy, such that each location may have one parent location. A
45
 * non-geographical grouping of locations, such as "All Community Health Centers" is not a location,
46
 * and should be modeled using {@link LocationTag}s.
47
 * Note: Prior to version 1.9 this class extended BaseMetadata
48
 */
49
@Entity
50
@Table(name = "location")
51
@Cacheable
52
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
53
@AttributeOverride(name = "attributes", column = @Column(name = "location_id"))
54
@Audited
55
public class Location extends BaseCustomizableMetadata<LocationAttribute> implements java.io.Serializable, Attributable<Location>, Address {
56
        
57
        public static final long serialVersionUID = 455634L;
58
        
59
        public static final int LOCATION_UNKNOWN = 1;
60
        
61
        // Fields
62
        @Id
63
        @Column(name = "location_id")
64
        @GeneratedValue(strategy = GenerationType.IDENTITY)
65
        private Integer locationId;
66
        
67
        @ManyToOne
68
        @JoinColumn(name = "location_type_concept_id")
69
        private Concept type;
70
        
71
        @Column(name = "address1")
72
        private String address1;
73
        
74
        @Column(name = "address2")
75
        private String address2;
76
        
77
        @Column(name = "city_village")
78
        private String cityVillage;
79
        
80
        @Column(name = "state_province")
81
        private String stateProvince;
82
        
83
        @Column(name = "country", length = 50)
84
        private String country;
85
        
86
        @Column(name = "postal_code", length = 50)
87
        private String postalCode;
88
        
89
        @Column(name = "latitude", length = 50)
90
        private String latitude;
91
        
92
        @Column(name = "longitude", length = 50)
93
        private String longitude;
94
        
95
        @Column(name = "county_district")
96
        private String countyDistrict;
97
        
98
        @Column(name = "address3")
99
        private String address3;
100
        
101
        @Column(name = "address4")
102
        private String address4;
103
        
104
        @Column(name = "address6")
105
        private String address6;
106
        
107
        @Column(name = "address5")
108
        private String address5;
109
        
110
        @Column(name = "address7")
111
        private String address7;
112
        
113
        @Column(name = "address8")
114
        private String address8;
115
        
116
        @Column(name = "address9")
117
        private String address9;
118
        
119
        @Column(name = "address10")
120
        private String address10;
121
        
122
        @Column(name = "address11")
123
        private String address11;
124
        
125
        @Column(name = "address12")
126
        private String address12;
127
        
128
        @Column(name = "address13")
129
        private String address13;
130
        
131
        @Column(name = "address14")
132
        private String address14;
133
        
134
        @Column(name = "address15")
135
        private String address15;
136

137
        @ManyToOne
138
        @JoinColumn(name = "parent_location")
139
        private Location parentLocation;
140
        
141
        @OneToMany(mappedBy = "parentLocation", cascade = CascadeType.ALL, orphanRemoval = true)
142
        @BatchSize(size = 100)
143
        @OrderBy("name")
144
        private Set<Location> childLocations;
145
        
146
        @ManyToMany(fetch = FetchType.LAZY)
147
        @JoinTable(
148
                name = "location_tag_map",
149
                joinColumns = @JoinColumn(name = "location_id"),
150
                inverseJoinColumns = @JoinColumn(name = "location_tag_id"))
151
        @Independent
152
        private Set<LocationTag> tags;
153
        
154
        // Constructors
155
        
156
        /** default constructor */
157
        public Location() {
1✔
158
        }
1✔
159
        
160
        /** constructor with id */
161
        public Location(Integer locationId) {
1✔
162
                this.locationId = locationId;
1✔
163
        }
1✔
164
        
165
        // Property accessors
166
        
167
        /**
168
         * @return Returns the address1.
169
         */
170
        @Override
171
        public String getAddress1() {
172
                return address1;
1✔
173
        }
174
        
175
        /**
176
         * @param address1 The address1 to set.
177
         */
178
        @Override
179
        public void setAddress1(String address1) {
180
                this.address1 = address1;
1✔
181
        }
1✔
182
        
183
        /**
184
         * @return Returns the address2.
185
         */
186
        @Override
187
        public String getAddress2() {
188
                return address2;
1✔
189
        }
190
        
191
        /**
192
         * @param address2 The address2 to set.
193
         */
194
        @Override
195
        public void setAddress2(String address2) {
196
                this.address2 = address2;
1✔
197
        }
1✔
198
        
199
        /**
200
         * @return Returns the cityVillage.
201
         */
202
        @Override
203
        public String getCityVillage() {
204
                return cityVillage;
1✔
205
        }
206
        
207
        /**
208
         * @param cityVillage The cityVillage to set.
209
         */
210
        @Override
211
        public void setCityVillage(String cityVillage) {
212
                this.cityVillage = cityVillage;
1✔
213
        }
1✔
214
        
215
        /**
216
         * @return Returns the country.
217
         */
218
        @Override
219
        public String getCountry() {
220
                return country;
1✔
221
        }
222
        
223
        /**
224
         * @param country The country to set.
225
         */
226
        @Override
227
        public void setCountry(String country) {
228
                this.country = country;
1✔
229
        }
1✔
230
        
231
        /**
232
         * @return Returns the latitude.
233
         */
234
        @Override
235
        public String getLatitude() {
236
                return latitude;
1✔
237
        }
238
        
239
        /**
240
         * @param latitude The latitude to set.
241
         */
242
        @Override
243
        public void setLatitude(String latitude) {
244
                this.latitude = latitude;
1✔
245
        }
1✔
246
        
247
        /**
248
         * @return Returns the locationId.
249
         */
250
        public Integer getLocationId() {
251
                return locationId;
1✔
252
        }
253
        
254
        /**
255
         * @param locationId The locationId to set.
256
         */
257
        public void setLocationId(Integer locationId) {
258
                this.locationId = locationId;
×
259
        }
×
260
        
261
        /**
262
         * @return Returns the longitude.
263
         */
264
        @Override
265
        public String getLongitude() {
266
                return longitude;
1✔
267
        }
268
        
269
        /**
270
         * @param longitude The longitude to set.
271
         */
272
        @Override
273
        public void setLongitude(String longitude) {
274
                this.longitude = longitude;
1✔
275
        }
1✔
276
        
277
        /**
278
         * @return Returns the postalCode.
279
         */
280
        @Override
281
        public String getPostalCode() {
282
                return postalCode;
1✔
283
        }
284
        
285
        /**
286
         * @param postalCode The postalCode to set.
287
         */
288
        @Override
289
        public void setPostalCode(String postalCode) {
290
                this.postalCode = postalCode;
1✔
291
        }
1✔
292
        
293
        /**
294
         * @return Returns the stateProvince.
295
         */
296
        @Override
297
        public String getStateProvince() {
298
                return stateProvince;
1✔
299
        }
300
        
301
        /**
302
         * @param stateProvince The stateProvince to set.
303
         */
304
        @Override
305
        public void setStateProvince(String stateProvince) {
306
                this.stateProvince = stateProvince;
1✔
307
        }
1✔
308
        
309
        @Override
310
        public String toString() {
311
                if (getName() != null) {
1✔
312
                        return getName();
1✔
313
                }
314
                if (getId() != null) {
1✔
315
                        return getId().toString();
×
316
                }
317
                return "";
1✔
318
        }
319
        
320
        /**
321
         * @return Returns the countyDistrict.
322
         */
323
        @Override
324
        public String getCountyDistrict() {
325
                return countyDistrict;
1✔
326
        }
327
        
328
        /**
329
         * @param countyDistrict The countyDistrict to set.
330
         */
331
        @Override
332
        public void setCountyDistrict(String countyDistrict) {
333
                this.countyDistrict = countyDistrict;
1✔
334
        }
1✔
335

336
        /**
337
         * @return Returns the code indicating the type of location this is
338
         * @since 2.5.0
339
         */
340
        public Concept getType() {
341
                return type;
1✔
342
        }
343
        
344
        /**
345
         * @param type The Concept for the type of location this is
346
         * @since 2.5.0
347
         */
348
        public void setType(Concept type) {
349
                this.type = type;
1✔
350
        }
1✔
351
        
352
        /**
353
         * @see org.openmrs.Attributable#findPossibleValues(java.lang.String)
354
         */
355
        @Override
356
        @Deprecated
357
        public List<Location> findPossibleValues(String searchText) {
358
                try {
359
                        return Context.getLocationService().getLocations(searchText);
×
360
                }
361
                catch (Exception e) {
×
362
                        return Collections.emptyList();
×
363
                }
364
        }
365
        
366
        /**
367
         * @see org.openmrs.Attributable#getPossibleValues()
368
         */
369
        @Override
370
        @Deprecated
371
        public List<Location> getPossibleValues() {
372
                try {
373
                        return Context.getLocationService().getAllLocations();
×
374
                }
375
                catch (Exception e) {
×
376
                        return Collections.emptyList();
×
377
                }
378
        }
379
        
380
        /**
381
         * @see org.openmrs.Attributable#hydrate(java.lang.String)
382
         */
383
        @Override
384
        public Location hydrate(String locationId) {
385
                try {
386
                        return Context.getLocationService().getLocation(Integer.valueOf(locationId));
×
387
                }
388
                catch (Exception e) {
×
389
                        return new Location();
×
390
                }
391
        }
392
        
393
        /**
394
         * @see org.openmrs.Attributable#serialize()
395
         */
396
        @Override
397
        public String serialize() {
398
                if (getLocationId() != null) {
×
399
                        return "" + getLocationId();
×
400
                } else {
401
                        return "";
×
402
                }
403
        }
404
        
405
        /**
406
         * @see org.openmrs.Attributable#getDisplayString()
407
         */
408
        @Override
409
        public String getDisplayString() {
410
                return getName();
×
411
        }
412
        
413
        /**
414
         * @return Returns the parentLocation.
415
         * @since 1.5
416
         */
417
        public Location getParentLocation() {
418
                return parentLocation;
1✔
419
        }
420
        
421
        /**
422
         * @param parentLocationId The parentLocation to set.
423
         * @since 1.5
424
         */
425
        public void setParentLocation(Location parentLocationId) {
426
                this.parentLocation = parentLocationId;
1✔
427
        }
1✔
428
        
429
        /**
430
         * @return Returns the childLocations.
431
         * @since 1.5
432
         */
433
        public Set<Location> getChildLocations() {
434
                return childLocations;
1✔
435
        }
436
        
437
        /**
438
         * Returns all childLocations where child.locationId = this.locationId.
439
         *
440
         * @param includeRetired specifies whether or not to include voided childLocations
441
         * @return Returns a Set&lt;Location&gt; of all the childLocations.
442
         * @since 1.5
443
         * <strong>Should</strong> return a set of locations
444
         */
445
        public Set<Location> getChildLocations(boolean includeRetired) {
446
                Set<Location> ret = new HashSet<>();
×
447
                if (includeRetired) {
×
448
                        ret = getChildLocations();
×
449
                } else if (getChildLocations() != null) {
×
450
                        for (Location l : getChildLocations()) {
×
451
                                if (!l.getRetired()) {
×
452
                                        ret.add(l);
×
453
                                }
454
                        }
×
455
                }
456
                return ret;
×
457
        }
458
        
459
        /**
460
         * Returns the descendant locations.
461
         *
462
         * @param includeRetired specifies whether or not to include voided childLocations
463
         * @return Returns a Set&lt;Location&gt; of the descendant location.
464
         * @since 1.10
465
         */
466
        public Set<Location> getDescendantLocations(boolean includeRetired) {
NEW
467
                return new HashSet<>(Context.getLocationService().getLocations(
×
NEW
468
                    new LocationSearchCriteriaBuilder().setDescendantOfLocation(getUuid()).includeRetired(includeRetired).build()));
×
469
        }
470
        
471
        /**
472
         * @param childLocations The childLocations to set.
473
         * @since 1.5
474
         */
475
        public void setChildLocations(Set<Location> childLocations) {
UNCOV
476
                this.childLocations = childLocations;
×
UNCOV
477
        }
×
478
        
479
        /**
480
         * @param child The child location to add.
481
         * @since 1.5
482
         * <strong>Should</strong> return null given null parameter
483
         * <strong>Should</strong> throw APIException given same object as child
484
         * <strong>Should</strong> throw APIException if child already in hierarchy
485
         */
486
        public void addChildLocation(Location child) {
487
                if (child == null) {
1✔
488
                        return;
×
489
                }
490
                
491
                if (getChildLocations() == null) {
1✔
492
                        childLocations = new HashSet<>();
1✔
493
                }
494
                
495
                if (child.equals(this)) {
1✔
496
                        throw new APIException("Location.cannot.be.its.own.child", (Object[]) null);
×
497
                }
498
                
499
                // Traverse all the way up (down?) to the root, then check whether the child is already
500
                // anywhere in the tree
501
                Location root = this;
1✔
502
                while (root.getParentLocation() != null) {
1✔
503
                        root = root.getParentLocation();
1✔
504
                }
505
                
506
                if (isInHierarchy(child, root)) {
1✔
507
                        throw new APIException("Location.hierarchy.loop", new Object[] { child, this });
×
508
                }
509
                
510
                child.setParentLocation(this);
1✔
511
                childLocations.add(child);
1✔
512
        }
1✔
513
        
514
        /**
515
         * Checks whether 'location' is a member of the tree starting at 'root'.
516
         *
517
         * @param location The location to be tested.
518
         * @param root Location node from which to start the testing (down in the hierarchy).
519
         * @since 1.5
520
         * <strong>Should</strong> return false given any null parameter
521
         * <strong>Should</strong> return true given same object in both parameters
522
         * <strong>Should</strong> return true given location that is already somewhere in hierarchy
523
         * <strong>Should</strong> return false given location that is not in hierarchy
524
         * <strong>Should</strong> should find location in hierarchy
525
         */
526
        public static Boolean isInHierarchy(Location location, Location root) {
527
                if (root == null) {
1✔
528
                        return false;
×
529
                }
530
                while (true) {
531
                        if (location == null) {
1✔
532
                                return false;
1✔
533
                        } else if (root.equals(location)) {
1✔
534
                                return true;
1✔
535
                        }
536
                        location = location.getParentLocation();
1✔
537
                }
538
        }
539
        
540
        /**
541
         * @param child The child location to remove.
542
         * @since 1.5
543
         */
544
        public void removeChildLocation(Location child) {
545
                if (getChildLocations() != null) {
1✔
546
                        childLocations.remove(child);
1✔
547
                }
548
        }
1✔
549
        
550
        /**
551
         * @return Returns the tags which have been attached to this Location.
552
         * @since 1.5
553
         */
554
        public Set<LocationTag> getTags() {
555
                return tags;
1✔
556
        }
557
        
558
        /**
559
         * Set the tags which are attached to this Location.
560
         *
561
         * @param tags The tags to set.
562
         * @since 1.5
563
         */
564
        public void setTags(Set<LocationTag> tags) {
565
                this.tags = tags;
×
566
        }
×
567
        
568
        /**
569
         * Attaches a tag to the Location.
570
         *
571
         * @param tag The tag to add.
572
         * @since 1.5
573
         */
574
        public void addTag(LocationTag tag) {
575
                if (getTags() == null) {
1✔
576
                        tags = new HashSet<>();
1✔
577
                }
578
                if (tag != null && !tags.contains(tag)) {
1✔
579
                        tags.add(tag);
1✔
580
                }
581
        }
1✔
582
        
583
        /**
584
         * Remove the tag from the Location.
585
         *
586
         * @param tag The tag to remove.
587
         * @since 1.5
588
         */
589
        public void removeTag(LocationTag tag) {
590
                if (getTags() != null) {
1✔
591
                        tags.remove(tag);
1✔
592
                }
593
        }
1✔
594
        
595
        /**
596
         * Checks whether the Location has a particular tag.
597
         *
598
         * @param tagToFind the string of the tag for which to check
599
         * @return true if the tags include the specified tag, false otherwise
600
         * @since 1.5
601
         * <strong>Should</strong> not fail given null parameter
602
         * <strong>Should</strong> return false given empty string parameter
603
         */
604
        public Boolean hasTag(String tagToFind) {
605
                if (tagToFind != null && getTags() != null) {
×
606
                        for (LocationTag locTag : getTags()) {
×
607
                                if (locTag.getName().equals(tagToFind)) {
×
608
                                        return true;
×
609
                                }
610
                        }
×
611
                }
612
                
613
                return false;
×
614
        }
615
        
616
        /**
617
         * @since 1.8
618
         * @return the address3
619
         */
620
        @Override
621
        public String getAddress3() {
622
                return address3;
1✔
623
        }
624
        
625
        /**
626
         * @since 1.8
627
         * @param address3 the address3 to set
628
         */
629
        @Override
630
        public void setAddress3(String address3) {
631
                this.address3 = address3;
1✔
632
        }
1✔
633
        
634
        /**
635
         * @since 1.8
636
         * @return the address4
637
         */
638
        @Override
639
        public String getAddress4() {
640
                return address4;
1✔
641
        }
642
        
643
        /**
644
         * @since 1.8
645
         * @param address4 the address4 to set
646
         */
647
        @Override
648
        public void setAddress4(String address4) {
649
                this.address4 = address4;
1✔
650
        }
1✔
651
        
652
        /**
653
         * @since 1.8
654
         * @return the address6
655
         */
656
        @Override
657
        public String getAddress6() {
658
                return address6;
1✔
659
        }
660
        
661
        /**
662
         * @since 1.8
663
         * @param address6 the address6 to set
664
         */
665
        @Override
666
        public void setAddress6(String address6) {
667
                this.address6 = address6;
1✔
668
        }
1✔
669
        
670
        /**
671
         * @since 1.8
672
         * @return the address5
673
         */
674
        @Override
675
        public String getAddress5() {
676
                return address5;
1✔
677
        }
678
        
679
        /**
680
         * @since 1.8
681
         * @param address5 the address5 to set
682
         */
683
        @Override
684
        public void setAddress5(String address5) {
685
                this.address5 = address5;
1✔
686
        }
1✔
687
        
688
        /**
689
         * @since 1.5
690
         * @see org.openmrs.OpenmrsObject#getId()
691
         */
692
        @Override
693
        public Integer getId() {
694
                
695
                return getLocationId();
1✔
696
        }
697
        
698
        /**
699
         * @since 1.5
700
         * @see org.openmrs.OpenmrsObject#setId(java.lang.Integer)
701
         */
702
        @Override
703
        public void setId(Integer id) {
704
                setLocationId(id);
×
705
                
706
        }
×
707

708
        /**
709
         * {@inheritDoc}
710
         */
711
        @Override
712
        public String getAddress7() {
713
                return address7;
1✔
714
        }
715

716
        /**
717
         * {@inheritDoc}
718
         */
719
        @Override
720
        public void setAddress7(String address7) {
721
                this.address7 = address7;
1✔
722
        }
1✔
723

724
        /**
725
         * {@inheritDoc}
726
         */
727
        @Override
728
        public String getAddress8() {
729
                return address8;
1✔
730
        }
731

732
        /**
733
         * {@inheritDoc}
734
         */
735
        @Override
736
        public void setAddress8(String address8) {
737
                this.address8 = address8;
1✔
738
        }
1✔
739

740
        /**
741
         * {@inheritDoc}
742
         */
743
        @Override
744
        public String getAddress9() {
745
                return address9;
1✔
746
        }
747

748
        /**
749
         * {@inheritDoc}
750
         */
751
        @Override
752
        public void setAddress9(String address9) {
753
                this.address9 = address9;
1✔
754
        }
1✔
755

756
        /**
757
         * {@inheritDoc}
758
         */
759
        @Override
760
        public String getAddress10() {
761
                return address10;
1✔
762
        }
763

764
        /**
765
         * {@inheritDoc}
766
         */
767
        @Override
768
        public void setAddress10(String address10) {
769
                this.address10 = address10;
1✔
770
        }
1✔
771

772
        /**
773
         * {@inheritDoc}
774
         */
775
        @Override
776
        public String getAddress11() {
777
                return address11;
1✔
778
        }
779

780
        /**
781
         * {@inheritDoc}
782
         */
783
        @Override
784
        public void setAddress11(String address11) {
785
                this.address11 = address11;
1✔
786
        }
1✔
787

788
        /**
789
         * {@inheritDoc}
790
         */
791
        @Override
792
        public String getAddress12() {
793
                return address12;
1✔
794
        }
795

796
        /**
797
         * {@inheritDoc}
798
         */
799
        @Override
800
        public void setAddress12(String address12) {
801
                this.address12 = address12;
1✔
802
        }
1✔
803

804
        /**
805
         * {@inheritDoc}
806
         */
807
        @Override
808
        public String getAddress13() {
809
                return address13;
1✔
810
        }
811

812
        /**
813
         * {@inheritDoc}
814
         */
815
        @Override
816
        public void setAddress13(String address13) {
817
                this.address13 = address13;
1✔
818
        }
1✔
819

820
        /**
821
         * {@inheritDoc}
822
         */
823
        @Override
824
        public String getAddress14() {
825
                return address14;
1✔
826
        }
827

828
        /**
829
         * {@inheritDoc}
830
         */
831
        @Override
832
        public void setAddress14(String address14) {
833
                this.address14 = address14;
1✔
834
        }
1✔
835

836
        /**
837
         * {@inheritDoc}
838
         */
839
        @Override
840
        public String getAddress15() {
841
                return address15;
1✔
842
        }
843

844
        /**
845
         * {@inheritDoc}
846
         */
847
        @Override
848
        public void setAddress15(String address15) {
849
                this.address15 = address15;
1✔
850
        }
1✔
851
}
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