ref #6369 adapt existing occurrences of interface to removed generics
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / IdentifiableServiceBase.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.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.UUID;
20
21 import org.apache.log4j.Logger;
22 import org.hibernate.criterion.Criterion;
23 import org.springframework.transaction.annotation.Transactional;
24
25 import eu.etaxonomy.cdm.api.service.config.IIdentifiableEntityServiceConfigurator;
26 import eu.etaxonomy.cdm.api.service.dto.IdentifiedEntityDTO;
27 import eu.etaxonomy.cdm.api.service.dto.MarkedEntityDTO;
28 import eu.etaxonomy.cdm.api.service.pager.Pager;
29 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
30 import eu.etaxonomy.cdm.common.monitor.DefaultProgressMonitor;
31 import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
32 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
33 import eu.etaxonomy.cdm.model.common.CdmBase;
34 import eu.etaxonomy.cdm.model.common.DefinedTerm;
35 import eu.etaxonomy.cdm.model.common.ISourceable;
36 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
37 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
38 import eu.etaxonomy.cdm.model.common.LSID;
39 import eu.etaxonomy.cdm.model.common.MarkerType;
40 import eu.etaxonomy.cdm.model.media.Rights;
41 import eu.etaxonomy.cdm.model.name.INonViralName;
42 import eu.etaxonomy.cdm.model.name.NonViralName;
43 import eu.etaxonomy.cdm.model.reference.Reference;
44 import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
45 import eu.etaxonomy.cdm.persistence.dao.common.IIdentifiableDao;
46 import eu.etaxonomy.cdm.persistence.dao.hibernate.HibernateBeanInitializer;
47 import eu.etaxonomy.cdm.persistence.dao.initializer.AutoPropertyInitializer;
48 import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
49 import eu.etaxonomy.cdm.persistence.query.MatchMode;
50 import eu.etaxonomy.cdm.persistence.query.OrderHint;
51 import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
52 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
53 import eu.etaxonomy.cdm.strategy.match.DefaultMatchStrategy;
54 import eu.etaxonomy.cdm.strategy.match.IMatchStrategy;
55 import eu.etaxonomy.cdm.strategy.match.IMatchable;
56 import eu.etaxonomy.cdm.strategy.match.MatchException;
57 import eu.etaxonomy.cdm.strategy.merge.IMergable;
58 import eu.etaxonomy.cdm.strategy.merge.IMergeStrategy;
59 import eu.etaxonomy.cdm.strategy.merge.MergeException;
60
61 public abstract class IdentifiableServiceBase<T extends IdentifiableEntity, DAO extends IIdentifiableDao<T>> extends AnnotatableServiceBase<T,DAO>
62 implements IIdentifiableEntityService<T>{
63
64
65 protected static final int UPDATE_TITLE_CACHE_DEFAULT_STEP_SIZE = 1000;
66 protected static final Logger logger = Logger.getLogger(IdentifiableServiceBase.class);
67
68 @Override
69 @Transactional(readOnly = true)
70 public Pager<Rights> getRights(T t, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
71 Integer numberOfResults = dao.countRights(t);
72
73 List<Rights> results = new ArrayList<Rights>();
74 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
75 results = dao.getRights(t, pageSize, pageNumber,propertyPaths);
76 }
77
78 return new DefaultPagerImpl<Rights>(pageNumber, numberOfResults, pageSize, results);
79 }
80
81 @Override
82 @Transactional(readOnly = true)
83 public Pager<IdentifiableSource> getSources(T t, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
84 Integer numberOfResults = dao.countSources(t);
85
86 List<IdentifiableSource> results = new ArrayList<IdentifiableSource>();
87 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
88 results = dao.getSources(t, pageSize, pageNumber,propertyPaths);
89 }
90
91 return new DefaultPagerImpl<IdentifiableSource>(pageNumber, numberOfResults, pageSize, results);
92 }
93
94
95 @Transactional(readOnly = false)
96 @Override
97 public T replace(T x, T y) {
98 return dao.replace(x, y);
99 }
100 /**
101 * FIXME Candidate for harmonization
102 * Given that this method is strongly typed, and generic, could we not simply expose it as
103 * List<T> findByTitle(String title) as it is somewhat less cumbersome. Admittedly, I don't
104 * understand what is going on with the configurators etc. so maybe there is a good reason for
105 * the design of this method.
106 * @param title
107 * @return
108 */
109 @Transactional(readOnly = true)
110 protected List<T> findCdmObjectsByTitle(String title){
111 return ((IIdentifiableDao)dao).findByTitle(title);
112 }
113
114 @Transactional(readOnly = true)
115 protected List<T> findCdmObjectsByTitle(String title, Class<T> clazz){
116 return ((IIdentifiableDao)dao).findByTitleAndClass(title, clazz);
117 }
118 @Transactional(readOnly = true)
119 protected List<T> findCdmObjectsByTitle(String title, CdmBase sessionObject){
120 return ((IIdentifiableDao)dao).findByTitle(title, sessionObject);
121 }
122
123 /*
124 * TODO - Migrated from CommonServiceBase
125 * (non-Javadoc)
126 * @see eu.etaxonomy.cdm.api.service.ICommonService#getSourcedObjectById(java.lang.String, java.lang.String)
127 */
128 @Transactional(readOnly = true)
129 @Override
130 public ISourceable getSourcedObjectByIdInSource(Class clazz, String idInSource, String idNamespace) {
131 ISourceable result = null;
132
133 List<T> list = dao.findOriginalSourceByIdInSource(idInSource, idNamespace);
134 if (! list.isEmpty()){
135 result = list.get(0);
136 }
137 return result;
138 }
139
140 @Transactional(readOnly = true)
141 @Override
142 public List<UuidAndTitleCache<T>> getUuidAndTitleCache(Integer limit, String pattern) {
143 return dao.getUuidAndTitleCache(limit, pattern);
144 }
145
146 @Transactional(readOnly = true)
147 @Override
148 public <S extends T> List<UuidAndTitleCache<S>> getUuidAndTitleCache(Class<S> clazz,Integer limit, String pattern) {
149 return dao.getUuidAndTitleCache(clazz, limit, pattern);
150 }
151
152 @Transactional(readOnly = true)
153 @Override
154 public String getTitleCache(UUID uuid, boolean refresh){
155 return dao.getTitleCache(uuid, refresh);
156 }
157
158 @Transactional(readOnly = true)
159 @Override
160 public Pager<T> findByTitle(Class<? extends T> clazz, String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
161 long numberOfResults = dao.countByTitle(clazz, queryString, matchmode, criteria);
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.findByTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
166 }
167
168 return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);
169 }
170
171 @Transactional(readOnly = true)
172 @Override
173 public Pager<T> findByTitle(IIdentifiableEntityServiceConfigurator<T> config){
174 return findByTitle(config.getClazz(), config.getTitleSearchStringSqlized(), config.getMatchMode(), config.getCriteria(), config.getPageSize(), config.getPageNumber(), config.getOrderHints(), config.getPropertyPaths());
175 }
176
177 @Transactional(readOnly = true)
178 @Override
179 public List<T> listByTitle(Class<? extends T> clazz, String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
180 long numberOfResults = dao.countByTitle(clazz, queryString, matchmode, criteria);
181
182 List<T> results = new ArrayList<T>();
183 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
184 results = dao.findByTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
185 }
186 return results;
187 }
188
189 @Transactional(readOnly = true)
190 @Override
191 public Pager<T> findTitleCache(Class<? extends T> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, MatchMode matchMode){
192 long numberOfResults = dao.countTitleCache(clazz, queryString, matchMode);
193
194 List<T> results = new ArrayList<T>();
195 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
196 results = dao.findTitleCache(clazz, queryString, pageSize, pageNumber, orderHints, matchMode);
197 }
198 long r = 0;
199 r += numberOfResults;
200
201 return new DefaultPagerImpl<T>(pageNumber, r , pageSize, results);
202 }
203
204 @Transactional(readOnly = true)
205 @Override
206 public List<T> listByReferenceTitle(Class<? extends T> clazz, String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
207 long numberOfResults = dao.countByReferenceTitle(clazz, queryString, matchmode, criteria);
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.findByReferenceTitle(clazz, queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
212 }
213 return results;
214 }
215
216 @Transactional(readOnly = true)
217 @Override
218 public T find(LSID lsid) {
219 return dao.find(lsid);
220 }
221
222 @Transactional(readOnly = true)
223 @Override
224 public Pager<T> search(Class<? extends T> clazz, String queryString, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
225 Integer numberOfResults = dao.count(clazz,queryString);
226
227 List<T> results = new ArrayList<T>();
228 if(numberOfResults > 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
229 results = dao.search(clazz,queryString, pageSize, pageNumber, orderHints, propertyPaths);
230 }
231
232 return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);
233 }
234
235 @Override
236 @Transactional(readOnly = false)
237 public void updateTitleCache() {
238 updateTitleCache(null, null, null, null);
239 }
240
241 @Transactional(readOnly = false) //TODO check transactional behaviour, e.g. what happens with the session if count is very large
242 protected <S extends T > void updateTitleCacheImpl(Class<S> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<T> cacheStrategy, IProgressMonitor monitor) {
243 if (stepSize == null){
244 stepSize = UPDATE_TITLE_CACHE_DEFAULT_STEP_SIZE;
245 }
246 if (monitor == null){
247 monitor = DefaultProgressMonitor.NewInstance();
248 }
249
250 int count = dao.count(clazz);
251 monitor.beginTask("update titles", count);
252 int worked = 0;
253 for(int i = 0 ; i < count ; i = i + stepSize){
254 // not sure if such strict ordering is necessary here, but for safety reasons I do it
255 ArrayList<OrderHint> orderHints = new ArrayList<OrderHint>();
256 orderHints.add( new OrderHint("id", OrderHint.SortOrder.ASCENDING));
257
258
259 Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> oldAutoInit = switchOfAutoinitializer();
260 List<S> list = this.list(clazz, stepSize, i, orderHints, null);
261 switchOnOldAutoInitializer(oldAutoInit);
262
263 List<T> entitiesToUpdate = new ArrayList<T>();
264 for (T entity : list){
265 HibernateProxyHelper.deproxy(entity, clazz);
266 if (entity.isProtectedTitleCache() == false){
267 updateTitleCacheForSingleEntity(cacheStrategy, entitiesToUpdate, entity);
268 }
269 worked++;
270 }
271 for (T entity: entitiesToUpdate){
272 if (entity.getTitleCache() != null){
273 //System.err.println(entity.getTitleCache());
274 }else{
275 //System.err.println("no titleCache" + ((TaxonNameBase<?,?>)entity).getNameCache());
276 }
277 }
278 saveOrUpdate(entitiesToUpdate);
279 monitor.worked(list.size());
280 if (monitor.isCanceled()){
281 monitor.done();
282 return;
283 }
284 }
285 monitor.done();
286 }
287
288 /**
289 * Brings back all auto initializers to the bean initializer
290 * @see #switchOfAutoinitializer()
291 * @param oldAutoInit
292 */
293 protected void switchOnOldAutoInitializer(
294 Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> oldAutoInit) {
295 HibernateBeanInitializer initializer = (HibernateBeanInitializer)this.appContext.getBean("defaultBeanInitializer");
296 initializer.setBeanAutoInitializers(oldAutoInit);
297 }
298
299 /**
300 * Removes all auto initializers from the bean initializer
301 *
302 * @see #switchOnOldAutoInitializer(Map)
303 * @return
304 */
305 protected Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> switchOfAutoinitializer() {
306 HibernateBeanInitializer initializer = (HibernateBeanInitializer)this.appContext.getBean("defaultBeanInitializer");
307 Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> oldAutoInitializers = initializer.getBeanAutoInitializers();
308 Map<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>> map = new HashMap<Class<? extends CdmBase>, AutoPropertyInitializer<CdmBase>>();
309 initializer.setBeanAutoInitializers(map);
310 return oldAutoInitializers;
311 }
312
313 /**
314 * @param cacheStrategy
315 * @param entitiesToUpdate
316 * @param entity
317 */
318 /**
319 * @param cacheStrategy
320 * @param entitiesToUpdate
321 * @param entity
322 */
323 @SuppressWarnings("unchecked")
324 private void updateTitleCacheForSingleEntity(
325 IIdentifiableEntityCacheStrategy<T> cacheStrategy,
326 List<T> entitiesToUpdate,
327 T entity) {
328
329 //assert (entity.isProtectedTitleCache() == false );
330
331 //exclude recursive inreferences
332 if (entity.isInstanceOf(Reference.class)){
333 Reference ref = CdmBase.deproxy(entity, Reference.class);
334 if (ref.getInReference() != null && ref.getInReference().equals(ref)){
335 return;
336 }
337 }
338
339
340 //define the correct cache strategy
341 IIdentifiableEntityCacheStrategy entityCacheStrategy = cacheStrategy;
342 if (entityCacheStrategy == null){
343 entityCacheStrategy = entity.getCacheStrategy();
344 //FIXME find out why the wrong cache strategy is loaded here, see #1876
345 if (entity instanceof Reference){
346 entityCacheStrategy = ReferenceFactory.newReference(((Reference)entity).getType()).getCacheStrategy();
347 }
348 }
349
350
351 //old titleCache
352 entity.setProtectedTitleCache(true);
353
354 String oldTitleCache = entity.getTitleCache();
355 entity.setTitleCache(oldTitleCache, false); //before we had entity.setProtectedTitleCache(false) but this deleted the titleCache itself
356 entity.setCacheStrategy(entityCacheStrategy);
357 //NonViralNames and Reference have more caches //TODO handle in NameService
358 String oldNameCache = null;
359 String oldFullTitleCache = null;
360 String oldAbbrevTitleCache = null;
361 if (entity instanceof NonViralName ){
362
363 try{
364 INonViralName nvn = (INonViralName) entity;
365 if (!nvn.isProtectedNameCache()){
366 nvn.setProtectedNameCache(true);
367 oldNameCache = nvn.getNameCache();
368 nvn.setProtectedNameCache(false);
369 }
370 if (!nvn.isProtectedFullTitleCache()){
371 nvn.setProtectedFullTitleCache(true);
372 oldFullTitleCache = nvn.getFullTitleCache();
373 nvn.setProtectedFullTitleCache(false);
374 }
375 }catch(ClassCastException e){
376 System.out.println("entity: " + entity.getTitleCache());
377 }
378
379 }else if (entity instanceof Reference){
380 Reference ref = (Reference) entity;
381 if (!ref.isProtectedAbbrevTitleCache()){
382 ref.setProtectedAbbrevTitleCache(true);
383 oldAbbrevTitleCache = ref.getAbbrevTitleCache();
384 ref.setProtectedAbbrevTitleCache(false);
385 }
386 }
387 setOtherCachesNull(entity);
388 String newTitleCache= null;
389 INonViralName nvn = null; //TODO find better solution
390 try{
391 if (entity instanceof NonViralName){
392 nvn = (INonViralName) entity;
393 newTitleCache = entityCacheStrategy.getTitleCache(nvn);
394 } else{
395 newTitleCache = entityCacheStrategy.getTitleCache(entity);
396 }
397 }catch (ClassCastException e){
398 nvn = HibernateProxyHelper.deproxy(entity, NonViralName.class);
399 newTitleCache = entityCacheStrategy.getTitleCache(nvn);
400 //System.out.println("titleCache: " +entity.getTitleCache());
401 }
402
403 if ( oldTitleCache == null || ! oldTitleCache.equals(newTitleCache) ){
404 entity.setTitleCache(null, false);
405 String newCache = entity.getTitleCache();
406
407 if (newCache == null){
408 logger.warn("newCache should never be null");
409 }
410 if (oldTitleCache == null){
411 logger.info("oldTitleCache should never be null");
412 }
413 if (nvn != null){
414 nvn.getNameCache();
415 nvn.getFullTitleCache();
416 }
417 if (entity instanceof Reference){
418 Reference ref = (Reference) entity;
419 ref.getAbbrevTitleCache();
420 }
421 entitiesToUpdate.add(entity);
422 }else if (nvn != null){
423 String newNameCache = nvn.getNameCache();
424 String newFullTitleCache = nvn.getFullTitleCache();
425 if ((oldNameCache == null && !nvn.isProtectedNameCache()) || (oldNameCache != null && !oldNameCache.equals(newNameCache))){
426 entitiesToUpdate.add(entity);
427 }else if ((oldFullTitleCache == null && !nvn.isProtectedFullTitleCache()) || (oldFullTitleCache != null && !oldFullTitleCache.equals(newFullTitleCache))){
428 entitiesToUpdate.add(entity);
429 }
430 }else if (entity instanceof Reference){
431 Reference ref = (Reference) entity;
432 String newAbbrevTitleCache = ref.getAbbrevTitleCache();
433 if ( (oldAbbrevTitleCache == null && !ref.isProtectedAbbrevTitleCache() ) || (oldAbbrevTitleCache != null && !oldAbbrevTitleCache.equals(newAbbrevTitleCache))){
434 entitiesToUpdate.add(entity);
435 }
436 }
437
438
439 }
440
441
442
443 /**
444 * Needs override if not only the title cache should be set to null to
445 * generate the correct new title cache
446 */
447 protected void setOtherCachesNull(T entity) {
448 return;
449 }
450
451
452
453 private class DeduplicateState{
454 String lastTitleCache;
455 Integer pageSize = 50;
456 int nPages = 3;
457 int startPage = 0;
458 boolean isCompleted = false;
459 int result;
460 }
461
462 @Override
463 @Transactional(readOnly = false)
464 public int deduplicate(Class<? extends T> clazz, IMatchStrategy matchStrategy, IMergeStrategy mergeStrategy) {
465 DeduplicateState dedupState = new DeduplicateState();
466
467 if (clazz == null){
468 logger.warn("Deduplication clazz must not be null!");
469 return 0;
470 }
471 if (! ( IMatchable.class.isAssignableFrom(clazz) && IMergable.class.isAssignableFrom(clazz) ) ){
472 logger.warn("Deduplication implemented only for classes implementing IMatchable and IMergeable. No deduplication performed!");
473 return 0;
474 }
475 Class matchableClass = clazz;
476 if (matchStrategy == null){
477 matchStrategy = DefaultMatchStrategy.NewInstance(matchableClass);
478 }
479 List<T> nextGroup = new ArrayList<T>();
480
481 int result = 0;
482 // double countTotal = count(clazz);
483 //
484 // Number countPagesN = Math.ceil(countTotal/dedupState.pageSize.doubleValue()) ;
485 // int countPages = countPagesN.intValue();
486 //
487
488 List<OrderHint> orderHints = Arrays.asList(new OrderHint[]{new OrderHint("titleCache", SortOrder.ASCENDING)});
489
490 while (! dedupState.isCompleted){
491 //get x page sizes
492 List<T> objectList = getPages(clazz, dedupState, orderHints);
493 //after each page check if any changes took place
494 int nUnEqualPages = handleAllPages(objectList, dedupState, nextGroup, matchStrategy, mergeStrategy);
495 nUnEqualPages = nUnEqualPages + dedupState.pageSize * dedupState.startPage;
496 //refresh start page counter
497 int finishedPages = nUnEqualPages / dedupState.pageSize;
498 dedupState.startPage = finishedPages;
499 }
500
501 result += handleLastGroup(nextGroup, matchStrategy, mergeStrategy);
502 return result;
503 }
504
505
506 private int handleAllPages(List<T> objectList, DeduplicateState dedupState, List<T> nextGroup, IMatchStrategy matchStrategy, IMergeStrategy mergeStrategy) {
507 int nUnEqual = 0;
508 for (T object : objectList){
509 String currentTitleCache = object.getTitleCache();
510 if (currentTitleCache != null && currentTitleCache.equals(dedupState.lastTitleCache)){
511 //=titleCache
512 nextGroup.add(object);
513 }else{
514 //<> titleCache
515 dedupState.result += handleLastGroup(nextGroup, matchStrategy, mergeStrategy);
516 nextGroup = new ArrayList<T>();
517 nextGroup.add(object);
518 nUnEqual++;
519 }
520 dedupState.lastTitleCache = currentTitleCache;
521 }
522 handleLastGroup(nextGroup, matchStrategy, mergeStrategy);
523 return nUnEqual;
524 }
525
526 private List<T> getPages(Class<? extends T> clazz, DeduplicateState dedupState, List<OrderHint> orderHints) {
527 List<T> result = new ArrayList<T>();
528 for (int pageNo = dedupState.startPage; pageNo < dedupState.startPage + dedupState.nPages; pageNo++){
529 List<T> objectList = listByTitle(clazz, null, null, null, dedupState.pageSize, pageNo, orderHints, null);
530 result.addAll(objectList);
531 }
532 if (result.size()< dedupState.nPages * dedupState.pageSize ){
533 dedupState.isCompleted = true;
534 }
535 return result;
536 }
537
538 private int handleLastGroup(List<T> group, IMatchStrategy matchStrategy, IMergeStrategy mergeStrategy) {
539 int result = 0;
540 int size = group.size();
541 Set<Integer> exclude = new HashSet<Integer>(); //set to collect all objects, that have been merged already
542 for (int i = 0; i < size - 1; i++){
543 if (exclude.contains(i)){
544 continue;
545 }
546 for (int j = i + 1; j < size; j++){
547 if (exclude.contains(j)){
548 continue;
549 }
550 T firstObject = group.get(i);
551 T secondObject = group.get(j);
552
553 try {
554 if (matchStrategy.invoke((IMatchable)firstObject, (IMatchable)secondObject)){
555 commonService.merge((IMergable)firstObject, (IMergable)secondObject, mergeStrategy);
556 exclude.add(j);
557 result++;
558 }
559 } catch (MatchException e) {
560 logger.warn("MatchException when trying to match " + firstObject.getTitleCache());
561 e.printStackTrace();
562 } catch (MergeException e) {
563 logger.warn("MergeException when trying to merge " + firstObject.getTitleCache());
564 e.printStackTrace();
565 }
566 }
567 }
568 return result;
569 }
570
571 @Transactional(readOnly = true)
572 @Override
573 public Integer countByTitle(Class<? extends T> clazz, String queryString,MatchMode matchmode, List<Criterion> criteria){
574 long numberOfResults = dao.countByTitle(clazz, queryString, matchmode, criteria);
575
576 return ((Number)numberOfResults).intValue();
577 }
578
579 @Transactional(readOnly = true)
580 @Override
581 public Integer countByTitle(IIdentifiableEntityServiceConfigurator<T> config){
582 return countByTitle(config.getClazz(), config.getTitleSearchStringSqlized(),
583 config.getMatchMode(), config.getCriteria());
584
585 }
586
587 @Override
588 @Transactional(readOnly = true)
589 public <S extends T> Pager<IdentifiedEntityDTO<S>> findByIdentifier(
590 Class<S> clazz, String identifier, DefinedTerm identifierType, MatchMode matchmode,
591 boolean includeEntity, Integer pageSize,
592 Integer pageNumber, List<String> propertyPaths) {
593
594 Integer numberOfResults = dao.countByIdentifier(clazz, identifier, identifierType, matchmode);
595 List<Object[]> daoResults = new ArrayList<Object[]>();
596 if(numberOfResults > 0) { // no point checking again
597 daoResults = dao.findByIdentifier(clazz, identifier, identifierType,
598 matchmode, includeEntity, pageSize, pageNumber, propertyPaths);
599 }
600
601 List<IdentifiedEntityDTO<S>> result = new ArrayList<IdentifiedEntityDTO<S>>();
602 for (Object[] daoObj : daoResults){
603 if (includeEntity){
604 result.add(new IdentifiedEntityDTO<S>((DefinedTerm)daoObj[0], (String)daoObj[1], (S)daoObj[2]));
605 }else{
606 result.add(new IdentifiedEntityDTO<S>((DefinedTerm)daoObj[0], (String)daoObj[1], (UUID)daoObj[2], (String)daoObj[3]));
607 }
608 }
609 return new DefaultPagerImpl<IdentifiedEntityDTO<S>>(pageNumber, numberOfResults, pageSize, result);
610 }
611
612 @Override
613 @Transactional(readOnly = true)
614 public <S extends T> Pager<MarkedEntityDTO<S>> findByMarker(
615 Class<S> clazz, MarkerType markerType, Boolean markerValue,
616 boolean includeEntity, Integer pageSize,
617 Integer pageNumber, List<String> propertyPaths) {
618
619 Long numberOfResults = dao.countByMarker(clazz, markerType, markerValue);
620 List<Object[]> daoResults = new ArrayList<>();
621 if(numberOfResults > 0) { // no point checking again
622 daoResults = dao.findByMarker(clazz, markerType, markerValue, includeEntity,
623 pageSize, pageNumber, propertyPaths);
624 }
625
626 List<MarkedEntityDTO<S>> result = new ArrayList<>();
627 for (Object[] daoObj : daoResults){
628 if (includeEntity){
629 result.add(new MarkedEntityDTO<S>((MarkerType)daoObj[0], (Boolean)daoObj[1], (S)daoObj[2]));
630 }else{
631 result.add(new MarkedEntityDTO<S>((MarkerType)daoObj[0], (Boolean)daoObj[1], (UUID)daoObj[2], (String)daoObj[3]));
632 }
633 }
634 return new DefaultPagerImpl<MarkedEntityDTO<S>>(pageNumber, numberOfResults, pageSize, result);
635 }
636 }
637