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