Downloads Documentation Community Contribute Demo






Show Sidebar
Login | Register

root/openmrs/trunk/src/api/org/openmrs/api/impl/PatientServiceImpl.java

Revision 5381, 52.1 kB (checked in by bwolfe, 1 day ago)

Readded additional patient identifier search back to the generic person dwr searches - #689

  • Property svn:eol-style set to CRLF
Line 
1 /**
2  * The contents of this file are subject to the OpenMRS Public License
3  * Version 1.0 (the "License"); you may not use this file except in
4  * compliance with the License. You may obtain a copy of the License at
5  * http://license.openmrs.org
6  *
7  * Software distributed under the License is distributed on an "AS IS"
8  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9  * License for the specific language governing rights and limitations
10  * under the License.
11  *
12  * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
13  */
14 package org.openmrs.api.impl;
15
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.Date;
20 import java.util.LinkedHashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.Vector;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.openmrs.Concept;
29 import org.openmrs.Encounter;
30 import org.openmrs.Location;
31 import org.openmrs.Obs;
32 import org.openmrs.Order;
33 import org.openmrs.Patient;
34 import org.openmrs.PatientIdentifier;
35 import org.openmrs.PatientIdentifierType;
36 import org.openmrs.PatientProgram;
37 import org.openmrs.PersonAddress;
38 import org.openmrs.PersonAttribute;
39 import org.openmrs.PersonName;
40 import org.openmrs.Relationship;
41 import org.openmrs.Tribe;
42 import org.openmrs.User;
43 import org.openmrs.api.APIException;
44 import org.openmrs.api.BlankIdentifierException;
45 import org.openmrs.api.DuplicateIdentifierException;
46 import org.openmrs.api.EncounterService;
47 import org.openmrs.api.IdentifierNotUniqueException;
48 import org.openmrs.api.InsufficientIdentifiersException;
49 import org.openmrs.api.InvalidCheckDigitException;
50 import org.openmrs.api.InvalidIdentifierFormatException;
51 import org.openmrs.api.MissingRequiredIdentifierException;
52 import org.openmrs.api.ObsService;
53 import org.openmrs.api.OrderService;
54 import org.openmrs.api.PatientIdentifierException;
55 import org.openmrs.api.PatientService;
56 import org.openmrs.api.PersonService;
57 import org.openmrs.api.ProgramWorkflowService;
58 import org.openmrs.api.context.Context;
59 import org.openmrs.api.db.PatientDAO;
60 import org.openmrs.order.OrderUtil;
61 import org.openmrs.patient.IdentifierValidator;
62 import org.openmrs.patient.UnallowedIdentifierException;
63 import org.openmrs.patient.impl.LuhnIdentifierValidator;
64 import org.openmrs.util.OpenmrsConstants;
65
66 /**
67  * Default implementation of the patient service.  This class should
68  * not be used on its own.  The current OpenMRS implementation
69  * should be fetched from the Context via <code>Context.getPatientService()</code>
70  *
71  * @see org.openmrs.api.context.Context
72  * @see org.openmrs.api.PatientService
73  * @see org.openmrs.api.PersonService
74  */
75 public class PatientServiceImpl extends BaseOpenmrsService implements PatientService {
76
77         private Log log = LogFactory.getLog(this.getClass());
78        
79         private PatientDAO dao;
80        
81         /**
82          * PatientIdentifierValidators registered through spring's applicationContext-service.xml
83          */
84         private static Map<Class<? extends IdentifierValidator>, IdentifierValidator> identifierValidators = null;
85        
86         /**
87          * @see org.openmrs.api.PatientService#setPatientDAO(org.openmrs.api.db.PatientDAO)
88          */
89         public void setPatientDAO(PatientDAO dao) {
90                 this.dao = dao;
91         }
92        
93         /**
94          * @see {@link #savePatient(Patient)}
95          * @deprecated replaced by #savePatient(Patient)
96          * @see org.openmrs.api.PatientService#createPatient(org.openmrs.Patient)
97          */
98         public Patient createPatient(Patient patient) throws APIException {
99                 return savePatient(patient);
100         }
101                        
102         /**
103          * @see org.openmrs.api.PatientService#savePatient(org.openmrs.Patient)
104          */
105         public Patient savePatient(Patient patient) throws APIException {
106                 if (patient.getPatientId() == null)
107                         Context.requirePrivilege(OpenmrsConstants.PRIV_ADD_PATIENTS);
108                 else
109                         Context.requirePrivilege(OpenmrsConstants.PRIV_EDIT_PATIENTS);
110                
111                 setCollectionProperties(patient);
112                
113                 checkPatientIdentifiers(patient);
114                
115                 Date now = new Date();
116                 if (patient.getDateCreated() == null)
117                         patient.setDateCreated(now);
118                 if (patient.getCreator() == null)
119                         patient.setCreator(Context.getAuthenticatedUser());
120                 if (patient.getPatientId() != null) {
121                         patient.setChangedBy(Context.getAuthenticatedUser());
122                         patient.setDateChanged(now);
123                 }
124                
125                 return dao.savePatient(patient);
126         }
127
128         /**
129          * @see org.openmrs.api.PatientService#getPatient(java.lang.Integer)
130          */
131         public Patient getPatient(Integer patientId) throws APIException {
132                 return dao.getPatient(patientId);
133         }
134
135         /**
136          * @see #savePatient(Patient)
137          * @deprecated replaced by #savePatient(Patient)
138          * @see org.openmrs.api.PatientService#updatePatient(org.openmrs.Patient)
139          */
140         public Patient updatePatient(Patient patient) throws APIException {
141                 return savePatient(patient);
142         }
143        
144         /**
145      * @see org.openmrs.api.PatientService#getAllPatients()
146      */
147     public List<Patient> getAllPatients() throws APIException {
148             return getAllPatients(false);
149     }
150
151         /**
152      * @see org.openmrs.api.PatientService#getAllPatients(boolean)
153      */
154     public List<Patient> getAllPatients(boolean includeVoided)
155             throws APIException {
156             return dao.getAllPatients(includeVoided);
157     }
158                
159         /**
160          * @deprecated replaced by {@link #getPatients(String, String, List, boolean)}
161      * @see org.openmrs.api.PatientService#getPatients(java.lang.String, java.lang.String, java.util.List)
162      */
163     public List<Patient> getPatients(String name, String identifier,
164             List<PatientIdentifierType> identifierTypes)
165             throws APIException {
166         return getPatients(name, identifier, identifierTypes, false);
167         }
168    
169     /**
170      * @see org.openmrs.api.PatientService#getPatients(java.lang.String, java.lang.String, java.util.List, boolean)
171      */
172     public List<Patient> getPatients(String name, String identifier,
173             List<PatientIdentifierType> identifierTypes, boolean matchIdentifierExactly)
174             throws APIException {
175                
176         if (identifierTypes == null)
177                 identifierTypes = Collections.emptyList();
178                
179         return dao.getPatients(name, identifier, identifierTypes, matchIdentifierExactly);
180         }
181        
182         /**
183          * @see org.openmrs.api.PatientService#checkPatientIdentifiers(org.openmrs.Patient)
184          */
185         public void checkPatientIdentifiers(Patient patient)
186                 throws PatientIdentifierException {
187                 // check patient has at least one identifier
188                 if (patient.getIdentifiers().size() < 1 )
189                         throw new InsufficientIdentifiersException("At least one Patient Identifier is required");
190
191                 List<PatientIdentifier> identifiers = new Vector<PatientIdentifier>();
192                 identifiers.addAll(patient.getIdentifiers());
193                 List<String> identifiersUsed = new Vector<String>();
194                 List<PatientIdentifierType> requiredTypes = getPatientIdentifierTypes(null, null, true, null);
195                 if (requiredTypes == null)
196                         requiredTypes = new ArrayList<PatientIdentifierType>();
197                 List<PatientIdentifierType> foundRequiredTypes = new ArrayList<PatientIdentifierType>();
198
199                 for (PatientIdentifier pi : identifiers) {
200                         if (pi.isVoided())
201                                 continue;
202
203                         try {
204                                 checkPatientIdentifier(pi);
205                         } catch ( BlankIdentifierException bie ) {
206                                 patient.removeIdentifier(pi);
207                                 throw bie;
208                         }
209                        
210                         // check if this is a required identifier
211                         for ( PatientIdentifierType requiredType : requiredTypes ) {
212                                 if ( pi.getIdentifierType().equals(requiredType) ) {
213                                         foundRequiredTypes.add(requiredType);
214                                         requiredTypes.remove(requiredType);
215                                         break;
216                                 }
217                         }
218
219                         // TODO: check patient has at least one "sufficient" identifier
220
221                         // check this patient for duplicate identifiers+identifierType
222                         if (identifiersUsed.contains(pi.getIdentifier() + " id type #: " + pi.getIdentifierType().getPatientIdentifierTypeId())) {
223                                 patient.removeIdentifier(pi);
224                                 throw new DuplicateIdentifierException("This patient has two identical identifiers of type "
225                                                                                + pi.getIdentifierType()
226                                                                                    .getName()
227                                                                                + ": "
228                                                                                + pi.getIdentifier()
229                                                                                + ", deleting one of them",
230                                                                        pi);
231                         }
232                         else {
233                                 identifiersUsed.add(pi.getIdentifier() + " id type #: " + pi.getIdentifierType().getPatientIdentifierTypeId());
234                         }
235                 }
236
237                 if ( requiredTypes.size() > 0 ) {
238                         String missingNames = "";
239                         for ( PatientIdentifierType pit : requiredTypes ) {
240                                 missingNames += (missingNames.length() > 0) ? ", "
241                                         + pit.getName() : pit.getName();
242                         }
243                         throw new MissingRequiredIdentifierException("Patient is missing the following required identifier(s): "
244                                 + missingNames);
245                 }
246         }
247
248         /**
249          * @see org.openmrs.api.PatientService#checkPatientIdentifier(org.openmrs.PatientIdentifier)
250          */
251         public void checkPatientIdentifier(PatientIdentifier pi)
252                 throws PatientIdentifierException {
253                
254                 // skip and remove invalid/empty identifiers
255                 if (pi == null)
256                         throw new BlankIdentifierException("Identifier cannot be null or blank",
257                                                            pi);
258                 else if (pi.getIdentifier() == null)
259                         throw new BlankIdentifierException("Identifier cannot be null or blank",
260                                                            pi);
261                 else if (pi.getIdentifier().length() == 0)
262                         throw new BlankIdentifierException("Identifier cannot be null or blank",
263                                                            pi);
264                
265                 // do not do any more checks if this identifier is voided
266                 if (pi.isVoided() == false) {
267                        
268                         // check is already in use by another patient
269                         Patient p = identifierInUse(pi);
270                         if (p != null) {
271                                 throw new IdentifierNotUniqueException("Identifier "
272                                         + pi.getIdentifier() + " already in use by patient #"
273                                         + p.getPatientId(), pi);
274                         }
275        
276                         // also validate regular expression - if it exists
277                         PatientIdentifierType pit = pi.getIdentifierType();
278                         String identifier = pi.getIdentifier();
279        
280                         String regExp = pit.getFormat();
281                         if ( regExp != null ) {
282                                 if ( regExp.length() > 0 ) {
283                                         // if this ID has a valid corresponding regular expression,
284                                         // check against it
285                                         log.debug("Trying to match " + identifier + " and "
286                                                 + regExp);
287                                         if ( !identifier.matches(regExp) ) {
288                                                 log.debug("The two DO NOT match");
289                                                 if ( pit.getFormatDescription() != null ) {
290                                                         if (pit.getFormatDescription().length() > 0)
291                                                                 throw new InvalidIdentifierFormatException("Identifier ["
292                                                                                                                    + identifier
293                                                                                                                    + "] does not match required format: "
294                                                                                                                    + pit.getFormatDescription(),
295                                                                                                            pi);
296                                                         else
297                                                                 throw new InvalidIdentifierFormatException("Identifier ["
298                                                                                                                    + identifier
299                                                                                                                    + "] does not match required format: "
300                                                                                                                    + pit.getFormat(),
301                                                                                                            pi);
302                                                 } else {
303                                                         throw new InvalidIdentifierFormatException("Identifier ["
304                                                                                                            + identifier
305                                                                                                            + "] does not match required format: "
306                                                                                                            + pit.getFormat(),
307                                                                                                    pi);
308                                                 }
309                                         } else {
310                                                 log.debug("The two match!!");
311                                         }
312                                 }
313                         }
314        
315                         // validate identifier
316                         if(pit.hasValidator()){
317                                 IdentifierValidator piv = getIdentifierValidator(pit.getValidator());
318                                 try{
319                                         if(!piv.isValid(identifier))
320                                                 throw new InvalidCheckDigitException("Invalid check digit for identifier: " + identifier, pi);
321                                 }catch(UnallowedIdentifierException e){
322                                         throw new InvalidCheckDigitException("Identifier " + identifier +" is not appropriate for validation scheme " + piv.getName() + ".", pi);
323                                 }
324                         }
325                 }
326         }
327        
328         /**
329          * checks if the given PatientIdentifer is in use by any other patient
330          *
331          * @param pi PatientIdentifier
332          * @return true/false whether or not this PatientIdentifier is in use
333          * @deprecated use {@link #getPatientsByIdentifier(String, boolean)}yIdentifier(String) instead
334          */
335         private Patient identifierInUse(PatientIdentifier pi) {
336                 return identifierInUse(pi.getIdentifier(),
337                                        pi.getIdentifierType(),
338                                        pi.getPatient());
339         }
340        
341         /**
342          * @see org.openmrs.api.PatientService#identifierInUse(java.lang.String,
343          *      org.openmrs.PatientIdentifierType, org.openmrs.Patient)
344          * @deprecated use getPatientByIdentifier(String) instead
345          */
346         public Patient identifierInUse(String identifier,
347                 PatientIdentifierType type, Patient ignorePatient) {
348                
349                 // get all patients with this identifier
350                 List<PatientIdentifierType> types = new Vector<PatientIdentifierType>();
351                 types.add(type);
352                 List<Patient> patients = getPatients(null, identifier, types, /* exact name+identifier search */ true);
353                
354                 // ignore this patient (loop until no changes made)
355                 while (patients.remove(ignorePatient)) { };
356                
357                 if (patients.size() > 0)
358                         return patients.get(0);
359                
360                 return null;
361         }
362
363         /**
364          * @deprecated replaced by @deprecated replaced by {@link #getPatients(String, String, List)}
365          * @see org.openmrs.api.PatientService#getPatientsByIdentifier(java.lang.String, boolean)
366          */
367         public List<Patient> getPatientsByIdentifier(String identifier,
368                 boolean includeVoided) throws APIException {
369                
370                 if (includeVoided == true)
371                         throw new APIException("Searching on voided patients is no longer allowed");
372                
373                 return getPatients(null, identifier, null);
374         }
375        
376         /**
377          * @deprecated replaced by {@link #getPatients(String, String, List, String)}
378          * @see org.openmrs.api.PatientService#getPatientsByIdentifierPattern(java.lang.String, boolean)
379          */
380         public List<Patient> getPatientsByIdentifierPattern(String identifier,
381                 boolean includeVoided) throws APIException {
382                
383                 if (includeVoided == true)
384                         throw new APIException("Searching on voided patients is no longer allowed");
385                
386                 return getPatients(null, identifier, null);
387         }
388        
389         /**
390          * @see org.openmrs.api.PatientService#getPatientsByName(java.lang.String)
391          * @deprecated replaced by {@link #getPatients(String, String, List, String)}
392          */
393         public List<Patient> getPatientsByName(String name) throws APIException {
394                 return getPatients(name, null, null);
395         }
396        
397         /**
398          * @deprecated replaced by {@link #getPatients(String, String, List, String)}
399          */
400         public List<Patient> getPatientsByName(String name, boolean includeVoided)
401                 throws APIException {
402                
403                 if (includeVoided == true)
404                         throw new APIException("Searching on voided patients is no longer allowed");
405                
406                 return getPatients(name, null, null);
407         }
408        
409         /**
410          * @see org.openmrs.api.PatientService#voidPatient(org.openmrs.Patient, java.lang.String)
411          */
412         public Patient voidPatient(Patient patient, String reason) throws APIException {
413                
414                 // TODO should we move voided attributes from the patient up to the person?
415                 // voiding a patient portion of a person and keeping the user portion doesn't
416                 // seem like a necessary goal
417                
418                 if (patient == null)
419                         return null;
420
421                 User voidedBy = Context.getAuthenticatedUser();
422                 Date voidDate = new Date();
423                
424                 if (patient.getIdentifiers() != null)
425                         for (PatientIdentifier pi : patient.getIdentifiers()) {
426                                 if (!pi.isVoided()) {
427                                         pi.setVoided(true);
428                                         pi.setVoidReason(reason);
429                                         pi.setDateVoided(voidDate);
430                                         pi.setVoidedBy(voidedBy);
431                                 }
432                         }
433                
434                 patient.setVoided(true);
435                 patient.setVoidedBy(voidedBy);
436                 patient.setDateVoided(voidDate);
437                 patient.setVoidReason(reason);
438                
439                 return savePatient(patient);
440         }
441
442         /**
443          * @see org.openmrs.api.PatientService#unvoidPatient(org.openmrs.Patient)
444          */
445         public Patient unvoidPatient(Patient patient) throws APIException {
446                 if (patient == null)
447                         return null;
448                
449                 String voidReason = patient.getVoidReason();
450                 if (voidReason == null)
451                         voidReason = "";
452                
453                 for (PatientIdentifier pi : patient.getIdentifiers()) {
454                         if (voidReason.equals(pi.getDateVoided())) {
455                                 pi.setVoided(false);
456                                 pi.setVoidReason(null);
457                                 pi.setDateVoided(null);
458                                 pi.setVoidedBy(null);
459                         }
460                 }
461                 patient.setVoided(false);
462                 patient.setVoidedBy(null);
463                 patient.setDateVoided(null);
464                 patient.setVoidReason(null);
465                
466                 return savePatient(patient);
467         }
468        
469         /**
470          * @see #voidPatient(org.openmrs.Patient,java.lang.String)
471          * @deprecated replaced by {@link #purgePatient(Patient)}
472          */
473         public void deletePatient(Patient patient) throws APIException {
474                 purgePatient(patient);
475         }
476
477         /**
478          * @see org.openmrs.api.PatientService#purgePatient(org.openmrs.Patient)
479          */
480         public void purgePatient(Patient patient) throws APIException {
481                 dao.deletePatient(patient);
482         }
483        
484        
485         // patient identifier section
486        
487         /**
488      * @see org.openmrs.api.PatientService#getPatientIdentifiers(java.lang.String, java.util.List, java.util.List, java.util.List, java.lang.Boolean)
489          */
490     public List<PatientIdentifier> getPatientIdentifiers(String identifier,
491             List<PatientIdentifierType> patientIdentifierTypes,
492             List<Location> locations, List<Patient> patients,
493             Boolean isPreferred)
494             throws APIException {
495            
496         if (patientIdentifierTypes == null)
497                 patientIdentifierTypes = new Vector<PatientIdentifierType>();
498        
499         if (locations == null)
500                 locations = new Vector<Location>();
501        
502         if (patients == null)
503                 patients = new Vector<Patient>();
504                
505             return dao.getPatientIdentifiers(identifier, patientIdentifierTypes, locations, patients, isPreferred);
506         }
507        
508         /**
509          * @deprecated replaced by {@link #getPatientIdentifiers(String, List, List, List, Boolean)}
510          * @see org.openmrs.api.PatientService#getPatientIdentifiers(org.openmrs.PatientIdentifierType)
511          */
512         public List<PatientIdentifier> getPatientIdentifiers(
513                 PatientIdentifierType pit) throws APIException {
514                 List<PatientIdentifierType> types = new Vector<PatientIdentifierType>();
515                 types.add(pit);
516                 return getPatientIdentifiers(null, types, null, null, null);
517         }
518                
519         /**
520          * @deprecated replaced by {@link #getPatientIdentifiers(String, List, List, List, Boolean, boolean)}
521          * @see org.openmrs.api.PatientService#getPatientIdentifiers(java.lang.String, org.openmrs.PatientIdentifierType)
522          */
523         public List<PatientIdentifier> getPatientIdentifiers(String identifier,
524                 PatientIdentifierType pit) throws APIException {
525                 List<PatientIdentifierType> types = new Vector<PatientIdentifierType>();
526                 types.add(pit);
527                 return getPatientIdentifiers(identifier, types, null, null, null);
528         }
529        
530         /**
531          * @deprecated replaced by {@link #getPatientIdentifiers(String, List, List, List, Boolean, boolean)}
532          * @see org.openmrs.api.PatientService#getPatientIdentifiers(java.lang.String,
533          *      org.openmrs.PatientIdentifierType, boolean)
534          */
535         public List<PatientIdentifier> getPatientIdentifiers(String identifier,
536                 PatientIdentifierType patientIdentifierType, boolean includeVoided)
537                 throws APIException {
538                
539                 if (includeVoided == true)
540                         throw new APIException("Searching on voided identifiers is no longer allowed");
541                
542                 List<PatientIdentifierType> types = new Vector<PatientIdentifierType>();
543                 types.add(patientIdentifierType);
544                 return getPatientIdentifiers(identifier, types, null, null, null);
545         }
546
547         /**
548          * @deprecated patient identifiers should not be updated directly; rather,
549          *             after changing patient identifiers, use
550          *             {@link #savePatient(Patient)} to save changes to the database
551          */
552         public void updatePatientIdentifier(PatientIdentifier pi)
553                 throws APIException {
554                 // this method allows you change only the Identifier type, so let's do
555                 // that and then do a full ID check
556                 Patient p = pi.getPatient();
557                 Set<PatientIdentifier> identifiers = p.getIdentifiers();
558                 for ( PatientIdentifier identifier : identifiers ) {
559                         if ( identifier.getIdentifier().equals(pi.getIdentifier())
560                                 && identifier.getLocation().equals(pi.getLocation())) {
561                                 identifier.setIdentifierType(pi.getIdentifierType());
562                                 break;
563                         }
564                 }
565                
566                 savePatient(p);
567         }
568        
569         // end patient identifier section
570        
571        
572         // patient identifier _type_ section
573
574         /**
575          * TODO: Add changedBy and DateChanged columns to table
576          * patient_identifier_type
577          *
578          * @see org.openmrs.api.PatientService#savePatientIdentifierType(org.openmrs.PatientIdentifierType)
579          */
580         public PatientIdentifierType savePatientIdentifierType(
581                 PatientIdentifierType patientIdentifierType) throws APIException {
582                 Date now = new Date();
583                 if (patientIdentifierType.getDateCreated() == null)
584                         patientIdentifierType.setDateCreated(now);
585                 if (patientIdentifierType.getCreator() == null)
586                         patientIdentifierType.setCreator(Context.getAuthenticatedUser());
587                 // if (patientIdentifierType.getPatientIdentifierTypeId() != null) {
588                 // patientIdentifierType.setChangedBy(Context.getAuthenticatedUser());
589                 // patientIdentifierType.setDateChanged(now);
590                 // }
591                 return dao.savePatientIdentifierType(patientIdentifierType);
592         }
593                
594         /**
595          * @deprecated replaced by {@link #getAllPatientIdentifierTypes()}
596          * @see org.openmrs.api.PatientService#getPatientIdentifierTypes()
597          * @see org.openmrs.api.PatientService#getAllPatientIdentifierTypes()
598          */
599         public List<PatientIdentifierType> getPatientIdentifierTypes()
600                 throws APIException {
601                 return getAllPatientIdentifierTypes();
602         }
603
604         /**
605          * @see org.openmrs.api.PatientService#getAllPatientIdentifierTypes()
606          */
607         public List<PatientIdentifierType> getAllPatientIdentifierTypes()
608                 throws APIException {
609                 return getAllPatientIdentifierTypes(false);
610         }
611
612         /**
613          * @see org.openmrs.api.PatientService#getAllPatientIdentifierTypes(boolean)
614          */
615         public List<PatientIdentifierType> getAllPatientIdentifierTypes(boolean includeRetired)
616                 throws APIException {
617                 return dao.getAllPatientIdentifierTypes(includeRetired);
618         }
619                
620         /**
621      * @see org.openmrs.api.PatientService#getPatientIdentifierTypes(java.lang.String, java.lang.String, java.lang.Boolean, java.lang.Boolean)
622      */
623     public List<PatientIdentifierType> getPatientIdentifierTypes(String name,
624             String format, Boolean required, Boolean hasCheckDigit)
625             throws APIException {
626                 return dao.getPatientIdentifierTypes(name, format, required, hasCheckDigit);
627     }
628
629         /**
630          * @see org.openmrs.api.PatientService#getPatientIdentifierType(java.lang.Integer)
631          */
632         public PatientIdentifierType getPatientIdentifierType(
633                 Integer patientIdentifierTypeId) throws APIException {
634                 return dao.getPatientIdentifierType(patientIdentifierTypeId);
635                                 }
636
637         /**
638          * @see org.openmrs.api.PatientService#getPatientIdentifierType(java.lang.String)
639          * @deprecated
640          */
641         public PatientIdentifierType getPatientIdentifierType(String name)
642                 throws APIException {
643                 return getPatientIdentifierTypeByName(name);
644                         }
645
646         /**
647          * @see org.openmrs.api.PatientService#getPatientIdentifierTypeByName(java.lang.String)
648          */
649         public PatientIdentifierType getPatientIdentifierTypeByName(String name)
650                 throws APIException {
651                 List<PatientIdentifierType> types = getPatientIdentifierTypes(name, null, null, null);
652                
653                 if (types.size() > 0)
654                         return types.get(0);
655                
656                 return null;
657         }
658
659         /**
660          * @see org.openmrs.api.PatientService#retirePatientIdentifierType(org.openmrs.PatientIdentifierType, String)
661          */
662         public PatientIdentifierType retirePatientIdentifierType(
663                 PatientIdentifierType patientIdentifierType, String reason) throws APIException {
664                 if (reason == null || reason.length() < 1)
665                         throw new APIException("A reason is required when retiring an identifier type");
666                
667                 patientIdentifierType.setRetired(true);
668                 patientIdentifierType.setRetiredBy(Context.getAuthenticatedUser());
669                 patientIdentifierType.setDateRetired(new Date());
670                 patientIdentifierType.setRetireReason(reason);
671                 return savePatientIdentifierType(patientIdentifierType);
672         }
673
674         /**
675          * @see org.openmrs.api.PatientService#unretirePatientIdentifierType(org.openmrs.PatientIdentifierType)
676          */
677         public PatientIdentifierType unretirePatientIdentifierType(
678                 PatientIdentifierType patientIdentifierType) throws APIException {
679                 patientIdentifierType.setRetired(false);
680                 patientIdentifierType.setRetiredBy(null);
681                 patientIdentifierType.setDateRetired(null);
682                 patientIdentifierType.setRetireReason(null);
683                 return savePatientIdentifierType(patientIdentifierType);
684         }
685                
686         /**
687          * @see org.openmrs.api.PatientService#purgePatientIdentifierType(org.openmrs.PatientIdentifierType)
688          */
689         public void purgePatientIdentifierType(
690                 PatientIdentifierType patientIdentifierType) throws APIException {
691                 dao.deletePatientIdentifierType(patientIdentifierType);
692         }
693        
694         // end patient identifier _type_ section
695        
696        
697         // tribe section
698
699         /**
700          * @deprecated tribe will be moved to patient attribute
701          * @see org.openmrs.api.PatientService#getTribe(java.lang.Integer)
702          */
703         public Tribe getTribe(Integer tribeId) throws APIException {
704                 return dao.getTribe(tribeId);
705         }
706        
707         /**
708          * @deprecated tribe will be moved to patient attributes
709          * @see org.openmrs.api.PatientService#getTribes()
710          */
711         public List<Tribe> getTribes() throws APIException {
712                 return dao.getTribes();
713         }
714                
715         /**
716          * @deprecated tribe will be moved to patient attributes
717          * @see org.openmrs.api.PatientService#findTribes(java.lang.String)
718          */
719         public List<Tribe> findTribes(String search) throws APIException {
720                 return dao.findTribes(search);
721         }
722        
723         // end tribe section
724        
725
726         /**
727          * @see org.openmrs.api.PatientService#findPatients(java.lang.String, boolean)
728          */
729         public List<Patient> findPatients(String query, boolean includeVoided) throws APIException {
730                 if (includeVoided == true)
731                         throw new APIException("Searching on voided patients is no longer allowed");
732                
733                 return getPatients(query);
734         }
735                
736         /**
737          * @see org.openmrs.api.PatientService#findPatients(java.lang.String)
738          */
739         public List<Patient> getPatients(String query) throws APIException {
740                 List<Patient> patients = new Vector<Patient>();
741                
742                 // TODO create a global property that users can change for this
743                 //query must be more than 2 characters
744                 if (query.length() < 3)
745                         return patients;
746                
747                 // if there is a number in the query string
748                 if (query.matches(".*\\d+.*")) {
749                         log.debug("[Identifier search] Query: " + query);
750                         return getPatients(null, query, null);
751                 } else {
752                         // there is no number in the string, search on name
753                         return getPatients(query, null, null);
754                 }
755         }
756        
757         /**
758          * @see org.openmrs.api.PatientService#findPatient(org.openmrs.Patient)
759          * @see #getPatientByExample(Patient)
760          * @deprecated
761          */
762         public Patient findPatient(Patient patientToMatch) throws APIException {
763                 return getPatientByExample(patientToMatch);
764         }
765        
766         /**
767          * This default implementation simply looks at the OpenMRS internal id
768          * (patient_id). If the id is null, assume this patient isn't found. If the
769          * patient_id is not null, try and find that id in the database
770          *
771          * @see org.openmrs.api.PatientService#getPatientByExample(org.openmrs.Patient)
772          */
773         public Patient getPatientByExample(Patient patientToMatch) throws APIException {
774                 if (patientToMatch == null || patientToMatch.getPatientId() == null)
775                         return null;
776                
777                 return getPatient(patientToMatch.getPatientId());
778         }
779
780         /**
781          * @deprecated use {@link #getDuplicatePatientsByAttributes(List)}
782          */
783         public List<Patient> findDuplicatePatients(Set<String> attributes) throws APIException {
784                 List<String> attributesAsList = new Vector<String>();
785                 attributesAsList.addAll(attributes);
786                
787                 return getDuplicatePatientsByAttributes(attributesAsList);
788         }
789        
790         /**
791      * @see org.openmrs.api.PatientService#getDuplicatePatientsByAttributes(java.util.List)
792          */
793     public List<Patient> getDuplicatePatientsByAttributes(
794             List<String> attributes) throws APIException {
795        
796         if (attributes == null || attributes.size() < 1) {
797                 throw new APIException("There must be at least one attribute supplied to search on");
798         }
799                
800             return dao.getDuplicatePatientsByAttributes(attributes);
801         }
802        
803         /**
804          * 1) Moves object (encounters/obs) pointing to <code>nonPreferred</code>
805          * to <code>preferred</code> 2) Copies data
806          * (gender/birthdate/names/ids/etc) from <code>nonPreferred</code> to
807          * <code>preferred</code> iff the data is missing or null in
808          * <code>preferred</code> 3) <code>notPreferred</code> is marked as
809          * voided
810          *
811          * @param preferred
812          * @param notPreferred
813          * @throws APIException
814          */
815         public void mergePatients(Patient preferred, Patient notPreferred)
816                 throws APIException {
817                 log.debug("Merging patients: (preferred)" + preferred.getPatientId()
818                         + ", (notPreferred) " + notPreferred.getPatientId());
819                
820                 // change all encounters. This will cascade to obs and orders