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

openmrs / openmrs-core / 26838258049

01 Jun 2026 09:45PM UTC coverage: 63.424% (+0.05%) from 63.376%
26838258049

push

github

chibongho
TRUNK-6633 Add a way to search Concepts by ConceptSearchCriteria

136 of 158 new or added lines in 4 files covered. (86.08%)

9 existing lines in 4 files now uncovered.

23680 of 37336 relevant lines covered (63.42%)

0.63 hits per line

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

75.44
/api/src/main/java/org/openmrs/parameter/ConceptSearchCriteriaBuilder.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.parameter;
11

12
import java.lang.reflect.Field;
13
import java.util.ArrayList;
14
import java.util.Collection;
15

16
import org.apache.commons.lang3.StringUtils;
17
import org.apache.commons.lang3.math.NumberUtils;
18
import org.openmrs.api.APIException;
19
import org.openmrs.api.context.Context;
20

21
/**
22
 * A builder class for constructing instances of {@link ConceptSearchCriteria}. This builder allows
23
 * for the flexible creation of {@link ConceptSearchCriteria} objects by providing a simple
24
 * interface to set various fields.
25
 *
26
 * @since 2.8.7
27
 */
28
public class ConceptSearchCriteriaBuilder {
29

30
        private Collection<String> uuids;
31

32
        private Collection<Integer> conceptIds;
33

34
        private Collection<String> mappings;
35

36
        private Collection<String> names;
37

38
        private boolean includeRetired = false;
1✔
39

40
        /**
41
         * Constructs a new {@link ConceptSearchCriteriaBuilder} instance.
42
         */
43
        public ConceptSearchCriteriaBuilder() {
1✔
44
                uuids = new ArrayList<>();
1✔
45
                conceptIds = new ArrayList<>();
1✔
46
                mappings = new ArrayList<>();
1✔
47
                names = new ArrayList<>();
1✔
48
        }
1✔
49

50
        /**
51
         * Adds one or more concept UUIDs to the search criteria.
52
         *
53
         * @param uuids the UUIDs of the concepts to include.
54
         * @return the current builder instance for method chaining.
55
         */
56
        public ConceptSearchCriteriaBuilder addUuids(String... uuids) {
57
                for (String uuid : uuids) {
1✔
58
                        this.uuids.add(uuid);
1✔
59
                }
60
                return this;
1✔
61
        }
62

63
        /**
64
         * Adds one or more concept integer IDs to the search criteria.
65
         *
66
         * @param conceptIds the integer IDs of the concepts to include.
67
         * @return the current builder instance for method chaining.
68
         */
69
        public ConceptSearchCriteriaBuilder addConceptIds(Integer... conceptIds) {
70
                for (Integer conceptId : conceptIds) {
1✔
71
                        this.conceptIds.add(conceptId);
1✔
72
                }
73
                return this;
1✔
74
        }
75

76
        /**
77
         * Adds a concept name to the search criteria.
78
         *
79
         * @param name the name of the concept to include.
80
         * @return the current builder instance for method chaining.
81
         */
82
        public ConceptSearchCriteriaBuilder addName(String name) {
83
                this.names.add(name);
1✔
84
                return this;
1✔
85
        }
86

87
        /**
88
         * Adds multiple concept names to the search criteria.
89
         *
90
         * @param names the collection of names to include.
91
         * @return the current builder instance for method chaining.
92
         */
93
        public ConceptSearchCriteriaBuilder addNames(Collection<String> names) {
NEW
94
                this.names.addAll(names);
×
NEW
95
                return this;
×
96
        }
97

98
        /**
99
         * Adds a concept mapping to the search criteria. The mapping must be in {@code "source:term"}
100
         * format, where {@code source} is the concept source name or HL7 code and {@code term} is the
101
         * concept code.
102
         *
103
         * @param mapping the mapping string in {@code "source:term"} format.
104
         * @return the current builder instance for method chaining.
105
         */
106
        public ConceptSearchCriteriaBuilder addMapping(String mapping) {
107
                this.mappings.add(mapping);
1✔
108
                return this;
1✔
109
        }
110

111
        /**
112
         * Adds multiple concept mappings to the search criteria. Each mapping must be in
113
         * {@code "source:term"} format, where {@code source} is the concept source name or HL7 code and
114
         * {@code term} is the concept code.
115
         *
116
         * @param mappings the collection of mapping strings in {@code "source:term"} format.
117
         * @return the current builder instance for method chaining.
118
         */
119
        public ConceptSearchCriteriaBuilder addMappings(Collection<String> mappings) {
NEW
120
                this.mappings.addAll(mappings);
×
NEW
121
                return this;
×
122
        }
123

124
        /**
125
         * Adds a concept reference to the search criteria, automatically classifying it into the
126
         * appropriate bucket. The reference is resolved in this order:
127
         * <ol>
128
         * <li>UUID — if the value is a valid UUID format</li>
129
         * <li>Mapping — if the value contains {@code ":"} (i.e. {@code "source:term"})</li>
130
         * <li>Concept ID — if the value is a non-negative integer</li>
131
         * <li>Static constant — if the value is a fully qualified Java constant name (e.g.
132
         * {@code "org.openmrs.module.emrapi.EmrApiConstants.CONCEPT_SOURCE_NAME"}), its value is resolved
133
         * via reflection and the result is classified recursively</li>
134
         * <li>Concept name — any other non-blank string is treated as a concept name</li>
135
         * </ol>
136
         * Blank references are silently ignored.
137
         *
138
         * @param ref the concept reference string.
139
         * @return the current builder instance for method chaining.
140
         */
141
        public ConceptSearchCriteriaBuilder addConceptReference(String ref) {
142
                if (StringUtils.isBlank(ref)) {
1✔
NEW
143
                        return this;
×
144
                }
145
                if (isValidUuidFormat(ref)) {
1✔
146
                        return addUuids(ref);
1✔
147
                }
148
                int idx = ref.indexOf(":");
1✔
149
                if (idx >= 0 && idx < ref.length() - 1) {
1✔
150
                        return addMapping(ref);
1✔
151
                }
152
                int conceptId = NumberUtils.toInt(ref, -1);
1✔
153
                if (conceptId >= 0) {
1✔
154
                        return addConceptIds(conceptId);
1✔
155
                }
156
                if (ref.contains(".")) {
1✔
157
                        try {
158
                                String resolved = evaluateStaticConstant(ref);
1✔
159
                                if (resolved != null) {
1✔
160
                                        return addConceptReference(resolved);
1✔
161
                                }
NEW
162
                        } catch (APIException e) {
×
163
                                // unresolvable constant — skip silently
NEW
164
                        }
×
NEW
165
                        return this;
×
166
                }
167
                return addName(ref);
1✔
168
        }
169

170
        /**
171
         * Adds multiple concept references to the search criteria. Each reference is classified
172
         * individually by {@link #addConceptReference(String)}.
173
         *
174
         * @param refs the collection of concept reference strings.
175
         * @return the current builder instance for method chaining.
176
         */
177
        public ConceptSearchCriteriaBuilder addConceptReferences(Collection<String> refs) {
NEW
178
                for (String ref : refs) {
×
NEW
179
                        addConceptReference(ref);
×
NEW
180
                }
×
NEW
181
                return this;
×
182
        }
183

184
        private static boolean isValidUuidFormat(String uuid) {
185
                return uuid.length() >= 36 && uuid.length() <= 38 && !uuid.contains(" ") && !uuid.contains(".");
1✔
186
        }
187

188
        private static String evaluateStaticConstant(String fqn) {
189
                int lastPeriod = fqn.lastIndexOf(".");
1✔
190
                String clazzName = fqn.substring(0, lastPeriod);
1✔
191
                String constantName = fqn.substring(lastPeriod + 1);
1✔
192
                try {
193
                        Class<?> clazz = Context.loadClass(clazzName);
1✔
194
                        Field constantField = clazz.getDeclaredField(constantName);
1✔
195
                        constantField.setAccessible(true);
1✔
196
                        Object val = constantField.get(null);
1✔
197
                        return val != null ? String.valueOf(val) : null;
1✔
NEW
198
                } catch (Exception ex) {
×
NEW
199
                        throw new APIException("Error while evaluating " + fqn + " as a constant", ex);
×
200
                }
201
        }
202

203
        /**
204
         * Sets whether retired concepts should be included in the search results.
205
         *
206
         * @param includeRetired true to include retired concepts, false otherwise.
207
         * @return the current instance of {@link ConceptSearchCriteriaBuilder} for method chaining.
208
         */
209
        public ConceptSearchCriteriaBuilder includeRetired(boolean includeRetired) {
210
                this.includeRetired = includeRetired;
1✔
211
                return this;
1✔
212
        }
213

214
        /**
215
         * Builds and returns a {@link ConceptSearchCriteria} instance based on the current state of the
216
         * builder.
217
         *
218
         * @return a new instance of {@link ConceptSearchCriteria}.
219
         */
220
        public ConceptSearchCriteria build() {
221
                return new ConceptSearchCriteria(uuids, conceptIds, mappings, names, includeRetired);
1✔
222
        }
223
}
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