Merge branch 'release/4.7.0'
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / TermServiceImpl.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.api.service;
11
12 import java.net.URI;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Enumeration;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Locale;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.UUID;
23
24 import org.apache.commons.lang.StringUtils;
25 import org.apache.log4j.Logger;
26 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.beans.factory.annotation.Qualifier;
28 import org.springframework.stereotype.Service;
29 import org.springframework.transaction.annotation.Transactional;
30
31 import eu.etaxonomy.cdm.api.service.UpdateResult.Status;
32 import eu.etaxonomy.cdm.api.service.config.DeleteConfiguratorBase;
33 import eu.etaxonomy.cdm.api.service.config.TermDeletionConfigurator;
34 import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
35 import eu.etaxonomy.cdm.api.service.exception.ReferencedObjectUndeletableException;
36 import eu.etaxonomy.cdm.api.service.pager.Pager;
37 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
38 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
39 import eu.etaxonomy.cdm.model.common.CdmBase;
40 import eu.etaxonomy.cdm.model.common.DefinedTermBase;
41 import eu.etaxonomy.cdm.model.common.Language;
42 import eu.etaxonomy.cdm.model.common.LanguageString;
43 import eu.etaxonomy.cdm.model.common.LanguageStringBase;
44 import eu.etaxonomy.cdm.model.common.Representation;
45 import eu.etaxonomy.cdm.model.common.TermBase;
46 import eu.etaxonomy.cdm.model.common.TermType;
47 import eu.etaxonomy.cdm.model.common.TermVocabulary;
48 import eu.etaxonomy.cdm.model.location.NamedArea;
49 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
50 import eu.etaxonomy.cdm.model.location.NamedAreaType;
51 import eu.etaxonomy.cdm.model.media.Media;
52 import eu.etaxonomy.cdm.persistence.dao.common.IDefinedTermDao;
53 import eu.etaxonomy.cdm.persistence.dao.common.ILanguageStringBaseDao;
54 import eu.etaxonomy.cdm.persistence.dao.common.ILanguageStringDao;
55 import eu.etaxonomy.cdm.persistence.dao.common.IRepresentationDao;
56 import eu.etaxonomy.cdm.persistence.query.OrderHint;
57 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
58
59 @Service
60 @Transactional(readOnly = true)
61 public class TermServiceImpl extends IdentifiableServiceBase<DefinedTermBase,IDefinedTermDao> implements ITermService{
62 @SuppressWarnings("unused")
63 private static final Logger logger = Logger.getLogger(TermServiceImpl.class);
64
65 private ILanguageStringDao languageStringDao;
66
67 @Autowired
68 @Qualifier("langStrBaseDao")
69 private ILanguageStringBaseDao languageStringBaseDao;
70 private IRepresentationDao representationDao;
71
72 @Autowired
73 public void setLanguageStringDao(ILanguageStringDao languageStringDao) {
74 this.languageStringDao = languageStringDao;
75 }
76
77 @Autowired
78 public void setRepresentationDao(IRepresentationDao representationDao) {
79 this.representationDao = representationDao;
80 }
81
82 @Override
83 @Autowired
84 protected void setDao(IDefinedTermDao dao) {
85 this.dao = dao;
86 }
87
88 @Override
89 public <T extends DefinedTermBase> List<T> listByTermType(TermType termType, Integer limit, Integer start,
90 List<OrderHint> orderHints, List<String> propertyPaths) {
91 return dao.listByTermType(termType, limit, start, orderHints, propertyPaths);
92 }
93
94 @Override
95 public DefinedTermBase getByUri(URI uri) {
96 return dao.findByUri(uri);
97 }
98
99 @Override
100 public Language getLanguageByIso(String iso639) {
101 return dao.getLanguageByIso(iso639);
102 }
103
104 @Override
105 public Language getLanguageByLabel(String label) {
106 return Language.getLanguageByLabel(label);
107 }
108
109 @Override
110 public List<Language> getLanguagesByLocale(Enumeration<Locale> locales){
111 return dao.getLanguagesByLocale(locales);
112 }
113
114 @Override
115 public <TERM extends DefinedTermBase> TERM findByIdInVocabulary(String id, UUID vocabularyUuid, Class<TERM> clazz) throws IllegalArgumentException {
116 List<TERM> list = dao.getDefinedTermByIdInVocabulary(id, vocabularyUuid, clazz, null, null);
117 if (list.isEmpty()){
118 return null;
119 }else if (list.size() == 1){
120 return list.get(0);
121 }else{
122 String message = "There is more then 1 (%d) term with the same id in vocabulary. This is forbidden. Check the state of your database.";
123 throw new IllegalStateException(String.format(message, list.size()));
124 }
125 }
126
127
128 @Override
129 public NamedArea getAreaByTdwgAbbreviation(String tdwgAbbreviation) {
130 if (StringUtils.isBlank(tdwgAbbreviation)){ //TDWG areas should always have a label
131 return null;
132 }
133 List<NamedArea> list = dao.getDefinedTermByIdInVocabulary(tdwgAbbreviation, NamedArea.uuidTdwgAreaVocabulary, NamedArea.class, null, null);
134 if (list.isEmpty()){
135 return null;
136 }else if (list.size() == 1){
137 return list.get(0);
138 }else{
139 String message = "There is more then 1 (%d) TDWG area with the same abbreviated label. This is forbidden. Check the state of your database.";
140 throw new IllegalStateException(String.format(message, list.size()));
141 }
142 }
143
144 @Override
145 public <T extends DefinedTermBase> Pager<T> getGeneralizationOf(T definedTerm, Integer pageSize, Integer pageNumber) {
146 Integer numberOfResults = dao.countGeneralizationOf(definedTerm);
147
148 List<T> results = new ArrayList<T>();
149 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
150 results = dao.getGeneralizationOf(definedTerm, pageSize, pageNumber);
151 }
152
153 return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);
154 }
155
156 @Override
157 public <T extends DefinedTermBase> Pager<T> getIncludes(Collection<T> definedTerms, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
158 Integer numberOfResults = dao.countIncludes(definedTerms);
159
160 List<T> results = new ArrayList<T>();
161 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
162 results = dao.getIncludes(definedTerms, pageSize, pageNumber,propertyPaths);
163 }
164
165 return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);
166 }
167
168 @Override
169 public Pager<Media> getMedia(DefinedTermBase definedTerm, Integer pageSize, Integer pageNumber) {
170 Integer numberOfResults = dao.countMedia(definedTerm);
171
172 List<Media> results = new ArrayList<Media>();
173 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
174 results = dao.getMedia(definedTerm, pageSize, pageNumber);
175 }
176
177 return new DefaultPagerImpl<Media>(pageNumber, numberOfResults, pageSize, results);
178 }
179
180 @Override
181 public <T extends DefinedTermBase> Pager<T> getPartOf(Set<T> definedTerms,Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
182 Integer numberOfResults = dao.countPartOf(definedTerms);
183
184 List<T> results = new ArrayList<T>();
185 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
186 results = dao.getPartOf(definedTerms, pageSize, pageNumber, propertyPaths);
187 }
188
189 return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);
190 }
191
192 @Override
193 public Pager<NamedArea> list(NamedAreaLevel level, NamedAreaType type, Integer pageSize, Integer pageNumber,
194 List<OrderHint> orderHints, List<String> propertyPaths) {
195 Integer numberOfResults = dao.count(level, type);
196
197 List<NamedArea> results = new ArrayList<NamedArea>();
198 if (numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
199 results = dao.list(level, type, pageSize, pageNumber, orderHints, propertyPaths);
200 }
201
202 return new DefaultPagerImpl<NamedArea>(pageNumber, numberOfResults, pageSize, results);
203 }
204
205 @Override
206 public <T extends DefinedTermBase> Pager<T> findByRepresentationText(String label, Class<T> clazz, Integer pageSize, Integer pageNumber) {
207 Integer numberOfResults = dao.countDefinedTermByRepresentationText(label,clazz);
208
209 List<T> results = new ArrayList<T>();
210 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
211 results = dao.getDefinedTermByRepresentationText(label, clazz, pageSize, pageNumber);
212 }
213
214 return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);
215 }
216
217 @Override
218 public <T extends DefinedTermBase> Pager<T> findByRepresentationAbbreviation(String abbrev, Class<T> clazz, Integer pageSize, Integer pageNumber) {
219 Integer numberOfResults = dao.countDefinedTermByRepresentationAbbrev(abbrev,clazz);
220
221 List<T> results = new ArrayList<T>();
222 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
223 results = dao.getDefinedTermByRepresentationAbbrev(abbrev, clazz, pageSize, pageNumber);
224 }
225
226 return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);
227 }
228
229 @Override
230 public List<LanguageString> getAllLanguageStrings(int limit, int start) {
231 return languageStringDao.list(limit, start);
232 }
233
234 @Override
235 public List<Representation> getAllRepresentations(int limit, int start) {
236 return representationDao.list(limit,start);
237 }
238
239 @Override
240 public UUID saveLanguageData(LanguageStringBase languageData) {
241 return languageStringBaseDao.save(languageData).getUuid();
242 }
243
244
245 /** @deprecated use {@link #delete(DefinedTermBase, TermDeletionConfigurator)} instead
246 * to allow DeleteResult return type*/
247 @Override
248 @Deprecated
249 public DeleteResult delete(DefinedTermBase term){
250 DeleteResult result = new DeleteResult();
251
252 TermDeletionConfigurator defaultConfig = new TermDeletionConfigurator();
253 result = delete(term, defaultConfig);
254 return result;
255 }
256
257 @Override
258 @Deprecated
259 @Transactional(readOnly = false)
260 public DeleteResult delete(UUID termUuid){
261 DeleteResult result = new DeleteResult();
262
263 TermDeletionConfigurator defaultConfig = new TermDeletionConfigurator();
264 result = delete(dao.load(termUuid), defaultConfig);
265 return result;
266 }
267
268 @Override
269 public DeleteResult delete(DefinedTermBase term, TermDeletionConfigurator config){
270 if (config == null){
271 config = new TermDeletionConfigurator();
272 }
273 // boolean isInternal = config.isInternal();
274
275 Set<DefinedTermBase> termsToSave = new HashSet<DefinedTermBase>();
276
277 DeleteResult result = isDeletable(term.getUuid(), config);
278 //CdmBase.deproxy(dao.merge(term), DefinedTermBase.class);
279 try {
280 //generalization of
281 Set<DefinedTermBase> specificTerms = term.getGeneralizationOf();
282 if (specificTerms.size()>0){
283 if (config.isDeleteGeneralizationOfRelations()){
284 DefinedTermBase generalTerm = term.getKindOf();
285 for (DefinedTermBase specificTerm: specificTerms){
286 term.removeGeneralization(specificTerm);
287 if (generalTerm != null){
288 generalTerm.addGeneralizationOf(specificTerm);
289 termsToSave.add(generalTerm);
290 }
291 }
292 }else{
293 //TODO Exception type
294 String message = "This term has specifing terms. Move or delete specifiing terms prior to delete or change delete configuration.";
295 result.addRelatedObjects(specificTerms);
296 result.setAbort();
297 Exception ex = new DataChangeNoRollbackException(message);
298 result.addException(ex);
299 }
300 }
301
302 //kind of
303 DefinedTermBase generalTerm = term.getKindOf();
304 if (generalTerm != null){
305 if (config.isDeleteKindOfRelations()){
306 generalTerm.removeGeneralization(term);
307 }else{
308 //TODO Exception type
309 String message = "This term is kind of another term. Move or delete kind of relationship prior to delete or change delete configuration.";
310 result.addRelatedObject(generalTerm);
311 result.setAbort();
312 DataChangeNoRollbackException ex = new DataChangeNoRollbackException(message);
313 result.addException(ex);
314 throw ex;
315 }
316 }
317
318 //part of
319 DefinedTermBase parentTerm = term.getPartOf();
320 if (parentTerm != null){
321 if (! config.isDeletePartOfRelations()){
322 //TODO Exception type
323 String message = "This term is included in another term. Remove from parent term prior to delete or change delete configuration.";
324 result.addRelatedObject(parentTerm);
325 result.setAbort();
326 DataChangeNoRollbackException ex = new DataChangeNoRollbackException(message);
327 result.addException(ex);
328 }
329 }
330
331
332 //included in
333 Set<DefinedTermBase> includedTerms = term.getIncludes();
334 if (includedTerms.size()> 0){
335 // if (config.isDeleteIncludedTerms()){
336 // for (DefinedTermBase includedTerm: includedTerms){
337 // config.setCheck(true);
338 // DeleteResult includedResult = this.delete(includedTerm, config);
339 //// config.setCheck(isCheck);
340 // result.includeResult(includedResult);
341 // }
342 // }else
343 if (config.isDeleteIncludedRelations()){
344 DefinedTermBase parent = term.getPartOf();
345 for (DefinedTermBase includedTerm: includedTerms){
346 term.removeIncludes(includedTerm);
347 if (parent != null){
348 parent.addIncludes(includedTerm);
349 termsToSave.add(parent);
350 }
351 }
352 }else{
353 //TODO Exception type
354 String message = "This term includes other terms. Move or delete included terms prior to delete or change delete configuration.";
355 result.addRelatedObjects(includedTerms);
356 result.setAbort();
357 Exception ex = new DataChangeNoRollbackException(message);
358 result.addException(ex);
359 }
360 }
361
362 //part of
363 if (parentTerm != null){
364 if (config.isDeletePartOfRelations()){
365 parentTerm.removeIncludes(term);
366 termsToSave.add(parentTerm);
367 }else{
368 //handelede before "included in"
369 }
370 }
371
372 // relatedObjects;
373
374
375 if (result.isOk()){
376 TermVocabulary voc = term.getVocabulary();
377 if (voc!= null){
378 voc.removeTerm(term);
379 }
380 //TODO save voc
381 if (true /*!config.isInternal()*/){
382 dao.delete(term);
383 dao.saveOrUpdateAll(termsToSave);
384 // for (DeleteResult.PersistPair persistPair : result.getObjectsToDelete()){
385 // persistPair.dao.delete(persistPair.objectToPersist);
386 // }
387 // for (DeleteResult.PersistPair persistPair : result.getObjectsToSave()){
388 // persistPair.dao.saveOrUpdate(persistPair.objectToPersist);
389 // }
390
391 }
392 }
393 } catch (DataChangeNoRollbackException e) {
394 result.setStatus(Status.ERROR);
395 }
396 return result;
397 }
398
399 @Override
400 @Transactional(readOnly = false)
401 public DeleteResult delete(UUID termUuid, TermDeletionConfigurator config){
402 return delete(dao.load(termUuid), config);
403 }
404
405 @Override
406 @Transactional(readOnly = false)
407 public void updateTitleCache(Class<? extends DefinedTermBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<DefinedTermBase> cacheStrategy, IProgressMonitor monitor) {
408 //TODO shouldnt this be TermBase instead of DefinedTermBase
409 if (clazz == null){
410 clazz = DefinedTermBase.class;
411 }
412 super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
413 }
414
415 @Override
416 public DeleteResult isDeletable(UUID termUuid, DeleteConfiguratorBase config){
417 DeleteResult result = new DeleteResult();
418 TermBase term = load(termUuid);
419 Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(term);
420 if (references != null){
421 result.addRelatedObjects(references);
422 Iterator<CdmBase> iterator = references.iterator();
423 CdmBase ref;
424 while (iterator.hasNext()){
425 ref = iterator.next();
426 if (ref instanceof TermVocabulary){
427 result.getRelatedObjects().remove(ref);
428 }else{
429
430 String message = "An object of " + ref.getClass().getName() + " with ID " + ref.getId() + " is referencing the object" ;
431 result.addException(new ReferencedObjectUndeletableException(message));
432 result.setAbort();
433 }
434
435 }
436 }
437 return result;
438 }
439
440 @Override
441 @Transactional(readOnly = false)
442 public Map<UUID, Representation> saveOrUpdateRepresentations(Collection<Representation> representations){
443 return representationDao.saveOrUpdateAll(representations);
444 }
445
446
447 }