Project

General

Profile

Download (30.3 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.CriteriaSpecification;
31
import org.hibernate.criterion.Criterion;
32
import org.hibernate.criterion.Order;
33
import org.hibernate.criterion.Restrictions;
34
import org.hibernate.engine.spi.SessionFactoryImplementor;
35
import org.hibernate.internal.SessionImpl;
36
import org.hibernate.metadata.ClassMetadata;
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.IntegerType;
45
import org.hibernate.type.LongType;
46
import org.hibernate.type.MaterializedClobType;
47
import org.hibernate.type.SerializableType;
48
import org.hibernate.type.StringType;
49
import org.hibernate.type.Type;
50
import org.jadira.usertype.dateandtime.joda.PersistentDateTime;
51
import org.springframework.dao.DataAccessException;
52
import org.springframework.stereotype.Repository;
53
import org.springframework.util.ReflectionUtils;
54

    
55
import eu.etaxonomy.cdm.common.CdmUtils;
56
import eu.etaxonomy.cdm.common.DoubleResult;
57
import eu.etaxonomy.cdm.database.data.FullCoverageDataGenerator;
58
import eu.etaxonomy.cdm.hibernate.DOIUserType;
59
import eu.etaxonomy.cdm.hibernate.EnumUserType;
60
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
61
import eu.etaxonomy.cdm.hibernate.PartialUserType;
62
import eu.etaxonomy.cdm.hibernate.URIUserType;
63
import eu.etaxonomy.cdm.hibernate.UUIDUserType;
64
import eu.etaxonomy.cdm.hibernate.WSDLDefinitionUserType;
65
import eu.etaxonomy.cdm.model.common.CdmBase;
66
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
67
import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
68
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
69
import eu.etaxonomy.cdm.strategy.match.CacheMatcher;
70
import eu.etaxonomy.cdm.strategy.match.DefaultMatchStrategy;
71
import eu.etaxonomy.cdm.strategy.match.FieldMatcher;
72
import eu.etaxonomy.cdm.strategy.match.IMatchStrategy;
73
import eu.etaxonomy.cdm.strategy.match.IMatchable;
74
import eu.etaxonomy.cdm.strategy.match.MatchException;
75
import eu.etaxonomy.cdm.strategy.match.MatchMode;
76
import eu.etaxonomy.cdm.strategy.match.Matching;
77
import eu.etaxonomy.cdm.strategy.merge.IMergeStrategy;
78
import eu.etaxonomy.cdm.strategy.merge.MergeException;
79

    
80
@Repository
81
public class CdmGenericDaoImpl extends CdmEntityDaoBase<CdmBase> implements ICdmGenericDao{
82
	private static final Logger logger = Logger.getLogger(CdmGenericDaoImpl.class);
83

    
84

    
85
	private Set<Class<? extends CdmBase>> allCdmClasses = null;
86
	private final Map<Class<? extends CdmBase>, Set<ReferenceHolder>> referenceMap = new HashMap<Class<? extends CdmBase>, Set<ReferenceHolder>>();
87

    
88

    
89
	protected class ReferenceHolder{
90
		String propertyName;
91
		Class<? extends CdmBase> otherClass;
92
		Class<?> itemClass;
93
		Class<?> targetClass;  //new as item class is used for isCollection we have a duplicate here
94
		public boolean isCollection(){return itemClass != null;}
95
		@Override
96
        public String toString(){return otherClass.getSimpleName() + "." + propertyName ;};
97
	}
98

    
99
	public CdmGenericDaoImpl() {
100
		super(CdmBase.class);
101
	}
102

    
103
	@Override
104
	public List<CdmBase> getCdmBasesByFieldAndClass(Class clazz, String propertyName, CdmBase referencedCdmBase){
105
		Session session = super.getSession();
106

    
107
		Criteria criteria = session.createCriteria(clazz);
108
		criteria.add(Restrictions.eq(propertyName, referencedCdmBase));
109
		return criteria.list();
110
	}
111

    
112
	@Override
113
	public List<CdmBase> getCdmBasesWithItemInCollection(Class itemClass, Class clazz, String propertyName, CdmBase item){
114
		Session session = super.getSession();
115
		String thisClassStr = itemClass.getSimpleName();
116
		String otherClassStr = clazz.getSimpleName();
117
		String queryStr = " SELECT other FROM "+ thisClassStr + " this, " + otherClassStr + " other " +
118
			" WHERE this = :referencedObject AND this member of other."+propertyName ;
119
		Query query = session.createQuery(queryStr).setEntity("referencedObject", item);
120
		@SuppressWarnings("unchecked")
121
		List<CdmBase> result = query.list();
122
		return result;
123
	}
124

    
125
	@Override
126
	public Set<Class<? extends CdmBase>> getAllPersistedClasses(boolean includeAbstractClasses){
127
		Set<Class<? extends CdmBase>> result = new HashSet<Class<? extends CdmBase>>();
128

    
129
		SessionFactory sessionFactory = getSession().getSessionFactory();
130
		Map<?,?> allClassMetadata = sessionFactory.getAllClassMetadata();
131
		Collection<?> keys = allClassMetadata.keySet();
132
		for (Object oKey : keys){
133
			if (oKey instanceof String){
134
				String strKey = (String)oKey;
135
				if (! strKey.endsWith("_AUD")){
136
					try {
137
						Class clazz = Class.forName(strKey);
138
						boolean isAbstractClass = Modifier.isAbstract(clazz.getModifiers());
139
						if (! isAbstractClass || includeAbstractClasses){
140
							result.add(clazz);
141
						}
142
					} catch (ClassNotFoundException e) {
143
						logger.warn("Class not found: " + strKey);
144
					}
145
				}
146
			}else{
147
				logger.warn("key is not of type String: " +  oKey);
148
			}
149
		}
150
		return result;
151
	}
152

    
153
	@Override
154
	public Set<CdmBase> getReferencingObjects(CdmBase referencedCdmBase){
155
		Set<CdmBase> result = new HashSet<CdmBase>();
156
		if (referencedCdmBase == null) {
157
			return null;
158
		}
159
		try {
160

    
161
			referencedCdmBase = HibernateProxyHelper.deproxy(referencedCdmBase);
162
			Class<? extends CdmBase> referencedClass = referencedCdmBase.getClass();
163

    
164
			Set<ReferenceHolder> holderSet = getOrMakeHolderSet(referencedClass);
165
			for (ReferenceHolder refHolder: holderSet){
166
				handleReferenceHolder(referencedCdmBase, result, refHolder);
167
			}
168
			return result;
169
		} catch (Exception e) {
170
			e.printStackTrace();
171
			throw new RuntimeException(e);
172
		}
173

    
174
	}
175

    
176
	protected Set<ReferenceHolder> getOrMakeHolderSet(
177
			Class<? extends CdmBase> referencedClass)
178
			throws ClassNotFoundException, NoSuchFieldException {
179
		Set<ReferenceHolder> holderSet = referenceMap.get(referencedClass);
180
		if (holderSet == null){
181
			holderSet = makeHolderSet(referencedClass);
182
			referenceMap.put(referencedClass, holderSet);
183
		}
184
		return holderSet;
185
	}
186

    
187
	@Override
188
	public Set<CdmBase> getReferencingObjectsForDeletion(CdmBase referencedCdmBase){
189
	    if (referencedCdmBase == null){
190
	        return null;
191
	    }
192
		Set<CdmBase> result = getReferencingObjects(referencedCdmBase);
193
		Set<ReferenceHolder> holderSet = referenceMap.get(IdentifiableEntity.class);
194
		try {
195
			if (holderSet == null){
196
				holderSet = makeHolderSet(IdentifiableEntity.class);
197
				referenceMap.put(IdentifiableEntity.class, holderSet);
198
			}
199
			Set<CdmBase> resultIdentifiableEntity = new HashSet<CdmBase>();
200
			for (ReferenceHolder refHolder: holderSet){
201
				handleReferenceHolder(referencedCdmBase, resultIdentifiableEntity, refHolder);
202
			}
203
			result.removeAll(resultIdentifiableEntity);
204

    
205
			return result;
206
		} catch (Exception e) {
207
			e.printStackTrace();
208
			throw new RuntimeException(e);
209
		}
210

    
211
	}
212

    
213
	/**
214
	 * @param referencedCdmBase
215
	 * @param result
216
	 * @param refHolder
217
	 */
218
	private void handleReferenceHolder(CdmBase referencedCdmBase,
219
			Set<CdmBase> result, ReferenceHolder refHolder) {
220
		boolean isCollection = refHolder.isCollection();
221
		if (isCollection){
222
			result.addAll(getCdmBasesWithItemInCollection(refHolder.itemClass, refHolder.otherClass, refHolder.propertyName, referencedCdmBase));
223
		}else{
224
			result.addAll(getCdmBasesByFieldAndClass(refHolder.otherClass, refHolder.propertyName, referencedCdmBase));
225
		}
226
	}
227

    
228

    
229
	/**
230
	 * @param referencedClass
231
	 * @return
232
	 * @throws NoSuchFieldException
233
	 * @throws ClassNotFoundException
234
	 */
235
	protected Set<ReferenceHolder> makeHolderSet(Class<?> referencedClass) throws ClassNotFoundException, NoSuchFieldException {
236
		Set<ReferenceHolder> result = new HashSet<ReferenceHolder>();
237

    
238
		//init
239
		if (allCdmClasses == null){
240
			allCdmClasses = getAllPersistedClasses(false); //findAllCdmClasses();
241
		}
242
		//referencedCdmBase = (CdmBase)HibernateProxyHelper.deproxy(referencedCdmBase);
243
		SessionFactory sessionFactory = getSession().getSessionFactory();
244

    
245

    
246
		for (Class<? extends CdmBase> cdmClass : allCdmClasses){
247
			ClassMetadata classMetadata = sessionFactory.getClassMetadata(cdmClass);
248
			Type[] propertyTypes = classMetadata.getPropertyTypes();
249
			int propertyNr = 0;
250
			for (Type propertyType: propertyTypes){
251
				String propertyName = classMetadata.getPropertyNames()[propertyNr];
252
				makePropertyType(result, referencedClass, sessionFactory, cdmClass, propertyType, propertyName, false);
253
				propertyNr++;
254
			}
255

    
256
		}
257
		return result;
258
	}
259

    
260
	/**
261
	 * @param referencedCdmBase
262
	 * @param result
263
	 * @param referencedClass
264
	 * @param sessionFactory
265
	 * @param cdmClass
266
	 * @param propertyType
267
	 * @param propertyName
268
	 * @throws ClassNotFoundException
269
	 * @throws NoSuchFieldException
270
	 */
271
	private void makePropertyType(
272
//			CdmBase referencedCdmBase,
273
			Set<ReferenceHolder> result,
274
			Class<?> referencedClass,
275
			SessionFactory sessionFactory, Class<? extends CdmBase> cdmClass,
276
			Type propertyType, String propertyName, boolean isCollection)
277
				throws ClassNotFoundException, NoSuchFieldException {
278

    
279

    
280
		if (propertyType.isEntityType()){
281
			EntityType entityType = (EntityType)propertyType;
282
			String associatedEntityName = entityType.getAssociatedEntityName();
283
			Class<?> entityClass = Class.forName(associatedEntityName);
284
			if (entityClass.isInterface()){
285
				logger.debug("There is an interface");
286
			}
287
			if (entityClass.isAssignableFrom(referencedClass)){
288
				makeSingleProperty(referencedClass, entityClass, propertyName, cdmClass, result, isCollection);
289
			}
290
		}else if (propertyType.isCollectionType()){
291
			CollectionType collectionType = (CollectionType)propertyType;
292
			//String role = collectionType.getRole();
293
			Type elType = collectionType.getElementType((SessionFactoryImplementor)sessionFactory);
294
			makePropertyType(result, referencedClass, sessionFactory, cdmClass, elType, propertyName, true);
295
		}else if (propertyType.isAnyType()){
296
//			AnyType anyType = (AnyType)propertyType;
297
			Field field = getDeclaredFieldRecursive(cdmClass, propertyName);
298
			Class<?> returnType = field.getType();
299
			if (returnType.isInterface()){
300
				logger.debug("There is an interface");
301
			}
302
			if (returnType.isAssignableFrom(referencedClass)){
303
				makeSingleProperty(referencedClass, returnType, propertyName, cdmClass, result, isCollection);
304
			}
305
		}else if (propertyType.isComponentType()){
306
			ComponentType componentType = (ComponentType)propertyType;
307
			Type[] subTypes = componentType.getSubtypes();
308
//			Field field = cdmClass.getDeclaredField(propertyName);
309
//			Class returnType = field.getType();
310
			int propertyNr = 0;
311
			for (Type subType: subTypes){
312
				String subPropertyName = componentType.getPropertyNames()[propertyNr];
313
				if (!isNoDoType(subType)){
314
					logger.warn("SubType not yet handled: " + subType);
315
				}
316
//				handlePropertyType(referencedCdmBase, result, referencedClass,
317
//						sessionFactory, cdmClass, subType, subPropertyName, isCollection);
318
				propertyNr++;
319
			}
320
		}else if (isNoDoType(propertyType)){
321
			//do nothing
322
		}else{
323
			logger.warn("propertyType not yet handled: " + propertyType.getName());
324
		}
325
		//OLD:
326
				//		if (! type.isInterface()){
327
		//		if (referencedClass.isAssignableFrom(type)||
328
		//				type.isAssignableFrom(referencedClass) && CdmBase.class.isAssignableFrom(type)){
329
		//			handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
330
		//		}
331
		//	//interface
332
		//	}else if (type.isAssignableFrom(referencedClass)){
333
		//			handleSingleClass(referencedClass, type, field, cdmClass, result, referencedCdmBase, false);
334

    
335
	}
336

    
337
	/**
338
     * @param cdmClass
339
     * @param propertyName
340
     * @return
341
     */
342
    private Field getDeclaredFieldRecursive(Class<? extends CdmBase> cdmClass, String propertyName) {
343
        for (Field field : cdmClass.getDeclaredFields()){
344
            if (field.getName().equals(propertyName)){
345
                return field;
346
            }
347
        }
348
        Class<?> superClass = cdmClass.getSuperclass();
349
        if (superClass == Object.class){
350
            throw new IllegalStateException("Field not found in class hierarchy: " + propertyName);
351
        }else{
352
            return getDeclaredFieldRecursive((Class<? extends CdmBase>)superClass, propertyName);
353
        }
354
    }
355

    
356
    private boolean makeSingleProperty(Class<?> itemClass, Class<?> type, String propertyName, Class cdmClass, Set<ReferenceHolder> result,/*CdmBase item,*/ boolean isCollection){
357
//			String fieldName = StringUtils.rightPad(propertyName, 30);
358
//			String className = StringUtils.rightPad(cdmClass.getSimpleName(), 30);
359
//			String returnTypeName = StringUtils.rightPad(type.getSimpleName(), 30);
360

    
361
//			logger.debug(fieldName +   "\t\t" + className + "\t\t" + returnTypeName);
362
			ReferenceHolder refHolder = new ReferenceHolder();
363
			refHolder.propertyName = propertyName;
364
			refHolder.otherClass = cdmClass;
365
			refHolder.itemClass = (isCollection ? itemClass : null) ;
366
			refHolder.targetClass = type ;
367

    
368
			result.add(refHolder);
369
		return true;
370
	}
371

    
372
	/**
373
	 * @param propertyType
374
	 * @return
375
	 */
376
	protected static boolean isNoDoType(Type propertyType) {
377
		boolean result = false;
378
		Class<?>[] classes = new Class[]{
379
				PersistentDateTime.class,
380
				WSDLDefinitionUserType.class,
381
				UUIDUserType.class,
382
				PartialUserType.class,
383
				StringType.class,
384
				BooleanType.class,
385
				IntegerType.class,
386
				MaterializedClobType.class,
387
				LongType.class,
388
				FloatType.class,
389
				SerializableType.class,
390
				DoubleType.class,
391
				URIUserType.class,
392
				EnumType.class,
393
				EnumUserType.class,
394
				DOIUserType.class
395
				};
396
		Set<String> classNames = new HashSet<String>();
397
		for (Class<?> clazz: classes){
398
			classNames.add(clazz.getCanonicalName());
399
			if (clazz == propertyType.getClass()){
400
				return true;
401
			}
402
		}
403
		String propertyTypeClassName = propertyType.getName();
404
		if (classNames.contains(propertyTypeClassName)){
405
			return true;
406
		}
407
		return result;
408
	}
409

    
410
	@Override
411
	public List<CdmBase> getHqlResult(String hqlQuery){
412
		Query query = getSession().createQuery(hqlQuery);
413
		@SuppressWarnings("unchecked")
414
        List<CdmBase> result = query.list();
415
		return result;
416
	}
417

    
418
	@Override
419
	public Query getHqlQuery(String hqlQuery){
420
		Query query = getSession().createQuery(hqlQuery);
421
		return query;
422
	}
423

    
424
	@Override
425
	public <T extends CdmBase> void   merge(T cdmBase1, T cdmBase2, IMergeStrategy mergeStrategy) throws MergeException {
426
		SessionImpl session = (SessionImpl) getSession();
427
		DeduplicationHelper helper = new DeduplicationHelper(session, this);
428
		helper.merge(cdmBase1, cdmBase2, mergeStrategy);
429
	}
430

    
431

    
432
	@Override
433
	public <T extends CdmBase> boolean isMergeable(T cdmBase1, T cdmBase2, IMergeStrategy mergeStrategy) throws MergeException {
434
		SessionImpl session = (SessionImpl) getSession();
435
		DeduplicationHelper helper = new DeduplicationHelper(session, this);
436
		return helper.isMergeable(cdmBase1, cdmBase2, mergeStrategy);
437
	}
438

    
439

    
440
	@Override
441
	public <T extends CdmBase> T find(Class<T> clazz, int id){
442
		Session session;
443
		session =  getSession();
444
		//session = getSession().getSessionFactory().getCurrentSession();
445
		Object o = session.get(clazz, id);
446
		return (T)o;
447
	}
448

    
449
	@Override
450
	public <T extends CdmBase> T find(Class<T> clazz, int id, List<String> propertyPaths){
451
	    Session session;
452
	    session =  getSession();
453
	    T bean = session.get(clazz, id);
454
	    if(bean == null){
455
            return bean;
456
        }
457
        defaultBeanInitializer.initialize(bean, propertyPaths);
458
        return bean;
459
	}
460

    
461
	@Override
462
    public <T extends CdmBase> T find(Class<T> clazz, UUID uuid){
463
	    return find(clazz, uuid, null);
464
	}
465

    
466
    @Override
467
    public <T extends CdmBase> T find(Class<T> clazz, UUID uuid, List<String> propertyPaths){
468
        Session session = getSession();
469
        Criteria crit = session.createCriteria(type);
470
        crit.add(Restrictions.eq("uuid", uuid));
471
        crit.addOrder(Order.desc("created"));
472
        @SuppressWarnings("unchecked")
473
        List<T> results = crit.list();
474
        if (results.isEmpty()){
475
            return null;
476
        }else{
477
            if(results.size() > 1){
478
                logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
479
            }
480
            T result = results.get(0);
481
            if (result == null || propertyPaths == null){
482
                return result;
483
            }else{
484
                defaultBeanInitializer.initialize(result, propertyPaths);
485
                return result;
486
            }
487
        }
488
    }
489

    
490

    
491
	@Override
492
	public <T extends IMatchable> List<T> findMatching(T objectToMatch,
493
			IMatchStrategy matchStrategy) throws MatchException {
494

    
495
		getSession().flush();
496
		try {
497
			List<T> result = new ArrayList<T>();
498
			if(objectToMatch == null){
499
				return result;
500
			}
501
			if (matchStrategy == null){
502
				matchStrategy = DefaultMatchStrategy.NewInstance(objectToMatch.getClass());
503
			}
504
			result.addAll(findMatchingNullSafe(objectToMatch, matchStrategy));
505
			return result;
506
		} catch (IllegalArgumentException e) {
507
			throw new MatchException(e);
508
		} catch (IllegalAccessException e) {
509
			throw new MatchException(e);
510
		}
511
	}
512

    
513
	private <T extends IMatchable> List<T> findMatchingNullSafe(T objectToMatch,	IMatchStrategy matchStrategy) throws IllegalArgumentException, IllegalAccessException, MatchException {
514
		List<T> result = new ArrayList<T>();
515
		Session session = getSession();
516
		Class<?> matchClass = objectToMatch.getClass();
517
		ClassMetadata classMetaData = session.getSessionFactory().getClassMetadata(matchClass.getCanonicalName());
518
		Criteria criteria = session.createCriteria(matchClass);
519
		boolean noMatch = makeCriteria(objectToMatch, matchStrategy, classMetaData, criteria);
520
		logger.debug(criteria);
521
		//session.flush();
522
		if (noMatch == false){
523
			List<T> matchCandidates = criteria.list();
524
			matchCandidates.remove(objectToMatch);
525
			for (T matchCandidate : matchCandidates ){
526
				if (matchStrategy.invoke(objectToMatch, matchCandidate)){
527
					result.add(matchCandidate);
528
				}else{
529
					logger.warn("Match candidate did not match: " + matchCandidate);
530
				}
531
			}
532
		}
533
		return result;
534
	}
535

    
536
	/**
537
	 * @param <T>
538
	 * @param objectToMatch
539
	 * @param matchStrategy
540
	 * @param classMetaData
541
	 * @param criteria
542
	 * @return
543
	 * @throws IllegalAccessException
544
	 * @throws MatchException
545
	 */
546
	private <T> boolean makeCriteria(T objectToMatch,
547
			IMatchStrategy matchStrategy, ClassMetadata classMetaData,
548
			Criteria criteria) throws IllegalAccessException, MatchException {
549
		Matching matching = matchStrategy.getMatching();
550
		boolean noMatch = false;
551
		Map<String, List<MatchMode>> replaceMatchers = new HashMap<String, List<MatchMode>>();
552
		for (CacheMatcher cacheMatcher: matching.getCacheMatchers()){
553
			boolean cacheProtected = (Boolean)cacheMatcher.getProtectedField(matching).get(objectToMatch);
554
			if (cacheProtected == true){
555
				String cacheValue = (String)cacheMatcher.getField().get(objectToMatch);
556
				if (StringUtils.isBlank(cacheValue)){
557
					return true;  //no match
558
				}else{
559
					criteria.add(Restrictions.eq(cacheMatcher.getPropertyName(), cacheValue));
560
					criteria.add(Restrictions.eq(cacheMatcher.getProtectedPropertyName(), cacheProtected));
561

    
562
					List<DoubleResult<String, MatchMode>> replacementModes = cacheMatcher.getReplaceMatchModes(matching);
563
					for (DoubleResult<String, MatchMode> replacementMode: replacementModes ){
564
						String propertyName = replacementMode.getFirstResult();
565
						List<MatchMode> replaceMatcherList = replaceMatchers.get(propertyName);
566
						if (replaceMatcherList == null){
567
							replaceMatcherList = new ArrayList<MatchMode>();
568
							replaceMatchers.put(propertyName, replaceMatcherList);
569
						}
570
						replaceMatcherList.add(replacementMode.getSecondResult());
571
					}
572

    
573
				}
574
			}
575
		}
576
		for (FieldMatcher fieldMatcher : matching.getFieldMatchers(false)){
577
			String propertyName = fieldMatcher.getPropertyName();
578
			Type propertyType = classMetaData.getPropertyType(propertyName);
579
			Object value = fieldMatcher.getField().get(objectToMatch);
580
			List<MatchMode> matchModes= new ArrayList<MatchMode>();
581
			matchModes.add(fieldMatcher.getMatchMode());
582
			if (replaceMatchers.get(propertyName) != null){
583
				matchModes.addAll(replaceMatchers.get(propertyName));
584
			}
585

    
586
			boolean isIgnore = false;
587
			for (MatchMode matchMode : matchModes){
588
				isIgnore |= matchMode.isIgnore(value);
589
			}
590
			if (! isIgnore ){
591
				if (propertyType.isComponentType()){
592
					matchComponentType(criteria, fieldMatcher, propertyName, value, matchModes);
593
				}else{
594
					noMatch = matchNonComponentType(criteria, fieldMatcher, propertyName, value, matchModes, propertyType);
595
				}
596
			}
597
			if (noMatch){
598
				return noMatch;
599
			}
600
		}
601
		return noMatch;
602
	}
603

    
604
	/**
605
	 * @param criteria
606
	 * @param fieldMatcher
607
	 * @param propertyName
608
	 * @param value
609
	 * @param matchMode
610
	 * @throws MatchException
611
	 * @throws IllegalAccessException
612
	 */
613
	private void matchComponentType(Criteria criteria,
614
			FieldMatcher fieldMatcher, String propertyName, Object value,
615
			List<MatchMode> matchModes) throws MatchException, IllegalAccessException {
616
		if (value == null){
617
			boolean requiresSecondNull = requiresSecondNull(matchModes, value);
618
			if (requiresSecondNull){
619
				criteria.add(Restrictions.isNull(propertyName));
620
			}else{
621
				//TODO
622
				logger.warn("Component type not yet implemented for (null) value: " + propertyName);
623
				throw new MatchException("Component type not yet fully implemented for (null) value. Property: " + propertyName);
624
			}
625
		}else{
626
			Class<?> componentClass = fieldMatcher.getField().getType();
627
			Map<String, Field> fields = CdmUtils.getAllFields(componentClass, Object.class, false, false, true, false);
628
			for (String fieldName : fields.keySet()){
629
				String restrictionPath = propertyName +"."+fieldName;
630
				Object componentValue = fields.get(fieldName).get(value);
631
				//TODO diffentiate matchMode
632
				createCriterion(criteria, restrictionPath, componentValue, matchModes);
633
			}
634
		}
635
	}
636

    
637
	private boolean matchNonComponentType(Criteria criteria,
638
			FieldMatcher fieldMatcher, String propertyName, Object value,
639
			List<MatchMode> matchModes, Type propertyType) throws HibernateException, DataAccessException, MatchException, IllegalAccessException{
640
		boolean noMatch = false;
641
		if (isRequired(matchModes) && value == null){
642
			noMatch = true;
643
			return noMatch;
644
		}else if (requiresSecondNull(matchModes,value)){
645
			criteria.add(Restrictions.isNull(propertyName));
646
		}else{
647
			if (isMatch(matchModes)){
648
				if (propertyType.isCollectionType()){
649
					//TODO collection not yet handled for match
650
				}else{
651
					int joinType = CriteriaSpecification.INNER_JOIN;
652
					if (! requiresSecondValue(matchModes,value)){
653
						joinType = CriteriaSpecification.LEFT_JOIN;
654
					}
655
					Criteria matchCriteria = criteria.createCriteria(propertyName, joinType).add(Restrictions.isNotNull("id"));
656
					Class matchClass = value.getClass();
657
					if (IMatchable.class.isAssignableFrom(matchClass)){
658
						IMatchStrategy valueMatchStrategy = DefaultMatchStrategy.NewInstance(matchClass);
659
						ClassMetadata valueClassMetaData = getSession().getSessionFactory().getClassMetadata(matchClass.getCanonicalName());;
660
						noMatch = makeCriteria(value, valueMatchStrategy, valueClassMetaData, matchCriteria);
661
					}else{
662
						logger.error("Class to match (" + matchClass + ") is not of type IMatchable");
663
						throw new MatchException("Class to match (" + matchClass + ") is not of type IMatchable");
664
					}
665
				}
666
			}else if (isEqual(matchModes)){
667
				createCriterion(criteria, propertyName, value, matchModes);
668
			}else {
669
				logger.warn("Unhandled match mode: " + matchModes + ", value: " + (value==null?"null":value));
670
			}
671
		}
672
		return noMatch;
673
	}
674

    
675
	/**
676
	 * @param criteria
677
	 * @param propertyName
678
	 * @param value
679
	 * @param matchMode
680
	 * @throws MatchException
681
	 */
682
	private void createCriterion(Criteria criteria, String propertyName,
683
			Object value, List<MatchMode> matchModes) throws MatchException {
684
		Criterion finalRestriction = null;
685
		Criterion equalRestriction = Restrictions.eq(propertyName, value);
686
		Criterion nullRestriction = Restrictions.isNull(propertyName);
687
		if (this.requiresSecondValue(matchModes, value)){
688
			finalRestriction = equalRestriction;
689
		}else if (requiresSecondNull(matchModes, value) ){
690
			finalRestriction = nullRestriction;
691
		}else{
692
			finalRestriction = Restrictions.or(equalRestriction, nullRestriction);
693
		}
694
		//return finalRestriction;
695
		criteria.add(finalRestriction);
696
	}
697

    
698
	/**
699
	 * @param matchModes
700
	 * @param value
701
	 * @return
702
	 * @throws MatchException
703
	 */
704
	private boolean requiresSecondNull(List<MatchMode> matchModes, Object value) throws MatchException {
705
		boolean result = true;
706
		for (MatchMode matchMode: matchModes){
707
			result &= matchMode.requiresSecondNull(value);
708
		}
709
		return result;
710
	}
711

    
712
	/**
713
	 * @param matchModes
714
	 * @param value
715
	 * @return
716
	 * @throws MatchException
717
	 */
718
	private boolean requiresSecondValue(List<MatchMode> matchModes, Object value) throws MatchException {
719
		boolean result = true;
720
		for (MatchMode matchMode: matchModes){
721
			result &= matchMode.requiresSecondValue(value);
722
		}
723
		return result;
724
	}
725

    
726
	/**
727
	 * @param matchModes
728
	 * @param value
729
	 * @return
730
	 * @throws MatchException
731
	 */
732
	private boolean isRequired(List<MatchMode> matchModes) throws MatchException {
733
		boolean result = true;
734
		for (MatchMode matchMode: matchModes){
735
			result &= matchMode.isRequired();
736
		}
737
		return result;
738
	}
739

    
740
	/**
741
	 * Returns true if at least one match mode is of typ MATCH_XXX
742
	 * @param matchModes
743
	 * @param value
744
	 * @return
745
	 * @throws MatchException
746
	 */
747
	private boolean isMatch(List<MatchMode> matchModes) throws MatchException {
748
		boolean result = false;
749
		for (MatchMode matchMode: matchModes){
750
			result |= matchMode.isMatch();
751
		}
752
		return result;
753
	}
754

    
755
	/**
756
	 * Returns true if at least one match mode is of typ EQUAL_XXX
757
	 * @param matchModes
758
	 * @param value
759
	 * @return
760
	 * @throws MatchException
761
	 */
762
	private boolean isEqual(List<MatchMode> matchModes) throws MatchException {
763
		boolean result = false;
764
		for (MatchMode matchMode: matchModes){
765
			result |= matchMode.isEqual();
766
		}
767
		return result;
768
	}
769

    
770
	/* (non-Javadoc)
771
	 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#saveMetaData(eu.etaxonomy.cdm.model.common.CdmMetaData)
772
	 */
773
	@Override
774
    public void saveMetaData(CdmMetaData cdmMetaData) {
775
		getSession().saveOrUpdate(cdmMetaData);
776
	}
777

    
778
	/* (non-Javadoc)
779
	 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#getMetaData()
780
	 */
781
	@Override
782
    public List<CdmMetaData> getMetaData() {
783
		Session session = getSession();
784
		Criteria crit = session.createCriteria(CdmMetaData.class);
785
		List<CdmMetaData> results = crit.list();
786
		return results;
787
	}
788

    
789
    @Override
790
    public Object initializeCollection(UUID ownerUuid, String fieldName, List<String> appendedPropertyPaths)  {
791
        List<String> propertyPaths = new ArrayList<String>();
792
        propertyPaths.add(fieldName);
793
        if(appendedPropertyPaths != null && !appendedPropertyPaths.isEmpty()) {
794
            for(String app : appendedPropertyPaths) {
795
                propertyPaths.add(fieldName + "." + app);
796
            }
797
        }
798
        CdmBase cdmBase = load(ownerUuid, propertyPaths);
799
        Field field = ReflectionUtils.findField(cdmBase.getClass(), fieldName);
800
        field.setAccessible(true);
801
        Object obj;
802
        try {
803
            obj = field.get(cdmBase);
804
        } catch (IllegalAccessException e) {
805
            throw new IllegalArgumentException("Requested object is not accessible");
806
        }
807
        if(obj instanceof Collection || obj instanceof Map) {
808
            return obj;
809
        } else {
810
            throw new IllegalArgumentException("Field name provided does not correspond to a collection or map");
811
        }
812
    }
813

    
814
    @Override
815
    public Object initializeCollection(UUID ownerUuid, String fieldName)  {
816
        return initializeCollection(ownerUuid, fieldName, null);
817
    }
818

    
819
    @Override
820
    public boolean isEmpty(UUID ownerUuid, String fieldName) {
821
        Object col = initializeCollection(ownerUuid, fieldName);
822
        if(col instanceof Collection) {
823
            return ((Collection)col).isEmpty();
824
        } else if(col instanceof Map){
825
            return ((Map)col).isEmpty();
826
        }
827

    
828
        return false;
829
    }
830

    
831
    @Override
832
    public int size(UUID ownerUuid, String fieldName) {
833
        Object col = initializeCollection(ownerUuid, fieldName);
834
        if(col instanceof Collection) {
835
            return ((Collection)col).size();
836
        } else if(col instanceof Map){
837
            return ((Map)col).size();
838
        }
839
        return 0;
840
    }
841

    
842

    
843
    @Override
844
    public Object get(UUID ownerUuid, String fieldName, int index) {
845
        Object col = initializeCollection(ownerUuid, fieldName);
846
        if(col instanceof List) {
847
            return ((List)col).get(index);
848
        } else {
849
            throw new IllegalArgumentException("Field name provided does not correspond to a list");
850
        }
851
    }
852

    
853
    @Override
854
    public boolean contains(UUID ownerUuid, String fieldName, Object element) {
855
        Object col = initializeCollection(ownerUuid, fieldName);
856
        if(col instanceof Collection) {
857
            return ((Collection)col).contains(element);
858
        } else {
859
            throw new IllegalArgumentException("Field name provided does not correspond to a collection");
860
        }
861
    }
862

    
863
    @Override
864
    public boolean containsKey(UUID ownerUuid, String fieldName, Object key) {
865
        Object col = initializeCollection(ownerUuid, fieldName);
866
        if(col instanceof Map) {
867
            return ((Map)col).containsKey(key);
868
        } else {
869
            throw new IllegalArgumentException("Field name provided does not correspond to a map");
870
        }
871
    }
872

    
873

    
874

    
875
    @Override
876
    public boolean containsValue(UUID ownerUuid, String fieldName, Object value) {
877
        Object col = initializeCollection(ownerUuid, fieldName);
878
        if(col instanceof Map) {
879
            return ((Map)col).containsValue(value);
880
        } else {
881
            throw new IllegalArgumentException("Field name provided does not correspond to a map");
882
        }
883
    }
884

    
885
    @Override
886
	public void createFullSampleData() {
887
		FullCoverageDataGenerator dataGenerator = new FullCoverageDataGenerator();
888
		dataGenerator.fillWithData(getSession());
889
	}
890

    
891

    
892

    
893
}
(4-4/23)