Project

General

Profile

Download (29.8 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
		@SuppressWarnings("unchecked")
110
        List<CdmBase> result = criteria.list();
111
		return result;
112
	}
113

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

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

    
131
		SessionFactory sessionFactory = getSession().getSessionFactory();
132
		Map<String,?> allClassMetadata = sessionFactory.getAllClassMetadata();
133
		Collection<String> keys = allClassMetadata.keySet();
134
		for (String strKey : keys){
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((Class)clazz);
141
					}
142
				} catch (ClassNotFoundException e) {
143
				    String message = "Persisted CDM class not found: " + strKey;
144
				    logger.warn(message);
145
				    //TODO better throw exception, but currently some keys are really not found yet
146
//					throw new RuntimeException("Persisted CDM class not found: " + strKey,e);
147
				}
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 = cdmClass.getDeclaredField(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
	private boolean makeSingleProperty(Class<?> itemClass, Class<?> type, String propertyName, Class<? extends CdmBase> cdmClass, Set<ReferenceHolder> result,/*CdmBase item,*/ boolean isCollection){
338
//			String fieldName = StringUtils.rightPad(propertyName, 30);
339
//			String className = StringUtils.rightPad(cdmClass.getSimpleName(), 30);
340
//			String returnTypeName = StringUtils.rightPad(type.getSimpleName(), 30);
341

    
342
//			logger.debug(fieldName +   "\t\t" + className + "\t\t" + returnTypeName);
343
			ReferenceHolder refHolder = new ReferenceHolder();
344
			refHolder.propertyName = propertyName;
345
			refHolder.otherClass = cdmClass;
346
			refHolder.itemClass = (isCollection ? itemClass : null) ;
347
			refHolder.targetClass = type ;
348

    
349
			result.add(refHolder);
350
		return true;
351
	}
352

    
353
	/**
354
	 * @param propertyType
355
	 * @return
356
	 */
357
	protected static boolean isNoDoType(Type propertyType) {
358
		boolean result = false;
359
		Class<?>[] classes = new Class[]{
360
				PersistentDateTime.class,
361
				WSDLDefinitionUserType.class,
362
				UUIDUserType.class,
363
				PartialUserType.class,
364
				StringType.class,
365
				BooleanType.class,
366
				IntegerType.class,
367
				MaterializedClobType.class,
368
				LongType.class,
369
				FloatType.class,
370
				SerializableType.class,
371
				DoubleType.class,
372
				URIUserType.class,
373
				EnumType.class,
374
				EnumUserType.class,
375
				DOIUserType.class
376
				};
377
		Set<String> classNames = new HashSet<String>();
378
		for (Class<?> clazz: classes){
379
			classNames.add(clazz.getCanonicalName());
380
			if (clazz == propertyType.getClass()){
381
				return true;
382
			}
383
		}
384
		String propertyTypeClassName = propertyType.getName();
385
		if (classNames.contains(propertyTypeClassName)){
386
			return true;
387
		}
388
		return result;
389
	}
390

    
391
	@Override
392
	public List<CdmBase> getHqlResult(String hqlQuery){
393
		Query query = getSession().createQuery(hqlQuery);
394
		@SuppressWarnings("unchecked")
395
        List<CdmBase> result = query.list();
396
		return result;
397
	}
398

    
399
	@Override
400
	public Query getHqlQuery(String hqlQuery){
401
		Query query = getSession().createQuery(hqlQuery);
402
		return query;
403
	}
404

    
405
	@Override
406
	public <T extends CdmBase> void   merge(T cdmBase1, T cdmBase2, IMergeStrategy mergeStrategy) throws MergeException {
407
		SessionImpl session = (SessionImpl) getSession();
408
		DeduplicationHelper helper = new DeduplicationHelper(session, this);
409
		helper.merge(cdmBase1, cdmBase2, mergeStrategy);
410
	}
411

    
412

    
413
	@Override
414
	public <T extends CdmBase> boolean isMergeable(T cdmBase1, T cdmBase2, IMergeStrategy mergeStrategy) throws MergeException {
415
		SessionImpl session = (SessionImpl) getSession();
416
		DeduplicationHelper helper = new DeduplicationHelper(session, this);
417
		return helper.isMergeable(cdmBase1, cdmBase2, mergeStrategy);
418
	}
419

    
420

    
421
	@Override
422
	public <T extends CdmBase> T find(Class<T> clazz, int id){
423
		Session session;
424
		session =  getSession();
425
		//session = getSession().getSessionFactory().getCurrentSession();
426
		T o = session.get(clazz, id);
427
		return o;
428
	}
429

    
430
	@Override
431
	public <T extends CdmBase> T find(Class<T> clazz, int id, List<String> propertyPaths){
432
	    Session session;
433
	    session =  getSession();
434
	    T bean = session.get(clazz, id);
435
	    if(bean == null){
436
            return bean;
437
        }
438
        defaultBeanInitializer.initialize(bean, propertyPaths);
439
        return bean;
440
	}
441

    
442
	@Override
443
    public <T extends CdmBase> T find(Class<T> clazz, UUID uuid){
444
	    return find(clazz, uuid, null);
445
	}
446

    
447
    @Override
448
    public <T extends CdmBase> T find(Class<T> clazz, UUID uuid, List<String> propertyPaths){
449
        Session session = getSession();
450
        Criteria crit = session.createCriteria(type);
451
        crit.add(Restrictions.eq("uuid", uuid));
452
        crit.addOrder(Order.desc("created"));
453
        @SuppressWarnings("unchecked")
454
        List<T> results = crit.list();
455
        if (results.isEmpty()){
456
            return null;
457
        }else{
458
            if(results.size() > 1){
459
                logger.error("findByUuid() delivers more than one result for UUID: " + uuid);
460
            }
461
            T result = results.get(0);
462
            if (result == null || propertyPaths == null){
463
                return result;
464
            }else{
465
                defaultBeanInitializer.initialize(result, propertyPaths);
466
                return result;
467
            }
468
        }
469
    }
470

    
471

    
472
	@Override
473
	public <T extends IMatchable> List<T> findMatching(T objectToMatch,
474
			IMatchStrategy matchStrategy) throws MatchException {
475

    
476
		getSession().flush();
477
		try {
478
			List<T> result = new ArrayList<T>();
479
			if(objectToMatch == null){
480
				return result;
481
			}
482
			if (matchStrategy == null){
483
				matchStrategy = DefaultMatchStrategy.NewInstance(objectToMatch.getClass());
484
			}
485
			result.addAll(findMatchingNullSafe(objectToMatch, matchStrategy));
486
			return result;
487
		} catch (IllegalArgumentException e) {
488
			throw new MatchException(e);
489
		} catch (IllegalAccessException e) {
490
			throw new MatchException(e);
491
		}
492
	}
493

    
494
	private <T extends IMatchable> List<T> findMatchingNullSafe(T objectToMatch,	IMatchStrategy matchStrategy) throws IllegalArgumentException, IllegalAccessException, MatchException {
495
		List<T> result = new ArrayList<T>();
496
		Session session = getSession();
497
		Class<?> matchClass = objectToMatch.getClass();
498
		ClassMetadata classMetaData = session.getSessionFactory().getClassMetadata(matchClass.getCanonicalName());
499
		Criteria criteria = session.createCriteria(matchClass);
500
		boolean noMatch = makeCriteria(objectToMatch, matchStrategy, classMetaData, criteria);
501
		logger.debug(criteria);
502
		//session.flush();
503
		if (noMatch == false){
504
			@SuppressWarnings("unchecked")
505
            List<T> matchCandidates = criteria.list();
506
			matchCandidates.remove(objectToMatch);
507
			for (T matchCandidate : matchCandidates ){
508
				if (matchStrategy.invoke(objectToMatch, matchCandidate)){
509
					result.add(matchCandidate);
510
				}else{
511
					logger.warn("Match candidate did not match: " + matchCandidate);
512
				}
513
			}
514
		}
515
		return result;
516
	}
517

    
518
	/**
519
	 * @param <T>
520
	 * @param objectToMatch
521
	 * @param matchStrategy
522
	 * @param classMetaData
523
	 * @param criteria
524
	 * @return
525
	 * @throws IllegalAccessException
526
	 * @throws MatchException
527
	 */
528
	private <T> boolean makeCriteria(T objectToMatch,
529
			IMatchStrategy matchStrategy, ClassMetadata classMetaData,
530
			Criteria criteria) throws IllegalAccessException, MatchException {
531
		Matching matching = matchStrategy.getMatching();
532
		boolean noMatch = false;
533
		Map<String, List<MatchMode>> replaceMatchers = new HashMap<String, List<MatchMode>>();
534
		for (CacheMatcher cacheMatcher: matching.getCacheMatchers()){
535
			boolean cacheProtected = (Boolean)cacheMatcher.getProtectedField(matching).get(objectToMatch);
536
			if (cacheProtected == true){
537
				String cacheValue = (String)cacheMatcher.getField().get(objectToMatch);
538
				if (StringUtils.isBlank(cacheValue)){
539
					return true;  //no match
540
				}else{
541
					criteria.add(Restrictions.eq(cacheMatcher.getPropertyName(), cacheValue));
542
					criteria.add(Restrictions.eq(cacheMatcher.getProtectedPropertyName(), cacheProtected));
543

    
544
					List<DoubleResult<String, MatchMode>> replacementModes = cacheMatcher.getReplaceMatchModes(matching);
545
					for (DoubleResult<String, MatchMode> replacementMode: replacementModes ){
546
						String propertyName = replacementMode.getFirstResult();
547
						List<MatchMode> replaceMatcherList = replaceMatchers.get(propertyName);
548
						if (replaceMatcherList == null){
549
							replaceMatcherList = new ArrayList<MatchMode>();
550
							replaceMatchers.put(propertyName, replaceMatcherList);
551
						}
552
						replaceMatcherList.add(replacementMode.getSecondResult());
553
					}
554

    
555
				}
556
			}
557
		}
558
		for (FieldMatcher fieldMatcher : matching.getFieldMatchers(false)){
559
			String propertyName = fieldMatcher.getPropertyName();
560
			Type propertyType = classMetaData.getPropertyType(propertyName);
561
			Object value = fieldMatcher.getField().get(objectToMatch);
562
			List<MatchMode> matchModes= new ArrayList<MatchMode>();
563
			matchModes.add(fieldMatcher.getMatchMode());
564
			if (replaceMatchers.get(propertyName) != null){
565
				matchModes.addAll(replaceMatchers.get(propertyName));
566
			}
567

    
568
			boolean isIgnore = false;
569
			for (MatchMode matchMode : matchModes){
570
				isIgnore |= matchMode.isIgnore(value);
571
			}
572
			if (! isIgnore ){
573
				if (propertyType.isComponentType()){
574
					matchComponentType(criteria, fieldMatcher, propertyName, value, matchModes);
575
				}else{
576
					noMatch = matchNonComponentType(criteria, fieldMatcher, propertyName, value, matchModes, propertyType);
577
				}
578
			}
579
			if (noMatch){
580
				return noMatch;
581
			}
582
		}
583
		return noMatch;
584
	}
585

    
586
	/**
587
	 * @param criteria
588
	 * @param fieldMatcher
589
	 * @param propertyName
590
	 * @param value
591
	 * @param matchMode
592
	 * @throws MatchException
593
	 * @throws IllegalAccessException
594
	 */
595
	private void matchComponentType(Criteria criteria,
596
			FieldMatcher fieldMatcher, String propertyName, Object value,
597
			List<MatchMode> matchModes) throws MatchException, IllegalAccessException {
598
		if (value == null){
599
			boolean requiresSecondNull = requiresSecondNull(matchModes, value);
600
			if (requiresSecondNull){
601
				criteria.add(Restrictions.isNull(propertyName));
602
			}else{
603
				//TODO
604
				logger.warn("Component type not yet implemented for (null) value: " + propertyName);
605
				throw new MatchException("Component type not yet fully implemented for (null) value. Property: " + propertyName);
606
			}
607
		}else{
608
			Class<?> componentClass = fieldMatcher.getField().getType();
609
			Map<String, Field> fields = CdmUtils.getAllFields(componentClass, Object.class, false, false, true, false);
610
			for (String fieldName : fields.keySet()){
611
				String restrictionPath = propertyName +"."+fieldName;
612
				Object componentValue = fields.get(fieldName).get(value);
613
				//TODO diffentiate matchMode
614
				createCriterion(criteria, restrictionPath, componentValue, matchModes);
615
			}
616
		}
617
	}
618

    
619
	private boolean matchNonComponentType(Criteria criteria,
620
			FieldMatcher fieldMatcher,
621
			String propertyName,
622
			Object value,
623
			List<MatchMode> matchModes,
624
			Type propertyType)
625
			throws HibernateException, DataAccessException, MatchException, IllegalAccessException{
626

    
627
	    boolean noMatch = false;
628
		if (isRequired(matchModes) && value == null){
629
			noMatch = true;
630
			return noMatch;
631
		}else if (requiresSecondNull(matchModes,value)){
632
			criteria.add(Restrictions.isNull(propertyName));
633
		}else{
634
			if (isMatch(matchModes)){
635
				if (propertyType.isCollectionType()){
636
					//TODO collection not yet handled for match
637
				}else{
638
					int joinType = CriteriaSpecification.INNER_JOIN;
639
					if (! requiresSecondValue(matchModes,value)){
640
						joinType = CriteriaSpecification.LEFT_JOIN;
641
					}
642
					Criteria matchCriteria = criteria.createCriteria(propertyName, joinType).add(Restrictions.isNotNull("id"));
643
					Class matchClass = value.getClass();
644
					if (IMatchable.class.isAssignableFrom(matchClass)){
645
						IMatchStrategy valueMatchStrategy = DefaultMatchStrategy.NewInstance(matchClass);
646
						ClassMetadata valueClassMetaData = getSession().getSessionFactory().getClassMetadata(matchClass.getCanonicalName());;
647
						noMatch = makeCriteria(value, valueMatchStrategy, valueClassMetaData, matchCriteria);
648
					}else{
649
						logger.error("Class to match (" + matchClass + ") is not of type IMatchable");
650
						throw new MatchException("Class to match (" + matchClass + ") is not of type IMatchable");
651
					}
652
				}
653
			}else if (isEqual(matchModes)){
654
				createCriterion(criteria, propertyName, value, matchModes);
655
			}else {
656
				logger.warn("Unhandled match mode: " + matchModes + ", value: " + (value==null?"null":value));
657
			}
658
		}
659
		return noMatch;
660
	}
661

    
662
	/**
663
	 * @param criteria
664
	 * @param propertyName
665
	 * @param value
666
	 * @param matchMode
667
	 * @throws MatchException
668
	 */
669
	private void createCriterion(Criteria criteria, String propertyName,
670
			Object value, List<MatchMode> matchModes) throws MatchException {
671
		Criterion finalRestriction = null;
672
		Criterion equalRestriction = Restrictions.eq(propertyName, value);
673
		Criterion nullRestriction = Restrictions.isNull(propertyName);
674
		if (this.requiresSecondValue(matchModes, value)){
675
			finalRestriction = equalRestriction;
676
		}else if (requiresSecondNull(matchModes, value) ){
677
			finalRestriction = nullRestriction;
678
		}else{
679
			finalRestriction = Restrictions.or(equalRestriction, nullRestriction);
680
		}
681
		//return finalRestriction;
682
		criteria.add(finalRestriction);
683
	}
684

    
685
	/**
686
	 * @param matchModes
687
	 * @param value
688
	 * @return
689
	 * @throws MatchException
690
	 */
691
	private boolean requiresSecondNull(List<MatchMode> matchModes, Object value) throws MatchException {
692
		boolean result = true;
693
		for (MatchMode matchMode: matchModes){
694
			result &= matchMode.requiresSecondNull(value);
695
		}
696
		return result;
697
	}
698

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

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

    
727
	/**
728
	 * Returns true if at least one match mode is of typ MATCH_XXX
729
	 * @param matchModes
730
	 * @param value
731
	 * @return
732
	 * @throws MatchException
733
	 */
734
	private boolean isMatch(List<MatchMode> matchModes) throws MatchException {
735
		boolean result = false;
736
		for (MatchMode matchMode: matchModes){
737
			result |= matchMode.isMatch();
738
		}
739
		return result;
740
	}
741

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

    
757
	/* (non-Javadoc)
758
	 * @see eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao#saveMetaData(eu.etaxonomy.cdm.model.common.CdmMetaData)
759
	 */
760
	@Override
761
    public void saveMetaData(CdmMetaData cdmMetaData) {
762
		getSession().saveOrUpdate(cdmMetaData);
763
	}
764

    
765

    
766
	@Override
767
    public List<CdmMetaData> getMetaData() {
768
		Session session = getSession();
769
		Criteria crit = session.createCriteria(CdmMetaData.class);
770
		@SuppressWarnings("unchecked")
771
        List<CdmMetaData> results = crit.list();
772
		return results;
773
	}
774

    
775
    @Override
776
    public Object initializeCollection(UUID ownerUuid, String fieldName, List<String> appendedPropertyPaths)  {
777
        List<String> propertyPaths = new ArrayList<String>();
778
        propertyPaths.add(fieldName);
779
        if(appendedPropertyPaths != null && !appendedPropertyPaths.isEmpty()) {
780
            for(String app : appendedPropertyPaths) {
781
                propertyPaths.add(fieldName + "." + app);
782
            }
783
        }
784
        CdmBase cdmBase = load(ownerUuid, propertyPaths);
785
        Field field = ReflectionUtils.findField(cdmBase.getClass(), fieldName);
786
        field.setAccessible(true);
787
        Object obj;
788
        try {
789
            obj = field.get(cdmBase);
790
        } catch (IllegalAccessException e) {
791
            throw new IllegalArgumentException("Requested object is not accessible");
792
        }
793
        if(obj instanceof Collection || obj instanceof Map) {
794
            return obj;
795
        } else {
796
            throw new IllegalArgumentException("Field name provided does not correspond to a collection or map");
797
        }
798
    }
799

    
800
    @Override
801
    public Object initializeCollection(UUID ownerUuid, String fieldName)  {
802
        return initializeCollection(ownerUuid, fieldName, null);
803
    }
804

    
805
    @Override
806
    public boolean isEmpty(UUID ownerUuid, String fieldName) {
807
        Object col = initializeCollection(ownerUuid, fieldName);
808
        if(col instanceof Collection) {
809
            return ((Collection<?>)col).isEmpty();
810
        } else if(col instanceof Map){
811
            return ((Map<?,?>)col).isEmpty();
812
        }
813

    
814
        return false;
815
    }
816

    
817
    @Override
818
    public int size(UUID ownerUuid, String fieldName) {
819
        Object col = initializeCollection(ownerUuid, fieldName);
820
        if(col instanceof Collection) {
821
            return ((Collection<?>)col).size();
822
        } else if(col instanceof Map){
823
            return ((Map<?,?>)col).size();
824
        }
825
        return 0;
826
    }
827

    
828

    
829
    @Override
830
    public Object get(UUID ownerUuid, String fieldName, int index) {
831
        Object col = initializeCollection(ownerUuid, fieldName);
832
        if(col instanceof List) {
833
            return ((List<?>)col).get(index);
834
        } else {
835
            throw new IllegalArgumentException("Field name provided does not correspond to a list");
836
        }
837
    }
838

    
839
    @Override
840
    public boolean contains(UUID ownerUuid, String fieldName, Object element) {
841
        Object col = initializeCollection(ownerUuid, fieldName);
842
        if(col instanceof Collection) {
843
            return ((Collection<?>)col).contains(element);
844
        } else {
845
            throw new IllegalArgumentException("Field name provided does not correspond to a collection");
846
        }
847
    }
848

    
849
    @Override
850
    public boolean containsKey(UUID ownerUuid, String fieldName, Object key) {
851
        Object col = initializeCollection(ownerUuid, fieldName);
852
        if(col instanceof Map) {
853
            return ((Map<?,?>)col).containsKey(key);
854
        } else {
855
            throw new IllegalArgumentException("Field name provided does not correspond to a map");
856
        }
857
    }
858

    
859

    
860

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

    
871
    @Override
872
	public void createFullSampleData() {
873
		FullCoverageDataGenerator dataGenerator = new FullCoverageDataGenerator();
874
		dataGenerator.fillWithData(getSession());
875
	}
876

    
877

    
878

    
879
}
(4-4/23)