• 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

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

337
        /**
338
         * @return Returns the code indicating the type of location this is
339
         * @since 2.5.0
340
         */
341
        public Concept getType() {
342
                return type;
1✔
343
        }
344
        
345
        /**
346
         * @param type The Concept for the type of location this is
347
         * @since 2.5.0
348
         */
349
        public void setType(Concept type) {
350
                this.type = type;
1✔
351
        }
1✔
352
        
353
        /**
354
         * @see org.openmrs.Attributable#findPossibleValues(java.lang.String)
355
         */
356
        @Override
357
        @Deprecated
358
        public List<Location> findPossibleValues(String searchText) {
359
                try {
360
                        return Context.getLocationService().getLocations(searchText);
×
361
                }
362
                catch (Exception e) {
×
363
                        return Collections.emptyList();
×
364
                }
365
        }
366
        
367
        /**
368
         * @see org.openmrs.Attributable#getPossibleValues()
369
         */
370
        @Override
371
        @Deprecated
372
        public List<Location> getPossibleValues() {
373
                try {
374
                        return Context.getLocationService().getAllLocations();
×
375
                }
376
                catch (Exception e) {
×
377
                        return Collections.emptyList();
×
378
                }
379
        }
380
        
381
        /**
382
         * @see org.openmrs.Attributable#hydrate(java.lang.String)
383
         */
384
        @Override
385
        public Location hydrate(String locationId) {
386
                try {
387
                        return Context.getLocationService().getLocation(Integer.valueOf(locationId));
×
388
                }
389
                catch (Exception e) {
×
390
                        return new Location();
×
391
                }
392
        }
393
        
394
        /**
395
         * @see org.openmrs.Attributable#serialize()
396
         */
397
        @Override
398
        public String serialize() {
399
                if (getLocationId() != null) {
×
400
                        return "" + getLocationId();
×
401
                } else {
402
                        return "";
×
403
                }
404
        }
405
        
406
        /**
407
         * @see org.openmrs.Attributable#getDisplayString()
408
         */
409
        @Override
410
        public String getDisplayString() {
411
                return getName();
×
412
        }
413
        
414
        /**
415
         * @return Returns the parentLocation.
416
         * @since 1.5
417
         */
418
        public Location getParentLocation() {
419
                return parentLocation;
1✔
420
        }
421
        
422
        /**
423
         * @param parentLocationId The parentLocation to set.
424
         * @since 1.5
425
         */
426
        public void setParentLocation(Location parentLocationId) {
427
                this.parentLocation = parentLocationId;
1✔
428
        }
1✔
429
        
430
        /**
431
         * @return Returns the childLocations.
432
         * @since 1.5
433
         */
434
        public Set<Location> getChildLocations() {
435
                return childLocations;
1✔
436
        }
437
        
438
        /**
439
         * Returns all childLocations where child.locationId = this.locationId.
440
         *
441
         * @param includeRetired specifies whether or not to include voided childLocations
442
         * @return Returns a Set&lt;Location&gt; of all the childLocations.
443
         * @since 1.5
444
         * <strong>Should</strong> return a set of locations
445
         */
446
        public Set<Location> getChildLocations(boolean includeRetired) {
447
                Set<Location> ret = new HashSet<>();
×
448
                if (includeRetired) {
×
449
                        ret = getChildLocations();
×
450
                } else if (getChildLocations() != null) {
×
451
                        for (Location l : getChildLocations()) {
×
452
                                if (!l.getRetired()) {
×
453
                                        ret.add(l);
×
454
                                }
455
                        }
×
456
                }
457
                return ret;
×
458
        }
459
        
460
        /**
461
         * Returns the descendant locations.
462
         *
463
         * @param includeRetired specifies whether or not to include voided childLocations
464
         * @return Returns a Set&lt;Location&gt; of the descendant location.
465
         * @since 1.10
466
         */
467
        public Set<Location> getDescendantLocations(boolean includeRetired) {
NEW
468
                return new HashSet<>(Context.getLocationService().getDescendantLocations(this, includeRetired));
×
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