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

openmrs / openmrs-core / 21949860769

12 Feb 2026 02:06PM UTC coverage: 63.493% (-0.01%) from 63.504%
21949860769

push

github

chibongho
TRUNK-6541 prevent ValidateUtil.validate() from throwing UnexpectedRo… (#5775)

* TRUNK-6541 prevent ValidateUtil.validate() from throwing UnexpectedRollbackException

* maven(deps): bump org.apache.lucene:lucene-analysis-phonetic (#5773)

Bumps org.apache.lucene:lucene-analysis-phonetic from 9.12.3 to 10.3.2.

---
updated-dependencies:
- dependency-name: org.apache.lucene:lucene-analysis-phonetic
  dependency-version: 10.3.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Refactor to provide a BOM project (#5760)

* add comment

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ian <52504170+ibacher@users.noreply.github.com>

13 of 18 new or added lines in 1 file covered. (72.22%)

2 existing lines in 2 files now uncovered.

23168 of 36489 relevant lines covered (63.49%)

0.63 hits per line

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

88.18
/api/src/main/java/org/openmrs/Person.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 javax.persistence.Cacheable;
13
import javax.persistence.Transient;
14
import java.text.ParseException;
15
import java.text.SimpleDateFormat;
16
import java.time.LocalDate;
17
import java.time.temporal.ChronoUnit;
18
import java.util.ArrayList;
19
import java.util.Calendar;
20
import java.util.Date;
21
import java.util.HashMap;
22
import java.util.List;
23
import java.util.Map;
24
import java.util.Set;
25
import java.util.TreeSet;
26

27
import org.codehaus.jackson.annotate.JsonIgnore;
28
import org.hibernate.annotations.Cache;
29
import org.hibernate.annotations.CacheConcurrencyStrategy;
30
import org.hibernate.envers.Audited;
31
import org.hibernate.envers.NotAudited;
32
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
33
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
34
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexingDependency;
35
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ObjectPath;
36
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyValue;
37
import org.openmrs.util.OpenmrsUtil;
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
40
import org.springframework.util.StringUtils;
41

42
/**
43
 * A Person in the system. This can be either a small person stub, or indicative of an actual
44
 * Patient in the system. This class holds the generic person things that both the stubs and
45
 * patients share. Things like birthdate, names, addresses, and attributes are all generified into
46
 * the person table (and hence this super class)
47
 * 
48
 * @see org.openmrs.Patient
49
 */
50
@Audited
51
@Cacheable
52
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
53
public class Person extends BaseChangeableOpenmrsData {
54
        
55
        public static final long serialVersionUID = 2L;
56
        
57
        private static final Logger log = LoggerFactory.getLogger(Person.class);
1✔
58
        
59
        @DocumentId
60
        protected Integer personId;
61
        
62
        private Set<PersonAddress> addresses = null;
1✔
63
        
64
        private Set<PersonName> names = null;
1✔
65
        
66
        private Set<PersonAttribute> attributes = null;
1✔
67
        
68
        @GenericField
69
        private String gender;
70
        
71
        @GenericField
72
        private Date birthdate;
73
        
74
        private Date birthtime;
75
        
76
        private Boolean birthdateEstimated = false;
1✔
77
        
78
        private Boolean deathdateEstimated = false;
1✔
79
        
80
        @GenericField
1✔
81
        private Boolean dead = false;
1✔
82
        
83
        private Date deathDate;
84
        
85
        private Concept causeOfDeath;
86
        
87
        private String causeOfDeathNonCoded;
88

89
        private User personCreator;
90
        
91
        private Date personDateCreated;
92

93
        private User personChangedBy;
94
        
95
        private Date personDateChanged;
96
        
97
        private Boolean personVoided = false;
1✔
98

99
        private User personVoidedBy;
100
        
101
        private Date personDateVoided;
102
        
103
        private String personVoidReason;
104
        
105
        @GenericField
106
        @NotAudited
107
        @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "patient")))
108
        private boolean isPatient;
109
        
110
        /**
111
         * Convenience map from PersonAttributeType.name to PersonAttribute.<br>
112
         * <br>
113
         * This is "cached" for each user upon first load. When an attribute is changed, the cache is
114
         * cleared and rebuilt on next access.
115
         */
116
        @Transient
1✔
117
        Map<String, PersonAttribute> attributeMap = null;
118
        
119
        @Transient
1✔
120
        private Map<String, PersonAttribute> allAttributeMap = null;
121
        
122
        /**
123
         * default empty constructor
124
         */
125
        public Person() {
1✔
126
        }
1✔
127
        
128
        /**
129
         * This constructor is used to build a new Person object copy from another person object
130
         * (usually a patient or a user subobject). All attributes are copied over to the new object.
131
         * NOTE! All child collection objects are copied as pointers, each individual element is not
132
         * copied. <br>
133
         *
134
         * @param person Person to create this person object from
135
         */
136
        public Person(Person person) {
1✔
137
                if (person == null) {
1✔
138
                        return;
×
139
                }
140
                
141
                personId = person.getPersonId();
1✔
142
                setUuid(person.getUuid());
1✔
143
                addresses = person.getAddresses();
1✔
144
                names = person.getNames();
1✔
145
                attributes = person.getAttributes();
1✔
146
                
147
                gender = person.getGender();
1✔
148
                birthdate = person.getBirthdate();
1✔
149
                birthtime = person.getBirthDateTime();
1✔
150
                birthdateEstimated = person.getBirthdateEstimated();
1✔
151
                deathdateEstimated = person.getDeathdateEstimated();
1✔
152
                dead = person.getDead();
1✔
153
                deathDate = person.getDeathDate();
1✔
154
                causeOfDeath = person.getCauseOfDeath();
1✔
155
                causeOfDeathNonCoded = person.getCauseOfDeathNonCoded();
1✔
156
                // base creator/voidedBy/changedBy info is not copied here
157
                // because that is specific to and will be recreated
158
                // by the subobject upon save
159
                
160
                setPersonCreator(person.getPersonCreator());
1✔
161
                setPersonDateCreated(person.getPersonDateCreated());
1✔
162
                setPersonChangedBy(person.getPersonChangedBy());
1✔
163
                setPersonDateChanged(person.getPersonDateChanged());
1✔
164
                setPersonVoided(person.getPersonVoided());
1✔
165
                setPersonVoidedBy(person.getPersonVoidedBy());
1✔
166
                setPersonDateVoided(person.getPersonDateVoided());
1✔
167
                setPersonVoidReason(person.getPersonVoidReason());
1✔
168
                
169
                setPatient(person.getIsPatient());
1✔
170
        }
1✔
171
        
172
        /**
173
         * Default constructor taking in the primary key personId value
174
         * 
175
         * @param personId Integer internal id for this person
176
         * <strong>Should</strong> set person id
177
         */
178
        public Person(Integer personId) {
1✔
179
                this.personId = personId;
1✔
180
        }
1✔
181
        
182
        // Property accessors
183
        
184
        /**
185
         * @return Returns the personId.
186
         */
187
        public Integer getPersonId() {
188
                return personId;
1✔
189
        }
190
        
191
        /**
192
         * @param personId The personId to set.
193
         */
194
        public void setPersonId(Integer personId) {
195
                this.personId = personId;
1✔
196
        }
1✔
197

198
        @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "personVoided")))
199
        @Override
200
        public Boolean getVoided() {
201
                return super.getVoided();
1✔
202
        }
203

204
        /**
205
         * @return person's gender
206
         */
207
        public String getGender() {
208
                return this.gender;
1✔
209
        }
210
        
211
        /**
212
         * @param gender person's gender
213
         */
214
        public void setGender(String gender) {
215
                this.gender = gender;
1✔
216
        }
1✔
217
        
218
        /**
219
         * @return person's date of birth
220
         */
221
        public Date getBirthdate() {
222
                return this.birthdate;
1✔
223
        }
224
        
225
        /**
226
         * @param birthdate person's date of birth
227
         */
228
        public void setBirthdate(Date birthdate) {
229
                this.birthdate = birthdate;
1✔
230
        }
1✔
231
        
232
        /**
233
         * @return true if person's birthdate is estimated
234
         * @deprecated as of 2.0, use {@link #getBirthdateEstimated()}
235
         */
236
        @Deprecated
237
        @JsonIgnore
238
        public Boolean isBirthdateEstimated() {
UNCOV
239
                return getBirthdateEstimated();
×
240
        }
241
        
242
        public Boolean getBirthdateEstimated() {
243
                return birthdateEstimated;
1✔
244
        }
245
        
246
        /**
247
         * @param birthdateEstimated true if person's birthdate is estimated
248
         */
249
        public void setBirthdateEstimated(Boolean birthdateEstimated) {
250
                this.birthdateEstimated = birthdateEstimated;
1✔
251
        }
1✔
252
        
253
        public Boolean getDeathdateEstimated() {
254
                return this.deathdateEstimated;
1✔
255
        }
256
        
257
        /**
258
         * @param deathdateEstimated true if person's deathdate is estimated
259
         */
260
        public void setDeathdateEstimated(Boolean deathdateEstimated) {
261
                this.deathdateEstimated = deathdateEstimated;
1✔
262
        }
1✔
263
        
264
        /**
265
         * @param birthtime person's time of birth
266
         */
267
        public void setBirthtime(Date birthtime) {
268
                this.birthtime = birthtime;
1✔
269
        }
1✔
270
        
271
        /**
272
         * @return person's time of birth with the date portion set to the date from person's birthdate
273
         */
274
        public Date getBirthDateTime() {
275
                if (birthdate != null && birthtime != null) {
1✔
276
                        String birthDateString = new SimpleDateFormat("yyyy-MM-dd").format(birthdate);
1✔
277
                        String birthTimeString = new SimpleDateFormat("HH:mm:ss").format(birthtime);
1✔
278
                        
279
                        try {
280
                                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(birthDateString + " " + birthTimeString);
1✔
281
                        }
282
                        catch (ParseException e) {
×
283
                                log.error("Failed to parse birth date string", e);
×
284
                        }
285
                }
286
                return null;
1✔
287
        }
288
        
289
        /**
290
         * @return person's time of birth.
291
         */
292
        public Date getBirthtime() {
293
                return this.birthtime;
1✔
294
        }
295
        
296
        /**
297
         * @return Returns the death status.
298
         * @deprecated as of 2.0, use {@link #getDead()}
299
         */
300
        @Deprecated
301
        @JsonIgnore
302
        public Boolean isDead() {
303
                return getDead();
1✔
304
        }
305
        
306
        /**
307
         * @return Returns the death status.
308
         */
309
        public Boolean getDead() {
310
                return dead;
1✔
311
        }
312
        
313
        /**
314
         * @param dead The dead to set.
315
         */
316
        public void setDead(Boolean dead) {
317
                this.dead = dead;
1✔
318
        }
1✔
319
        
320
        /**
321
         * @return date of person's death
322
         */
323
        public Date getDeathDate() {
324
                return this.deathDate;
1✔
325
        }
326
        
327
        /**
328
         * @param deathDate date of person's death
329
         */
330
        public void setDeathDate(Date deathDate) {
331
                this.deathDate = deathDate;
1✔
332
                if (deathDate != null) {
1✔
333
                        setDead(true);
1✔
334
                }
335
        }
1✔
336
        
337
        /**
338
         * @return cause of person's death
339
         */
340
        public Concept getCauseOfDeath() {
341
                return this.causeOfDeath;
1✔
342
        }
343
        
344
        /**
345
         * @param causeOfDeath cause of person's death
346
         */
347
        public void setCauseOfDeath(Concept causeOfDeath) {
348
                this.causeOfDeath = causeOfDeath;
1✔
349
        }
1✔
350
        
351
        /**
352
         * This method returns the non coded cause of death
353
         * 
354
         * @return non coded cause of death
355
         * @since 2.2.0
356
         */
357
        public String getCauseOfDeathNonCoded() {
358
                return this.causeOfDeathNonCoded;
1✔
359
        }
360
        
361
        /**
362
         * This method sets the non coded cause of death with the value given as parameter
363
         * 
364
         * @param causeOfDeathNonCoded is a String that describes as text the cause of death
365
         * @since 2.2.0
366
         * <strong>Should</strong> not fail with null causeOfDeathNonCoded
367
         * <strong>Should</strong> set the attribute causeOfDeathNonCoded with the given parameter
368
         */
369
        public void setCauseOfDeathNonCoded(String causeOfDeathNonCoded) {
370
                this.causeOfDeathNonCoded = causeOfDeathNonCoded;
1✔
371
        }
1✔
372
        
373
        /**
374
         * @return list of known addresses for person
375
         * @see org.openmrs.PersonAddress
376
         * <strong>Should</strong> not get voided addresses
377
         * <strong>Should</strong> not fail with null addresses
378
         */
379
        public Set<PersonAddress> getAddresses() {
380
                if (addresses == null) {
1✔
381
                        addresses = new TreeSet<>();
1✔
382
                }
383
                return this.addresses;
1✔
384
        }
385
        
386
        /**
387
         * @param addresses Set&lt;PersonAddress&gt; list of known addresses for person
388
         * @see org.openmrs.PersonAddress
389
         */
390
        public void setAddresses(Set<PersonAddress> addresses) {
391
                this.addresses = addresses;
1✔
392
        }
1✔
393
        
394
        /**
395
         * @return all known names for person
396
         * @see org.openmrs.PersonName
397
         * <strong>Should</strong> not get voided names
398
         * <strong>Should</strong> not fail with null names
399
         */
400
        public Set<PersonName> getNames() {
401
                if (names == null) {
1✔
402
                        names = new TreeSet<>();
1✔
403
                }
404
                return this.names;
1✔
405
        }
406
        
407
        /**
408
         * @param names update all known names for person
409
         * @see org.openmrs.PersonName
410
         */
411
        public void setNames(Set<PersonName> names) {
412
                this.names = names;
1✔
413
        }
1✔
414
        
415
        /**
416
         * @return all known attributes for person
417
         * @see org.openmrs.PersonAttribute
418
         * <strong>Should</strong> not get voided attributes
419
         * <strong>Should</strong> not fail with null attributes
420
         */
421
        public Set<PersonAttribute> getAttributes() {
422
                if (attributes == null) {
1✔
423
                        attributes = new TreeSet<>();
1✔
424
                }
425
                return this.attributes;
1✔
426
        }
427
        
428
        /**
429
         * Returns only the non-voided attributes for this person
430
         * 
431
         * @return list attributes
432
         * <strong>Should</strong> not get voided attributes
433
         * <strong>Should</strong> not fail with null attributes
434
         */
435
        public List<PersonAttribute> getActiveAttributes() {
436
                List<PersonAttribute> attrs = new ArrayList<>();
1✔
437
                for (PersonAttribute attr : getAttributes()) {
1✔
438
                        if (!attr.getVoided()) {
1✔
439
                                attrs.add(attr);
1✔
440
                        }
441
                }
1✔
442
                return attrs;
1✔
443
        }
444
        
445
        /**
446
         * @param attributes update all known attributes for person
447
         * @see org.openmrs.PersonAttribute
448
         */
449
        public void setAttributes(Set<PersonAttribute> attributes) {
450
                this.attributes = attributes;
1✔
451
                attributeMap = null;
1✔
452
                allAttributeMap = null;
1✔
453
        }
1✔
454
        
455
        // Convenience methods
456
        
457
        /**
458
         * Convenience method to add the <code>attribute</code> to this person's attribute list if the
459
         * attribute doesn't exist already.<br>
460
         * <br>
461
         * Voids any current attribute with type = <code>newAttribute.getAttributeType()</code><br>
462
         * <br>
463
         * NOTE: This effectively limits persons to only one attribute of any given type **
464
         * 
465
         * @param newAttribute PersonAttribute to add to the Person
466
         * <strong>Should</strong> fail when new attribute exist
467
         * <strong>Should</strong> fail when new atribute are the same type with same value
468
         * <strong>Should</strong> void old attribute when new attribute are the same type with different value
469
         * <strong>Should</strong> remove attribute when old attribute are temporary
470
         * <strong>Should</strong> not save an attribute with a null value
471
         * <strong>Should</strong> not save an attribute with a blank string value
472
         * <strong>Should</strong> void old attribute when a null or blank string value is added
473
         */
474
        public void addAttribute(PersonAttribute newAttribute) {
475
                newAttribute.setPerson(this);
1✔
476
                boolean newIsNull = !StringUtils.hasText(newAttribute.getValue());
1✔
477
                
478
                for (PersonAttribute currentAttribute : getActiveAttributes()) {
1✔
479
                        if (currentAttribute.equals(newAttribute)) {
1✔
480
                                // if we have the same PersonAttributeId, don't add the new attribute
481
                                return;
1✔
482
                        } else if (currentAttribute.getAttributeType().equals(newAttribute.getAttributeType())) {
1✔
483
                                if (currentAttribute.getValue() != null && currentAttribute.getValue().equals(newAttribute.getValue())) {
1✔
484
                                        // this person already has this attribute
485
                                        return;
1✔
486
                                }
487
                                
488
                                // if the to-be-added attribute isn't already voided itself
489
                                // and if we have the same type, different value
490
                                if (!newAttribute.getVoided() || newIsNull) {
1✔
491
                                        if (currentAttribute.getCreator() != null) {
1✔
492
                                                currentAttribute.voidAttribute("New value: " + newAttribute.getValue());
1✔
493
                                        } else {
494
                                                // remove the attribute if it was just temporary (didn't have a creator
495
                                                // attached to it yet)
496
                                                removeAttribute(currentAttribute);
×
497
                                        }
498
                                }
499
                        }
500
                }
1✔
501
                attributeMap = null;
1✔
502
                allAttributeMap = null;
1✔
503
                if (!OpenmrsUtil.collectionContains(attributes, newAttribute) && !newIsNull) {
1✔
504
                        attributes.add(newAttribute);
1✔
505
                }
506
        }
1✔
507
        
508
        /**
509
         * Convenience method to get the <code>attribute</code> from this person's attribute list if the
510
         * attribute exists already.
511
         * 
512
         * @param attribute
513
         * <strong>Should</strong> not fail when person attribute is null
514
         * <strong>Should</strong> not fail when person attribute is not exist
515
         * <strong>Should</strong> remove attribute when exist
516
         */
517
        public void removeAttribute(PersonAttribute attribute) {
518
                if (attributes != null && attributes.remove(attribute)) {
1✔
519
                        attributeMap = null;
1✔
520
                        allAttributeMap = null;
1✔
521
                }
522
        }
1✔
523
        
524
        /**
525
         * Convenience Method to return the first non-voided person attribute matching a person
526
         * attribute type. <br>
527
         * <br>
528
         * Returns null if this person has no non-voided {@link PersonAttribute} with the given
529
         * {@link PersonAttributeType}, the given {@link PersonAttributeType} is null, or this person
530
         * has no attributes.
531
         * 
532
         * @param pat the PersonAttributeType to look for (can be a stub, see
533
         *            {@link PersonAttributeType#equals(Object)} for how its compared)
534
         * @return PersonAttribute that matches the given type
535
         * <strong>Should</strong> not fail when attribute type is null
536
         * <strong>Should</strong> not return voided attribute
537
         * <strong>Should</strong> return null when existing PersonAttributeType is voided
538
         */
539
        public PersonAttribute getAttribute(PersonAttributeType pat) {
540
                if (pat != null) {
1✔
541
                        for (PersonAttribute attribute : getAttributes()) {
1✔
542
                                if (pat.equals(attribute.getAttributeType()) && !attribute.getVoided()) {
1✔
543
                                        return attribute;
×
544
                                }
545
                        }
1✔
546
                }
547
                return null;
1✔
548
        }
549
        
550
        /**
551
         * Convenience method to get this person's first attribute that has a PersonAttributeType.name
552
         * equal to <code>attributeName</code>.<br>
553
         * <br>
554
         * Returns null if this person has no non-voided {@link PersonAttribute} with the given type
555
         * name, the given name is null, or this person has no attributes.
556
         * 
557
         * @param attributeName the name string to match on
558
         * @return PersonAttribute whose {@link PersonAttributeType#getName()} matchs the given name
559
         *         string
560
         * <strong>Should</strong> return person attribute based on attributeName
561
         * <strong>Should</strong> return null if AttributeName is voided
562
         */
563
        public PersonAttribute getAttribute(String attributeName) {
564
                if (attributeName != null) {
1✔
565
                        for (PersonAttribute attribute : getAttributes()) {
1✔
566
                                PersonAttributeType type = attribute.getAttributeType();
1✔
567
                                if (type != null && attributeName.equals(type.getName()) && !attribute.getVoided()) {
1✔
568
                                        return attribute;
1✔
569
                                }
570
                        }
1✔
571
                }
572
                
573
                return null;
1✔
574
        }
575
        
576
        /**
577
         * Convenience method to get this person's first attribute that has a PersonAttributeTypeId
578
         * equal to <code>attributeTypeId</code>.<br>
579
         * <br>
580
         * Returns null if this person has no non-voided {@link PersonAttribute} with the given type id
581
         * or this person has no attributes.<br>
582
         * <br>
583
         * The given id cannot be null.
584
         * 
585
         * @param attributeTypeId the id of the {@link PersonAttributeType} to look for
586
         * @return PersonAttribute whose {@link PersonAttributeType#getId()} equals the given Integer id
587
         * <strong>Should</strong> return PersonAttribute based on attributeTypeId
588
         * <strong>Should</strong> return null when existing personAttribute with matching attribute type id is voided
589
         */
590
        public PersonAttribute getAttribute(Integer attributeTypeId) {
591
                for (PersonAttribute attribute : getActiveAttributes()) {
1✔
592
                        if (attributeTypeId.equals(attribute.getAttributeType().getPersonAttributeTypeId())) {
1✔
593
                                return attribute;
1✔
594
                        }
595
                }
1✔
596
                return null;
1✔
597
        }
598
        
599
        /**
600
         * Convenience method to get all of this person's attributes that have a
601
         * PersonAttributeType.name equal to <code>attributeName</code>.
602
         * 
603
         * @param attributeName
604
         * <strong>Should</strong> return all PersonAttributes with matching attributeType names
605
         */
606
        public List<PersonAttribute> getAttributes(String attributeName) {
607
                List<PersonAttribute> ret = new ArrayList<>();
1✔
608
                
609
                for (PersonAttribute attribute : getActiveAttributes()) {
1✔
610
                        PersonAttributeType type = attribute.getAttributeType();
1✔
611
                        if (type != null && attributeName.equals(type.getName())) {
1✔
612
                                ret.add(attribute);
1✔
613
                        }
614
                }
1✔
615
                
616
                return ret;
1✔
617
        }
618
        
619
        /**
620
         * Convenience method to get all of this person's attributes that have a PersonAttributeType.id
621
         * equal to <code>attributeTypeId</code>.
622
         * 
623
         * @param attributeTypeId
624
         * <strong>Should</strong> return empty list when matching personAttribute by id is voided
625
         * <strong>Should</strong> return list of person attributes based on AttributeTypeId
626
         */
627
        public List<PersonAttribute> getAttributes(Integer attributeTypeId) {
628
                List<PersonAttribute> ret = new ArrayList<>();
1✔
629
                
630
                for (PersonAttribute attribute : getActiveAttributes()) {
1✔
631
                        if (attributeTypeId.equals(attribute.getAttributeType().getPersonAttributeTypeId())) {
1✔
632
                                ret.add(attribute);
1✔
633
                        }
634
                }
1✔
635
                
636
                return ret;
1✔
637
        }
638
        
639
        /**
640
         * Convenience method to get all of this person's attributes that have a PersonAttributeType
641
         * equal to <code>personAttributeType</code>.
642
         * 
643
         * @param personAttributeType
644
         */
645
        public List<PersonAttribute> getAttributes(PersonAttributeType personAttributeType) {
646
                List<PersonAttribute> ret = new ArrayList<>();
×
647
                for (PersonAttribute attribute : getAttributes()) {
×
648
                        if (personAttributeType.equals(attribute.getAttributeType()) && !attribute.getVoided()) {
×
649
                                ret.add(attribute);
×
650
                        }
651
                }
×
652
                return ret;
×
653
        }
654
        
655
        /**
656
         * Convenience method to get this person's active attributes in map form: &lt;String,
657
         * PersonAttribute&gt;.
658
         */
659
        public Map<String, PersonAttribute> getAttributeMap() {
660
                if (attributeMap != null) {
×
661
                        return attributeMap;
×
662
                }
663
                
664
                log.debug("Current Person Attributes: \n{}", printAttributes());
×
665
                
666
                attributeMap = new HashMap<>();
×
667
                for (PersonAttribute attribute : getActiveAttributes()) {
×
668
                        attributeMap.put(attribute.getAttributeType().getName(), attribute);
×
669
                }
×
670
                
671
                return attributeMap;
×
672
        }
673
        
674
        /**
675
         * Convenience method to get all of this person's attributes (including voided ones) in map
676
         * form: &lt;String, PersonAttribute&gt;.
677
         * 
678
         * @return All person's attributes in map form
679
         * @since 1.12
680
         */
681
        public Map<String, PersonAttribute> getAllAttributeMap() {
682
                if (allAttributeMap != null) {
×
683
                        return allAttributeMap;
×
684
                }
685
                
686
                log.debug("Current Person Attributes: \n{}", printAttributes());
×
687
                
688
                allAttributeMap = new HashMap<>();
×
689
                for (PersonAttribute attribute : getAttributes()) {
×
690
                        allAttributeMap.put(attribute.getAttributeType().getName(), attribute);
×
691
                }
×
692
                
693
                return allAttributeMap;
×
694
        }
695
        
696
        /**
697
         * Convenience method for viewing all of the person's current attributes
698
         * 
699
         * @return Returns a string with all the attributes
700
         */
701
        public String printAttributes() {
702
                StringBuilder s = new StringBuilder("");
×
703
                
704
                for (PersonAttribute attribute : getAttributes()) {
×
705
                        s.append(attribute.getAttributeType()).append(" : ").append(attribute.getValue()).append(" : voided? ")
×
706
                                .append(attribute.getVoided()).append("\n");
×
707
                }
×
708
                
709
                return s.toString();
×
710
        }
711
        
712
        /**
713
         * Convenience method to add the <code>name</code> to this person's name list if the name
714
         * doesn't exist already.
715
         * 
716
         * @param name
717
         */
718
        public void addName(PersonName name) {
719
                if (name != null) {
1✔
720
                        name.setPerson(this);
1✔
721
                        if (names == null) {
1✔
722
                                names = new TreeSet<>();
1✔
723
                        }
724
                        if (!OpenmrsUtil.collectionContains(names, name)) {
1✔
725
                                names.add(name);
1✔
726
                        }
727
                }
728
        }
1✔
729
        
730
        /**
731
         * Convenience method remove the <code>name</code> from this person's name list if the name
732
         * exists already.
733
         * 
734
         * @param name
735
         */
736
        public void removeName(PersonName name) {
737
                if (names != null) {
1✔
738
                        names.remove(name);
1✔
739
                }
740
        }
1✔
741
        
742
        /**
743
         * Convenience method to add the <code>address</code> to this person's address list if the
744
         * address doesn't exist already.
745
         * 
746
         * @param address
747
         * <strong>Should</strong> not add a person address with blank fields
748
         */
749
        public void addAddress(PersonAddress address) {
750
                if (address != null) {
1✔
751
                        address.setPerson(this);
1✔
752
                        if (addresses == null) {
1✔
753
                                addresses = new TreeSet<>();
1✔
754
                        }
755
                        if (!OpenmrsUtil.collectionContains(addresses, address) && !address.isBlank()) {
1✔
756
                                addresses.add(address);
1✔
757
                        }
758
                }
759
        }
1✔
760
        
761
        /**
762
         * Convenience method to remove the <code>address</code> from this person's address list if the
763
         * address exists already.
764
         * 
765
         * @param address
766
         */
767
        public void removeAddress(PersonAddress address) {
768
                if (addresses != null) {
1✔
769
                        addresses.remove(address);
1✔
770
                }
771
        }
1✔
772
        
773
        /**
774
         * Convenience method to get the {@link PersonName} object that is marked as "preferred". <br>
775
         * <br>
776
         * If two names are marked as preferred (or no names), the database ordering comes into effect
777
         * and the one that was created most recently will be returned. <br>
778
         * <br>
779
         * This method will never return a voided name, even if it is marked as preferred. <br>
780
         * <br>
781
         * Null is returned if this person has no names or all voided names.
782
         * 
783
         * @return the "preferred" person name.
784
         * @see #getNames()
785
         * @see PersonName#getPreferred()
786
         * <strong>Should</strong> get preferred and not-voided person name if exist
787
         * <strong>Should</strong> get not-voided person name if preferred address does not exist
788
         * <strong>Should</strong> get voided person address if person is voided and not-voided address does not exist
789
         * <strong>Should</strong> return null if person is not-voided and have voided names
790
         */
791
        public PersonName getPersonName() {
792
                // normally the DAO layer returns these in the correct order, i.e. preferred and non-voided first, but it's possible that someone
793
                // has fetched a Person, changed their names around, and then calls this method, so we have to be careful.
794
                if (getNames() != null && !getNames().isEmpty()) {
1✔
795
                        for (PersonName name : getNames()) {
1✔
796
                                if (name.getPreferred() && !name.getVoided()) {
1✔
797
                                        return name;
1✔
798
                                }
799
                        }
1✔
800
                        for (PersonName name : getNames()) {
1✔
801
                                if (!name.getVoided()) {
1✔
802
                                        return name;
1✔
803
                                }
804
                        }
1✔
805
                        
806
                        if (getVoided()) {
1✔
807
                                return getNames().iterator().next();
1✔
808
                        }
809
                }
810
                return null;
1✔
811
        }
812
        
813
        /**
814
         * Convenience method to get the given name attribute on this person's preferred PersonName
815
         * 
816
         * @return String given name of the person
817
         */
818
        public String getGivenName() {
819
                PersonName personName = getPersonName();
1✔
820
                if (personName == null) {
1✔
821
                        return "";
×
822
                } else {
823
                        return personName.getGivenName();
1✔
824
                }
825
        }
826
        
827
        /**
828
         * Convenience method to get the middle name attribute on this person's preferred PersonName
829
         * 
830
         * @return String middle name of the person
831
         */
832
        public String getMiddleName() {
833
                PersonName personName = getPersonName();
1✔
834
                if (personName == null) {
1✔
835
                        return "";
×
836
                } else {
837
                        return personName.getMiddleName();
1✔
838
                }
839
        }
840
        
841
        /**
842
         * Convenience method to get the family name attribute on this person's preferred PersonName
843
         * 
844
         * @return String family name of the person
845
         */
846
        public String getFamilyName() {
847
                PersonName personName = getPersonName();
1✔
848
                if (personName == null) {
1✔
849
                        return "";
×
850
                } else {
851
                        return personName.getFamilyName();
1✔
852
                }
853
        }
854
        
855
        /**
856
         * Convenience method to get the {@link PersonAddress} object that is marked as "preferred". <br>
857
         * <br>
858
         * If two addresses are marked as preferred (or no addresses), the database ordering comes into
859
         * effect and the one that was created most recently will be returned. <br>
860
         * <br>
861
         * This method will never return a voided address, even if it is marked as preferred. <br>
862
         * <br>
863
         * Null is returned if this person has no addresses or all voided addresses.
864
         * 
865
         * @return the "preferred" person address.
866
         * @see #getAddresses()
867
         * @see PersonAddress#getPreferred()
868
         * <strong>Should</strong> get preferred and not-voided person address if exist
869
         * <strong>Should</strong> get not-voided person address if preferred address does not exist
870
         * <strong>Should</strong> get voided person address if person is voided and not-voided address does not exist
871
         * <strong>Should</strong> return null if person is not-voided and have voided address
872
         */
873
        public PersonAddress getPersonAddress() {
874
                // normally the DAO layer returns these in the correct order, i.e. preferred and non-voided first, but it's possible that someone
875
                // has fetched a Person, changed their addresses around, and then calls this method, so we have to be careful.
876
                if (getAddresses() != null && !getAddresses().isEmpty()) {
1✔
877
                        for (PersonAddress addr : getAddresses()) {
1✔
878
                                if (addr.getPreferred() && !addr.getVoided()) {
1✔
879
                                        return addr;
1✔
880
                                }
881
                        }
1✔
882
                        for (PersonAddress addr : getAddresses()) {
1✔
883
                                if (!addr.getVoided()) {
1✔
884
                                        return addr;
1✔
885
                                }
886
                        }
1✔
887
                        
888
                        if (getVoided()) {
1✔
889
                                return getAddresses().iterator().next();
1✔
890
                        }
891
                }
892
                return null;
1✔
893
        }
894
        
895
        /**
896
         * Convenience method to calculate this person's age based on the birthdate For a person who
897
         * lived 1990 to 2000, age would be -5 in 1985, 5 in 1995, 10 in 2000, and 10 2010.
898
         * 
899
         * @return Returns age as an Integer.
900
         * <strong>Should</strong> get correct age after death
901
         */
902
        public Integer getAge() {
903
                return getAge(null);
1✔
904
        }
905
        
906
        /**
907
         * Convenience method: calculates the person's age on a given date based on the birthdate
908
         * 
909
         * @param onDate (null defaults to today)
910
         * @return int value of the person's age
911
         * <strong>Should</strong> get age before birthday
912
         * <strong>Should</strong> get age on birthday with no minutes defined
913
         * <strong>Should</strong> get age on birthday with minutes defined
914
         * <strong>Should</strong> get age after birthday
915
         * <strong>Should</strong> get age after death
916
         * <strong>Should</strong> get age with given date after death
917
         * <strong>Should</strong> get age with given date before death
918
         * <strong>Should</strong> get age with given date before birth
919
         */
920
        public Integer getAge(Date onDate) {
921
                if (birthdate == null) {
1✔
922
                        return null;
1✔
923
                }
924
                
925
                // Use default end date as today.
926
                Calendar today = Calendar.getInstance();
1✔
927
                // But if given, use the given date.
928
                if (onDate != null) {
1✔
929
                        today.setTime(onDate);
1✔
930
                }
931
                
932
                // If date given is after date of death then use date of death as end date
933
                if (getDeathDate() != null && today.getTime().after(getDeathDate())) {
1✔
934
                        today.setTime(getDeathDate());
1✔
935
                }
936
                
937
                Calendar bday = Calendar.getInstance();
1✔
938
                bday.setTime(birthdate);
1✔
939
                
940
                int age = today.get(Calendar.YEAR) - bday.get(Calendar.YEAR);
1✔
941
                
942
                // Adjust age when today's date is before the person's birthday
943
                int todaysMonth = today.get(Calendar.MONTH);
1✔
944
                int bdayMonth = bday.get(Calendar.MONTH);
1✔
945
                int todaysDay = today.get(Calendar.DAY_OF_MONTH);
1✔
946
                int bdayDay = bday.get(Calendar.DAY_OF_MONTH);
1✔
947
                
948
                if (todaysMonth < bdayMonth) {
1✔
949
                        age--;
1✔
950
                } else if (todaysMonth == bdayMonth && todaysDay < bdayDay) {
1✔
951
                        // we're only comparing on month and day, not minutes, etc
952
                        age--;
1✔
953
                }
954
                
955
                return age;
1✔
956
        }
957

958
        /**
959
         * Method to get the age of a person in months.
960
         *
961
         * @return the age in months as an Integer e.g. 20 (to mean 20 months)
962
         *
963
         * @since 2.7.0
964
         */
965
        public Integer getAgeInMonths() {
966
                return getAgeInChronoUnit(ChronoUnit.MONTHS);
1✔
967
        }
968

969
        /**
970
         * Method to get the age of a person in weeks.
971
         *
972
         * @return the age in weeks as an Integer e.g. 20 (to mean 20 weeks)
973
         *
974
         * @since 2.7.0
975
         */
976
        public Integer getAgeInWeeks() {
977
                return getAgeInChronoUnit(ChronoUnit.WEEKS);
1✔
978
        }
979

980
        /**
981
         * Method to get the age of a person in days.
982
         *
983
         * @return the age in days as an Integer e.g. 20 (to mean 20 days)
984
         *
985
         * @since 2.7.0
986
         */
987
        public Integer getAgeInDays() {
988
                return getAgeInChronoUnit(ChronoUnit.DAYS);
1✔
989
        }
990

991
        /**
992
         * Gets the age of a person with the specified ChronoUnit.
993
         *
994
         * @param chronoUnit the unit of precision for the age calculation (e.g. WEEKS, MONTHS, YEARS)
995
         * @return the age in the specified unit as an Integer
996
         *
997
         * @since 2.7.0
998
         */
999
        private Integer getAgeInChronoUnit(ChronoUnit chronoUnit) {
1000
                if (this.birthdate == null) {
1✔
1001
                        return null;
1✔
1002
                }
1003

1004
                LocalDate birthDate = new java.sql.Date(this.birthdate.getTime()).toLocalDate();
1✔
1005
                LocalDate endDate = LocalDate.now();
1✔
1006

1007
                // If date given is after date of death then use date of death as end date
1008
                if (this.deathDate != null) {
1✔
1009
                        LocalDate deathDate = new java.sql.Date(this.deathDate.getTime()).toLocalDate();
1✔
1010

1011
                        if (endDate.isAfter(deathDate)) {
1✔
1012
                                endDate = deathDate;
1✔
1013
                        }
1014
                }
1015

1016
                switch (chronoUnit) {
1✔
1017
                        case DAYS:
1018
                                return (int) ChronoUnit.DAYS.between(birthDate, endDate);
1✔
1019
                        case WEEKS:
1020
                                return (int) ChronoUnit.WEEKS.between(birthDate, endDate);
1✔
1021
                        case MONTHS:
1022
                                return (int) ChronoUnit.MONTHS.between(birthDate, endDate);
1✔
1023
                        default:
1024
                                throw new IllegalArgumentException("Unsupported ChronoUnit: " + chronoUnit);
×
1025
                }
1026
        }
1027
        
1028
        /**
1029
         * Convenience method: sets a person's birth date from an age as of the given date Also sets
1030
         * flag indicating that the birth date is inexact. This sets the person's birth date to January
1031
         * 1 of the year that matches this age and date
1032
         * 
1033
         * @param age (the age to set)
1034
         * @param ageOnDate (null defaults to today)
1035
         */
1036
        public void setBirthdateFromAge(int age, Date ageOnDate) {
1037
                Calendar c = Calendar.getInstance();
1✔
1038
                c.setTime(ageOnDate == null ? new Date() : ageOnDate);
1✔
1039
                c.set(Calendar.DATE, 1);
1✔
1040
                c.set(Calendar.MONTH, Calendar.JANUARY);
1✔
1041
                c.add(Calendar.YEAR, -1 * age);
1✔
1042
                setBirthdate(c.getTime());
1✔
1043
                setBirthdateEstimated(true);
1✔
1044
                
1045
        }
1✔
1046
        
1047
        public User getPersonChangedBy() {
1048
                return personChangedBy;
1✔
1049
        }
1050
        
1051
        public void setPersonChangedBy(User changedBy) {
1052
                this.personChangedBy = changedBy;
1✔
1053
                this.setChangedBy(changedBy);
1✔
1054
        }
1✔
1055
        
1056
        public Date getPersonDateChanged() {
1057
                return personDateChanged;
1✔
1058
        }
1059
        
1060
        public void setPersonDateChanged(Date dateChanged) {
1061
                this.personDateChanged = dateChanged;
1✔
1062
                this.setDateChanged(dateChanged);
1✔
1063
        }
1✔
1064
        
1065
        public User getPersonCreator() {
1066
                return personCreator;
1✔
1067
        }
1068
        
1069
        public void setPersonCreator(User creator) {
1070
                this.personCreator = creator;
1✔
1071
                this.setCreator(creator);
1✔
1072
        }
1✔
1073
        
1074
        public Date getPersonDateCreated() {
1075
                return personDateCreated;
1✔
1076
        }
1077
        
1078
        public void setPersonDateCreated(Date dateCreated) {
1079
                this.personDateCreated = dateCreated;
1✔
1080
                this.setDateCreated(dateCreated);
1✔
1081
        }
1✔
1082
        
1083
        public Date getPersonDateVoided() {
1084
                return personDateVoided;
1✔
1085
        }
1086
        
1087
        public void setPersonDateVoided(Date dateVoided) {
1088
                this.personDateVoided = dateVoided;
1✔
1089
                this.setDateVoided(dateVoided);
1✔
1090
        }
1✔
1091
        
1092
        public void setPersonVoided(Boolean voided) {
1093
                this.personVoided = voided;
1✔
1094
                this.setVoided(voided);
1✔
1095
        }
1✔
1096
        
1097
        public Boolean getPersonVoided() {
1098
                return personVoided;
1✔
1099
        }
1100
        
1101
        /**
1102
         * @deprecated as of 2.0, use {@link #getPersonVoided()}
1103
         */
1104
        @Deprecated
1105
        @JsonIgnore
1106
        public Boolean isPersonVoided() {
1107
                return getPersonVoided();
×
1108
        }
1109
        
1110
        public User getPersonVoidedBy() {
1111
                return personVoidedBy;
1✔
1112
        }
1113
        
1114
        public void setPersonVoidedBy(User voidedBy) {
1115
                this.personVoidedBy = voidedBy;
1✔
1116
                this.setVoidedBy(voidedBy);
1✔
1117
        }
1✔
1118
        
1119
        public String getPersonVoidReason() {
1120
                return personVoidReason;
1✔
1121
        }
1122
        
1123
        public void setPersonVoidReason(String voidReason) {
1124
                this.personVoidReason = voidReason;
1✔
1125
                this.setVoidReason(voidReason);
1✔
1126
        }
1✔
1127
        
1128
        /**
1129
         * @return true/false whether this person is a patient or not
1130
         * @deprecated as of 2.0, use {@link #getIsPatient()}
1131
         */
1132
        @Deprecated
1133
        @JsonIgnore
1134
        @NotAudited
1135
        public boolean isPatient() {
1136
                return getIsPatient();
1✔
1137
        }
1138
        
1139
        @NotAudited
1140
        public boolean getIsPatient() {
1141
                return isPatient;
1✔
1142
        }
1143
        
1144
        /**
1145
         * This should only be set by the database layer by looking at whether a row exists in the
1146
         * patient table
1147
         * 
1148
         * @param isPatient whether this person is a patient or not
1149
         */
1150
        protected void setPatient(boolean isPatient) {
1151
                this.isPatient = isPatient;
1✔
1152
        }
1✔
1153
        
1154
        /**
1155
         * @see java.lang.Object#toString()
1156
         */
1157
        @Override
1158
        public String toString() {
1159
                return "Person(personId=" + personId + ")";
1✔
1160
        }
1161
        
1162
        /**
1163
         * @since 1.5
1164
         * @see org.openmrs.OpenmrsObject#getId()
1165
         */
1166
        @Override
1167
        public Integer getId() {
1168
                
1169
                return getPersonId();
1✔
1170
        }
1171
        
1172
        /**
1173
         * @since 1.5
1174
         * @see org.openmrs.OpenmrsObject#setId(java.lang.Integer)
1175
         */
1176
        @Override
1177
        public void setId(Integer id) {
1178
                setPersonId(id);
1✔
1179
                
1180
        }
1✔
1181
}
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