Project

General

Profile

Download (36.5 KB) Statistics
| Branch: | Tag: | Revision:
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.persistence.dao.hibernate.common;
11

    
12
import java.lang.reflect.Field;
13
import java.lang.reflect.Modifier;
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
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.hibernate.Criteria;
26
import org.hibernate.HibernateException;
27
import org.hibernate.Query;
28
import org.hibernate.Session;
29
import org.hibernate.SessionFactory;
30
import org.hibernate.criterion.Criterion;
31
import org.hibernate.criterion.Order;
32
import org.hibernate.criterion.Restrictions;
33
import org.hibernate.engine.spi.SessionFactoryImplementor;
34
import org.hibernate.internal.SessionImpl;
35
import org.hibernate.metadata.ClassMetadata;
36
import org.hibernate.sql.JoinType;
37
import org.hibernate.type.BooleanType;
38
import org.hibernate.type.CollectionType;
39
import org.hibernate.type.ComponentType;
40
import org.hibernate.type.DoubleType;
41
import org.hibernate.type.EntityType;
42
import org.hibernate.type.EnumType;
43
import org.hibernate.type.FloatType;
44
import org.hibernate.type.ForeignKeyDirection;
45
import org.hibernate.type.IntegerType;
46
import org.hibernate.type.LongType;
47
import org.hibernate.type.MaterializedClobType;
48
import org.hibernate.type.OneToOneType;
49
import org.hibernate.type.SerializableType;
50
import org.hibernate.type.StringType;
51
import org.hibernate.type.Type;
52
import org.jadira.usertype.dateandtime.joda.PersistentDateTime;
53
import org.springframework.dao.DataAccessException;
54
import org.springframework.stereotype.Repository;
55
import org.springframework.util.ReflectionUtils;
56

    
57
import eu.etaxonomy.cdm.common.CdmUtils;
58
import eu.etaxonomy.cdm.common.DoubleResult;
59
import eu.etaxonomy.cdm.database.data.FullCoverageDataGenerator;
60
import eu.etaxonomy.cdm.hibernate.BigDecimalUserType;
61
import eu.etaxonomy.cdm.hibernate.DOIUserType;
62
import eu.etaxonomy.cdm.hibernate.EnumSetUserType;
63
import eu.etaxonomy.cdm.hibernate.EnumUserType;
64
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
65
import eu.etaxonomy.cdm.hibernate.OrcidUserType;
66
import eu.etaxonomy.cdm.hibernate.PartialUserType;
67
import eu.etaxonomy.cdm.hibernate.SeverityUserType;
68
import eu.etaxonomy.cdm.hibernate.ShiftUserType;
69
import eu.etaxonomy.cdm.hibernate.URIUserType;
70
import eu.etaxonomy.cdm.hibernate.UUIDUserType;
71
import eu.etaxonomy.cdm.hibernate.WSDLDefinitionUserType;
72
import eu.etaxonomy.cdm.model.common.CdmBase;
73
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
74
import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
75
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
76
import eu.etaxonomy.cdm.persistence.dto.ReferencingObjectDto;
77
import eu.etaxonomy.cdm.strategy.match.CacheMatcher;
78
import eu.etaxonomy.cdm.strategy.match.DefaultMatchStrategy;
79
import eu.etaxonomy.cdm.strategy.match.FieldMatcher;
80
import eu.etaxonomy.cdm.strategy.match.IMatchStrategy;
81
import eu.etaxonomy.cdm.strategy.match.IMatchable;
82
import eu.etaxonomy.cdm.strategy.match.MatchException;
83
import eu.etaxonomy.cdm.strategy.match.MatchMode;
84
import eu.etaxonomy.cdm.strategy.match.Matching;
85
import eu.etaxonomy.cdm.strategy.merge.IMergeStrategy;
86
import eu.etaxonomy.cdm.strategy.merge.MergeException;
87

    
88
@Repository
89
public class CdmGenericDaoImpl
90
         extends CdmEntityDaoBase<CdmBase>
91
         implements ICdmGenericDao{
92

    
93
    private static final Logger logger = Logger.getLogger(CdmGenericDaoImpl.class);
94

    
95
	private Set<Class<? extends CdmBase>> allCdmClasses = null;
96
	private final Map<Class<? extends CdmBase>, Set<ReferenceHolder>> referenceMap = new HashMap<>();
97

    
98

    
99
	protected class ReferenceHolder{
100
		String propertyName;
101
		Class<? extends CdmBase> otherClass;
102
		Class<?> itemClass;
103
		Class<?> targetClass;  //new as item class is used for isCollection we have a duplicate here
104
		public boolean isCollection(){return itemClass != null;}
105
		@Override
106
        public String toString(){return otherClass.getSimpleName() + "." + propertyName ;}
107
	}
108

    
109
	public CdmGenericDaoImpl() {
110
		super(CdmBase.class);
111
	}
112

    
113
//    @Override
114
    private List<ReferencingObjectDto> getCdmBasesByFieldAndClassDto(Class<? extends CdmBase> clazz, String propertyName,
115
            CdmBase referencedCdmBase, Integer limit){
116

    
117
        Query query = getSession().createQuery("SELECT new eu.etaxonomy.cdm.persistence.dto.ReferencingObjectDto(this.uuid, this.id) "
118
                    + "FROM "+ clazz.getSimpleName() + " this "
119
                    + "WHERE this." + propertyName +" = :referencedObject")
120
                .setEntity("referencedObject", referencedCdmBase);
121

    
122
        if (limit != null){
123
            query.setMaxResults(limit);
124
        }
125
        @SuppressWarnings("unchecked")
126
        List<ReferencingObjectDto> result = query.list();
127
        result.forEach(dto->dto.setType((Class<CdmBase>)clazz));
128
        return result;
129
    }
130

    
131
	@Override
132
    public List<CdmBase> getCdmBasesByFieldAndClass(Class<? extends CdmBase> clazz, String propertyName, CdmBase referencedCdmBase, Integer limit){
133
        Session session = super.getSession();
134

    
135
      Criteria criteria = session.createCriteria(clazz);
136
      criteria.add(Restrictions.eq(propertyName, referencedCdmBase));
137
      if (limit != null){
138
          criteria.setMaxResults(limit);
139
      }
140

    
141
        @SuppressWarnings("unchecked")
142
        List<CdmBase> result = criteria.list();
143
        return result;
144
	}
145

    
146
	@Override
147
    public long getCountByFieldAndClass(Class<? extends CdmBase> clazz, String propertyName, CdmBase referencedCdmBase){
148
        Query query = getSession().createQuery("SELECT count(this) "
149
                + "FROM "+ clazz.getSimpleName() + " this "
150
                + "WHERE this." + propertyName +" = :referencedObject")
151
                .setEntity("referencedObject", referencedCdmBase);
152

    
153
        long result =(Long)query.uniqueResult();
154
        return result;
155
    }
156

    
157
    @Override
158
    public List<ReferencingObjectDto> getCdmBasesWithItemInCollectionDto(Class<?> itemClass,
159
            Class<? extends CdmBase> clazz, String propertyName, CdmBase item, Integer limit){
160

    
161
        String queryStr = withItemInCollectionHql(itemClass, clazz, propertyName,
162
                "new eu.etaxonomy.cdm.persistence.dto.ReferencingObjectDto(other.uuid, other.id)");
163
        Query query = getSession().createQuery(queryStr).setEntity("referencedObject", item);
164
        if (limit != null){
165
            query.setMaxResults(limit);
166
        }
167
        @SuppressWarnings("unchecked")
168
        List<ReferencingObjectDto> result = query.list();
169
        result.forEach(dto->dto.setType((Class)clazz));
170
        return result;
171
    }
172

    
173
	@Override
174
	public List<CdmBase> getCdmBasesWithItemInCollection(Class<?> itemClass,
175
	        Class<?> clazz, String propertyName, CdmBase item, Integer limit){
176

    
177
		String queryStr = withItemInCollectionHql(itemClass, clazz, propertyName, "other");
178
		Query query = getSession().createQuery(queryStr).setEntity("referencedObject", item);
179
		if (limit != null){
180
		    query.setMaxResults(limit);
181
		}
182
		@SuppressWarnings("unchecked")
183
		List<CdmBase> result = query.list();
184
		return result;
185
	}
186

    
187
    private String withItemInCollectionHql(Class<?> itemClass, Class<?> clazz, String propertyName, String select) {
188
        String thisClassStr = itemClass.getSimpleName();
189
        String otherClassStr = clazz.getSimpleName();
190
        String result =  "SELECT "+select+" FROM "+ thisClassStr + " this, " + otherClassStr + " other " +
191
                " WHERE this = :referencedObject AND this member of other." + propertyName ;
192
        return result;
193
    }
194

    
195
    @Override
196
    public long getCountWithItemInCollection(Class<?> itemClass, Class<?> clazz, String propertyName,
197
            CdmBase item){
198

    
199
        String queryStr = withItemInCollectionHql(itemClass, clazz, propertyName, "count(this)");
200

    
201
        Query query = getSession().createQuery(queryStr).setEntity("referencedObject", item);
202
        long result =(Long)query.uniqueResult();
203
        return result;
204
    }
205

    
206
	@Override
207
	public Set<Class<? extends CdmBase>> getAllPersistedClasses(boolean includeAbstractClasses){
208
		Set<Class<? extends CdmBase>> result = new HashSet<>();
209

    
210
		SessionFactory sessionFactory = getSession().getSessionFactory();
211
		Map<String,?> allClassMetadata = sessionFactory.getAllClassMetadata();
212
		Collection<String> keys = allClassMetadata.keySet();
213
		for (String strKey : keys){
214
			if (! strKey.endsWith("_AUD") && !strKey.endsWith("_AUD1")){
215
				try {
216
                    Class<?> clazz = Class.forName(strKey);
217
					boolean isAbstractClass = Modifier.isAbstract(clazz.getModifiers());
218
					if (! isAbstractClass || includeAbstractClasses){
219
						result.add((Class)clazz);
220
					}
221
				} catch (ClassNotFoundException e) {
222
				    String message = "Persisted CDM class not found: " + strKey;
223
				    logger.warn(message);
224
				    //TODO better throw exception, but currently some keys are really not found yet
225
//					throw new RuntimeException("Persisted CDM class not found: " + strKey,e);
226
				}
227
			}
228
		}
229
		return result;
230
	}
231

    
232
    @Override
233
    public Set<ReferencingObjectDto> getReferencingObjectsDto(CdmBase referencedCdmBase){
234
        Set<ReferencingObjectDto> result = new HashSet<>();
235
        if (referencedCdmBase == null) {
236
            return null;
237
        }
238
        try {
239

    
240
            referencedCdmBase = HibernateProxyHelper.deproxy(referencedCdmBase);
241
            Class<? extends CdmBase> referencedClass = referencedCdmBase.getClass();
242

    
243
            Set<ReferenceHolder> holderSet = getOrMakeHolderSet(referencedClass);
244
            for (ReferenceHolder refHolder: holderSet){
245
                handleReferenceHolderDto(referencedCdmBase, result, refHolder, false);
246
            }
247
            return result;
248
        } catch (Exception e) {
249
            e.printStackTrace();
250
            throw new RuntimeException(e);
251
        }
252
    }
253

    
254
	@Override
255
	public Set<CdmBase> getReferencingObjects(CdmBase referencedCdmBase){
256
		Set<CdmBase> result = new HashSet<>();
257
		if (referencedCdmBase == null) {
258
			return null;
259
		}
260
		try {
261

    
262
			referencedCdmBase = HibernateProxyHelper.deproxy(referencedCdmBase);
263
			Class<? extends CdmBase> referencedClass = referencedCdmBase.getClass();
264

    
265
			Set<ReferenceHolder> holderSet = getOrMakeHolderSet(referencedClass);
266
			//Integer count = getReferencingObjectsCount(referencedCdmBase);
267
			for (ReferenceHolder refHolder: holderSet){
268
//			    if (count > 100000) {
269
//                    handleReferenceHolder(referencedCdmBase, result, refHolder, true);
270
//                }else{
271
                    handleReferenceHolder(referencedCdmBase, result, refHolder, false);
272
//                }
273
			}
274
			return result;
275
		} catch (Exception e) {
276
			e.printStackTrace();
277
			throw new RuntimeException(e);
278
		}
279
	}
280

    
281
	@Override
282
    public long getReferencingObjectsCount(CdmBase referencedCdmBase){
283
        long result = 0;
284
        if (referencedCdmBase == null) {
285
            return 0;
286
        }
287
        try {
288

    
289
            referencedCdmBase = HibernateProxyHelper.deproxy(referencedCdmBase);
290
            Class<? extends CdmBase> referencedClass = referencedCdmBase.getClass();
291

    
292
            Set<ReferenceHolder> holderSet = getOrMakeHolderSet(referencedClass);
293
            for (ReferenceHolder refHolder: holderSet){
294
                result =+ handleReferenceHolderForCount(referencedCdmBase, result, refHolder);
295
            }
296
            return result;
297
        } catch (Exception e) {
298
            e.printStackTrace();
299
            throw new RuntimeException(e);
300
        }
301

    
302
    }
303

    
304
	protected Set<ReferenceHolder> getOrMakeHolderSet(
305
			Class<? extends CdmBase> referencedClass)
306
			throws ClassNotFoundException, NoSuchFieldException {
307
		Set<ReferenceHolder> holderSet = referenceMap.get(referencedClass);
308
		if (holderSet == null){
309
			holderSet = makeHolderSet(referencedClass);
310
			referenceMap.put(referencedClass, holderSet);
311
		}
312
		return holderSet;
313
	}
314

    
315
	@Override
316
	public Set<CdmBase> getReferencingObjectsForDeletion(CdmBase referencedCdmBase){
317
	    if (referencedCdmBase == null){
318
	        return null;
319
	    }
320
		Set<CdmBase> result = getReferencingObjects(referencedCdmBase);
321
		Set<ReferenceHolder> holderSet = referenceMap.get(IdentifiableEntity.class);
322
		try {
323
			if (holderSet == null){
324
				holderSet = makeHolderSet(IdentifiableEntity.class);
325
				referenceMap.put(IdentifiableEntity.class, holderSet);
326
			}
327
			Set<CdmBase> resultIdentifiableEntity = new HashSet<>();
328
			for (ReferenceHolder refHolder: holderSet){
329
				handleReferenceHolder(referencedCdmBase, resultIdentifiableEntity, refHolder, false);
330
			}
331
			result.removeAll(resultIdentifiableEntity);
332

    
333
			return result;
334
		} catch (Exception e) {
335
			e.printStackTrace();
336
			throw new RuntimeException(e);
337
		}
338
	}
339

    
340
    private void handleReferenceHolderDto(CdmBase referencedCdmBase,
341
            Set<ReferencingObjectDto> result, ReferenceHolder refHolder, boolean limited) {
342

    
343
        boolean isCollection = refHolder.isCollection();
344
        if (isCollection){
345
            if (limited){
346
                result.addAll(getCdmBasesWithItemInCollectionDto(refHolder.itemClass, refHolder.otherClass, refHolder.propertyName, referencedCdmBase, 100));
347
            }else{
348
                result.addAll(getCdmBasesWithItemInCollectionDto(refHolder.itemClass, refHolder.otherClass, refHolder.propertyName, referencedCdmBase, null));
349
            }
350
        }else{
351
            if (limited){
352
                result.addAll(getCdmBasesByFieldAndClassDto(refHolder.otherClass, refHolder.propertyName, referencedCdmBase, 100));
353
            }else{
354
                result.addAll(getCdmBasesByFieldAndClassDto(refHolder.otherClass, refHolder.propertyName, referencedCdmBase, null));
355
            }
356
        }
357
    }
358

    
359
	private void handleReferenceHolder(CdmBase referencedCdmBase,
360
			Set<CdmBase> result, ReferenceHolder refHolder, boolean limited) {
361

    
362
	    boolean isCollection = refHolder.isCollection();
363
		if (isCollection){
364
		    if (limited){
365
		        result.addAll(getCdmBasesWithItemInCollection(refHolder.itemClass, refHolder.otherClass, refHolder.propertyName, referencedCdmBase, 100));
366
		    }else{
367
		        result.addAll(getCdmBasesWithItemInCollection(refHolder.itemClass, refHolder.otherClass, refHolder.propertyName, referencedCdmBase, null));
368
		    }
369
		}else{
370
		    if (limited){
371
		        result.addAll(getCdmBasesByFieldAndClass(refHolder.otherClass, refHolder.propertyName, referencedCdmBase, 100));
372
		    }else{
373
		        result.addAll(getCdmBasesByFieldAndClass(refHolder.otherClass, refHolder.propertyName, referencedCdmBase, null));
374
		    }
375
		}
376
	}
377

    
378
    private Long handleReferenceHolderForCount(CdmBase referencedCdmBase,
379
            Long result, ReferenceHolder refHolder) {
380
        boolean isCollection = refHolder.isCollection();
381
        if (isCollection){
382
            result += getCountWithItemInCollection(refHolder.itemClass, refHolder.otherClass, refHolder.propertyName, referencedCdmBase);
383
        }else{
384
            result += getCountByFieldAndClass(refHolder.otherClass, refHolder.propertyName, referencedCdmBase);
385
        }
386
        return result;
387
    }
388

    
389
	/**
390
	 * @param referencedClass
391
	 * @return
392
	 * @throws NoSuchFieldException
393
	 * @throws ClassNotFoundException
394
	 */
395
	protected Set<ReferenceHolder> makeHolderSet(Class<?> referencedClass) throws ClassNotFoundException, NoSuchFieldException {
396
		Set<ReferenceHolder> result = new HashSet<>();
397

    
398
		//init
399
		if (allCdmClasses == null){
400
			allCdmClasses = getAllPersistedClasses(false); //findAllCdmClasses();
401
		}
402
		SessionFactory sessionFactory = getSession().getSessionFactory();
403

    
404
		for (Class<? extends CdmBase> cdmClass : allCdmClasses){
405
			ClassMetadata classMetadata = sessionFactory.getClassMetadata(cdmClass);
406
			Type[] propertyTypes = classMetadata.getPropertyTypes();
407
			int propertyNr = 0;
408
			for (Type propertyType: propertyTypes){
409
				String propertyName = classMetadata.getPropertyNames()[propertyNr];
410
				makePropertyType(result, referencedClass, sessionFactory, cdmClass, propertyType, propertyName, false);
411
				propertyNr++;
412
			}
413
		}
414
		return result;
415
	}
416

    
417
	private void makePropertyType(
418
//			CdmBase referencedCdmBase,
419
			Set<ReferenceHolder> result,
420
			Class<?> referencedClass,
421
			SessionFactory sessionFactory, Class<? extends CdmBase> cdmClass,
422
			Type propertyType, String propertyName, boolean isCollection)
423
				throws ClassNotFoundException, NoSuchFieldException {
424

    
425
		if (propertyType.isEntityType()){
426
			EntityType entityType = (EntityType)propertyType;
427
			String associatedEntityName = entityType.getAssociatedEntityName();
428
			Class<?> entityClass = Class.forName(associatedEntityName);
429
			if (entityClass.isInterface()){
430
			    logger.debug("There is an interface");
431
			}
432
			if (entityType instanceof OneToOneType){
433
			    OneToOneType oneToOneType = (OneToOneType)entityType;
434
			    ForeignKeyDirection direction = oneToOneType.getForeignKeyDirection();
435
			    if (direction == ForeignKeyDirection.TO_PARENT){  //this
436
			        return;
437
			    }
438
			}
439
			if (entityClass.isAssignableFrom(referencedClass)){
440
			    makeSingleProperty(referencedClass, entityClass, propertyName, cdmClass, result, isCollection);
441
			}
442
		}else if (propertyType.isCollectionType()){
443
			CollectionType collectionType = (CollectionType)propertyType;
444
			//String role = collectionType.getRole();
445
			Type elType = collectionType.getElementType((SessionFactoryImplementor)sessionFactory);
446
			makePropertyType(result, referencedClass, sessionFactory, cdmClass, elType, propertyName, true);
447
		}else if (propertyType.isAnyType()){
448
//			AnyType anyType = (AnyType)propertyType;
449
			Field field = cdmClass.getDeclaredField(propertyName);
450
			Class<?> returnType = field.getType();
451
			if (returnType.isInterface()){
452
				logger.debug("There is an interface");
453
			}
454
			if (returnType.isAssignableFrom(referencedClass)){
455
				makeSingleProperty(referencedClass, returnType, propertyName, cdmClass, result, isCollection);
456
			}
457
		}else if (propertyType.isComponentType()){
458
			ComponentType componentType = (ComponentType)propertyType;
459
			Type[] subTypes = componentType.getSubtypes();
460
//			Field field = cdmClass.getDeclaredField(propertyName);
461
//			Class returnType = field.getType();
462
			int propertyNr = 0;
463
			for (Type subType: subTypes){
464
				String subPropertyName = componentType.getPropertyNames()[propertyNr];
465
				if (!isNoDoType(subType)){
466
					logger.warn("SubType not yet handled: " + subType);
467
				}
468
//				handlePropertyType(referencedCdmBase, result, referencedClass,
469
//						sessionFactory, cdmClass, subType, subPropertyName, isCollection);
470
				propertyNr++;
471
			}
472
		}else if (isNoDoType(propertyType)){
473
			//do nothing
474
		}else{
475
			logger.warn("propertyType not yet handled: " + propertyType.getName());
476
		}
477
		//OLD:
478
				//		if (! type.isInterface()){
479
		//		if (referencedClass.isAssignableFrom(type)||
480
		//				type.isAssignableFrom(referencedClass) && CdmBase.class.isAssignableFrom(type)){
481
		//			handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
482
		//		}
483
		//	//interface
484
		//	}else if (type.isAssignableFrom(referencedClass)){
485
		//			handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
486

    
487
	}
488

    
489
	private boolean makeSingleProperty(Class<?> itemClass, Class<?> type, String propertyName, Class<? extends CdmBase> cdmClass, Set<ReferenceHolder> result,/*CdmBase item,*/ boolean isCollection){
490
//			String fieldName = StringUtils.rightPad(propertyName, 30);
491
//			String className = StringUtils.rightPad(cdmClass.getSimpleName(), 30);
492
//			String returnTypeName = StringUtils.rightPad(type.getSimpleName(), 30);
493

    
494
//			logger.debug(fieldName +   "\t\t" + className + "\t\t" + returnTypeName);
495
			ReferenceHolder refHolder = new ReferenceHolder();
496
			refHolder.propertyName = propertyName;
497
			refHolder.otherClass = cdmClass;
498
			refHolder.itemClass = (isCollection ? itemClass : null) ;
499
			refHolder.targetClass = type ;
500

    
501
			result.add(refHolder);
502
		return true;
503
	}
504

    
505
	protected static boolean isNoDoType(Type propertyType) {
506
		boolean result = false;
507
		Class<?>[] classes = new Class[]{
508
				PersistentDateTime.class,
509
				WSDLDefinitionUserType.class,
510
				UUIDUserType.class,
511
				PartialUserType.class,
512
				StringType.class,
513
				BooleanType.class,
514
				IntegerType.class,
515
				MaterializedClobType.class,
516
				LongType.class,
517
				FloatType.class,
518
				SerializableType.class,
519
				DoubleType.class,
520
				URIUserType.class,
521
				EnumType.class,
522
				EnumUserType.class,
523
				DOIUserType.class,
524
				OrcidUserType.class,
525
				ShiftUserType.class,
526
				EnumSetUserType.class,
527
				SeverityUserType.class,
528
				BigDecimalUserType.class,
529
				};
530
		Set<String> classNames = new HashSet<>();
531
		for (Class<?> clazz: classes){
532
			classNames.add(clazz.getCanonicalName());
533
			if (clazz == propertyType.getClass()){
534
				return true;
535
			}
536
		}
537
		String propertyTypeClassName = propertyType.getName();
538
		if (classNames.contains(propertyTypeClassName)){
539
			return true;
540
		}
541
		return result;
542
	}
543

    
544
	@Override
545
	public List<CdmBase> getHqlResult(String hqlQuery, Object[] params){
546
		Query query = getSession().createQuery(hqlQuery);
547
		for(int i = 0; i<params.length; i++){
548
		    query.setParameter(String.valueOf(i), params[i]);  //for some reason using int, not String, throws exceptions, this seems to be a hibernate bug
549
		}
550
		@SuppressWarnings("unchecked")
551
        List<CdmBase> result = query.list();
552
		return result;
553
	}
554

    
555
	@Override
556
	public Query getHqlQuery(String hqlQuery){
557
		Query query = getSession().createQuery(hqlQuery);
558
		return query;
559
	}
560

    
561
	@Override
562
	public <T extends CdmBase> void   merge(T cdmBase1, T cdmBase2, IMergeStrategy mergeStrategy) throws MergeException {
563
		SessionImpl session = (SessionImpl) getSession();
564

    
565
		DeduplicationHelper helper = new DeduplicationHelper(session, this);
566
		helper.merge(cdmBase1, cdmBase2, mergeStrategy);
567
	}
568

    
569

    
570
	@Override
571
	public <T extends CdmBase> boolean isMergeable(T cdmBase1, T cdmBase2, IMergeStrategy mergeStrategy) throws MergeException {
572
		SessionImpl session = (SessionImpl) getSession();
573
		DeduplicationHelper helper = new DeduplicationHelper(session, this);
574
		return helper.isMergeable(cdmBase1, cdmBase2, mergeStrategy);
575
	}
576

    
577

    
578
	@Override
579
	public <T extends CdmBase> T find(Class<T> clazz, int id){
580
		Session session;
581
		session =  getSession();
582
		//session = getSession().getSessionFactory().getCurrentSession();
583
		T o = session.get(clazz, id);
584
		return o;
585
	}
586

    
587
	@Override
588
	public <T extends CdmBase> T find(Class<T> clazz, int id, List<String> propertyPaths){
589
	    Session session;
590
	    session =  getSession();
591
	    T bean = session.get(clazz, id);
592
	    if(bean == null){
593
            return bean;
594
        }
595
        defaultBeanInitializer.initialize(bean, propertyPaths);
596
        return bean;
597
	}
598

    
599
	@Override
600
    public <T extends CdmBase> T find(Class<T> clazz, UUID uuid){
601
	    return find(clazz, uuid, null);
602
	}
603

    
604
    @Override
605
    public <T extends CdmBase> T find(Class<T> clazz, UUID uuid, List<String> propertyPaths){
606
        Session session = getSession();
607
        Criteria crit = session.createCriteria(type);
608
        crit.add(Restrictions.eq("uuid", uuid));
609
        crit.addOrder(Order.desc("created"));
610
        @SuppressWarnings("unchecked")
611
        List<T> results = crit.list();
612
        if (results.isEmpty()){
613
            return null;
614
        }else{
615
            if(results.size() > 1){
616
                logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
617
            }
618
            T result = results.get(0);
619
            if (result == null || propertyPaths == null){
620
                return result;
621
            }else{
622
                defaultBeanInitializer.initialize(result, propertyPaths);
623
                return result;
624
            }
625
        }
626
    }
627

    
628
	@Override
629
	public <T extends IMatchable> List<T> findMatching(T objectToMatch,
630
			IMatchStrategy matchStrategy) throws MatchException {
631

    
632
		getSession().flush();
633
		try {
634
			List<T> result = new ArrayList<>();
635
			if(objectToMatch == null){
636
				return result;
637
			}
638
			if (matchStrategy == null){
639
				matchStrategy = DefaultMatchStrategy.NewInstance(objectToMatch.getClass());
640
			}
641
			result.addAll(findMatchingNullSafe(objectToMatch, matchStrategy));
642
			return result;
643
		} catch (IllegalArgumentException e) {
644
			throw new MatchException(e);
645
		} catch (IllegalAccessException e) {
646
			throw new MatchException(e);
647
		}
648
	}
649

    
650
	private <T extends IMatchable> List<T> findMatchingNullSafe(T objectToMatch,
651
	        IMatchStrategy matchStrategy) throws IllegalArgumentException, IllegalAccessException, MatchException {
652

    
653
	    List<T> result = new ArrayList<>();
654
		Session session = getSession();
655
		Class<?> matchClass = objectToMatch.getClass();
656
		ClassMetadata classMetaData = session.getSessionFactory().getClassMetadata(matchClass.getCanonicalName());
657
		Criteria criteria = session.createCriteria(matchClass);
658
		boolean noMatch = makeCriteria(objectToMatch, matchStrategy, classMetaData, criteria);
659
		if (logger.isDebugEnabled()){logger.debug(criteria);}
660
		//session.flush();
661
		if (noMatch == false){
662
			@SuppressWarnings("unchecked")
663
            List<T> matchCandidates = criteria.list();
664
			matchCandidates.remove(objectToMatch);
665
			for (T matchCandidate : matchCandidates ){
666
				if (matchStrategy.invoke(objectToMatch, matchCandidate).isSuccessful()){
667
					result.add(matchCandidate);
668
				}else{
669
					logger.info("Match candidate did not match: " + matchCandidate);
670
				}
671
			}
672
		}
673
		return result;
674
	}
675

    
676
	private <T> boolean makeCriteria(T objectToMatch,
677
			IMatchStrategy matchStrategy, ClassMetadata classMetaData,
678
			Criteria criteria) throws IllegalAccessException, MatchException {
679

    
680
	    Matching matching = matchStrategy.getMatching((IMatchable)objectToMatch);
681
		boolean noMatch = false;
682
		Map<String, List<MatchMode>> replaceMatchers = new HashMap<>();
683
		for (CacheMatcher cacheMatcher: matching.getCacheMatchers()){
684
		    Field protectedField = cacheMatcher.getProtectedField(matching);
685
			boolean cacheProtected = protectedField == null ? false : (Boolean)protectedField.get(objectToMatch);
686
			if (cacheProtected == true){
687
				String cacheValue = (String)cacheMatcher.getField().get(objectToMatch);
688
				if (StringUtils.isBlank(cacheValue)){
689
					return true;  //no match
690
				}else{
691
					criteria.add(Restrictions.eq(cacheMatcher.getPropertyName(), cacheValue));
692
					criteria.add(Restrictions.eq(cacheMatcher.getProtectedPropertyName(), cacheProtected));
693

    
694
					List<DoubleResult<String, MatchMode>> replacementModes = cacheMatcher.getReplaceMatchModes(matching);
695
					for (DoubleResult<String, MatchMode> replacementMode: replacementModes ){
696
						String propertyName = replacementMode.getFirstResult();
697
						List<MatchMode> replaceMatcherList = replaceMatchers.get(propertyName);
698
						if (replaceMatcherList == null){
699
							replaceMatcherList = new ArrayList<>();
700
							replaceMatchers.put(propertyName, replaceMatcherList);
701
						}
702
						replaceMatcherList.add(replacementMode.getSecondResult());
703
					}
704

    
705
				}
706
			}
707
		}
708
		for (FieldMatcher fieldMatcher : matching.getFieldMatchers(false)){
709
			String propertyName = fieldMatcher.getPropertyName();
710
			Type propertyType = classMetaData.getPropertyType(propertyName);
711
			Object value = fieldMatcher.getField().get(objectToMatch);
712
			List<MatchMode> matchModes= new ArrayList<>();
713
			matchModes.add(fieldMatcher.getMatchMode());
714
			if (replaceMatchers.get(propertyName) != null){
715
				matchModes.addAll(replaceMatchers.get(propertyName));
716
			}
717

    
718
			boolean isIgnore = false;
719
			for (MatchMode matchMode : matchModes){
720
				isIgnore |= matchMode.isIgnore(value);
721
			}
722
			if (! isIgnore ){
723
				if (propertyType.isComponentType()){
724
					matchComponentType(criteria, fieldMatcher, propertyName, value, matchModes);
725
				}else{
726
					noMatch = matchNonComponentType(criteria, fieldMatcher, propertyName, value, matchModes, propertyType);
727
				}
728
			}
729
			if (noMatch){
730
				return noMatch;
731
			}
732
		}
733
		return noMatch;
734
	}
735

    
736
	private void matchComponentType(Criteria criteria,
737
			FieldMatcher fieldMatcher, String propertyName, Object value,
738
			List<MatchMode> matchModes) throws MatchException, IllegalAccessException {
739
		if (value == null){
740
			boolean requiresSecondNull = requiresSecondNull(matchModes, null);
741
			if (requiresSecondNull){
742
				criteria.add(Restrictions.isNull(propertyName));
743
			}else{
744
				//this should not happen, should be handled as ignore before
745
				logger.warn("Component type not yet implemented for (null) value: " + propertyName);
746
				throw new MatchException("Component type not yet fully implemented for (null) value. Property: " + propertyName);
747
			}
748
		}else{
749
			Class<?> componentClass = fieldMatcher.getField().getType();
750
			Map<String, Field> fields = CdmUtils.getAllFields(componentClass, Object.class, false, false, true, false);
751
			for (String fieldName : fields.keySet()){
752
				String restrictionPath = propertyName +"."+fieldName;
753
				Object componentValue = fields.get(fieldName).get(value);
754
				//TODO differentiate matchMode
755
				createCriterion(criteria, restrictionPath, componentValue, matchModes);
756
			}
757
		}
758
	}
759

    
760
    private boolean matchNonComponentType(Criteria criteria,
761
			FieldMatcher fieldMatcher,
762
			String propertyName,
763
			Object value,
764
			List<MatchMode> matchModes,
765
			Type propertyType)
766
			throws HibernateException, DataAccessException, MatchException, IllegalAccessException{
767

    
768
	    boolean noMatch = false;
769
		if (isRequired(matchModes) && value == null){
770
			noMatch = true;
771
			return noMatch;
772
		}else if (requiresSecondNull(matchModes,value)){
773
			criteria.add(Restrictions.isNull(propertyName));
774
		}else{
775
			if (isMatch(matchModes)){
776
				if (propertyType.isCollectionType()){
777
					//TODO collection not yet handled for match
778
				}else{
779
					JoinType joinType = JoinType.INNER_JOIN;
780
					if (! requiresSecondValue(matchModes,value)){
781
						joinType = JoinType.LEFT_OUTER_JOIN;
782
					}
783
					Criteria matchCriteria = criteria.createCriteria(propertyName, joinType).add(Restrictions.isNotNull("id"));
784
					@SuppressWarnings("rawtypes")
785
                    Class matchClass = value.getClass();
786
					if (IMatchable.class.isAssignableFrom(matchClass)){
787
						IMatchStrategy valueMatchStrategy = fieldMatcher.getMatchStrategy() != null? fieldMatcher.getMatchStrategy() : DefaultMatchStrategy.NewInstance(matchClass);
788
						ClassMetadata valueClassMetaData = getSession().getSessionFactory().getClassMetadata(matchClass.getCanonicalName());
789
						noMatch = makeCriteria(value, valueMatchStrategy, valueClassMetaData, matchCriteria);
790
					}else{
791
						logger.error("Class to match (" + matchClass + ") is not of type IMatchable");
792
						throw new MatchException("Class to match (" + matchClass + ") is not of type IMatchable");
793
					}
794
				}
795
			}else if (isEqual(matchModes)){
796
				createCriterion(criteria, propertyName, value, matchModes);
797
			}else {
798
				logger.warn("Unhandled match mode: " + matchModes + ", value: " + (value==null?"null":value));
799
			}
800
		}
801
		return noMatch;
802
	}
803

    
804
	private void createCriterion(Criteria criteria, String propertyName,
805
			Object value, List<MatchMode> matchModes) throws MatchException {
806
		Criterion finalRestriction = null;
807
		Criterion equalRestriction = Restrictions.eq(propertyName, value);
808
		Criterion nullRestriction = Restrictions.isNull(propertyName);
809
		if (this.requiresSecondValue(matchModes, value)){
810
			finalRestriction = equalRestriction;
811
		}else if (requiresSecondNull(matchModes, value) ){
812
			finalRestriction = nullRestriction;
813
		}else{
814
			finalRestriction = Restrictions.or(equalRestriction, nullRestriction);
815
		}
816
		//return finalRestriction;
817
		criteria.add(finalRestriction);
818
	}
819

    
820
	private boolean requiresSecondNull(List<MatchMode> matchModes, Object value) throws MatchException {
821
		boolean result = true;
822
		for (MatchMode matchMode: matchModes){
823
			result &= matchMode.requiresSecondNull(value);
824
		}
825
		return result;
826
	}
827

    
828
	private boolean requiresSecondValue(List<MatchMode> matchModes, Object value) {
829
		boolean result = true;
830
		for (MatchMode matchMode: matchModes){
831
			result &= matchMode.requiresSecondValue(value);
832
		}
833
		return result;
834
	}
835

    
836
	private boolean isRequired(List<MatchMode> matchModes) {
837
		boolean result = true;
838
		for (MatchMode matchMode: matchModes){
839
			result &= matchMode.isRequired();
840
		}
841
		return result;
842
	}
843

    
844
	/**
845
	 * Returns true if at least one match mode is of type MATCH_XXX
846
	 * @param matchModes
847
	 * @param value
848
	 * @return
849
	 * @throws MatchException
850
	 */
851
	private boolean isMatch(List<MatchMode> matchModes) throws MatchException {
852
		boolean result = false;
853
		for (MatchMode matchMode: matchModes){
854
			result |= matchMode.isMatch();
855
		}
856
		return result;
857
	}
858

    
859
	/**
860
	 * Returns true if at least one match mode is of typ EQUAL_XXX
861
	 * @param matchModes
862
	 * @param value
863
	 * @return
864
	 * @throws MatchException
865
	 */
866
	private boolean isEqual(List<MatchMode> matchModes) throws MatchException {
867
		boolean result = false;
868
		for (MatchMode matchMode: matchModes){
869
			result |= matchMode.isEqual();
870
		}
871
		return result;
872
	}
873

    
874
	@Override
875
    public void saveMetaData(CdmMetaData cdmMetaData) {
876
		getSession().saveOrUpdate(cdmMetaData);
877
	}
878

    
879
	@Override
880
    public List<CdmMetaData> getMetaData() {
881
		Session session = getSession();
882
		Criteria crit = session.createCriteria(CdmMetaData.class);
883
		@SuppressWarnings("unchecked")
884
        List<CdmMetaData> results = crit.list();
885
		return results;
886
	}
887

    
888
    @Override
889
    public Object initializeCollection(UUID ownerUuid, String fieldName, List<String> appendedPropertyPaths)  {
890
        List<String> propertyPaths = new ArrayList<>();
891
        propertyPaths.add(fieldName);
892
        if(appendedPropertyPaths != null && !appendedPropertyPaths.isEmpty()) {
893
            for(String app : appendedPropertyPaths) {
894
                propertyPaths.add(fieldName + "." + app);
895
            }
896
        }
897
        CdmBase cdmBase = load(ownerUuid, propertyPaths);
898
        Field field = ReflectionUtils.findField(cdmBase.getClass(), fieldName);
899
        field.setAccessible(true);
900
        Object obj;
901
        try {
902
            obj = field.get(cdmBase);
903
        } catch (IllegalAccessException e) {
904
            throw new IllegalArgumentException("Requested object is not accessible");
905
        }
906
        if(obj instanceof Collection || obj instanceof Map) {
907
            return obj;
908
        } else {
909
            throw new IllegalArgumentException("Field name provided does not correspond to a collection or map");
910
        }
911
    }
912

    
913
    @Override
914
    public Object initializeCollection(UUID ownerUuid, String fieldName)  {
915
        return initializeCollection(ownerUuid, fieldName, null);
916
    }
917

    
918
    @Override
919
    public boolean isEmpty(UUID ownerUuid, String fieldName) {
920
        Object col = initializeCollection(ownerUuid, fieldName);
921
        if(col instanceof Collection) {
922
            return ((Collection<?>)col).isEmpty();
923
        } else if(col instanceof Map){
924
            return ((Map<?,?>)col).isEmpty();
925
        }
926

    
927
        return false;
928
    }
929

    
930
    @Override
931
    public int size(UUID ownerUuid, String fieldName) {
932
        Object col = initializeCollection(ownerUuid, fieldName);
933
        if(col instanceof Collection) {
934
            return ((Collection<?>)col).size();
935
        } else if(col instanceof Map){
936
            return ((Map<?,?>)col).size();
937
        }
938
        return 0;
939
    }
940

    
941
    @Override
942
    public Object get(UUID ownerUuid, String fieldName, int index) {
943
        Object col = initializeCollection(ownerUuid, fieldName);
944
        if(col instanceof List) {
945
            return ((List<?>)col).get(index);
946
        } else {
947
            throw new IllegalArgumentException("Field name provided does not correspond to a list");
948
        }
949
    }
950

    
951
    @Override
952
    public boolean contains(UUID ownerUuid, String fieldName, Object element) {
953
        Object col = initializeCollection(ownerUuid, fieldName);
954
        if(col instanceof Collection) {
955
            return ((Collection<?>)col).contains(element);
956
        } else {
957
            throw new IllegalArgumentException("Field name provided does not correspond to a collection");
958
        }
959
    }
960

    
961
    @Override
962
    public boolean containsKey(UUID ownerUuid, String fieldName, Object key) {
963
        Object col = initializeCollection(ownerUuid, fieldName);
964
        if(col instanceof Map) {
965
            return ((Map<?,?>)col).containsKey(key);
966
        } else {
967
            throw new IllegalArgumentException("Field name provided does not correspond to a map");
968
        }
969
    }
970

    
971
    @Override
972
    public boolean containsValue(UUID ownerUuid, String fieldName, Object value) {
973
        Object col = initializeCollection(ownerUuid, fieldName);
974
        if(col instanceof Map) {
975
            return ((Map<?,?>)col).containsValue(value);
976
        } else {
977
            throw new IllegalArgumentException("Field name provided does not correspond to a map");
978
        }
979
    }
980

    
981
    @Override
982
	public void createFullSampleData() {
983
		FullCoverageDataGenerator dataGenerator = new FullCoverageDataGenerator();
984
		dataGenerator.fillWithData(getSession());
985
	}
986

    
987
    @Override
988
    public List<UUID> listUuid(Class<? extends CdmBase> clazz) {
989
        String queryString = "SELECT uuid FROM " + clazz.getSimpleName();
990
        Query query = getSession().createQuery(queryString);
991
        @SuppressWarnings("unchecked")
992
        List<UUID> list = query.list();
993
        return list;
994
    }
995
}
(4-4/18)