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

openmrs / openmrs-core / 25840851474

14 May 2026 04:00AM UTC coverage: 63.457% (+0.04%) from 63.419%
25840851474

Pull #6089

github

web-flow
Merge d819b4286 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%)

4 existing lines in 3 files now uncovered.

23326 of 36759 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
        @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().getLocations(
×
NEW
469
                    new LocationSearchCriteriaBuilder().setDescendantOfLocation(this).includeRetired(includeRetired).build()));
×
470
        }
471
        
472
        /**
473
         * @param childLocations The childLocations to set.
474
         * @since 1.5
475
         */
476
        public void setChildLocations(Set<Location> childLocations) {
UNCOV
477
                this.childLocations = childLocations;
×
UNCOV
478
        }
×
479
        
480
        /**
481
         * @param child The child location to add.
482
         * @since 1.5
483
         * <strong>Should</strong> return null given null parameter
484
         * <strong>Should</strong> throw APIException given same object as child
485
         * <strong>Should</strong> throw APIException if child already in hierarchy
486
         */
487
        public void addChildLocation(Location child) {
488
                if (child == null) {
1✔
489
                        return;
×
490
                }
491
                
492
                if (getChildLocations() == null) {
1✔
493
                        childLocations = new HashSet<>();
1✔
494
                }
495
                
496
                if (child.equals(this)) {
1✔
497
                        throw new APIException("Location.cannot.be.its.own.child", (Object[]) null);
×
498
                }
499
                
500
                // Traverse all the way up (down?) to the root, then check whether the child is already
501
                // anywhere in the tree
502
                Location root = this;
1✔
503
                while (root.getParentLocation() != null) {
1✔
504
                        root = root.getParentLocation();
1✔
505
                }
506
                
507
                if (isInHierarchy(child, root)) {
1✔
508
                        throw new APIException("Location.hierarchy.loop", new Object[] { child, this });
×
509
                }
510
                
511
                child.setParentLocation(this);
1✔
512
                childLocations.add(child);
1✔
513
        }
1✔
514
        
515
        /**
516
         * Checks whether 'location' is a member of the tree starting at 'root'.
517
         *
518
         * @param location The location to be tested.
519
         * @param root Location node from which to start the testing (down in the hierarchy).
520
         * @since 1.5
521
         * <strong>Should</strong> return false given any null parameter
522
         * <strong>Should</strong> return true given same object in both parameters
523
         * <strong>Should</strong> return true given location that is already somewhere in hierarchy
524
         * <strong>Should</strong> return false given location that is not in hierarchy
525
         * <strong>Should</strong> should find location in hierarchy
526
         */
527
        public static Boolean isInHierarchy(Location location, Location root) {
528
                if (root == null) {
1✔
529
                        return false;
×
530
                }
531
                while (true) {
532
                        if (location == null) {
1✔
533
                                return false;
1✔
534
                        } else if (root.equals(location)) {
1✔
535
                                return true;
1✔
536
                        }
537
                        location = location.getParentLocation();
1✔
538
                }
539
        }
540
        
541
        /**
542
         * @param child The child location to remove.
543
         * @since 1.5
544
         */
545
        public void removeChildLocation(Location child) {
546
                if (getChildLocations() != null) {
1✔
547
                        childLocations.remove(child);
1✔
548
                }
549
        }
1✔
550
        
551
        /**
552
         * @return Returns the tags which have been attached to this Location.
553
         * @since 1.5
554
         */
555
        public Set<LocationTag> getTags() {
556
                return tags;
1✔
557
        }
558
        
559
        /**
560
         * Set the tags which are attached to this Location.
561
         *
562
         * @param tags The tags to set.
563
         * @since 1.5
564
         */
565
        public void setTags(Set<LocationTag> tags) {
566
                this.tags = tags;
×
567
        }
×
568
        
569
        /**
570
         * Attaches a tag to the Location.
571
         *
572
         * @param tag The tag to add.
573
         * @since 1.5
574
         */
575
        public void addTag(LocationTag tag) {
576
                if (getTags() == null) {
1✔
577
                        tags = new HashSet<>();
1✔
578
                }
579
                if (tag != null && !tags.contains(tag)) {
1✔
580
                        tags.add(tag);
1✔
581
                }
582
        }
1✔
583
        
584
        /**
585
         * Remove the tag from the Location.
586
         *
587
         * @param tag The tag to remove.
588
         * @since 1.5
589
         */
590
        public void removeTag(LocationTag tag) {
591
                if (getTags() != null) {
1✔
592
                        tags.remove(tag);
1✔
593
                }
594
        }
1✔
595
        
596
        /**
597
         * Checks whether the Location has a particular tag.
598
         *
599
         * @param tagToFind the string of the tag for which to check
600
         * @return true if the tags include the specified tag, false otherwise
601
         * @since 1.5
602
         * <strong>Should</strong> not fail given null parameter
603
         * <strong>Should</strong> return false given empty string parameter
604
         */
605
        public Boolean hasTag(String tagToFind) {
606
                if (tagToFind != null && getTags() != null) {
×
607
                        for (LocationTag locTag : getTags()) {
×
608
                                if (locTag.getName().equals(tagToFind)) {
×
609
                                        return true;
×
610
                                }
611
                        }
×
612
                }
613
                
614
                return false;
×
615
        }
616
        
617
        /**
618
         * @since 1.8
619
         * @return the address3
620
         */
621
        @Override
622
        public String getAddress3() {
623
                return address3;
1✔
624
        }
625
        
626
        /**
627
         * @since 1.8
628
         * @param address3 the address3 to set
629
         */
630
        @Override
631
        public void setAddress3(String address3) {
632
                this.address3 = address3;
1✔
633
        }
1✔
634
        
635
        /**
636
         * @since 1.8
637
         * @return the address4
638
         */
639
        @Override
640
        public String getAddress4() {
641
                return address4;
1✔
642
        }
643
        
644
        /**
645
         * @since 1.8
646
         * @param address4 the address4 to set
647
         */
648
        @Override
649
        public void setAddress4(String address4) {
650
                this.address4 = address4;
1✔
651
        }
1✔
652
        
653
        /**
654
         * @since 1.8
655
         * @return the address6
656
         */
657
        @Override
658
        public String getAddress6() {
659
                return address6;
1✔
660
        }
661
        
662
        /**
663
         * @since 1.8
664
         * @param address6 the address6 to set
665
         */
666
        @Override
667
        public void setAddress6(String address6) {
668
                this.address6 = address6;
1✔
669
        }
1✔
670
        
671
        /**
672
         * @since 1.8
673
         * @return the address5
674
         */
675
        @Override
676
        public String getAddress5() {
677
                return address5;
1✔
678
        }
679
        
680
        /**
681
         * @since 1.8
682
         * @param address5 the address5 to set
683
         */
684
        @Override
685
        public void setAddress5(String address5) {
686
                this.address5 = address5;
1✔
687
        }
1✔
688
        
689
        /**
690
         * @since 1.5
691
         * @see org.openmrs.OpenmrsObject#getId()
692
         */
693
        @Override
694
        public Integer getId() {
695
                
696
                return getLocationId();
1✔
697
        }
698
        
699
        /**
700
         * @since 1.5
701
         * @see org.openmrs.OpenmrsObject#setId(java.lang.Integer)
702
         */
703
        @Override
704
        public void setId(Integer id) {
705
                setLocationId(id);
×
706
                
707
        }
×
708

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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