2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.api
.service
;
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
;
19 import java
.util
.UUID
;
21 import org
.apache
.log4j
.Logger
;
22 import org
.hibernate
.criterion
.Criterion
;
23 import org
.springframework
.transaction
.annotation
.Transactional
;
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
.ITaxonName
;
43 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
44 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
45 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
46 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
47 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IIdentifiableDao
;
48 import eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.HibernateBeanInitializer
;
49 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.AutoPropertyInitializer
;
50 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
51 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
52 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
53 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
54 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
55 import eu
.etaxonomy
.cdm
.strategy
.match
.DefaultMatchStrategy
;
56 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchStrategy
;
57 import eu
.etaxonomy
.cdm
.strategy
.match
.IMatchable
;
58 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchException
;
59 import eu
.etaxonomy
.cdm
.strategy
.merge
.IMergable
;
60 import eu
.etaxonomy
.cdm
.strategy
.merge
.IMergeStrategy
;
61 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeException
;
63 public abstract class IdentifiableServiceBase
<T
extends IdentifiableEntity
, DAO
extends IIdentifiableDao
<T
>> extends AnnotatableServiceBase
<T
,DAO
>
64 implements IIdentifiableEntityService
<T
>{
67 protected static final int UPDATE_TITLE_CACHE_DEFAULT_STEP_SIZE
= 1000;
68 protected static final Logger logger
= Logger
.getLogger(IdentifiableServiceBase
.class);
71 @Transactional(readOnly
= true)
72 public Pager
<Rights
> getRights(T t
, Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
) {
73 Integer numberOfResults
= dao
.countRights(t
);
75 List
<Rights
> results
= new ArrayList
<Rights
>();
76 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
77 results
= dao
.getRights(t
, pageSize
, pageNumber
,propertyPaths
);
80 return new DefaultPagerImpl
<Rights
>(pageNumber
, numberOfResults
, pageSize
, results
);
84 @Transactional(readOnly
= true)
85 public Pager
<IdentifiableSource
> getSources(T t
, Integer pageSize
, Integer pageNumber
, List
<String
> propertyPaths
) {
86 Integer numberOfResults
= dao
.countSources(t
);
88 List
<IdentifiableSource
> results
= new ArrayList
<IdentifiableSource
>();
89 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
90 results
= dao
.getSources(t
, pageSize
, pageNumber
,propertyPaths
);
93 return new DefaultPagerImpl
<IdentifiableSource
>(pageNumber
, numberOfResults
, pageSize
, results
);
97 @Transactional(readOnly
= false)
99 public T
replace(T x
, T y
) {
100 return dao
.replace(x
, y
);
103 * FIXME Candidate for harmonization
104 * Given that this method is strongly typed, and generic, could we not simply expose it as
105 * List<T> findByTitle(String title) as it is somewhat less cumbersome. Admittedly, I don't
106 * understand what is going on with the configurators etc. so maybe there is a good reason for
107 * the design of this method.
111 @Transactional(readOnly
= true)
112 protected List
<T
> findCdmObjectsByTitle(String title
){
113 return ((IIdentifiableDao
)dao
).findByTitle(title
);
116 @Transactional(readOnly
= true)
117 protected List
<T
> findCdmObjectsByTitle(String title
, Class
<T
> clazz
){
118 return ((IIdentifiableDao
)dao
).findByTitleAndClass(title
, clazz
);
120 @Transactional(readOnly
= true)
121 protected List
<T
> findCdmObjectsByTitle(String title
, CdmBase sessionObject
){
122 return ((IIdentifiableDao
)dao
).findByTitle(title
, sessionObject
);
126 * TODO - Migrated from CommonServiceBase
128 * @see eu.etaxonomy.cdm.api.service.ICommonService#getSourcedObjectById(java.lang.String, java.lang.String)
130 @Transactional(readOnly
= true)
132 public ISourceable
getSourcedObjectByIdInSource(Class clazz
, String idInSource
, String idNamespace
) {
133 ISourceable result
= null;
135 List
<T
> list
= dao
.findOriginalSourceByIdInSource(idInSource
, idNamespace
);
136 if (! list
.isEmpty()){
137 result
= list
.get(0);
142 @Transactional(readOnly
= true)
144 public List
<UuidAndTitleCache
<T
>> getUuidAndTitleCache(Integer limit
, String pattern
) {
145 return dao
.getUuidAndTitleCache(limit
, pattern
);
148 @Transactional(readOnly
= true)
150 public <S
extends T
> List
<UuidAndTitleCache
<S
>> getUuidAndTitleCache(Class
<S
> clazz
,Integer limit
, String pattern
) {
151 return dao
.getUuidAndTitleCache(clazz
, limit
, pattern
);
154 @Transactional(readOnly
= true)
156 public String
getTitleCache(UUID uuid
, boolean refresh
){
157 return dao
.getTitleCache(uuid
, refresh
);
160 @Transactional(readOnly
= true)
162 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
) {
163 long numberOfResults
= dao
.countByTitle(clazz
, queryString
, matchmode
, criteria
);
165 List
<T
> results
= new ArrayList
<T
>();
166 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
167 results
= dao
.findByTitle(clazz
, queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
170 return new DefaultPagerImpl
<T
>(pageNumber
, numberOfResults
, pageSize
, results
);
173 @Transactional(readOnly
= true)
175 public Pager
<T
> findByTitle(IIdentifiableEntityServiceConfigurator
<T
> config
){
176 return findByTitle(config
.getClazz(), config
.getTitleSearchStringSqlized(), config
.getMatchMode(), config
.getCriteria(), config
.getPageSize(), config
.getPageNumber(), config
.getOrderHints(), config
.getPropertyPaths());
179 @Transactional(readOnly
= true)
181 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
) {
182 long numberOfResults
= dao
.countByTitle(clazz
, queryString
, matchmode
, criteria
);
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
.findByTitle(clazz
, queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
191 @Transactional(readOnly
= true)
193 public Pager
<T
> findTitleCache(Class
<?
extends T
> clazz
, String queryString
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, MatchMode matchMode
){
194 long numberOfResults
= dao
.countTitleCache(clazz
, queryString
, matchMode
);
196 List
<T
> results
= new ArrayList
<T
>();
197 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
198 results
= dao
.findTitleCache(clazz
, queryString
, pageSize
, pageNumber
, orderHints
, matchMode
);
201 r
+= numberOfResults
;
203 return new DefaultPagerImpl
<T
>(pageNumber
, r
, pageSize
, results
);
206 @Transactional(readOnly
= true)
208 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
) {
209 long numberOfResults
= dao
.countByReferenceTitle(clazz
, queryString
, matchmode
, criteria
);
211 List
<T
> results
= new ArrayList
<T
>();
212 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
213 results
= dao
.findByReferenceTitle(clazz
, queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
218 @Transactional(readOnly
= true)
220 public T
find(LSID lsid
) {
221 return dao
.find(lsid
);
224 @Transactional(readOnly
= true)
226 public Pager
<T
> search(Class
<?
extends T
> clazz
, String queryString
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
227 Integer numberOfResults
= dao
.count(clazz
,queryString
);
229 List
<T
> results
= new ArrayList
<T
>();
230 if(numberOfResults
> 0) { // no point checking again //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)
231 results
= dao
.search(clazz
,queryString
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
234 return new DefaultPagerImpl
<T
>(pageNumber
, numberOfResults
, pageSize
, results
);
238 @Transactional(readOnly
= false)
239 public void updateTitleCache() {
240 updateTitleCache(null, null, null, null);
243 @Transactional(readOnly
= false) //TODO check transactional behaviour, e.g. what happens with the session if count is very large
244 protected <S
extends T
> void updateTitleCacheImpl(Class
<S
> clazz
, Integer stepSize
, IIdentifiableEntityCacheStrategy
<T
> cacheStrategy
, IProgressMonitor monitor
) {
245 if (stepSize
== null){
246 stepSize
= UPDATE_TITLE_CACHE_DEFAULT_STEP_SIZE
;
248 if (monitor
== null){
249 monitor
= DefaultProgressMonitor
.NewInstance();
252 int count
= dao
.count(clazz
);
253 monitor
.beginTask("update titles", count
);
255 for(int i
= 0 ; i
< count
; i
= i
+ stepSize
){
256 // not sure if such strict ordering is necessary here, but for safety reasons I do it
257 ArrayList
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
258 orderHints
.add( new OrderHint("id", OrderHint
.SortOrder
.ASCENDING
));
261 Map
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>> oldAutoInit
= switchOfAutoinitializer();
262 List
<S
> list
= this.list(clazz
, stepSize
, i
, orderHints
, null);
263 switchOnOldAutoInitializer(oldAutoInit
);
265 List
<T
> entitiesToUpdate
= new ArrayList
<T
>();
266 for (T entity
: list
){
267 HibernateProxyHelper
.deproxy(entity
, clazz
);
268 if (entity
.isProtectedTitleCache() == false){
269 updateTitleCacheForSingleEntity(cacheStrategy
, entitiesToUpdate
, entity
);
273 for (T entity
: entitiesToUpdate
){
274 if (entity
.getTitleCache() != null){
275 //System.err.println(entity.getTitleCache());
277 //System.err.println("no titleCache" + ((TaxonNameBase<?,?>)entity).getNameCache());
280 saveOrUpdate(entitiesToUpdate
);
281 monitor
.worked(list
.size());
282 if (monitor
.isCanceled()){
291 * Brings back all auto initializers to the bean initializer
292 * @see #switchOfAutoinitializer()
295 protected void switchOnOldAutoInitializer(
296 Map
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>> oldAutoInit
) {
297 HibernateBeanInitializer initializer
= (HibernateBeanInitializer
)this.appContext
.getBean("defaultBeanInitializer");
298 initializer
.setBeanAutoInitializers(oldAutoInit
);
302 * Removes all auto initializers from the bean initializer
304 * @see #switchOnOldAutoInitializer(Map)
307 protected Map
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>> switchOfAutoinitializer() {
308 HibernateBeanInitializer initializer
= (HibernateBeanInitializer
)this.appContext
.getBean("defaultBeanInitializer");
309 Map
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>> oldAutoInitializers
= initializer
.getBeanAutoInitializers();
310 Map
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>> map
= new HashMap
<Class
<?
extends CdmBase
>, AutoPropertyInitializer
<CdmBase
>>();
311 initializer
.setBeanAutoInitializers(map
);
312 return oldAutoInitializers
;
316 * @param cacheStrategy
317 * @param entitiesToUpdate
321 * @param cacheStrategy
322 * @param entitiesToUpdate
325 @SuppressWarnings("unchecked")
326 private void updateTitleCacheForSingleEntity(
327 IIdentifiableEntityCacheStrategy
<T
> cacheStrategy
,
328 List
<T
> entitiesToUpdate
,
331 //assert (entity.isProtectedTitleCache() == false );
333 //exclude recursive inreferences
334 if (entity
.isInstanceOf(Reference
.class)){
335 Reference ref
= CdmBase
.deproxy(entity
, Reference
.class);
336 if (ref
.getInReference() != null && ref
.getInReference().equals(ref
)){
342 //define the correct cache strategy
343 IIdentifiableEntityCacheStrategy entityCacheStrategy
= cacheStrategy
;
344 if (entityCacheStrategy
== null){
345 entityCacheStrategy
= entity
.getCacheStrategy();
346 //FIXME find out why the wrong cache strategy is loaded here, see #1876
347 if (entity
instanceof Reference
){
348 entityCacheStrategy
= ReferenceFactory
.newReference(((Reference
)entity
).getType()).getCacheStrategy();
354 entity
.setProtectedTitleCache(true);
356 String oldTitleCache
= entity
.getTitleCache();
357 entity
.setTitleCache(oldTitleCache
, false); //before we had entity.setProtectedTitleCache(false) but this deleted the titleCache itself
358 entity
.setCacheStrategy(entityCacheStrategy
);
359 //NonViralNames and Reference have more caches //TODO handle in NameService
360 String oldNameCache
= null;
361 String oldFullTitleCache
= null;
362 String oldAbbrevTitleCache
= null;
363 if (entity
instanceof NonViralName
){
366 INonViralName nvn
= (INonViralName
) entity
;
367 if (!nvn
.isProtectedNameCache()){
368 nvn
.setProtectedNameCache(true);
369 oldNameCache
= nvn
.getNameCache();
370 nvn
.setProtectedNameCache(false);
372 if (!nvn
.isProtectedFullTitleCache()){
373 nvn
.setProtectedFullTitleCache(true);
374 oldFullTitleCache
= nvn
.getFullTitleCache();
375 nvn
.setProtectedFullTitleCache(false);
377 }catch(ClassCastException e
){
378 System
.out
.println("entity: " + entity
.getTitleCache());
381 }else if (entity
instanceof Reference
){
382 Reference ref
= (Reference
) entity
;
383 if (!ref
.isProtectedAbbrevTitleCache()){
384 ref
.setProtectedAbbrevTitleCache(true);
385 oldAbbrevTitleCache
= ref
.getAbbrevTitleCache();
386 ref
.setProtectedAbbrevTitleCache(false);
389 setOtherCachesNull(entity
);
390 String newTitleCache
= null;
391 INonViralName nvn
= null; //TODO find better solution
393 if (entity
instanceof TaxonNameBase
){
394 nvn
= (ITaxonName
) entity
;
395 newTitleCache
= entityCacheStrategy
.getTitleCache(nvn
);
397 newTitleCache
= entityCacheStrategy
.getTitleCache(entity
);
399 }catch (ClassCastException e
){
400 nvn
= HibernateProxyHelper
.deproxy(entity
, TaxonNameBase
.class);
401 newTitleCache
= entityCacheStrategy
.getTitleCache(nvn
);
404 if ( oldTitleCache
== null || ! oldTitleCache
.equals(newTitleCache
) ){
405 entity
.setTitleCache(null, false);
406 String newCache
= entity
.getTitleCache();
408 if (newCache
== null){
409 logger
.warn("newCache should never be null");
411 if (oldTitleCache
== null){
412 logger
.info("oldTitleCache should never be null");
416 nvn
.getFullTitleCache();
418 if (entity
instanceof Reference
){
419 Reference ref
= (Reference
) entity
;
420 ref
.getAbbrevTitleCache();
422 entitiesToUpdate
.add(entity
);
423 }else if (nvn
!= null){
424 String newNameCache
= nvn
.getNameCache();
425 String newFullTitleCache
= nvn
.getFullTitleCache();
426 if ((oldNameCache
== null && !nvn
.isProtectedNameCache()) || (oldNameCache
!= null && !oldNameCache
.equals(newNameCache
))){
427 entitiesToUpdate
.add(entity
);
428 }else if ((oldFullTitleCache
== null && !nvn
.isProtectedFullTitleCache()) || (oldFullTitleCache
!= null && !oldFullTitleCache
.equals(newFullTitleCache
))){
429 entitiesToUpdate
.add(entity
);
431 }else if (entity
instanceof Reference
){
432 Reference ref
= (Reference
) entity
;
433 String newAbbrevTitleCache
= ref
.getAbbrevTitleCache();
434 if ( (oldAbbrevTitleCache
== null && !ref
.isProtectedAbbrevTitleCache() ) || (oldAbbrevTitleCache
!= null && !oldAbbrevTitleCache
.equals(newAbbrevTitleCache
))){
435 entitiesToUpdate
.add(entity
);
445 * Needs override if not only the title cache should be set to null to
446 * generate the correct new title cache
448 protected void setOtherCachesNull(T entity
) {
454 private class DeduplicateState
{
455 String lastTitleCache
;
456 Integer pageSize
= 50;
459 boolean isCompleted
= false;
464 @Transactional(readOnly
= false)
465 public int deduplicate(Class
<?
extends T
> clazz
, IMatchStrategy matchStrategy
, IMergeStrategy mergeStrategy
) {
466 DeduplicateState dedupState
= new DeduplicateState();
469 logger
.warn("Deduplication clazz must not be null!");
472 if (! ( IMatchable
.class.isAssignableFrom(clazz
) && IMergable
.class.isAssignableFrom(clazz
) ) ){
473 logger
.warn("Deduplication implemented only for classes implementing IMatchable and IMergeable. No deduplication performed!");
476 Class matchableClass
= clazz
;
477 if (matchStrategy
== null){
478 matchStrategy
= DefaultMatchStrategy
.NewInstance(matchableClass
);
480 List
<T
> nextGroup
= new ArrayList
<T
>();
483 // double countTotal = count(clazz);
485 // Number countPagesN = Math.ceil(countTotal/dedupState.pageSize.doubleValue()) ;
486 // int countPages = countPagesN.intValue();
489 List
<OrderHint
> orderHints
= Arrays
.asList(new OrderHint
[]{new OrderHint("titleCache", SortOrder
.ASCENDING
)});
491 while (! dedupState
.isCompleted
){
493 List
<T
> objectList
= getPages(clazz
, dedupState
, orderHints
);
494 //after each page check if any changes took place
495 int nUnEqualPages
= handleAllPages(objectList
, dedupState
, nextGroup
, matchStrategy
, mergeStrategy
);
496 nUnEqualPages
= nUnEqualPages
+ dedupState
.pageSize
* dedupState
.startPage
;
497 //refresh start page counter
498 int finishedPages
= nUnEqualPages
/ dedupState
.pageSize
;
499 dedupState
.startPage
= finishedPages
;
502 result
+= handleLastGroup(nextGroup
, matchStrategy
, mergeStrategy
);
507 private int handleAllPages(List
<T
> objectList
, DeduplicateState dedupState
, List
<T
> nextGroup
, IMatchStrategy matchStrategy
, IMergeStrategy mergeStrategy
) {
509 for (T object
: objectList
){
510 String currentTitleCache
= object
.getTitleCache();
511 if (currentTitleCache
!= null && currentTitleCache
.equals(dedupState
.lastTitleCache
)){
513 nextGroup
.add(object
);
516 dedupState
.result
+= handleLastGroup(nextGroup
, matchStrategy
, mergeStrategy
);
517 nextGroup
= new ArrayList
<T
>();
518 nextGroup
.add(object
);
521 dedupState
.lastTitleCache
= currentTitleCache
;
523 handleLastGroup(nextGroup
, matchStrategy
, mergeStrategy
);
527 private List
<T
> getPages(Class
<?
extends T
> clazz
, DeduplicateState dedupState
, List
<OrderHint
> orderHints
) {
528 List
<T
> result
= new ArrayList
<T
>();
529 for (int pageNo
= dedupState
.startPage
; pageNo
< dedupState
.startPage
+ dedupState
.nPages
; pageNo
++){
530 List
<T
> objectList
= listByTitle(clazz
, null, null, null, dedupState
.pageSize
, pageNo
, orderHints
, null);
531 result
.addAll(objectList
);
533 if (result
.size()< dedupState
.nPages
* dedupState
.pageSize
){
534 dedupState
.isCompleted
= true;
539 private int handleLastGroup(List
<T
> group
, IMatchStrategy matchStrategy
, IMergeStrategy mergeStrategy
) {
541 int size
= group
.size();
542 Set
<Integer
> exclude
= new HashSet
<Integer
>(); //set to collect all objects, that have been merged already
543 for (int i
= 0; i
< size
- 1; i
++){
544 if (exclude
.contains(i
)){
547 for (int j
= i
+ 1; j
< size
; j
++){
548 if (exclude
.contains(j
)){
551 T firstObject
= group
.get(i
);
552 T secondObject
= group
.get(j
);
555 if (matchStrategy
.invoke((IMatchable
)firstObject
, (IMatchable
)secondObject
)){
556 commonService
.merge((IMergable
)firstObject
, (IMergable
)secondObject
, mergeStrategy
);
560 } catch (MatchException e
) {
561 logger
.warn("MatchException when trying to match " + firstObject
.getTitleCache());
563 } catch (MergeException e
) {
564 logger
.warn("MergeException when trying to merge " + firstObject
.getTitleCache());
572 @Transactional(readOnly
= true)
574 public Integer
countByTitle(Class
<?
extends T
> clazz
, String queryString
,MatchMode matchmode
, List
<Criterion
> criteria
){
575 long numberOfResults
= dao
.countByTitle(clazz
, queryString
, matchmode
, criteria
);
577 return ((Number
)numberOfResults
).intValue();
580 @Transactional(readOnly
= true)
582 public Integer
countByTitle(IIdentifiableEntityServiceConfigurator
<T
> config
){
583 return countByTitle(config
.getClazz(), config
.getTitleSearchStringSqlized(),
584 config
.getMatchMode(), config
.getCriteria());
589 @Transactional(readOnly
= true)
590 public <S
extends T
> Pager
<IdentifiedEntityDTO
<S
>> findByIdentifier(
591 Class
<S
> clazz
, String identifier
, DefinedTerm identifierType
, MatchMode matchmode
,
592 boolean includeEntity
, Integer pageSize
,
593 Integer pageNumber
, List
<String
> propertyPaths
) {
595 Integer numberOfResults
= dao
.countByIdentifier(clazz
, identifier
, identifierType
, matchmode
);
596 List
<Object
[]> daoResults
= new ArrayList
<Object
[]>();
597 if(numberOfResults
> 0) { // no point checking again
598 daoResults
= dao
.findByIdentifier(clazz
, identifier
, identifierType
,
599 matchmode
, includeEntity
, pageSize
, pageNumber
, propertyPaths
);
602 List
<IdentifiedEntityDTO
<S
>> result
= new ArrayList
<IdentifiedEntityDTO
<S
>>();
603 for (Object
[] daoObj
: daoResults
){
605 result
.add(new IdentifiedEntityDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (S
)daoObj
[2]));
607 result
.add(new IdentifiedEntityDTO
<S
>((DefinedTerm
)daoObj
[0], (String
)daoObj
[1], (UUID
)daoObj
[2], (String
)daoObj
[3]));
610 return new DefaultPagerImpl
<IdentifiedEntityDTO
<S
>>(pageNumber
, numberOfResults
, pageSize
, result
);
614 @Transactional(readOnly
= true)
615 public <S
extends T
> Pager
<MarkedEntityDTO
<S
>> findByMarker(
616 Class
<S
> clazz
, MarkerType markerType
, Boolean markerValue
,
617 boolean includeEntity
, Integer pageSize
,
618 Integer pageNumber
, List
<String
> propertyPaths
) {
620 Long numberOfResults
= dao
.countByMarker(clazz
, markerType
, markerValue
);
621 List
<Object
[]> daoResults
= new ArrayList
<>();
622 if(numberOfResults
> 0) { // no point checking again
623 daoResults
= dao
.findByMarker(clazz
, markerType
, markerValue
, includeEntity
,
624 pageSize
, pageNumber
, propertyPaths
);
627 List
<MarkedEntityDTO
<S
>> result
= new ArrayList
<>();
628 for (Object
[] daoObj
: daoResults
){
630 result
.add(new MarkedEntityDTO
<S
>((MarkerType
)daoObj
[0], (Boolean
)daoObj
[1], (S
)daoObj
[2]));
632 result
.add(new MarkedEntityDTO
<S
>((MarkerType
)daoObj
[0], (Boolean
)daoObj
[1], (UUID
)daoObj
[2], (String
)daoObj
[3]));
635 return new DefaultPagerImpl
<MarkedEntityDTO
<S
>>(pageNumber
, numberOfResults
, pageSize
, result
);