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