View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krms.impl.repository;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.criteria.QueryByCriteria;
20  import org.kuali.rice.core.api.criteria.QueryResults;
21  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
22  import org.kuali.rice.krad.data.DataObjectService;
23  import org.kuali.rice.krad.data.PersistenceOption;
24  import org.kuali.rice.krad.service.KRADServiceLocator;
25  import org.kuali.rice.krms.api.repository.term.TermDefinition;
26  import org.kuali.rice.krms.api.repository.term.TermResolverDefinition;
27  import org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition;
28  import org.kuali.rice.krms.impl.util.KrmsImplConstants;
29  import org.springframework.util.CollectionUtils;
30  
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.List;
35  import java.util.Map;
36  
37  import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findMatching;
38  import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findSingleMatching;
39  
40  /**
41   * Implementation of {@link TermBoService}
42   *
43   * @author Kuali Rice Team (rice.collab@kuali.org)
44   */
45  public class TermBoServiceImpl implements TermBoService {
46  
47      private DataObjectService dataObjectService;
48  
49      /**
50       * @see org.kuali.rice.krms.impl.repository.TermBoService#getTermSpecificationById(java.lang.String)
51       */
52      @Override
53      public TermSpecificationDefinition getTermSpecificationById(String id) {
54          TermSpecificationDefinition result = null;
55  
56          if (StringUtils.isBlank(id)) {
57              throw new RiceIllegalArgumentException("id must not be blank or null");
58          }
59  
60          TermSpecificationBo termSpecificationBo = getDataObjectService().find(TermSpecificationBo.class, id);
61  
62          if (termSpecificationBo != null) {
63              List<ContextValidTermBo> contextValidTermBos =
64                      findMatching(getDataObjectService(), ContextValidTermBo.class,
65                              Collections.singletonMap("termSpecification.id", termSpecificationBo.getId()));
66  
67              if (contextValidTermBos != null) for (ContextValidTermBo contextValidTerm : contextValidTermBos) {
68                  termSpecificationBo.getContextIds().add(contextValidTerm.getContextId());
69              }
70  
71              result = TermSpecificationDefinition.Builder.create(termSpecificationBo).build();
72          }
73  
74          return result;
75      }
76  
77      /**
78       * @see org.kuali.rice.krms.impl.repository.TermBoService#createTermSpecification(org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition)
79       */
80      @Override
81      public TermSpecificationDefinition createTermSpecification(TermSpecificationDefinition termSpec) {
82          if (!StringUtils.isBlank(termSpec.getId())) {
83              throw new RiceIllegalArgumentException("for creation, TermSpecification.id must be null");
84          }
85  
86          TermSpecificationBo termSpecBo = TermSpecificationBo.from(termSpec);
87  
88          // save relations to the contexts on the BO
89          if (!CollectionUtils.isEmpty(termSpec.getContextIds())) {
90              for (String contextId : termSpec.getContextIds()) {
91                  ContextValidTermBo contextValidTerm = new ContextValidTermBo();
92                  contextValidTerm.setContextId(contextId);
93                  contextValidTerm.setTermSpecification(termSpecBo);
94  
95                  termSpecBo.getContextValidTerms().add(contextValidTerm);
96              }
97          }
98  
99          termSpecBo = getDataObjectService().save(termSpecBo, PersistenceOption.FLUSH);
100 
101         return TermSpecificationBo.to(termSpecBo);
102     }
103 
104     @Override
105     public void updateTermSpecification(TermSpecificationDefinition termSpec) throws RiceIllegalArgumentException {
106         if (termSpec == null) {
107             throw new IllegalArgumentException("term specification is null");
108         }
109 
110         // must already exist to be able to update
111         final String termSpecificationId = termSpec.getId();
112         final TermSpecificationBo existing = getDataObjectService().find(TermSpecificationBo.class, termSpecificationId);
113 
114         if (existing == null) {
115             throw new IllegalStateException("the term specification does not exist: " + termSpec);
116         }
117 
118         final TermSpecificationDefinition toUpdate;
119 
120         if (!existing.getId().equals(termSpec.getId())) {
121             // if passed in id does not match existing id, correct it
122             final TermSpecificationDefinition.Builder builder = TermSpecificationDefinition.Builder.create(termSpec);
123             builder.setId(existing.getId());
124             toUpdate = builder.build();
125         } else {
126             toUpdate = termSpec;
127         }
128 
129         // copy all updateable fields to bo
130         TermSpecificationBo boToUpdate = TermSpecificationBo.from(toUpdate);
131         reconcileContextValidTerms(existing, boToUpdate);
132 
133         // update the rule and create new attributes
134         getDataObjectService().save(boToUpdate, PersistenceOption.FLUSH);
135     }
136 
137     /**
138      * Transfer any ContextValidTermBos that still apply from existing to boToUpdate, and create new ContextValidTermBos
139      * for any new context IDs that are found in boToUpdate.
140      *
141      * <p>This method is side effecting, it makes modifications to boToUpdate.contextValidTerms. </p>
142      *
143      * @param existing the TermSpecificationBo which has been fetched from the database
144      * @param boToUpdate the new TermSpecificationBo which will (later) be persisted
145      */
146     private void reconcileContextValidTerms(TermSpecificationBo existing,
147             TermSpecificationBo boToUpdate) {
148 
149         // add all contextValidTerms that still apply
150         for (ContextValidTermBo contextValidTerm : existing.getContextValidTerms()) {
151             if (boToUpdate.getContextIds().contains(contextValidTerm.getContextId())) {
152                 boToUpdate.getContextValidTerms().add(contextValidTerm);
153             }
154         }
155 
156         // add new contextValidTerms for new context IDs
157         for (String contextId : boToUpdate.getContextIds()) {
158             boolean alreadyInContextValidTerms = false;
159 
160             for (ContextValidTermBo contextValidTerm : boToUpdate.getContextValidTerms()) {
161                 if (contextId.equals(contextValidTerm.getContextId())) {
162                     alreadyInContextValidTerms = true;
163                     break;
164                 }
165             }
166 
167             if (!alreadyInContextValidTerms) {
168                 ContextValidTermBo contextValidTerm = new ContextValidTermBo();
169                 contextValidTerm.setContextId(contextId);
170                 contextValidTerm.setTermSpecification(boToUpdate);
171 
172                 boToUpdate.getContextValidTerms().add(contextValidTerm);
173             }
174         }
175     }
176 
177     @Override
178     public void deleteTermSpecification(String id) throws RiceIllegalArgumentException {
179         if (id == null) {
180             throw new RiceIllegalArgumentException("agendaId is null");
181         }
182 
183         final TermSpecificationBo existing = getDataObjectService().find(TermSpecificationBo.class, id);
184 
185         if (existing == null) {
186             throw new IllegalStateException("the TermSpecification to delete does not exists: " + id);
187         }
188 
189         getDataObjectService().delete(existing);
190     }
191 
192     /**
193      * @see org.kuali.rice.krms.impl.repository.TermBoService#createTerm(org.kuali.rice.krms.api.repository.term.TermDefinition)
194      */
195     @Override
196     public TermDefinition createTerm(TermDefinition termDef) {
197         if (!StringUtils.isBlank(termDef.getId())) {
198             throw new RiceIllegalArgumentException("for creation, TermDefinition.id must be null");
199         }
200 
201         TermBo termBo = TermBo.from(termDef);
202         termBo = getDataObjectService().save(termBo, PersistenceOption.FLUSH);
203 
204         return TermBo.to(termBo);
205     }
206 
207     @Override
208     public void updateTerm(TermDefinition term) throws RiceIllegalArgumentException {
209         if (term == null) {
210             throw new IllegalArgumentException("term is null");
211         }
212 
213         // must already exist to be able to update
214         final String termId = term.getId();
215         final TermBo existing = getDataObjectService().find(TermBo.class, termId);
216 
217         if (existing == null) {
218             throw new IllegalStateException("the term resolver does not exist: " + term);
219         }
220 
221         final TermDefinition toUpdate;
222 
223         if (!existing.getId().equals(term.getId())) {
224             // if passed in id does not match existing id, correct it
225             final TermDefinition.Builder builder = TermDefinition.Builder.create(term);
226             builder.setId(existing.getId());
227             toUpdate = builder.build();
228         } else {
229             toUpdate = term;
230         }
231 
232         // copy all updateable fields to bo
233         TermBo boToUpdate = TermBo.from(toUpdate);
234 
235         // update the rule and create new attributes
236         getDataObjectService().save(boToUpdate, PersistenceOption.FLUSH);
237     }
238 
239     @Override
240     public void deleteTerm(String id) throws RiceIllegalArgumentException {
241         if (id == null) {
242             throw new RiceIllegalArgumentException("termId is null");
243         }
244 
245         TermBo existing = getDataObjectService().find(TermBo.class, id);
246 
247         if (existing == null) {
248             throw new IllegalStateException("the term to delete does not exists: " + id);
249         }
250 
251         getDataObjectService().delete(existing);
252     }
253 
254     /**
255      * @see org.kuali.rice.krms.impl.repository.TermBoService#createTermResolver(org.kuali.rice.krms.api.repository.term.TermResolverDefinition)
256      */
257     @Override
258     public TermResolverDefinition createTermResolver(TermResolverDefinition termResolver) {
259         if (!StringUtils.isBlank(termResolver.getId())) {
260             throw new RiceIllegalArgumentException("for creation, TermResolverDefinition.id must be null");
261         }
262 
263         TermResolverBo termResolverBo = TermResolverBo.from(termResolver);
264 
265         termResolverBo = (TermResolverBo) getDataObjectService().save(termResolverBo, PersistenceOption.FLUSH);
266 
267         return TermResolverBo.to(termResolverBo);
268     }
269 
270     @Override
271     public void updateTermResolver(TermResolverDefinition termResolver) throws RiceIllegalArgumentException {
272         if (termResolver == null) {
273             throw new IllegalArgumentException("term resolver is null");
274         }
275 
276         // must already exist to be able to update
277         final String termResolverId = termResolver.getId();
278         final TermResolverBo existing = getDataObjectService().find(TermResolverBo.class, termResolverId);
279 
280         if (existing == null) {
281             throw new IllegalStateException("the term resolver does not exist: " + termResolver);
282         }
283 
284         final TermResolverDefinition toUpdate;
285 
286         if (!existing.getId().equals(termResolver.getId())) {
287             // if passed in id does not match existing id, correct it
288             final TermResolverDefinition.Builder builder = TermResolverDefinition.Builder.create(termResolver);
289             builder.setId(existing.getId());
290             toUpdate = builder.build();
291         } else {
292             toUpdate = termResolver;
293         }
294 
295         // copy all updateable fields to bo
296         TermResolverBo boToUpdate = TermResolverBo.from(toUpdate);
297 
298         // delete any old, existing attributes
299         QueryByCriteria crit =
300                 QueryByCriteria.Builder.forAttribute(KrmsImplConstants.PropertyNames.TermResolver.TERM_RESOLVER_ID, toUpdate.getId()).build();
301 
302         getDataObjectService().deleteMatching(TermResolverAttributeBo.class, crit);
303 
304         // update the rule and create new attributes
305         getDataObjectService().save(boToUpdate, PersistenceOption.FLUSH);
306     }
307 
308     @Override
309     public void deleteTermResolver(String id) throws RiceIllegalArgumentException {
310         if (id == null) {
311             throw new RiceIllegalArgumentException("agendaId is null");
312         }
313 
314         TermSpecificationBo existing = getDataObjectService().find(TermSpecificationBo.class, id);
315 
316         if (existing == null) {
317             throw new IllegalStateException("the TermResolver to delete does not exists: " + id);
318         }
319 
320         getDataObjectService().delete(existing);
321     }
322 
323     /**
324      * @see org.kuali.rice.krms.impl.repository.TermBoService#getTerm(java.lang.String)
325      */
326     @Override
327     public TermDefinition getTerm(String id) {
328         TermDefinition result = null;
329 
330         if (StringUtils.isBlank(id)) {
331             throw new RiceIllegalArgumentException("id must not be blank or null");
332         }
333 
334         TermBo termBo = getDataObjectService().find(TermBo.class, id);
335 
336         if (termBo != null) {
337             result = TermBo.to(termBo);
338         }
339 
340         return result;
341     }
342 
343     /**
344      * @see org.kuali.rice.krms.impl.repository.TermBoService#getTermResolverById(java.lang.String)
345      */
346     @Override
347     public TermResolverDefinition getTermResolverById(String id) {
348         TermResolverDefinition result = null;
349 
350         if (StringUtils.isBlank(id)) {
351             throw new RiceIllegalArgumentException("id must not be blank or null");
352         }
353 
354         TermResolverBo termResolverBo = getDataObjectService().find(TermResolverBo.class, id);
355 
356         if (termResolverBo != null) {
357             result = TermResolverBo.to(termResolverBo);
358         }
359 
360         return result;
361     }
362 
363     @Override
364     public List<TermResolverDefinition> findTermResolversByOutputId(String id, String namespace) {
365         List<TermResolverDefinition> results = null;
366 
367         if (StringUtils.isBlank(id)) {
368             throw new RiceIllegalArgumentException("id must not be blank or null");
369         }
370 
371         if (StringUtils.isBlank(namespace)) {
372             throw new RiceIllegalArgumentException("namespace must not be blank or null");
373         }
374 
375         Map<String, String> critMap = new HashMap<String, String>(2);
376 
377         critMap.put("outputId", id);
378         critMap.put("namespace", namespace);
379 
380         QueryByCriteria crit = QueryByCriteria.Builder.andAttributes(critMap).build();
381 
382         QueryResults<TermResolverBo> termResolverBos = getDataObjectService().findMatching(TermResolverBo.class, crit);
383 
384         if (!CollectionUtils.isEmpty(termResolverBos.getResults())) {
385             results = new ArrayList<TermResolverDefinition>(termResolverBos.getResults().size());
386 
387             for (TermResolverBo termResolverBo : termResolverBos.getResults()) {
388                 results.add(TermResolverBo.to(termResolverBo));
389             }
390         } else {
391             results = Collections.emptyList();
392         }
393 
394         return results;
395     }
396 
397     @Override
398     public List<TermResolverDefinition> findTermResolversByNamespace(String namespace) {
399         List<TermResolverDefinition> results = null;
400 
401         if (StringUtils.isBlank(namespace)) {
402             throw new RiceIllegalArgumentException("namespace must not be blank or null");
403         }
404 
405         QueryByCriteria crit = QueryByCriteria.Builder.forAttribute("namespace", namespace).build();
406 
407         QueryResults<TermResolverBo> termResolverBos = getDataObjectService().findMatching(TermResolverBo.class, crit);
408 
409         if (!CollectionUtils.isEmpty(termResolverBos.getResults())) {
410             results = new ArrayList<TermResolverDefinition>(termResolverBos.getResults().size());
411 
412             for (TermResolverBo termResolverBo : termResolverBos.getResults()) {
413                 if (termResolverBo != null) {
414                     results.add(TermResolverBo.to(termResolverBo));
415                 }
416             }
417         } else {
418             results = Collections.emptyList();
419         }
420 
421         return results;
422     }
423 
424     @Override
425     public TermResolverDefinition getTermResolverByNameAndNamespace(String name,
426             String namespace) throws RiceIllegalArgumentException {
427         if (StringUtils.isBlank(name)) {
428             throw new IllegalArgumentException("name is null or blank");
429         }
430 
431         if (StringUtils.isBlank(namespace)) {
432             throw new IllegalArgumentException("namespace is null or blank");
433         }
434 
435         final Map<String, Object> map = new HashMap<String, Object>();
436         map.put("name", name);
437         map.put("namespace", namespace);
438         TermResolverBo bo = getDataObjectService().find(TermResolverBo.class, map);
439 
440         return TermResolverBo.to(bo);
441     }
442 
443     @Override
444     public TermSpecificationDefinition getTermSpecificationByNameAndNamespace(String name,
445             String namespace) throws RiceIllegalArgumentException {
446         if (StringUtils.isBlank(name)) {
447             throw new IllegalArgumentException("name is null or blank");
448         }
449 
450         if (StringUtils.isBlank(namespace)) {
451             throw new IllegalArgumentException("namespace is null or blank");
452         }
453 
454         final Map<String, Object> map = new HashMap<String, Object>();
455         map.put("name", name);
456         map.put("namespace", namespace);
457         TermSpecificationBo bo = findSingleMatching(getDataObjectService(), TermSpecificationBo.class, map);
458 
459         return TermSpecificationBo.to(bo);
460     }
461 
462     @Override
463     public List<TermSpecificationDefinition> findAllTermSpecificationsByContextId(String contextId) {
464         List<TermSpecificationDefinition> results = null;
465 
466         if (StringUtils.isBlank(contextId)) {
467             throw new RiceIllegalArgumentException("contextId must not be blank or null");
468         }
469 
470         QueryByCriteria crit = QueryByCriteria.Builder.forAttribute("contextId", contextId).build();
471 
472         QueryResults<ContextValidTermBo> contextValidTerms =
473                 getDataObjectService().findMatching(ContextValidTermBo.class, crit);
474 
475         if (!CollectionUtils.isEmpty(contextValidTerms.getResults())) {
476             results = new ArrayList<TermSpecificationDefinition>(contextValidTerms.getResults().size());
477 
478             for (ContextValidTermBo validTerm : contextValidTerms.getResults()) {
479                 results.add(TermSpecificationBo.to(validTerm.getTermSpecification()));
480             }
481         } else {
482             results = Collections.emptyList();
483         }
484 
485         return results;
486     }
487 
488     /**
489      * Gets the {@link DataObjectService}.
490      *
491      * @return the {@link DataObjectService}
492      */
493     public DataObjectService getDataObjectService() {
494         if (dataObjectService == null) {
495             dataObjectService = KRADServiceLocator.getDataObjectService();
496         }
497 
498         return dataObjectService;
499     }
500 
501     /**
502      * Sets the {@link DataObjectService}.
503      *
504      * @param dataObjectService the {@link DataObjectService} to set
505      */
506     public void setDataObjectService(DataObjectService dataObjectService) {
507         this.dataObjectService = dataObjectService;
508     }
509 
510 }