Downloads Documentation Community Contribute Demo






Show Sidebar
Login | Register

root/openmrs/trunk/src/api/org/openmrs/api/db/hibernate/HibernateConceptDAO.java

Revision 4567, 31.1 kB (checked in by bwolfe, 3 months ago)

Fixing concept name editing bug - #801

  • 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.db.hibernate;
15
16 import java.sql.Connection;
17 import java.sql.PreparedStatement;
18 import java.sql.SQLException;
19 import java.util.Collection;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Locale;
24 import java.util.Set;
25 import java.util.Vector;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.hibernate.Criteria;
30 import org.hibernate.NonUniqueObjectException;
31 import org.hibernate.Query;
32 import org.hibernate.SessionFactory;
33 import org.hibernate.criterion.Conjunction;
34 import org.hibernate.criterion.DetachedCriteria;
35 import org.hibernate.criterion.Expression;
36 import org.hibernate.criterion.MatchMode;
37 import org.hibernate.criterion.Order;
38 import org.hibernate.criterion.Projections;
39 import org.hibernate.criterion.Property;
40 import org.hibernate.criterion.Restrictions;
41 import org.hibernate.criterion.Subqueries;
42 import org.openmrs.Concept;
43 import org.openmrs.ConceptAnswer;
44 import org.openmrs.ConceptClass;
45 import org.openmrs.ConceptDatatype;
46 import org.openmrs.ConceptDerived;
47 import org.openmrs.ConceptName;
48 import org.openmrs.ConceptNumeric;
49 import org.openmrs.ConceptProposal;
50 import org.openmrs.ConceptSet;
51 import org.openmrs.ConceptSetDerived;
52 import org.openmrs.ConceptWord;
53 import org.openmrs.Drug;
54 import org.openmrs.api.context.Context;
55 import org.openmrs.api.db.ConceptDAO;
56 import org.openmrs.api.db.DAOException;
57 import org.openmrs.util.OpenmrsConstants;
58
59 /**
60  * The Hibernate class for Concepts, Drugs, and related classes
61  *
62  * @see org.openmrs.ConceptService to access these methods
63  */
64 public class HibernateConceptDAO implements ConceptDAO {
65
66         protected final Log log = LogFactory.getLog(getClass());
67         private SessionFactory sessionFactory;
68        
69         /**
70          * Sets the session factory
71          *
72          * @param sessionFactory
73          */
74         public void setSessionFactory(SessionFactory sessionFactory) {
75                 this.sessionFactory = sessionFactory;
76         }
77        
78         /**
79          * @see org.openmrs.api.db.ConceptDAO#saveConcept(org.openmrs.Concept)
80          */
81         public Concept saveConcept(Concept concept) throws DAOException {
82                 if (concept.getConceptId() == null || concept.getConceptId() < 1)
83                         concept.setConceptId(this.getNextAvailableId());
84                 else {
85                         // this method checks the concept_numeric, concept_derived, etc tables
86                         // to see if a row exists there or not.  This is needed because hibernate
87                         // doesn't like to insert into concept_numeric but update concept in the
88                         // same go.  It assumes that its either in both tables or no tables
89                         insertRowIntoSubclassIfNecessary(concept);
90                 }
91                
92                 sessionFactory.getCurrentSession().saveOrUpdate(concept);
93                 return concept;
94         }
95
96         /**
97      * Convenience method that will check this concept for subtype values (ConceptNumeric, ConceptDerived, etc)
98      * and insert a line into that subtable if needed.
99      *
100      * This prevents a hibernate ConstraintViolationException
101      *
102      * @param concept the concept that will be inserted
103      */
104     private void insertRowIntoSubclassIfNecessary(Concept concept) {
105         Connection connection = sessionFactory.getCurrentSession().connection();
106                
107         // check the concept_numeric table
108         if (concept instanceof ConceptNumeric) {
109                
110                 try {
111                                 PreparedStatement ps = connection.prepareStatement("SELECT * FROM concept WHERE concept_id = ? and not exists (select * from concept_numeric WHERE concept_id = ?)");
112                                 ps.setInt(1, concept.getConceptId());
113                                 ps.setInt(2, concept.getConceptId());
114                                 ps.execute();
115                
116                                 if (ps.getResultSet().next()) {
117                                         // we have to evict the current concept out of the session because
118                                         // the user probably had to change the class of this object to get it
119                                         // to now be a numeric
120                                         // (must be done before the "insert into...")
121                                         sessionFactory.getCurrentSession().clear();
122                                        
123                                         ps = connection.prepareStatement("INSERT INTO concept_numeric (concept_id, precise) VALUES (?, false)");
124                                         ps.setInt(1, concept.getConceptId());
125                                         ps.executeUpdate();
126                                 }
127                                 else {
128                                         // no stub insert is needed because either a concept row
129                                         // doesn't exist or a concept_numeric row does exist
130                                 }
131                                
132                         }
133                         catch (SQLException e) {
134                                 log.error("Error while trying to see if this ConceptNumeric is in the concept_numeric table already", e);
135                         }
136         }
137         else if (concept instanceof ConceptDerived) {
138                 // check the concept_derived table
139         }
140     }
141
142         /**
143          * @see org.openmrs.api.db.ConceptDAO#purgeConcept(org.openmrs.Concept)
144          */
145         public void purgeConcept(Concept concept) throws DAOException  {
146                 // must delete all the stored concept words first
147                 sessionFactory.getCurrentSession().createQuery("delete from ConceptWord where concept_id = :c")
148                                         .setInteger("c", concept.getConceptId())
149                                         .executeUpdate();
150                
151                 // now we can safely delete the concept
152                 sessionFactory.getCurrentSession().delete(concept);
153         }
154
155         /**
156          * @see org.openmrs.api.db.ConceptDAO#getConcept(java.lang.Integer)
157          */
158         public Concept getConcept(Integer conceptId) throws DAOException  {
159                 return (Concept)sessionFactory.getCurrentSession().get(Concept.class, conceptId);
160         }
161
162         /**
163          * @see org.openmrs.api.db.ConceptDAO#getConceptAnswer(java.lang.Integer)
164          */
165         public ConceptAnswer getConceptAnswer(Integer conceptAnswerId) throws DAOException  {
166                 return (ConceptAnswer)sessionFactory.getCurrentSession().get(ConceptAnswer.class, conceptAnswerId);
167         }
168
169         /**
170          * @see org.openmrs.api.db.ConceptDAO#getAllConcepts(java.lang.String, boolean, boolean)
171          */
172         @SuppressWarnings("unchecked")
173         public List<Concept> getAllConcepts(String sortBy, boolean asc, boolean includeRetired) throws DAOException  {
174                 String sql = "from Concept concept";   
175                
176                 if (!includeRetired)
177                         sql += " where retired = false ";       
178                 try {
179                         Concept.class.getDeclaredField(sortBy);
180                 }
181                 catch (NoSuchFieldException e) {
182                         try {
183                                 ConceptName.class.getDeclaredField(sortBy);
184                                 sortBy = "names." + sortBy;
185                         }
186                         catch (NoSuchFieldException e2) {
187                                 sortBy = "conceptId";
188                         }
189                 }
190                 sql += " order by concept." + sortBy;
191                 if (!asc)
192                         sql += " desc";
193                 else
194                         sql += " asc";         
195                 Query query = sessionFactory.getCurrentSession().createQuery(sql);     
196                 return (List<Concept>) query.list();
197         }
198
199         /**
200          * @see org.openmrs.api.db.ConceptDAO#saveDrug(org.openmrs.Drug)
201          */
202         public Drug saveDrug(Drug drug) throws DAOException {
203                         sessionFactory.getCurrentSession().saveOrUpdate(drug);
204                         return drug;
205         }
206
207         /**
208          * @see org.openmrs.api.db.ConceptDAO#getDrug(java.lang.Integer)
209          */
210         public Drug getDrug(Integer drugId) throws DAOException {
211                 return (Drug)sessionFactory.getCurrentSession().get(Drug.class, drugId);
212         }
213        
214         /**
215          * @see org.openmrs.api.db.ConceptDAO#getDrugs(java.lang.String, org.openmrs.Concept, boolean)
216          */
217         @SuppressWarnings("unchecked")
218         public List<Drug> getDrugs(String drugName, Concept concept, boolean includeRetired) throws DAOException {
219                 Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug");
220                 if (includeRetired == false)
221                         searchCriteria.add(Expression.eq("drug.retired", false));
222                 if (concept != null)
223                         searchCriteria.add(Expression.eq("drug.concept", concept));
224                 if (drugName != null)
225                         searchCriteria.add(Expression.eq("drug.name", drugName));
226                 return (List<Drug>) searchCriteria.list();
227         }
228        
229         /**
230          * @see org.openmrs.api.db.ConceptDAO#getDrugs(java.lang.String)
231          */
232         @SuppressWarnings("unchecked")
233         public List<Drug> getDrugs(String phrase) throws DAOException {
234                 List<String> words = ConceptWord.getUniqueWords(phrase);
235                 List<Drug> conceptDrugs = new Vector<Drug>();
236                
237                 if (words.size() > 0) {
238                
239                         Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug");
240                        
241                         searchCriteria.add(Expression.eq("drug.retired", false));
242                        
243                         Iterator<String> word = words.iterator();
244                         searchCriteria.add(Expression.like("name", word.next(), MatchMode.ANYWHERE));
245                         while (word.hasNext()) {
246                                 String w = word.next();
247                                 log.debug(w);
248                                 searchCriteria.add(Expression.like("name", w, MatchMode.ANYWHERE));
249                         }
250                         searchCriteria.addOrder(Order.asc("drug.concept"));
251                         conceptDrugs = searchCriteria.list();
252                 }
253
254                 return conceptDrugs;
255         }
256        
257         /**
258          * @see org.openmrs.api.db.ConceptDAO#getConceptClass(java.lang.Integer)
259          */
260         public ConceptClass getConceptClass(Integer i) throws DAOException {
261                 return (ConceptClass)sessionFactory.getCurrentSession().get(ConceptClass.class, i);
262         }
263        
264         /**
265          * @see org.openmrs.api.db.ConceptDAO#getConceptClasses(java.lang.String)
266          */
267         @SuppressWarnings("unchecked")
268         public List<ConceptClass> getConceptClasses(String name) throws DAOException {
269                 Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptClass.class);
270                 if (name != null)       
271                         crit.add(Expression.eq("name", name));                 
272                 return crit.list();
273         }
274        
275         /**
276          * @see org.openmrs.api.db.ConceptDAO#getAllConceptClasses(boolean)
277          */
278         @SuppressWarnings("unchecked")
279         public List<ConceptClass> getAllConceptClasses(boolean includeRetired) throws DAOException {
280                 Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptClass.class);
281                
282                 if (includeRetired = false)     
283                         crit.add(Expression.eq("retired", false));
284                
285                 return crit.list();
286         }
287
288         /**
289          * @see org.openmrs.api.db.ConceptDAO#saveConceptClass(org.openmrs.ConceptClass)
290          */
291         public ConceptClass saveConceptClass(ConceptClass cc) throws DAOException {
292                  sessionFactory.getCurrentSession().saveOrUpdate(cc);
293                  return cc;
294         }
295
296         /**
297          * @see org.openmrs.api.db.ConceptDAO#purgeConceptClass(org.openmrs.ConceptClass)
298          */
299         public void purgeConceptClass(ConceptClass cc) throws DAOException  {
300                 sessionFactory.getCurrentSession().delete(cc);
301         }
302
303         /**
304          * @see org.openmrs.api.db.ConceptDAO#getConceptDatatype(java.lang.Integer)
305          */
306         public ConceptDatatype getConceptDatatype(Integer i) {
307                 return (ConceptDatatype)sessionFactory.getCurrentSession().get(ConceptDatatype.class, i);
308         }
309        
310     /**
311      * @see org.openmrs.api.db.ConceptDAO#getAllConceptDatatypes(boolean)
312      */
313     @SuppressWarnings("unchecked")
314     public List<ConceptDatatype> getAllConceptDatatypes(boolean includeRetired)
315             throws DAOException {
316         Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptDatatype.class);
317        
318                 if (includeRetired == false)   
319                         crit.add(Expression.eq("retired", false));
320                
321                 return crit.list();
322     }
323
324     /**
325      * @see org.openmrs.api.db.ConceptDAO#getConceptDatatypes(java.lang.String)
326      */
327     @SuppressWarnings("unchecked")
328     public List<ConceptDatatype> getConceptDatatypes(String name)
329             throws DAOException {
330         Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptDatatype.class);
331        
332                 if (name != null)       
333                         crit.add(Expression.like("name", name, MatchMode.START));
334                
335                 return crit.list();
336     }
337        
338         /**
339          * @see org.openmrs.api.db.ConceptDAO#saveConceptDatatype(org.openmrs.ConceptDatatype)
340          */
341         public ConceptDatatype saveConceptDatatype(ConceptDatatype cd) throws DAOException {
342                 sessionFactory.getCurrentSession().saveOrUpdate(cd);
343                 return cd;
344         }
345        
346         /**
347          * @see org.openmrs.api.db.ConceptDAO#purgeConceptDatatype(org.openmrs.ConceptDatatype)
348          */
349         public void purgeConceptDatatype(ConceptDatatype cd) throws DAOException {
350                 sessionFactory.getCurrentSession().delete(cd);
351         }
352
353         /**
354          * @see org.openmrs.api.db.ConceptDAO#getConceptNumeric(java.lang.Integer)
355          */
356         public ConceptNumeric getConceptNumeric(Integer i) {
357                 ConceptNumeric cn;
358                 Object obj = sessionFactory.getCurrentSession().get(ConceptNumeric.class, i);
359                 // If Concept has already been read & cached, we may get back a Concept instead of
360                 // ConceptNumeric.  If this happens, we need to clear the object from the cache
361                 // and re-fetch it as a ConceptNumeric
362                 if (obj != null && !obj.getClass().equals(ConceptNumeric.class)) {
363                         sessionFactory.getCurrentSession().evict(obj); // remove from cache
364                         // session.get() did not work here, we need to perform a query to get a ConceptNumeric
365                         Query query = sessionFactory.getCurrentSession().createQuery("from ConceptNumeric where conceptId = :conceptId")
366                                 .setParameter("conceptId", i);
367                         obj = query.uniqueResult();
368                 }
369                 cn = (ConceptNumeric)obj;
370
371                 return cn;
372         }
373
374         /**
375          * @see org.openmrs.api.db.ConceptDAO#getConcepts(java.lang.String, java.util.Locale, boolean, java.util.List, java.util.List)
376          */
377         @SuppressWarnings("unchecked")
378         public List<Concept> getConcepts(String name, Locale loc, boolean searchOnPhrase, List<ConceptClass> classes, List<ConceptDatatype> datatypes) throws DAOException {
379                
380                 Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Concept.class);
381                
382                 criteria.add(Expression.eq("retired", false));
383                
384                 if (name != null) {
385                         if (loc == null)
386                                 throw new DAOException("Locale must be not null");
387                        
388                         criteria.createAlias("names", "names");
389                         MatchMode matchmode = MatchMode.EXACT;
390                         if (searchOnPhrase)
391                                 matchmode = MatchMode.ANYWHERE;
392                        
393                         criteria.add(Expression.like("names.name", name, matchmode));
394                         criteria.add(Expression.eq("names.locale", loc.getLanguage().substring(0, 2)));
395                 }
396                
397                 if (classes.size() > 0)
398                         criteria.add(Expression.in("conceptClass", classes));
399                
400                 if (datatypes.size() > 0)
401                         criteria.add(Expression.in("datatype", datatypes));
402                
403                 return criteria.list();
404         }
405        
406         /**
407          * @see org.openmrs.api.db.ConceptDAO#getConceptWords(java.lang.String, java.util.List, boolean, java.util.List, java.util.List, java.util.List, java.util.List, org.openmrs.Concept, java.lang.Integer, java.lang.Integer)
408          */
409         @SuppressWarnings("unchecked")
410         public List<ConceptWord> getConceptWords(String phrase, List<Locale> locales, boolean includeRetired,
411                         List<ConceptClass> requireClasses, List<ConceptClass> excludeClasses,
412                         List<ConceptDatatype> requireDatatypes, List<ConceptDatatype> excludeDatatypes, Concept answersToConcept,
413                         Integer start, Integer size) throws DAOException
414                         {
415                
416                 //add the language-only portion of locale if its not in the list of locales already
417                 List<Locale> localesToAdd = new Vector<Locale>();
418                 for (Locale locale : locales) {
419                         Locale languageOnly = new Locale(locale.getLanguage());
420                         if (locales.contains(languageOnly) == false)
421                                 localesToAdd.add(languageOnly);
422                 }
423                
424                 locales.addAll(localesToAdd);
425                
426                 //String locale = loc.getLanguage().substring(0, 2);           
427                 List<String> words = ConceptWord.getUniqueWords(phrase); //assumes getUniqueWords() removes quote(') characters.  (otherwise we would have a security leak)
428                
429                 // these are the answers to restrict on
430                 List<Concept> answers = new Vector<Concept>();
431                
432                 if (answersToConcept != null && answersToConcept.getAnswers() != null) {
433                         for (ConceptAnswer conceptAnswer : answersToConcept.getAnswers()) {
434                                 answers.add(conceptAnswer.getAnswerConcept());
435                         }
436                 }
437                
438                 List<ConceptWord> conceptWords = new Vector<ConceptWord>();
439                
440                 if (words.size() > 0 || !answers.isEmpty()) {
441                
442                         Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(ConceptWord.class, "cw1");
443                         searchCriteria.add(Expression.in("locale", locales));
444                        
445                         if (includeRetired == false) {
446                                 searchCriteria.createAlias("concept", "concept");
447                                 searchCriteria.add(Expression.eq("concept.retired", false));
448                         }
449                        
450                         // Only restrict on answers if there are any
451                         if (!answers.isEmpty())
452                                 searchCriteria.add(Expression.in("cw1.concept", answers));
453                        
454                         if (words.size() > 0) {
455                                 Iterator<String> word = words.iterator();
456                                 searchCriteria.add(Expression.like("word", word.next(), MatchMode.START));
457                                 Conjunction junction = Expression.conjunction();
458                                 while (word.hasNext()) {
459                                         String w = word.next();
460                                        
461                                         if (log.isDebugEnabled())
462                                                 log.debug("Current word: " + w);
463                                        
464                                         DetachedCriteria crit = DetachedCriteria.forClass(ConceptWord.class)
465                                                                 .setProjection(Property.forName("concept"))
466                                                                 .add(Expression.eqProperty("concept", "cw1.concept"))
467                                                                 .add(Restrictions.like("word", w, MatchMode.START))
468                                                                 .add(Expression.in("locale", locales));
469                                         junction.add(Subqueries.exists(crit));
470                                 }
471                                 searchCriteria.add(junction);
472                         }
473                        
474                         if (requireClasses.size() > 0)
475                                 searchCriteria.add(Expression.in("concept.conceptClass", requireClasses));
476                        
477                         if (excludeClasses.size() > 0)
478                                 searchCriteria.add(Expression.not(Expression.in("concept.conceptClass", excludeClasses)));
479                        
480                         if (requireDatatypes.size() > 0)
481                                 searchCriteria.add(Expression.in("concept.datatype", requireDatatypes));
482                        
483                         if (excludeDatatypes.size() > 0)
484                                 searchCriteria.add(Expression.not(Expression.in("concept.datatype", excludeDatatypes)));
485                        
486                         searchCriteria.addOrder(Order.asc("synonym"));
487                         conceptWords = searchCriteria.list();
488                        
489                         // trim down the list
490                         // TODO: put this in the criteria object?
491                         if (start != null && size != null){
492                                 List<ConceptWord> subList = conceptWords.subList(start, start + size);
493                                 return subList;
494                         }
495                 }
496                
497                 if (log.isDebugEnabled())
498                         log.debug("ConceptWords found: " + conceptWords.size());
499                
500                 return conceptWords;
501         }
502        
503         /**
504          * gets questions for the given answer concept
505          * @see org.openmrs.api.db.ConceptDAO#getConceptsByAnswer(org.openmrs.Concept)
506          */
507         @SuppressWarnings("unchecked")
508         public List<Concept> getConceptsByAnswer(Concept concept) {
509                 // TODO broken until Hibernate fixes component and HQL code
510                 String q = "select c from Concept c join c.answers ca where ca.answerConcept = :answer";
511                 Query query = sessionFactory.getCurrentSession().createQuery(q);
512                 query.setParameter("answer", concept);
513                
514                 return query.list();
515         }
516        
517         /**
518          * @see org.openmrs.api.db.ConceptDAO#getPrevConcept(org.openmrs.Concept)
519          */
520         @SuppressWarnings("unchecked")
521         public Concept getPrevConcept(Concept c) {
522                 Integer i = c.getConceptId();
523                
524                 List<Concept> concepts = sessionFactory.getCurrentSession().createCriteria(Concept.class)
525                                 .add(Expression.lt("conceptId", i))
526                                 .addOrder(Order.desc("conceptId"))
527                                 .setFetchSize(1)
528                                 .list();
529                
530                 if (concepts.size() < 1)
531                         return null;
532                 return concepts.get(0);
533         }
534
535         /**
536          * @see org.openmrs.api.db.ConceptDAO#getNextConcept(org.openmrs.Concept)
537          */
538         @SuppressWarnings("unchecked")
539         public Concept getNextConcept(Concept c) {
540                 Integer i = c.getConceptId();
541                
542                 List<Concept> concepts = sessionFactory.getCurrentSession().createCriteria(Concept.class)
543                                 .add(Expression.gt("conceptId", i))
544                                 .addOrder(Order.asc("conceptId"))
545                                 .setFetchSize(1)
546                                 .list();
547
548                 if (concepts.size() < 1)
549                         return null;
550                 return concepts.get(0);
551         }
552
553         /**
554          * @see org.openmrs.api.db.ConceptDAO#getNextAvailableId()
555          */
556         @SuppressWarnings("unchecked")
557         public Integer getNextAvailableId() {
558                 Query query = sessionFactory.getCurrentSession()
559                                             .createQuery("select distinct conceptId from Concept order by concept_id");
560                 List<Object> conceptIds = query.list();
561                 Integer maxConceptId = 0;
562                 if (conceptIds.size() != 0)
563                 maxConceptId = (Integer) conceptIds.get(conceptIds.size() - 1);
564                 Integer ret = maxConceptId + 1;
565                 boolean found = true;
566                 for (Integer i = 1; i <= maxConceptId; i++) {
567                         if (found = true){
568                                 found = false;
569                                 for (Object tmpObj : conceptIds) {
570                                         Integer tmpId = (Integer) tmpObj;
571                                         if (i == tmpId || i.equals(tmpId)){
572                                                 found = true;
573                                                 break;
574                                         }
575                                 }
576                                 if (found == false) {
577                                         ret = i;
578                                         break;
579                                 }
580                         }       
581                 }
582                 return ret;
583         }
584        
585         /**
586          * @see org.openmrs.api.db.ConceptDAO#getConceptsWithDrugsInFormulary()
587          */
588         @SuppressWarnings("unchecked")
589         public List<Concept> getConceptsWithDrugsInFormulary() {
590                 Query query = sessionFactory.getCurrentSession().createQuery("select distinct concept from Drug d where d.retired = false");
591                 return query.list();
592         }
593        
594         /**
595          * @see org.openmrs.api.db.ConceptDAO#purgeDrug(org.openmrs.Drug)
596          */
597         public void purgeDrug(Drug drug) throws DAOException {
598                 sessionFactory.getCurrentSession().delete(drug);
599         }
600        
601         /**
602          * @see org.openmrs.api.db.ConceptDAO#updateConceptWord(org.openmrs.Concept)
603          */
604         public void updateConceptWord(Concept concept) throws DAOException {
605                 if (concept != null) {
606                         // remove all old words
607                         if (concept.getConceptId() != null && concept.getConceptId() > 0)
608                                
609                         deleteConceptWord(concept);
610                        
611                         // add all new words
612                         Collection<ConceptWord> words = ConceptWord.makeConceptWords(concept);
613                         log.debug("words: " + words);
614                         for (ConceptWord word : words) {
615                                 try {
616                                         sessionFactory.getCurrentSession().save(word);
617                                 }
618                                 catch (NonUniqueObjectException e) {
619                                         ConceptWord tmp  = (ConceptWord)sessionFactory.getCurrentSession().merge(word);
620                                         sessionFactory.getCurrentSession().evict(tmp);
621                                         sessionFactory.getCurrentSession().save(word);
622                                 }
623                         }
624                 }
625         }
626        
627         /**
628          *
629          * Deletes all concept words for a concept.  Called by updateConceptWord()
630          *
631          * @param concept
632          * @throws DAOException
633          */
634         @SuppressWarnings("unchecked")
635     private void deleteConceptWord(Concept concept) throws DAOException {
636                 if (concept != null) {
637                         Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptWord.class);
638                         crit.add(Expression.eq("concept", concept));
639                        
640                         List<ConceptWord> words = crit.list();
641                        
642                         Integer authUserId = null;
643                         if (Context.isAuthenticated())
644                                 authUserId = Context.getAuthenticatedUser().getUserId();
645                        
646                         log.debug(authUserId + "|ConceptWord|" + words);
647                        
648                         sessionFactory.getCurrentSession().createQuery("delete from ConceptWord where concept_id = :c")
649                                         .setInteger("c", concept.getConceptId())
650                                         .executeUpdate();
651                 }
652         }
653        
654         /**
655          * @see org.openmrs.api.db.ConceptDAO#saveConceptProposal(org.openmrs.ConceptProposal)
656          */
657         public ConceptProposal saveConceptProposal(ConceptProposal cp) throws DAOException {
658                         sessionFactory.getCurrentSession().saveOrUpdate(cp);
659                         return cp;
660         }
661        
662         /**
663          * @see org.openmrs.api.db.ConceptDAO#purgeConceptProposal(org.openmrs.ConceptProposal)
664          */
665         public void purgeConceptProposal(ConceptProposal cp) throws DAOException {
666                         sessionFactory.getCurrentSession().delete(cp);
667                         return;
668         }
669        
670         /**
671          * @see org.openmrs.api.db.ConceptDAO#getAllConceptProposals(boolean)
672          */
673         @SuppressWarnings("unchecked")
674         public List<ConceptProposal> getAllConceptProposals(boolean includeCompleted) throws DAOException {
675                 Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class);
676                
677                 if (includeCompleted == false) {
678                         crit.add(Expression.eq("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED));
679                 }
680                 crit.addOrder(Order.asc("originalText"));
681                 return crit.list();
682         }
683        
684         /**
685          * @see org.openmrs.api.db.ConceptDAO#getConceptProposal(java.lang.Integer)
686          */
687         public ConceptProposal getConceptProposal(Integer conceptProposalId) throws DAOException {
688                 return (ConceptProposal)sessionFactory.getCurrentSession().get(ConceptProposal.class, conceptProposalId);
689         }
690        
691         /**
692          * @see org.openmrs.api.db.ConceptDAO#getConceptProposals(java.lang.String)
693          */
694         @SuppressWarnings("unchecked")
695         public List<ConceptProposal> getConceptProposals(String text) throws DAOException {
696                 Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class);
697                 crit.add(Expression.eq("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED));
698                 crit.add(Expression.eq("originalText", text)); 
699                 return crit.list();
700         }
701        
702         /**
703          * @see org.openmrs.api.db.ConceptDAO#getProposedConcepts(java.lang.String)
704          */
705         @SuppressWarnings("unchecked")
706         public List<Concept> getProposedConcepts(String text) throws  DAOException {
707                 Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class);
708                 crit.add(Expression.ne("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED));
709                 crit.add(Expression.eq("originalText", text));
710                 crit.add(Expression.isNotNull("mappedConcept"));
711                 crit.setProjection(Projections.distinct(Projections.property("mappedConcept")));
712                
713                 return crit.list();
714         }
715        
716         /**
717          * @see org.openmrs.api.db.ConceptDAO#getConceptSetsByConcept(org.openmrs.Concept)
718          */
719         @SuppressWarnings("unchecked")
720         public List<ConceptSet> getConceptSetsByConcept(Concept concept) {
721                 return sessionFactory.getCurrentSession().createCriteria(ConceptSet.class)
722                                         .add(Restrictions.eq("conceptSet", concept))
723                                         .addOrder(Order.asc("sortWeight"))
724                                         .list();
725         }
726        
727         /**
728          * @see org.openmrs.api.db.ConceptDAO#getSetsContainingConcept(org.openmrs.Concept)
729          */
730         @SuppressWarnings("unchecked")
731         public List<ConceptSet> getSetsContainingConcept(Concept concept) {
732                 return sessionFactory.getCurrentSession().createCriteria(ConceptSet.class)
733                                         .add(Restrictions.eq("concept", concept))
734                                         .list();
735         }
736        
737         //TODO:  eventually, this method should probably just run updateConceptSetDerived(Concept) inside an iteration of all concepts... (or something else less transactionally-intense)
738         /**
739          * @see org.openmrs.api.db.ConceptDAO#updateConceptSetDerived()
740          */
741         public void updateConceptSetDerived() throws DAOException {
742                 sessionFactory.getCurrentSession().createQuery("delete from ConceptSetDerived").executeUpdate();
743                 try {
744                         // remake the derived table by copying over the basic concept_set table
745                         sessionFactory.getCurrentSession().connection().prepareStatement("insert into concept_set_derived (concept_id, concept_set, sort_weight) select cs.concept_id, cs.concept_set, cs.sort_weight from concept_set cs where not exists (select concept_id from concept_set_derived csd where csd.concept_id = cs.concept_id and csd.concept_set = cs.concept_set)").execute();
746                
747                         // burst the concept sets -- make grandchildren direct children of grandparents
748                         sessionFactory.getCurrentSession().connection().prepareStatement("insert into concept_set_derived (concept_id, concept_set, sort_weight) select cs1.concept_id, cs2.concept_set, cs1.sort_weight from concept_set cs1 join concept_set cs2 where cs2.concept_id = cs1.concept_set and not exists (select concept_id from concept_set_derived csd where csd.concept_id = cs1.concept_id and csd.concept_set = cs2.concept_set)").execute();
749                        
750                         // burst the concept sets -- make greatgrandchildren direct child of greatgrandparents
751                         sessionFactory.getCurrentSession().connection().prepareStatement("insert into concept_set_derived (concept_id, concept_set, sort_weight) select cs1.concept_id, cs3.concept_set, cs1.sort_weight from concept_set cs1 join concept_set cs2 join concept_set cs3 where cs1.concept_set = cs2.concept_id and cs2.concept_set = cs3.concept_id and not exists (select concept_id from concept_set_derived csd where csd.concept_id = cs1.concept_id and csd.concept_set = cs3.concept_set)").execute();
752                        
753                         // TODO This 'algorithm' only solves three layers of children.  Options for correction:
754                         //      1) Add a few more join statements to cover 5 layers (conceivable upper limit of layers)
755                         //      2) Find the deepest layer and programmatically create the sql statements
756                         //      3) Run the joins on
757                 }
758                 catch (SQLException e) {
759                         throw new DAOException (e);
760                 }
761         }
762        
763        
764         /**
765          * utility method used in updateConceptSetDerived(...)
766          *
767          * @param List of parent Concept objects
768          * @param Concept current
769          * @return Set of ConceptSetDerived
770          */
771         private Set<ConceptSetDerived> deriveChildren(List<Concept> parents, Concept current) {
772                 Set<ConceptSetDerived> updates = new HashSet<ConceptSetDerived>();
773                
774                 ConceptSetDerived derivedSet = null;
775                 // make each child a direct child of each parent/grandparent
776                 for (ConceptSet childSet : current.getConceptSets()) {
777                         Concept child = childSet.getConcept();
778                         log.debug("Deriving child: " + child.getConceptId());
779                         Double sort_weight = childSet.getSortWeight();
780                         for (Concept parent : parents) {
781                                 log.debug("Matching child: " + child.getConceptId() + " with parent: " + parent.getConceptId());
782                                 derivedSet = new ConceptSetDerived(parent, child, sort_weight++);
783                                 updates.add(derivedSet);
784                         }
785                        
786                         //recurse if this child is a set as well
787                         if (child.isSet()) {
788                                 log.debug("Concept id: " + child.getConceptId() + " is a set");
789                                 List<Concept> new_parents = new Vector<Concept>();
790                                 new_parents.addAll(parents);
791                                 new_parents.add(child);
792                                 updates.addAll(deriveChildren(new_parents, child));
793                         }
794                 }