[hibernate-commits] Hibernate SVN: r15406 - in core/trunk: envers and 105 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Mon Oct 27 14:56:32 EDT 2008


Author: steve.ebersole at jboss.com
Date: 2008-10-27 14:56:31 -0400 (Mon, 27 Oct 2008)
New Revision: 15406

Added:
   core/trunk/envers/
   core/trunk/envers/pom.xml
   core/trunk/envers/src/
   core/trunk/envers/src/demo/
   core/trunk/envers/src/demo/java/
   core/trunk/envers/src/demo/java/org/
   core/trunk/envers/src/demo/java/org/jboss/
   core/trunk/envers/src/demo/java/org/jboss/envers/
   core/trunk/envers/src/demo/java/org/jboss/envers/demo/
   core/trunk/envers/src/demo/java/org/jboss/envers/demo/Address.java
   core/trunk/envers/src/demo/java/org/jboss/envers/demo/Person.java
   core/trunk/envers/src/demo/java/org/jboss/envers/demo/TestConsole.java
   core/trunk/envers/src/demo/resources/
   core/trunk/envers/src/demo/resources/META-INF/
   core/trunk/envers/src/demo/resources/META-INF/persistence.xml
   core/trunk/envers/src/main/
   core/trunk/envers/src/main/java/
   core/trunk/envers/src/main/java/org/
   core/trunk/envers/src/main/java/org/hibernate/
   core/trunk/envers/src/main/java/org/hibernate/tool/
   core/trunk/envers/src/main/java/org/hibernate/tool/ant/
   core/trunk/envers/src/main/java/org/hibernate/tool/ant/EnversHibernateToolTask.java
   core/trunk/envers/src/main/java/org/jboss/
   core/trunk/envers/src/main/java/org/jboss/envers/
   core/trunk/envers/src/main/java/org/jboss/envers/DefaultRevisionEntity.java
   core/trunk/envers/src/main/java/org/jboss/envers/ModificationStore.java
   core/trunk/envers/src/main/java/org/jboss/envers/RevisionEntity.java
   core/trunk/envers/src/main/java/org/jboss/envers/RevisionListener.java
   core/trunk/envers/src/main/java/org/jboss/envers/RevisionNumber.java
   core/trunk/envers/src/main/java/org/jboss/envers/RevisionTimestamp.java
   core/trunk/envers/src/main/java/org/jboss/envers/RevisionType.java
   core/trunk/envers/src/main/java/org/jboss/envers/SecondaryVersionsTable.java
   core/trunk/envers/src/main/java/org/jboss/envers/SecondaryVersionsTables.java
   core/trunk/envers/src/main/java/org/jboss/envers/Unversioned.java
   core/trunk/envers/src/main/java/org/jboss/envers/Versioned.java
   core/trunk/envers/src/main/java/org/jboss/envers/VersionsJoinTable.java
   core/trunk/envers/src/main/java/org/jboss/envers/VersionsReader.java
   core/trunk/envers/src/main/java/org/jboss/envers/VersionsReaderFactory.java
   core/trunk/envers/src/main/java/org/jboss/envers/VersionsTable.java
   core/trunk/envers/src/main/java/org/jboss/envers/ant/
   core/trunk/envers/src/main/java/org/jboss/envers/ant/AnnotationConfigurationTaskWithEnvers.java
   core/trunk/envers/src/main/java/org/jboss/envers/ant/ConfigurationTaskWithEnvers.java
   core/trunk/envers/src/main/java/org/jboss/envers/ant/JPAConfigurationTaskWithEnvers.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/EntitiesConfigurator.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/GlobalConfiguration.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/PersistentClassGraphDefiner.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/RevisionInfoConfiguration.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/VersionsConfiguration.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/VersionsEntitiesConfiguration.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/AnnotationsMetadataReader.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/BasicMetadataGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/CollectionMetadataGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/EntityXmlMappingData.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/IdMetadataGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/InheritanceType.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/MetadataTools.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/PersistentClassVersioningData.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/PropertyStoreInfo.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/QueryGeneratorBuilder.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/ToOneRelationMetadataGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/VersionsMetadataGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/EntitiesConfigurations.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/EntityConfiguration.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/EntityInstantiator.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/IdMappingData.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/RelationDescription.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/RelationType.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/RevisionTypeType.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/CompositeMapperBuilder.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/ExtendedPropertyMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/MapPropertyMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/MultiPropertyMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/PersistentCollectionChangeData.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/PropertyMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SimpleMapperBuilder.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SinglePropertyMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SubclassPropertyMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/AbstractCompositeIdMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/AbstractIdMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/EmbeddedIdMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/IdMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/MultipleIdMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/QueryParameterData.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/SimpleIdMapperBuilder.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/SingleIdMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/AbstractCollectionMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/BasicCollectionMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/CommonCollectionMapperData.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/ListCollectionMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MapCollectionMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MiddleComponentData.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MiddleIdData.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/OneToOneNotOwningMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/ToOneIdMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleComponentMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleDummyComponentMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleRelatedComponentMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleSimpleComponentMapper.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/AbstractDelegateSessionImplementor.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/ToOneDelegateSessionImplementor.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/AbstractCollectionInitializor.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/ArrayCollectionInitializor.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/BasicCollectionInitializor.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/Initializor.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/ListCollectionInitializor.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/MapCollectionInitializor.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/CollectionProxy.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/ListProxy.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/MapProxy.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SetProxy.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SortedMapProxy.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SortedSetProxy.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/OneEntityQueryGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/OneVersionsEntityQueryGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/QueryGeneratorTools.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/RelationQueryGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/ThreeEntityQueryGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/TwoEntityQueryGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/entity/
   core/trunk/envers/src/main/java/org/jboss/envers/entity/VersionsInheritanceEntityPersister.java
   core/trunk/envers/src/main/java/org/jboss/envers/event/
   core/trunk/envers/src/main/java/org/jboss/envers/event/VersionsEventListener.java
   core/trunk/envers/src/main/java/org/jboss/envers/exception/
   core/trunk/envers/src/main/java/org/jboss/envers/exception/NotVersionedException.java
   core/trunk/envers/src/main/java/org/jboss/envers/exception/RevisionDoesNotExistException.java
   core/trunk/envers/src/main/java/org/jboss/envers/exception/VersionsException.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/
   core/trunk/envers/src/main/java/org/jboss/envers/query/RevisionProperty.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/RevisionTypeProperty.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsQuery.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsQueryCreator.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsRestrictions.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/AggregatedFieldVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/BetweenVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/CriteriaTools.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/ExtendableCriterion.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/IdentifierEqVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/InVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/LogicalVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NotNullVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NotVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NullVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/PropertyVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/RelatedVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/RevisionVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/SimpleVersionsExpression.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsConjunction.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsCriterion.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsDisjunction.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/impl/
   core/trunk/envers/src/main/java/org/jboss/envers/query/impl/AbstractVersionsQuery.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/impl/EntitiesAtRevisionQuery.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/impl/RevisionsOfEntityQuery.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/order/
   core/trunk/envers/src/main/java/org/jboss/envers/query/order/RevisionVersionsOrder.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/order/VersionsOrder.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/projection/
   core/trunk/envers/src/main/java/org/jboss/envers/query/projection/RevisionVersionsProjection.java
   core/trunk/envers/src/main/java/org/jboss/envers/query/projection/VersionsProjection.java
   core/trunk/envers/src/main/java/org/jboss/envers/reader/
   core/trunk/envers/src/main/java/org/jboss/envers/reader/FirstLevelCache.java
   core/trunk/envers/src/main/java/org/jboss/envers/reader/VersionsReaderImpl.java
   core/trunk/envers/src/main/java/org/jboss/envers/reader/VersionsReaderImplementor.java
   core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/
   core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/DefaultRevisionInfoGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoGenerator.java
   core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoNumberReader.java
   core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoQueryCreator.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/VersionsSync.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/VersionsSyncManager.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/AbstractVersionsWorkUnit.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/AddWorkUnit.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/CollectionChangeWorkUnit.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/DelWorkUnit.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckDispatcher.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckResult.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckVisitor.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/ModWorkUnit.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java
   core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/VersionsWorkUnit.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/
   core/trunk/envers/src/main/java/org/jboss/envers/tools/ArgumentsTools.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/ArraysTools.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/ConcurrentReferenceHashMap.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/HibernateVersion.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/MutableBoolean.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/MutableInteger.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/Pair.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/StringTools.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/Tools.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/Triple.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/
   core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/GraphDefiner.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/GraphTopologicalSort.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/TopologicalSort.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/Vertex.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/log/
   core/trunk/envers/src/main/java/org/jboss/envers/tools/log/YLog.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/log/YLogManager.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/query/
   core/trunk/envers/src/main/java/org/jboss/envers/tools/query/Parameters.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/query/QueryBuilder.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/
   core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/ReflectionTools.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YClass.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YMethodsAndClasses.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YProperty.java
   core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YReflectionManager.java
   core/trunk/envers/src/main/resources/
   core/trunk/envers/src/main/resources/META-INF/
   core/trunk/envers/src/main/resources/META-INF/COPYING.txt
   core/trunk/envers/src/main/resources/META-INF/copyright.txt
   core/trunk/envers/src/test/
   core/trunk/envers/src/test/java/
   core/trunk/envers/src/test/java/org/
   core/trunk/envers/src/test/java/org/jboss/
   core/trunk/envers/src/test/java/org/jboss/envers/
   core/trunk/envers/src/test/java/org/jboss/envers/test/
   core/trunk/envers/src/test/java/org/jboss/envers/test/AbstractEntityTest.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/IntTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/StrIntTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/StrTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/UnversionedEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/UnversionedStrTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/EnumSetEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringListEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringMapEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringSetEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/Component1.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/Component2.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/ComponentTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/Component.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/CompositeCustomTypeEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/CompositeTestUserType.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/ParametrizedCustomTypeEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/ParametrizedTestUserType.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/EmbId.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/EmbIdTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/MulId.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/MulIdTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/ListOwnedEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/ListOwningEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/MapOwnedEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/MapOwningEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/SetOwnedEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/SetOwningEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/ListUniEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/MapUniEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/SetUniEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/CollectionRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/CollectionRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ListRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ListRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/SetRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/SetRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/DoubleSetRefCollEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ListRefCollEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/SetJoinColumnRefCollEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/SetRefCollEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/SetRefCollEntityEmbId.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/SetRefCollEntityMulId.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefEdEmbIdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefEdMulIdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefIngEmbIdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefIngMulIdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/CustomPropertyAccessRevEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/CustomRevEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity1.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity2.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity3.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity4.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/Delete.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/GlobalVersioned.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/ManyOperationsInTransaction.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/NotVersioned.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/NullProperties.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/Simple.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/SingleOperationInTransaction.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/UnversionedPropertiesChange.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/UnversionedProperty.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/OneToManyCache.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/OneToOneCache.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/QueryCache.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/EnumSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringList.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringMap.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/ComponentMapKey.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/ComponentMapKeyEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/IdMapKey.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/IdMapKeyEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/components/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/components/Components.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/CompositeCustom.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/ParametrizedCustom.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/DateTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Dates.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/EnumTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Enums.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/LobTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Lobs.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/SerObject.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/SerializableTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Serializables.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/AbstractFlushTest.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushAddDel.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushAddMod.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushModDel.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushModMod.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/ManualFlush.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/ids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/ids/CompositeIds.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ChildEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ChildVersioning.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ParentEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ParentVersioning.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ChildIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ChildReferencing.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ParentNotIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ReferencedEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ChildIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ParentIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/PolymorphicCollection.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ReferencedEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicList.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicMap.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/TernaryMap.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/TernaryMapEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniList.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniMap.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/BasicNaming.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/DetachedNamingTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNaming.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNamingRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNamingRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/NamingTestEntity1.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/OneToManyUnidirectionalNaming.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/VersionsJoinTableNaming.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/VersionsJoinTableTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/EmbIdNaming.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNaming.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNamingRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNamingRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNaming.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNamingRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNamingRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/MulIdNaming.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/NotInsertable.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/NotInsertableTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicCollection.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicList.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithEmbId.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithMulId.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithNullsDelete.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BidirectionalMapKey.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/RefEdMapKeyEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/RefIngMapKeyEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedList.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSetWithEmbId.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSetWithMulId.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicJoinColumnSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/DataChangesDetachedSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/DoubleDetachedSet.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BiRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BiRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/Bidirectional.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/Bidirectional2.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BidirectionalNoNulls.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiEmbIdRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiEmbIdRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiMulIdRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiMulIdRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/EmbIdBidirectional.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/MulIdBidirectional.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UniRefEdEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UniRefIngEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/Unidirectional.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UnidirectionalWithNulls.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/PropertiesTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/UnversionedOptimisticLockingField.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/UnversionedOptimisticLockingFieldEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/VersionsProperties.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/AggregateQuery.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/CustomRevEntityQuery.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/DeletedEntities.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/MaximalizePropertyQuery.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/OrderByLimitQuery.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/RevisionConstraintQuery.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/SimpleQuery.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/EmbIdOneToManyQuery.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/MulIdOneToManyQuery.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Custom.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomBoxed.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomBoxedRevEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomPropertyAccess.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Inherited.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/InheritedRevEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Listener.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/ListenerRevEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/LongRevNumber.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/LongRevNumberRevEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/TestRevisionListener.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/revfordate/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/revfordate/RevisionForDate.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIdTestEntity1.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIdTestEntity2.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIds.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/BasicSecondary.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/NamingSecondary.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/SecondaryNamingTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/SecondaryTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/EmbIdSecondary.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/MulIdSecondary.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/SecondaryEmbIdTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/SecondaryMulIdTestEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/MappedSubclassing.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/SubclassEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/SuperclassOfEntity.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/performance/
   core/trunk/envers/src/test/java/org/jboss/envers/test/performance/AbstractPerformanceTest.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/performance/InsertsPerformance.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/performance/UpdatesPerformance.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/tools/
   core/trunk/envers/src/test/java/org/jboss/envers/test/tools/TestTools.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/various/
   core/trunk/envers/src/test/java/org/jboss/envers/test/various/Address.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/various/HsqlTest.java
   core/trunk/envers/src/test/java/org/jboss/envers/test/various/Person.java
   core/trunk/envers/src/test/resources/
   core/trunk/envers/src/test/resources/hibernate.test.cfg.xml
   core/trunk/envers/src/test/resources/testng.xml
Modified:
   core/trunk/pom.xml
Log:
HHH-3351 : import envers as core module


Property changes on: core/trunk/envers
___________________________________________________________________
Name: svn:ignore
   + target
local
*.ipr
*.iws
*.iml
.classpath
.project
.nbattrs
*.log
*.properties
.clover


Added: core/trunk/envers/pom.xml
===================================================================
--- core/trunk/envers/pom.xml	                        (rev 0)
+++ core/trunk/envers/pom.xml	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,235 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.hibernate</groupId>
+        <artifactId>hibernate-parent</artifactId>
+        <version>3.4.0-SNAPSHOT</version>
+        <relativePath>../parent/pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.hibernate</groupId>
+    <artifactId>hibernate-envers</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Hibernate Envers</name>
+    <description>Support for entity historizing</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <systemProperties>
+                    </systemProperties>
+                    <suiteXmlFiles>
+                        <suiteXmlFile>resources/test/testng.xml</suiteXmlFile>
+                    </suiteXmlFiles>
+                </configuration>
+            </plugin>
+
+            <!-- javadocs : we want these run in the 'package' lifecycle phase-->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>javadoc</goal>
+                        </goals>
+                        <configuration>
+                            <aggregate>${jbossenvers.reports.aggregate}</aggregate>
+                            <links>
+                                <link>http://java.sun.com/j2se/1.5.0/docs/api/</link>
+                                <link>http://java.sun.com/javaee/5/docs/api/</link>
+                            </links>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.persistence</groupId>
+            <artifactId>persistence-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-annotations</artifactId>
+            <exclusions>
+                <!-- todo : only needed until we migrate annotations/em back to core -->
+                <exclusion>
+                    <groupId>org.hibernate</groupId>
+                    <artifactId>hibernate</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>net.sf.ehcache</groupId>
+                    <artifactId>ehcache</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>cglib</groupId>
+                    <artifactId>cglib</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>asm</groupId>
+                    <artifactId>asm</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>asm</groupId>
+                    <artifactId>asm-attrs</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-commons-annotations</artifactId>
+            <exclusions>
+                <!-- todo : only needed until we migrate annotations/em back to core -->
+                <exclusion>
+                    <groupId>org.hibernate</groupId>
+                    <artifactId>hibernate</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>net.sf.ehcache</groupId>
+                    <artifactId>ehcache</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>cglib</groupId>
+                    <artifactId>cglib</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>asm</groupId>
+                    <artifactId>asm</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>asm</groupId>
+                    <artifactId>asm-attrs</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-entitymanager</artifactId>
+            <exclusions>
+                <!-- todo : only needed until we migrate annotations/em back to core -->
+                <exclusion>
+                    <groupId>org.hibernate</groupId>
+                    <artifactId>hibernate</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>net.sf.ehcache</groupId>
+                    <artifactId>ehcache</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>cglib</groupId>
+                    <artifactId>cglib</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>asm</groupId>
+                    <artifactId>asm</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>asm</groupId>
+                    <artifactId>asm-attrs</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-tools</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ant</groupId>
+            <artifactId>ant</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <version>5.8</version>
+            <scope>test</scope>
+            <classifier>jdk15</classifier>
+        </dependency>
+        <dependency>
+            <groupId>hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <version>1.8.0.7</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>5.1.6</version>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.hibernate</groupId>
+                <artifactId>hibernate-core</artifactId>
+                <version>${version}</version>
+            </dependency>
+            <dependency>
+                <groupId>javax.persistence</groupId>
+                <artifactId>persistence-api</artifactId>
+                <version>1.0</version>
+            </dependency>
+            <!-- todo : change to work like hibernate-core after we import annotations and em back into core -->
+            <dependency>
+                <groupId>org.hibernate</groupId>
+                <artifactId>hibernate-annotations</artifactId>
+                <version>3.3.0.ga</version>
+            </dependency>
+            <!-- todo : change to work like hibernate-core after we import annotations and em back into core -->
+            <dependency>
+                <groupId>org.hibernate</groupId>
+                <artifactId>hibernate-commons-annotations</artifactId>
+                <version>3.3.0.ga</version>
+            </dependency>
+            <!-- todo : change to work like hibernate-core after we import annotations and em back into core -->
+            <dependency>
+                <groupId>org.hibernate</groupId>
+                <artifactId>hibernate-entitymanager</artifactId>
+                <version>3.3.1.ga</version>
+            </dependency>
+            <dependency>
+                <groupId>org.hibernate</groupId>
+                <artifactId>hibernate-tools</artifactId>
+                <version>3.2.0.ga</version>
+            </dependency>
+            <dependency>
+                <groupId>ant</groupId>
+                <artifactId>ant</artifactId>
+                <version>1.6.5</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <properties>
+        <!-- for now, at least, lets aggregate them -->
+        <jbossenvers.reports.aggregate>true</jbossenvers.reports.aggregate>
+    </properties>
+
+</project>

Added: core/trunk/envers/src/demo/java/org/jboss/envers/demo/Address.java
===================================================================
--- core/trunk/envers/src/demo/java/org/jboss/envers/demo/Address.java	                        (rev 0)
+++ core/trunk/envers/src/demo/java/org/jboss/envers/demo/Address.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,113 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.demo;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.Set;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class Address {
+    @Id
+    @GeneratedValue
+    private int id;
+
+    @Versioned
+    private String streetName;
+
+    @Versioned
+    private Integer houseNumber;
+
+    @Versioned
+    private Integer flatNumber;
+
+    @Versioned
+    @OneToMany(mappedBy = "address")
+    private Set<Person> persons;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getStreetName() {
+        return streetName;
+    }
+
+    public void setStreetName(String streetName) {
+        this.streetName = streetName;
+    }
+
+    public Integer getHouseNumber() {
+        return houseNumber;
+    }
+
+    public void setHouseNumber(Integer houseNumber) {
+        this.houseNumber = houseNumber;
+    }
+
+    public Integer getFlatNumber() {
+        return flatNumber;
+    }
+
+    public void setFlatNumber(Integer flatNumber) {
+        this.flatNumber = flatNumber;
+    }
+
+    public Set<Person> getPersons() {
+        return persons;
+    }
+
+    public void setPersons(Set<Person> persons) {
+        this.persons = persons;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Address)) return false;
+
+        Address address = (Address) o;
+
+        if (id != address.id) return false;
+        if (flatNumber != null ? !flatNumber.equals(address.flatNumber) : address.flatNumber != null) return false;
+        if (houseNumber != null ? !houseNumber.equals(address.houseNumber) : address.houseNumber != null) return false;
+        if (streetName != null ? !streetName.equals(address.streetName) : address.streetName != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = id;
+        result = 31 * result + (streetName != null ? streetName.hashCode() : 0);
+        result = 31 * result + (houseNumber != null ? houseNumber.hashCode() : 0);
+        result = 31 * result + (flatNumber != null ? flatNumber.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/demo/java/org/jboss/envers/demo/Person.java
===================================================================
--- core/trunk/envers/src/demo/java/org/jboss/envers/demo/Person.java	                        (rev 0)
+++ core/trunk/envers/src/demo/java/org/jboss/envers/demo/Person.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,99 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.demo;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class Person {
+    @Id
+    @GeneratedValue
+    private int id;
+
+    @Versioned
+    private String name;
+
+    @Versioned
+    private String surname;
+
+    @Versioned
+    @ManyToOne
+    private Address address;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSurname() {
+        return surname;
+    }
+
+    public void setSurname(String surname) {
+        this.surname = surname;
+    }
+
+    public Address getAddress() {
+        return address;
+    }
+
+    public void setAddress(Address address) {
+        this.address = address;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Person)) return false;
+
+        Person person = (Person) o;
+
+        if (id != person.id) return false;
+        if (name != null ? !name.equals(person.name) : person.name != null) return false;
+        if (surname != null ? !surname.equals(person.surname) : person.surname != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = id;
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (surname != null ? surname.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/demo/java/org/jboss/envers/demo/TestConsole.java
===================================================================
--- core/trunk/envers/src/demo/java/org/jboss/envers/demo/TestConsole.java	                        (rev 0)
+++ core/trunk/envers/src/demo/java/org/jboss/envers/demo/TestConsole.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,467 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.demo;
+
+import org.jboss.envers.VersionsReader;
+import org.jboss.envers.VersionsReaderFactory;
+import org.jboss.envers.DefaultRevisionEntity;
+import org.jboss.envers.query.VersionsRestrictions;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import java.util.*;
+import java.io.PrintStream;
+import java.io.File;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class TestConsole {
+    private EntityManager entityManager;
+
+    public TestConsole(EntityManager entityManager) {
+        this.entityManager = entityManager;
+    }
+
+    private String convertString(String s, String def) {
+        if ("NULL".equals(s)) { return null; }
+        if ("".equals(s)) { return def; }
+        return s;
+    }
+
+    private int convertStringToInteger(String s, int def) {
+        if ("".equals(s)) { return def; }
+        try {
+            return Integer.parseInt(s);
+        } catch (NumberFormatException e) {
+            System.err.println("Invalid number, returning 0.");
+            return 0;
+        }
+    }
+
+    private void printPerson(StringBuilder sb, Person p) {
+        sb.append("id = ").append(p.getId()).append(", name = ").append(p.getName())
+                .append(", surname = ").append(p.getSurname());
+
+        Address a = p.getAddress();
+        if (a != null) {
+            sb.append(", address = <").append(a.getId()).append("> ").append(a.getStreetName()).append(" ")
+                    .append(a.getHouseNumber()).append("/").append(a.getFlatNumber());
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void printPersons(StringBuilder sb) {
+        List<Person> persons = entityManager.createQuery(
+                "select p from Person p order by p.id").getResultList();
+
+        sb.append("Persons:\n");
+        for (Person p : persons) {
+            printPerson(sb, p);
+            sb.append("\n");
+        }
+    }
+
+    private void printPersonHistory(StringBuilder sb, int personId) {
+        VersionsReader reader = VersionsReaderFactory.get(entityManager);
+
+        List personHistory = reader.createQuery()
+                .forRevisionsOfEntity(Person.class, false, true)
+                .add(VersionsRestrictions.idEq(personId))
+                .getResultList();
+
+        if (personHistory.size() == 0) {
+            sb.append("A person with id ").append(personId).append(" does not exist.\n");
+        } else {
+            for (Object historyObj : personHistory) {
+                Object[] history = (Object[]) historyObj;
+                DefaultRevisionEntity revision = (DefaultRevisionEntity) history[1];
+                sb.append("revision = ").append(revision.getId()).append(", ");
+                printPerson(sb, (Person) history[0]);
+                sb.append(" (").append(revision.getRevisionDate()).append(")\n");
+            }
+        }
+    }
+
+    private void printPersonAtRevision(StringBuilder sb, int personId, int revision) {
+        VersionsReader reader = VersionsReaderFactory.get(entityManager);
+
+        Person p = reader.find(Person.class, personId, revision);
+        if (p == null) {
+            sb.append("This person does not exist at that revision.");
+        } else {
+            printPerson(sb, p);
+        }
+    }
+
+    private void readAndSetAddress(Scanner scanner, Person p) {
+        Address old = p.getAddress();
+
+        String input = scanner.nextLine();
+        if ("NULL".equals(input)) {
+            p.setAddress(null);
+            if (old != null) {
+                old.getPersons().remove(p);
+            }
+        } else if ("".equals(input)) {
+        } else {
+            try {
+                Integer id = Integer.valueOf(input);
+
+                Address a = entityManager.find(Address.class, id);
+
+                if (a == null) {
+                    System.err.println("Unknown address id, setting to NULL.");
+                    p.setAddress(null);
+                    if (old != null) {
+                        old.getPersons().remove(p);
+                    }
+                } else {
+                    p.setAddress(a);
+
+                    a.getPersons().add(p);
+
+                    if (old != null) {
+                        old.getPersons().remove(p);
+                    }
+                }
+            } catch (NumberFormatException e) {
+                System.err.println("Invalid address id, setting to NULL.");
+                p.setAddress(null);
+                if (old != null) {
+                    old.getPersons().remove(p);
+                }
+            }
+        }
+    }
+
+    private Person readNewPerson(PrintStream out, Scanner scanner) {
+        Person p = new Person();
+
+        out.print("Person name (NULL for null): ");
+        p.setName(convertString(scanner.nextLine(), ""));
+
+        out.print("Person surname (NULL for null): ");
+        p.setSurname(convertString(scanner.nextLine(), ""));
+
+        out.print("Person address id (NULL for null): ");
+        readAndSetAddress(scanner, p);
+
+        return p;
+    }
+
+    private void readModifyPerson(PrintStream out, Scanner scanner, int personId) {
+        Person current = entityManager.find(Person.class, personId);
+
+        if (current == null) {
+            out.println("Person with id " + personId + " does not exist.");
+            return;
+        }
+
+        out.print("Person name (NULL for null, enter for no change, current - " + current.getName() + "): ");
+        current.setName(convertString(scanner.nextLine(), current.getName()));
+
+        out.print("Person surname (NULL for null, enter for no change, current - " + current.getSurname() + "): ");
+        current.setSurname(convertString(scanner.nextLine(), current.getSurname()));
+
+        out.print("Person address id (NULL for null, enter for no change, current - " +
+                (current.getAddress() == null ? "NULL" : current.getAddress().getId()) + "): ");
+        readAndSetAddress(scanner, current);
+    }
+
+    private void printAddress(StringBuilder sb, Address a) {
+        sb.append("id = ").append(a.getId()).append(", streetName = ").append(a.getStreetName())
+                .append(", houseNumber = ").append(a.getHouseNumber())
+                .append(", flatNumber = ").append(a.getFlatNumber())
+                .append(", persons = (");
+
+        Iterator<Person> iter = a.getPersons().iterator();
+        while (iter.hasNext()) {
+            Person p = iter.next();
+            sb.append("<").append(p.getId()).append("> ").append(p.getName()).append(" ").append(p.getSurname());
+            if (iter.hasNext()) {
+                sb.append(", ");
+            }
+        }
+
+        sb.append(")");
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void printAddresses(StringBuilder sb) {
+        List<Address> addresses = entityManager.createQuery(
+                "select a from Address a order by a.id").getResultList();
+
+        sb.append("Addresses:\n");
+        for (Address a : addresses) {
+            printAddress(sb, a);
+            sb.append("\n");
+        }
+    }
+
+    private void printAddressHistory(StringBuilder sb, int addressId) {
+        VersionsReader reader = VersionsReaderFactory.get(entityManager);
+
+        List addressHistory = reader.createQuery()
+                .forRevisionsOfEntity(Address.class, false, true)
+                .add(VersionsRestrictions.idEq(addressId))
+                .getResultList();
+
+        if (addressHistory.size() == 0) {
+            sb.append("A address with id ").append(addressId).append(" does not exist.\n");
+        } else {
+            for (Object historyObj : addressHistory) {
+                Object[] history = (Object[]) historyObj;
+                DefaultRevisionEntity revision = (DefaultRevisionEntity) history[1];
+                sb.append("revision = ").append(revision.getId()).append(", ");
+                printAddress(sb, (Address) history[0]);
+                sb.append(" (").append(revision.getRevisionDate()).append(")\n");
+            }
+        }
+    }
+
+    private void printAddressAtRevision(StringBuilder sb, int addressId, int revision) {
+        VersionsReader reader = VersionsReaderFactory.get(entityManager);
+
+        Address a = reader.find(Address.class, addressId, revision);
+        if (a == null) {
+            sb.append("This address does not exist at that revision.");
+        } else {
+            printAddress(sb, a);
+        }
+    }
+
+    private Address readNewAddress(PrintStream out, Scanner scanner) {
+        Address a = new Address();
+
+        out.print("Street name (NULL for null): ");
+        a.setStreetName(convertString(scanner.nextLine(), ""));
+
+        out.print("House number: ");
+        a.setHouseNumber(convertStringToInteger(scanner.nextLine(), 0));
+
+        out.print("Flat number: ");
+        a.setFlatNumber(convertStringToInteger(scanner.nextLine(), 0));
+
+        a.setPersons(new HashSet<Person>());
+
+        return a;
+    }
+
+    private void readModifyAddress(PrintStream out, Scanner scanner, int addressId) {
+        Address current = entityManager.find(Address.class, addressId);
+
+        if (current == null) {
+            out.println("Address with id " + addressId + " does not exist.");
+            return;
+        }
+
+        out.print("Street name (NULL for null, enter for no change, current - " + current.getStreetName() + "): ");
+        current.setStreetName(convertString(scanner.nextLine(), current.getStreetName()));
+
+        out.print("House number (enter for no change, current - " + current.getHouseNumber() + "): ");
+        current.setHouseNumber(convertStringToInteger(scanner.nextLine(), current.getHouseNumber()));
+
+        out.print("Flat number (enter for no change, current - " + current.getFlatNumber() + "): ");
+        current.setFlatNumber(convertStringToInteger(scanner.nextLine(), current.getFlatNumber()));
+    }
+
+    private void start() {
+        Scanner scanner = new Scanner(System.in);
+        PrintStream out = System.out;
+
+        while (true) {
+            out.println("-----------------------------------------------");
+            out.println("1 - list persons             5 - list addresses");
+            out.println("2 - list person history      6 - list addresses history");
+            out.println("3 - new person               7 - new address");
+            out.println("4 - modify person            8 - modify address");
+            out.println("9 - get person at revision  10 - get address at revision");
+            out.println("                             0 - end");
+
+            try {
+                int choice = scanner.nextInt();
+
+                scanner.nextLine();
+
+                entityManager.getTransaction().begin();
+
+                StringBuilder sb;
+                int personId;
+                int addressId;
+                int revision;
+                switch (choice) {
+                    case 1:
+                        sb = new StringBuilder();
+                        printPersons(sb);
+                        out.println(sb.toString());
+                        break;
+                    case 2:
+                        out.print("Person id: ");
+                        personId = scanner.nextInt(); scanner.nextLine();
+                        sb = new StringBuilder();
+                        printPersonHistory(sb, personId);
+                        out.println(sb.toString());
+                        break;
+                    case 3:
+                        Person p = readNewPerson(out, scanner);
+                        entityManager.persist(p);
+                        break;
+                    case 4:
+                        out.print("Person id: ");
+                        personId = scanner.nextInt(); scanner.nextLine();
+                        readModifyPerson(out, scanner, personId);
+                        break;
+                    case 5:
+                        sb = new StringBuilder();
+                        printAddresses(sb);
+                        out.println(sb.toString());
+                        break;
+                    case 6:
+                        out.print("Address id: ");
+                        addressId = scanner.nextInt(); scanner.nextLine();
+                        sb = new StringBuilder();
+                        printAddressHistory(sb, addressId);
+                        out.println(sb.toString());
+                        break;
+                    case 7:
+                        Address a = readNewAddress(out, scanner);
+                        entityManager.persist(a);
+                        break;
+                    case 8:
+                        out.print("Address id: ");
+                        addressId = scanner.nextInt(); scanner.nextLine();
+                        readModifyAddress(out, scanner, addressId);
+                        break;
+                    case 9:
+                        out.print("Person id: ");
+                        personId = scanner.nextInt(); scanner.nextLine();
+                        out.print("Revision number: ");
+                        revision = scanner.nextInt(); scanner.nextLine();
+                        if (revision <= 0) {
+                            System.out.println("Revision must be greater then 0!");
+                            continue;
+                        }
+                        sb = new StringBuilder();
+                        printPersonAtRevision(sb, personId, revision);
+                        out.println(sb.toString());
+                        break;
+                    case 10:
+                        out.print("Address id: ");
+                        addressId = scanner.nextInt(); scanner.nextLine();
+                        out.print("Revision number: ");
+                        revision = scanner.nextInt(); scanner.nextLine();
+                        if (revision <= 0) {
+                            System.out.println("Revision must be greater then 0!");
+                            continue;
+                        }
+                        sb = new StringBuilder();
+                        printAddressAtRevision(sb, addressId, revision);
+                        out.println(sb.toString());
+                        break;
+
+                    case 0:
+                        return;
+                }
+            } catch (InputMismatchException e) {
+                // continuing
+            } finally {
+                entityManager.getTransaction().commit();
+            }
+        }
+    }
+
+    private boolean hasData() {
+        return (((Long) entityManager.createQuery("select count(a) from Address a").getSingleResult()) +
+                ((Long) entityManager.createQuery("select count(p) from Person p").getSingleResult())) > 0;
+    }
+
+    private void populateTestData() {
+        entityManager.getTransaction().begin();
+
+        if (!hasData()) {
+            Person p1 = new Person();
+            Person p2 = new Person();
+            Person p3 = new Person();
+
+            Address a1 = new Address();
+            Address a2 = new Address();
+
+            p1.setName("James");
+            p1.setSurname("Bond");
+            p1.setAddress(a1);
+
+            p2.setName("John");
+            p2.setSurname("McClane");
+            p2.setAddress(a2);
+
+            p3.setName("Holly");
+            p3.setSurname("Gennaro");
+            p3.setAddress(a2);
+
+            a1.setStreetName("MI6");
+            a1.setHouseNumber(18);
+            a1.setFlatNumber(25);
+            a1.setPersons(new HashSet<Person>());
+            a1.getPersons().add(p1);
+
+            a2.setStreetName("Nakatomi Plaza");
+            a2.setHouseNumber(10);
+            a2.setFlatNumber(34);
+            a2.setPersons(new HashSet<Person>());
+            a2.getPersons().add(p2);
+            a2.getPersons().add(p3);
+
+            entityManager.persist(a1);
+            entityManager.persist(a2);
+
+            entityManager.persist(p1);
+            entityManager.persist(p2);
+            entityManager.persist(p3);
+
+            System.out.println("The DB was populated with example data.");
+        }
+
+        entityManager.getTransaction().commit();
+    }
+
+    public static void main(String[] args) {
+        String userDbFile = System.getProperty("java.io.tmpdir") + File.separator + "_versions_demo.db";
+
+        Map<String, String> configurationOverrides = new HashMap<String, String>();
+        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ConsolePU", configurationOverrides);
+        EntityManager entityManager = emf.createEntityManager();
+
+        TestConsole console = new TestConsole(entityManager);
+
+        System.out.println("");
+        System.out.println("Welcome to EntityVersions demo!");
+        System.out.println("HSQLDB database file location: " + userDbFile);
+
+        console.populateTestData();
+        console.start();
+
+        entityManager.close();
+        emf.close();
+    }
+}

Added: core/trunk/envers/src/demo/resources/META-INF/persistence.xml
===================================================================
--- core/trunk/envers/src/demo/resources/META-INF/persistence.xml	                        (rev 0)
+++ core/trunk/envers/src/demo/resources/META-INF/persistence.xml	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
+    <persistence-unit name="ConsolePU">
+        <provider>org.hibernate.ejb.HibernatePersistence</provider>
+        <class>org.jboss.envers.demo.Address</class>
+        <class>org.jboss.envers.demo.Person</class>
+        <exclude-unlisted-classes />
+        <properties>
+            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
+            <property name="hibernate.connection.url" value="jdbc:hsqldb:file:${java.io.tmpdir}/_versions_demo.db"/>
+            <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
+            <property name="hibernate.connection.username" value="sa"/>
+            <property name="hibernate.connection.password" value=""/>
+
+            <!--<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
+            <property name="hibernate.connection.url" value="jdbc:mysql:///hibernate_tests?useUnicode=true&amp;characterEncoding=UTF-8"/>
+            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
+            <property name="hibernate.connection.username" value="root"/>
+            <property name="hibernate.connection.password" value=""/>-->
+
+            <property name="hibernate.hbm2ddl.auto" value="update"/>
+            <!--<property name="hibernate.show_sql" value="true"/>
+            <property name="hibernate.format_sql" value="true"/>-->
+            
+            <property name="hibernate.ejb.event.post-insert" value="org.jboss.envers.event.VersionsEventListener" />
+            <property name="hibernate.ejb.event.post-update" value="org.jboss.envers.event.VersionsEventListener" />
+            <property name="hibernate.ejb.event.post-delete" value="org.jboss.envers.event.VersionsEventListener" />
+        </properties>
+    </persistence-unit>
+</persistence>
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/hibernate/tool/ant/EnversHibernateToolTask.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/tool/ant/EnversHibernateToolTask.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/hibernate/tool/ant/EnversHibernateToolTask.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,59 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.hibernate.tool.ant;
+
+import org.jboss.envers.ant.JPAConfigurationTaskWithEnvers;
+import org.jboss.envers.ant.ConfigurationTaskWithEnvers;
+import org.jboss.envers.ant.AnnotationConfigurationTaskWithEnvers;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EnversHibernateToolTask extends HibernateToolTask {
+    private void checkConfiguration() {
+        if (configurationTask!=null) {
+            throw new BuildException("Only a single configuration is allowed.");
+        }
+    }
+
+    public JPAConfigurationTask createJpaConfiguration() {
+        checkConfiguration();
+        JPAConfigurationTask task = new JPAConfigurationTaskWithEnvers();
+        configurationTask = task;
+        return task;
+    }
+
+    public ConfigurationTask createConfiguration() {
+        checkConfiguration();
+        ConfigurationTaskWithEnvers task = new ConfigurationTaskWithEnvers();
+        configurationTask = task;
+        return task;
+    }
+
+    public AnnotationConfigurationTask createAnnotationConfiguration() {
+        checkConfiguration();
+        AnnotationConfigurationTaskWithEnvers task = new AnnotationConfigurationTaskWithEnvers();
+        configurationTask = task;
+        return task;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/DefaultRevisionEntity.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/DefaultRevisionEntity.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/DefaultRevisionEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,87 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Transient;
+import java.util.Date;
+import java.text.DateFormat;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at MappedSuperclass
+public class DefaultRevisionEntity {
+    @Id
+    @GeneratedValue
+    @RevisionNumber
+    private int id;
+
+    @RevisionTimestamp
+    private long timestamp;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    @Transient
+    public Date getRevisionDate() {
+        return new Date(timestamp);
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DefaultRevisionEntity)) return false;
+
+        DefaultRevisionEntity that = (DefaultRevisionEntity) o;
+
+        if (id != that.id) return false;
+        if (timestamp != that.timestamp) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = id;
+        result = 31 * result + (int) (timestamp ^ (timestamp >>> 32));
+        return result;
+    }
+
+    public String toString() {
+        return "DefaultRevisionEntity(id = " + id + ", revisionDate = " + DateFormat.getDateTimeInstance().format(getRevisionDate()) + ")";
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/ModificationStore.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/ModificationStore.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/ModificationStore.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,29 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public enum ModificationStore {
+    FULL
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/RevisionEntity.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/RevisionEntity.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/RevisionEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,41 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * Marks an entity to be created whenever a new revision is generated. The revisions entity must have
+ * an integer-valued unique property (preferrably the primary id) annotated with {@link RevisionNumber}
+ * and a long-valued property annotated with {@link RevisionTimestamp}. The {@link DefaultRevisionEntity}
+ * already has those two fields, so you may extend it, but you may also write your own revision entity
+ * from scratch.
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.TYPE)
+public @interface RevisionEntity {
+    Class<? extends RevisionListener> value()    default RevisionListener.class;
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/RevisionListener.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/RevisionListener.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/RevisionListener.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,36 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+/**
+ * An implementation of this class, having a no-arg constructor, should be passed as an argument to the
+ * {@link RevisionEntity} annotation.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RevisionListener {
+    /**
+     * Called when a new revision is created.
+     * @param revisionEntity An instance of the entity annotated with {@link RevisionEntity}, which will be persisted
+     * after this method returns. All properties on this entity that are to be persisted should be set by this method.
+     */
+    void newRevision(Object revisionEntity);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/RevisionNumber.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/RevisionNumber.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/RevisionNumber.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,40 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * Marks a property which will hold the number of the revision in a revision entity, see
+ * {@link RevisionListener}. Values of this property should form a strictly-increasing sequence
+ * of numbers. The value of this property won't be set by Envers. In most cases, this should be
+ * an auto-generated database-assigned primary id.
+ * @author Adam Warski (adam at warski dot org)
+ * @author Sanne Grinovero
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ElementType.METHOD, ElementType.FIELD})
+public @interface RevisionNumber {
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/RevisionTimestamp.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/RevisionTimestamp.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/RevisionTimestamp.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,38 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * Marks a property which will hold the timestamp of the revision in a revision entity, see
+ * {@link RevisionListener}. The value of this property will be automatically set by Envers.
+ * @author Adam Warski (adam at warski dot org)
+ * @author Sanne Grinovero
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ElementType.METHOD, ElementType.FIELD})
+public @interface RevisionTimestamp {
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/RevisionType.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/RevisionType.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/RevisionType.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,65 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+/**
+ * Type of the revision.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public enum RevisionType {
+    /**
+     * Indicates that the entity was added (persisted) at that revision.
+     */
+    ADD((byte) 0),
+    /**
+     * Indicates that the entity was modified (one or more of its fields) at that revision.
+     */
+    MOD((byte) 1),
+    /**
+     * Indicates that the entity was deleted (removed) at that revision.
+     */
+    DEL((byte) 2);
+
+    private Byte representation;
+
+    RevisionType(byte representation) {
+        this.representation = representation;
+    }
+
+    public Byte getRepresentation() {
+        return representation;
+    }
+
+    public static RevisionType fromRepresentation(Object representation) {
+        if (representation == null || !(representation instanceof Byte)) {
+            return null;
+        }
+
+        switch ((Byte) representation) {
+            case 0: return ADD;
+            case 1: return MOD;
+            case 2: return DEL;
+        }
+
+        throw new IllegalArgumentException("Unknown representation: " + representation);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/SecondaryVersionsTable.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/SecondaryVersionsTable.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/SecondaryVersionsTable.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,38 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Target(ElementType.TYPE)
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface SecondaryVersionsTable {
+    String secondaryTableName();
+
+    String secondaryVersionsTableName();
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/SecondaryVersionsTables.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/SecondaryVersionsTables.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/SecondaryVersionsTables.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,36 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Target(ElementType.TYPE)
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface SecondaryVersionsTables {
+    SecondaryVersionsTable[] value();
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/Unversioned.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/Unversioned.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/Unversioned.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,37 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * When applied to a field, indicates that this field should not be versioned.
+ * @author Sebastian Komander
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ElementType.METHOD, ElementType.FIELD})
+public @interface Unversioned {
+
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/Versioned.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/Versioned.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/Versioned.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,38 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * When applied to a class, indicates that all of its properties should be versioned.
+ * When applied to a field, indicates that this field should be versioned.
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+public @interface Versioned {
+    ModificationStore modStore()    default ModificationStore.FULL;
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/VersionsJoinTable.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/VersionsJoinTable.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/VersionsJoinTable.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,57 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import javax.persistence.JoinColumn;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ElementType.FIELD, ElementType.METHOD})
+public @interface VersionsJoinTable {
+    /**
+     * @return Name of the join table. Defaults to a concatenation of the names of the primary table of the entity
+     * owning the association and of the primary table of the entity referenced by the association.
+     */
+    String name() default "";
+
+    /**
+     * @return The schema of the join table. Defaults to the schema of the entity owning the association.
+     */
+    String schema() default "";
+
+    /**
+     * @return The catalog of the join table. Defaults to the catalog of the entity owning the association.
+     */
+    String catalog() default "";
+
+    /**
+     * @return The foreign key columns of the join table which reference the primary table of the entity that does not
+     * own the association (i.e. the inverse side of the association).
+     */
+    JoinColumn[] inverseJoinColumns() default {};
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/VersionsReader.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/VersionsReader.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/VersionsReader.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,108 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import org.jboss.envers.exception.NotVersionedException;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+import org.jboss.envers.query.VersionsQueryCreator;
+
+import java.util.List;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface VersionsReader {
+    /**
+     * Find an entity by primary key at the given revision.
+     * @param cls Class of the entity.
+     * @param primaryKey Primary key of the entity.
+     * @param revision Revision in which to get the entity.
+     * @return The found entity instance at the given revision (its properties may be partially filled
+     * if not all properties are versioned) or null, if an entity with that id didn't exist at that
+     * revision.
+     * @throws IllegalArgumentException If cls or primaryKey is null or revision is less or equal to 0.
+     * @throws NotVersionedException When entities of the given class are not versioned.
+     * @throws IllegalStateException If the associated entity manager is closed.
+     */
+    <T> T find(Class<T> cls, Object primaryKey, Number revision) throws
+            IllegalArgumentException, NotVersionedException, IllegalStateException;
+
+    /**
+     * Get a list of revision numbers, at which an entity was modified.
+     * @param cls Class of the entity.
+     * @param primaryKey Primary key of the entity.
+     * @return A list of revision numbers, at which the entity was modified, sorted in ascending order (so older
+     * revisions come first).
+     * @throws NotVersionedException When entities of the given class are not versioned.
+     * @throws IllegalArgumentException If cls or primaryKey is null.
+     * @throws IllegalStateException If the associated entity manager is closed.
+     */
+    List<Number> getRevisions(Class<?> cls, Object primaryKey)
+            throws IllegalArgumentException, NotVersionedException, IllegalStateException;
+
+    /**
+     * Get the date, at which a revision was created.
+     * @param revision Number of the revision for which to get the date.
+     * @return Date of commiting the given revision.
+     * @throws IllegalArgumentException If revision is less or equal to 0.
+     * @throws RevisionDoesNotExistException If the revision does not exist.
+     * @throws IllegalStateException If the associated entity manager is closed.
+     */
+    Date getRevisionDate(Number revision) throws IllegalArgumentException, RevisionDoesNotExistException,
+            IllegalStateException;
+
+    /**
+     * Gets the revision number, that corresponds to the given date. More precisely, returns
+     * the number of the highest revision, which was created on or before the given date. So:
+     * <code>getRevisionDate(getRevisionNumberForDate(date)) <= date</code> and
+     * <code>getRevisionDate(getRevisionNumberForDate(date)+1) > date</code>.
+     * @param date Date for which to get the revision.
+     * @return Revision number corresponding to the given date.
+     * @throws IllegalStateException If the associated entity manager is closed.
+     * @throws RevisionDoesNotExistException If the given date is before the first revision.
+     * @throws IllegalArgumentException If <code>date</code> is <code>null</code>.
+     */
+    Number getRevisionNumberForDate(Date date) throws IllegalStateException, RevisionDoesNotExistException,
+            IllegalArgumentException;
+
+    /**
+     * A helper method; should be used only if a custom revision entity is used. See also {@link RevisionEntity}.
+     * @param revisionEntityClass Class of the revision entity. Should be annotated with {@link RevisionEntity}.
+     * @param revision Number of the revision for which to get the data.
+     * @return Entity containing data for the given revision.
+     * @throws IllegalArgumentException If revision is less or equal to 0 or if the class of the revision entity
+     * is invalid.
+     * @throws RevisionDoesNotExistException If the revision does not exist.
+     * @throws IllegalStateException If the associated entity manager is closed.
+     */
+    <T> T findRevision(Class<T> revisionEntityClass, Number revision) throws IllegalArgumentException,
+            RevisionDoesNotExistException, IllegalStateException;
+
+    /**
+     *
+     * @return A query creator, associated with this VersionsReader instance, with which queries can be
+     * created and later executed. Shouldn't be used after the associated Session or EntityManager
+     * is closed.
+     */
+    VersionsQueryCreator createQuery();
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/VersionsReaderFactory.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/VersionsReaderFactory.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/VersionsReaderFactory.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,90 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import org.hibernate.Session;
+import org.hibernate.event.EventListeners;
+import org.hibernate.event.PostInsertEventListener;
+import org.hibernate.engine.SessionImplementor;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.event.VersionsEventListener;
+import static org.jboss.envers.tools.ArraysTools.arrayIncludesInstanceOf;
+import org.jboss.envers.reader.VersionsReaderImpl;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsReaderFactory {
+    private VersionsReaderFactory() { }
+
+    /**
+     * Create a versions reader associated with an open session.
+     * <b>WARNING:</b> Using Envers with Hibernate (not with Hibernate Entity Manager/JPA) is experimental,
+     * if possible, use {@link org.jboss.envers.VersionsReaderFactory#get(javax.persistence.EntityManager)}.
+     * @param session An open session.
+     * @return A versions reader associated with the given sesison. It shouldn't be used
+     * after the session is closed.
+     * @throws VersionsException When the given required listeners aren't installed.
+     */
+    public static VersionsReader get(Session session) throws VersionsException {
+        SessionImplementor sessionImpl = (SessionImplementor) session;
+
+        EventListeners listeners = sessionImpl.getListeners();
+
+        for (PostInsertEventListener listener : listeners.getPostInsertEventListeners()) {
+            if (listener instanceof VersionsEventListener) {
+                if (arrayIncludesInstanceOf(listeners.getPostUpdateEventListeners(), VersionsEventListener.class) &&
+                        arrayIncludesInstanceOf(listeners.getPostDeleteEventListeners(), VersionsEventListener.class)) {
+                    return new VersionsReaderImpl(((VersionsEventListener) listener).getVerCfg(), session,
+                            sessionImpl);
+                }
+            }
+        }
+
+        throw new VersionsException("You need install the org.jboss.envers.event.VersionsEventListener " +
+                "class as post insert, update and delete event listener.");
+    }
+
+    /**
+     * Create a versions reader associated with an open entity manager.
+     * @param entityManager An open entity manager.
+     * @return A versions reader associated with the given entity manager. It shouldn't be used
+     * after the entity manager is closed.
+     * @throws VersionsException When the given entity manager is not based on Hibernate, or if the required
+     * listeners aren't installed.
+     */
+    public static VersionsReader get(EntityManager entityManager) throws VersionsException {
+        if (entityManager.getDelegate() instanceof Session) {
+            return get((Session) entityManager.getDelegate());
+        }
+
+        if (entityManager.getDelegate() instanceof EntityManager) {
+            if (entityManager.getDelegate() instanceof Session) {
+                return get((Session) entityManager.getDelegate());
+            }
+        }
+
+        throw new VersionsException("Hibernate EntityManager not present!");
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/VersionsTable.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/VersionsTable.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/VersionsTable.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,46 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers;
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.TYPE)
+public @interface VersionsTable {
+    String value();
+
+    /**
+     * @return The schema of the table. Defaults to the schema of the annotated entity.
+     */
+    String schema() default "";
+
+    /**
+     * @return The catalog of the table. Defaults to the catalog of the annotated entity.
+     */
+    String catalog() default "";
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/ant/AnnotationConfigurationTaskWithEnvers.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/ant/AnnotationConfigurationTaskWithEnvers.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/ant/AnnotationConfigurationTaskWithEnvers.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,37 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.ant;
+
+import org.hibernate.tool.ant.AnnotationConfigurationTask;
+import org.hibernate.cfg.Configuration;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class AnnotationConfigurationTaskWithEnvers extends AnnotationConfigurationTask {
+    protected void doConfiguration(Configuration configuration) {
+        VersionsConfiguration.getFor(configuration);
+
+        super.doConfiguration(configuration);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/ant/ConfigurationTaskWithEnvers.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/ant/ConfigurationTaskWithEnvers.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/ant/ConfigurationTaskWithEnvers.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,37 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.ant;
+
+import org.hibernate.tool.ant.ConfigurationTask;
+import org.hibernate.cfg.Configuration;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ConfigurationTaskWithEnvers extends ConfigurationTask {
+    protected void doConfiguration(Configuration configuration) {
+        VersionsConfiguration.getFor(configuration);
+
+        super.doConfiguration(configuration);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/ant/JPAConfigurationTaskWithEnvers.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/ant/JPAConfigurationTaskWithEnvers.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/ant/JPAConfigurationTaskWithEnvers.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,37 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.ant;
+
+import org.hibernate.tool.ant.JPAConfigurationTask;
+import org.hibernate.cfg.Configuration;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class JPAConfigurationTaskWithEnvers extends JPAConfigurationTask {
+    protected void doConfiguration(Configuration configuration) {
+        VersionsConfiguration.getFor(configuration);
+
+        super.doConfiguration(configuration);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/EntitiesConfigurator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/EntitiesConfigurator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/EntitiesConfigurator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,143 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration;
+
+import org.jboss.envers.entities.EntitiesConfigurations;
+import org.jboss.envers.configuration.metadata.VersionsMetadataGenerator;
+import org.jboss.envers.configuration.metadata.PersistentClassVersioningData;
+import org.jboss.envers.configuration.metadata.AnnotationsMetadataReader;
+import org.jboss.envers.configuration.metadata.EntityXmlMappingData;
+import org.jboss.envers.tools.graph.GraphTopologicalSort;
+import org.jboss.envers.tools.reflection.YReflectionManager;
+import org.jboss.envers.tools.StringTools;
+import org.dom4j.io.DOMWriter;
+import org.dom4j.io.XMLWriter;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.MappingException;
+import org.hibernate.cfg.Configuration;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.ByteArrayOutputStream;
+import java.io.Writer;
+import java.io.PrintWriter;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EntitiesConfigurator {
+    public EntitiesConfigurations configure(Configuration cfg, YReflectionManager reflectionManager,
+                                            GlobalConfiguration globalCfg, VersionsEntitiesConfiguration verEntCfg,
+                                            Document revisionInfoXmlMapping, Element revisionInfoRelationMapping) {
+        VersionsMetadataGenerator versionsMetaGen = new VersionsMetadataGenerator(cfg, globalCfg, verEntCfg,
+                revisionInfoRelationMapping);
+        DOMWriter writer = new DOMWriter();
+
+        // Sorting the persistent class topologically - superclass always before subclass
+        Iterator<PersistentClass> classes = GraphTopologicalSort.sort(new PersistentClassGraphDefiner(cfg)).iterator();
+
+        Map<PersistentClass, PersistentClassVersioningData> pcDatas =
+                new HashMap<PersistentClass, PersistentClassVersioningData>();
+        Map<PersistentClass, EntityXmlMappingData> xmlMappings = new HashMap<PersistentClass, EntityXmlMappingData>();
+
+        // First pass
+        while (classes.hasNext()) {
+            PersistentClass pc = classes.next();
+            // Collecting information from annotations on the persistent class pc
+            AnnotationsMetadataReader annotationsMetadataReader =
+                    new AnnotationsMetadataReader(globalCfg, reflectionManager, pc);
+            PersistentClassVersioningData versioningData = annotationsMetadataReader.getVersioningData();
+
+            if (versioningData.isVersioned()) {
+                pcDatas.put(pc, versioningData);
+
+                if (!StringTools.isEmpty(versioningData.versionsTable.value())) {
+                    verEntCfg.addCustomVersionsTableName(pc.getEntityName(), versioningData.versionsTable.value());
+                }
+
+                EntityXmlMappingData xmlMappingData = new EntityXmlMappingData();
+                versionsMetaGen.generateFirstPass(pc, versioningData, xmlMappingData);
+                xmlMappings.put(pc, xmlMappingData);
+            }
+        }
+
+        // Second pass
+        for (Map.Entry<PersistentClass, PersistentClassVersioningData> pcDatasEntry : pcDatas.entrySet()) {
+            EntityXmlMappingData xmlMappingData = xmlMappings.get(pcDatasEntry.getKey());
+
+            versionsMetaGen.generateSecondPass(pcDatasEntry.getKey(), pcDatasEntry.getValue(), xmlMappingData);
+
+            try {
+                cfg.addDocument(writer.write(xmlMappingData.getMainXmlMapping()));
+                // TODO
+                //writeDocument(xmlMappingData.getMainXmlMapping());
+
+                for (Document additionalMapping : xmlMappingData.getAdditionalXmlMappings()) {
+                    cfg.addDocument(writer.write(additionalMapping));
+                    // TODO
+                    //writeDocument(additionalMapping);
+                }
+            } catch (DocumentException e) {
+                throw new MappingException(e);
+            }
+        }
+
+        // Only if there are any versioned classes
+        if (pcDatas.size() > 0) {
+            try {
+                if (revisionInfoXmlMapping !=  null) {
+                    // TODO
+                    //writeDocument(revisionInfoXmlMapping);
+                    cfg.addDocument(writer.write(revisionInfoXmlMapping));
+                }
+            } catch (DocumentException e) {
+                throw new MappingException(e);
+            }
+        }
+
+        return new EntitiesConfigurations(versionsMetaGen.getEntitiesConfigurations());
+    }
+
+    // todo
+    private void writeDocument(Document e) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        Writer w = new PrintWriter(baos);
+
+        try {
+            XMLWriter xw = new XMLWriter(w, new OutputFormat(" ", true));
+            xw.write(e);
+            w.flush();
+        } catch (IOException e1) {
+            e1.printStackTrace();
+        }
+
+        System.out.println("-----------");
+        System.out.println(baos.toString());
+        System.out.println("-----------");
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/GlobalConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/GlobalConfiguration.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/GlobalConfiguration.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.  
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration;
+
+import java.util.Properties;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ * @author Nicolas Doroskevich
+ */
+public class GlobalConfiguration {
+    // Should a revision be generated when a not-owned relation field changes
+    private final boolean generateRevisionsForCollections;
+
+    // Should a warning, instead of an error and an exception, be logged, when an unsupported type is versioned
+    private final boolean warnOnUnsupportedTypes;
+
+    // Should the optimistic locking property of an entity be considered unversioned
+    private final boolean unversionedOptimisticLockingField;
+
+    /*
+     Which operator to use in correlated subqueries (when we want a property to be equal to the result of
+     a correlated subquery, for example: e.p <operator> (select max(e2.p) where e2.p2 = e.p2 ...).
+     Normally, this should be "=". However, HSQLDB has an issue related to that, so as a workaround,
+     "in" is used. See {@link org.jboss.envers.test.various.HsqlTest}.
+     */
+    private final String correlatedSubqueryOperator;
+
+    public GlobalConfiguration(Properties properties) {
+        String generateRevisionsForCollectionsStr = properties.getProperty("org.jboss.envers.revisionOnCollectionChange",
+                "true");
+        generateRevisionsForCollections = Boolean.parseBoolean(generateRevisionsForCollectionsStr);
+
+        String warnOnUnsupportedTypesStr = properties.getProperty("org.jboss.envers.warnOnUnsupportedTypes",
+                "false");
+        warnOnUnsupportedTypes = Boolean.parseBoolean(warnOnUnsupportedTypesStr);
+
+        String ignoreOptimisticLockingPropertyStr = properties.getProperty("org.jboss.envers.unversionedOptimisticLockingField",
+                "false");
+        unversionedOptimisticLockingField = Boolean.parseBoolean(ignoreOptimisticLockingPropertyStr);
+
+        correlatedSubqueryOperator = "org.hibernate.dialect.HSQLDialect".equals(
+                properties.getProperty("hibernate.dialect")) ? "in" : "=";
+    }
+
+    public boolean isGenerateRevisionsForCollections() {
+        return generateRevisionsForCollections;
+    }
+
+    public boolean isWarnOnUnsupportedTypes() {
+        return warnOnUnsupportedTypes;
+    }
+
+    public boolean isUnversionedOptimisticLockingField() {
+        return unversionedOptimisticLockingField;
+    }
+
+    public String getCorrelatedSubqueryOperator() {
+        return correlatedSubqueryOperator;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/PersistentClassGraphDefiner.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/PersistentClassGraphDefiner.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/PersistentClassGraphDefiner.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,75 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration;
+
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.cfg.Configuration;
+import org.jboss.envers.tools.graph.GraphDefiner;
+import org.jboss.envers.tools.Tools;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * Defines a graph, where the vertexes are all persistent classes, and there is an edge from
+ * p.c. A to p.c. B iff A is a superclass of B.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PersistentClassGraphDefiner implements GraphDefiner<PersistentClass, String> {
+    private Configuration cfg;
+
+    public PersistentClassGraphDefiner(Configuration cfg) {
+        this.cfg = cfg;
+    }
+
+    public String getRepresentation(PersistentClass pc) {
+        return pc.getEntityName();
+    }
+
+    public PersistentClass getValue(String entityName) {
+        return cfg.getClassMapping(entityName);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addNeighbours(List<PersistentClass> neighbours, Iterator<PersistentClass> subclassIterator) {
+        while (subclassIterator.hasNext()) {
+            PersistentClass subclass = subclassIterator.next();
+            neighbours.add(subclass);
+            addNeighbours(neighbours, (Iterator<PersistentClass>) subclass.getSubclassIterator());
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public List<PersistentClass> getNeighbours(PersistentClass pc) {
+        List<PersistentClass> neighbours = new ArrayList<PersistentClass>();
+
+        addNeighbours(neighbours, (Iterator<PersistentClass>) pc.getSubclassIterator());
+
+        return neighbours;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public List<PersistentClass> getValues() {
+        return Tools.iteratorToList((Iterator<PersistentClass>) cfg.getClassMappings());
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/RevisionInfoConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/RevisionInfoConfiguration.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/RevisionInfoConfiguration.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,265 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration;
+
+import org.jboss.envers.tools.reflection.YClass;
+import org.jboss.envers.tools.reflection.YReflectionManager;
+import org.jboss.envers.tools.reflection.YProperty;
+import org.jboss.envers.tools.MutableBoolean;
+import org.jboss.envers.*;
+import org.jboss.envers.configuration.metadata.MetadataTools;
+import org.jboss.envers.revisioninfo.*;
+import org.hibernate.MappingException;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.cfg.Configuration;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionInfoConfiguration {
+    private String revisionInfoEntityName;
+    private String revisionInfoIdName;
+    private String revisionInfoTimestampName;
+    private String revisionInfoTimestampType;
+
+    private String revisionPropType;
+
+    public RevisionInfoConfiguration() {
+        revisionInfoEntityName = "org.jboss.envers.DefaultRevisionEntity";
+        revisionInfoIdName = "id";
+        revisionInfoTimestampName = "timestamp";
+        revisionInfoTimestampType = "long";
+
+        revisionPropType = "integer";
+    }
+
+    private Document generateDefaultRevisionInfoXmlMapping() {
+        Document document = DocumentHelper.createDocument();
+
+        Element class_mapping = MetadataTools.createEntity(document, null, null, null, null, null);
+
+        class_mapping.addAttribute("name", revisionInfoEntityName);
+        class_mapping.addAttribute("table", "_revisions_info");
+
+        Element idProperty = MetadataTools.addNativelyGeneratedId(class_mapping, revisionInfoIdName,
+                revisionPropType);
+        MetadataTools.addColumn(idProperty, "revision_id", null);
+
+        Element timestampProperty = MetadataTools.addProperty(class_mapping, revisionInfoTimestampName,
+                revisionInfoTimestampType, true, false);
+        MetadataTools.addColumn(timestampProperty, "revision_timestamp", null);
+
+        return document;
+    }
+
+    private Element generateRevisionInfoRelationMapping() {
+        Document document = DocumentHelper.createDocument();
+        Element rev_rel_mapping =document.addElement("key-many-to-one");
+        rev_rel_mapping.addAttribute("type", revisionPropType);
+        rev_rel_mapping.addAttribute("class", revisionInfoEntityName);
+
+        return rev_rel_mapping;
+    }
+
+    private void searchForRevisionInfoCfgInProperties(YClass clazz, YReflectionManager reflectionManager,
+                                    MutableBoolean revisionNumberFound, MutableBoolean revisionTimestampFound,
+                                    String accessType) {
+        for (YProperty property : clazz.getDeclaredProperties(accessType)) {
+            RevisionNumber revisionNumber = property.getAnnotation(RevisionNumber.class);
+            RevisionTimestamp revisionTimestamp = property.getAnnotation(RevisionTimestamp.class);
+
+            if (revisionNumber != null) {
+                if (revisionNumberFound.isSet()) {
+                    throw new MappingException("Only one property may be annotated with @RevisionNumber!");
+                }
+
+                YClass revisionNumberClass = property.getType();
+                if (reflectionManager.equals(revisionNumberClass, Integer.class) ||
+                        reflectionManager.equals(revisionNumberClass, Integer.TYPE)) {
+                    revisionInfoIdName = property.getName();
+                    revisionNumberFound.set();
+                } else if (reflectionManager.equals(revisionNumberClass, Long.class) ||
+                        reflectionManager.equals(revisionNumberClass, Long.TYPE)) {
+                    revisionInfoIdName = property.getName();
+                    revisionNumberFound.set();
+
+                    // The default is integer
+                    revisionPropType = "long";
+                } else {
+                    throw new MappingException("The field annotated with @RevisionNumber must be of type " +
+                            "int, Integer, long or Long");
+                }
+            }
+
+            if (revisionTimestamp != null) {
+                if (revisionTimestampFound.isSet()) {
+                    throw new MappingException("Only one property may be annotated with @RevisionTimestamp!");
+                }
+
+                YClass revisionTimestampClass = property.getType();
+                if (reflectionManager.equals(revisionTimestampClass, Long.class) ||
+                        reflectionManager.equals(revisionTimestampClass, Long.TYPE)) {
+                    revisionInfoTimestampName = property.getName();
+                    revisionTimestampFound.set();
+                } else {
+                    throw new MappingException("The field annotated with @RevisionTimestamp must be of type " +
+                            "long or Long");
+                }
+            }
+        }
+    }
+
+    private void searchForRevisionInfoCfg(YClass clazz, YReflectionManager reflectionManager,
+                                          MutableBoolean revisionNumberFound, MutableBoolean revisionTimestampFound) {
+        YClass superclazz = clazz.getSuperclass();
+        if (!"java.lang.Object".equals(superclazz.getName())) {
+            searchForRevisionInfoCfg(superclazz, reflectionManager, revisionNumberFound, revisionTimestampFound);
+        }
+
+        searchForRevisionInfoCfgInProperties(clazz, reflectionManager, revisionNumberFound, revisionTimestampFound,
+                "field");
+        searchForRevisionInfoCfgInProperties(clazz, reflectionManager, revisionNumberFound, revisionTimestampFound,
+                "property");
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public RevisionInfoConfigurationResult configure(Configuration cfg, YReflectionManager reflectionManager) {
+        Iterator<PersistentClass> classes = (Iterator<PersistentClass>) cfg.getClassMappings();
+        boolean revisionEntityFound = false;
+        RevisionInfoGenerator revisionInfoGenerator = null;
+
+        Class<?> revisionInfoClass = null;
+
+        while (classes.hasNext()) {
+            PersistentClass pc = classes.next();
+            YClass clazz;
+            try {
+                clazz = reflectionManager.classForName(pc.getClassName(), this.getClass());
+            } catch (ClassNotFoundException e) {
+                throw new MappingException(e);
+            }
+
+            RevisionEntity revisionEntity = clazz.getAnnotation(RevisionEntity.class);
+            if (revisionEntity != null) {
+                if (revisionEntityFound) {
+                    throw new MappingException("Only one entity may be annotated with @RevisionEntity!");
+                }
+
+                // Checking if custom revision entity isn't versioned
+                if (clazz.getAnnotation(Versioned.class) != null) {
+                    throw new MappingException("An entity annotated with @RevisionEntity cannot be versioned!");
+                }
+
+                revisionEntityFound = true;
+
+                MutableBoolean revisionNumberFound = new MutableBoolean();
+                MutableBoolean revisionTimestampFound = new MutableBoolean();
+
+                searchForRevisionInfoCfg(clazz, reflectionManager, revisionNumberFound, revisionTimestampFound);
+
+                if (!revisionNumberFound.isSet()) {
+                    throw new MappingException("An entity annotated with @RevisionEntity must have a field annotated " +
+                            "with @RevisionNumber!");
+                }
+
+                if (!revisionTimestampFound.isSet()) {
+                    throw new MappingException("An entity annotated with @RevisionEntity must have a field annotated " +
+                            "with @RevisionTimestamp!");
+                }
+
+                revisionInfoEntityName = pc.getEntityName();
+
+                revisionInfoClass = pc.getMappedClass();
+                revisionInfoGenerator = new DefaultRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,
+                        revisionEntity.value(), revisionInfoTimestampName);
+            }
+        }
+
+        // In case of a custom revision info generator, the mapping will be null.
+        Document revisionInfoXmlMapping = null;
+
+        if (revisionInfoGenerator == null) {
+            revisionInfoClass = DefaultRevisionEntity.class;
+            revisionInfoGenerator = new DefaultRevisionInfoGenerator(revisionInfoEntityName, revisionInfoClass,
+                    RevisionListener.class, revisionInfoTimestampName);
+            revisionInfoXmlMapping = generateDefaultRevisionInfoXmlMapping();
+        }
+
+        return new RevisionInfoConfigurationResult(
+                revisionInfoGenerator, revisionInfoXmlMapping,
+                new RevisionInfoQueryCreator(revisionInfoEntityName, revisionInfoIdName, revisionInfoTimestampName),
+                generateRevisionInfoRelationMapping(),
+                new RevisionInfoNumberReader(revisionInfoClass, revisionInfoIdName), revisionInfoEntityName);
+    }
+}
+
+class RevisionInfoConfigurationResult {
+    private final RevisionInfoGenerator revisionInfoGenerator;
+    private final Document revisionInfoXmlMapping;
+    private final RevisionInfoQueryCreator revisionInfoQueryCreator;
+    private final Element revisionInfoRelationMapping;
+    private final RevisionInfoNumberReader revisionInfoNumberReader;
+    private final String revisionInfoEntityName;
+
+    RevisionInfoConfigurationResult(RevisionInfoGenerator revisionInfoGenerator,
+                                    Document revisionInfoXmlMapping, RevisionInfoQueryCreator revisionInfoQueryCreator,
+                                    Element revisionInfoRelationMapping,
+                                    RevisionInfoNumberReader revisionInfoNumberReader, String revisionInfoEntityName) {
+        this.revisionInfoGenerator = revisionInfoGenerator;
+        this.revisionInfoXmlMapping = revisionInfoXmlMapping;
+        this.revisionInfoQueryCreator = revisionInfoQueryCreator;
+        this.revisionInfoRelationMapping = revisionInfoRelationMapping;
+        this.revisionInfoNumberReader = revisionInfoNumberReader;
+        this.revisionInfoEntityName = revisionInfoEntityName;
+    }
+
+    public RevisionInfoGenerator getRevisionInfoGenerator() {
+        return revisionInfoGenerator;
+    }
+
+    public Document getRevisionInfoXmlMapping() {
+        return revisionInfoXmlMapping;
+    }
+
+    public RevisionInfoQueryCreator getRevisionInfoQueryCreator() {
+        return revisionInfoQueryCreator;
+    }
+
+    public Element getRevisionInfoRelationMapping() {
+        return revisionInfoRelationMapping;
+    }
+
+    public RevisionInfoNumberReader getRevisionInfoNumberReader() {
+        return revisionInfoNumberReader;
+    }
+
+    public String getRevisionInfoEntityName() {
+        return revisionInfoEntityName;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/VersionsConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/VersionsConfiguration.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/VersionsConfiguration.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,101 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration;
+
+import org.hibernate.cfg.Configuration;
+import org.jboss.envers.entities.EntitiesConfigurations;
+import org.jboss.envers.synchronization.VersionsSyncManager;
+import org.jboss.envers.tools.reflection.YReflectionManager;
+import org.jboss.envers.revisioninfo.RevisionInfoQueryCreator;
+import org.jboss.envers.revisioninfo.RevisionInfoNumberReader;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsConfiguration {
+    private final GlobalConfiguration globalCfg;
+    private final VersionsEntitiesConfiguration verEntCfg;
+    private final VersionsSyncManager versionsSyncManager;
+    private final EntitiesConfigurations entCfg;
+    private final RevisionInfoQueryCreator revisionInfoQueryCreator;
+    private final RevisionInfoNumberReader revisionInfoNumberReader;
+
+    public VersionsEntitiesConfiguration getVerEntCfg() {
+        return verEntCfg;
+    }
+
+    public VersionsSyncManager getSyncManager() {
+        return versionsSyncManager;
+    }
+
+    public GlobalConfiguration getGlobalCfg() {
+        return globalCfg;
+    }
+
+    public EntitiesConfigurations getEntCfg() {
+        return entCfg;
+    }
+
+    public RevisionInfoQueryCreator getRevisionInfoQueryCreator() {
+        return revisionInfoQueryCreator;
+    }
+
+    public RevisionInfoNumberReader getRevisionInfoNumberReader() {
+        return revisionInfoNumberReader;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public VersionsConfiguration(Configuration cfg) {
+        Properties properties = cfg.getProperties();
+
+        YReflectionManager reflectionManager = YReflectionManager.get(cfg);
+        RevisionInfoConfiguration revInfoCfg = new RevisionInfoConfiguration();
+        RevisionInfoConfigurationResult revInfoCfgResult = revInfoCfg.configure(cfg, reflectionManager);
+        verEntCfg = new VersionsEntitiesConfiguration(properties, revInfoCfgResult.getRevisionInfoEntityName());
+        globalCfg = new GlobalConfiguration(properties);
+        versionsSyncManager = new VersionsSyncManager(revInfoCfgResult.getRevisionInfoGenerator());
+        revisionInfoQueryCreator = revInfoCfgResult.getRevisionInfoQueryCreator();
+        revisionInfoNumberReader = revInfoCfgResult.getRevisionInfoNumberReader();
+        entCfg = new EntitiesConfigurator().configure(cfg, reflectionManager, globalCfg, verEntCfg,
+                revInfoCfgResult.getRevisionInfoXmlMapping(), revInfoCfgResult.getRevisionInfoRelationMapping());
+    }
+
+    //
+
+    private static Map<Configuration, VersionsConfiguration> cfgs
+            = new WeakHashMap<Configuration, VersionsConfiguration>();
+
+    public synchronized static VersionsConfiguration getFor(Configuration cfg) {
+        VersionsConfiguration verCfg = cfgs.get(cfg);
+
+        if (verCfg == null) {
+            verCfg = new VersionsConfiguration(cfg);
+            cfgs.put(cfg, verCfg);
+            
+            cfg.buildMappings();
+        }
+
+        return verCfg;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/VersionsEntitiesConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/VersionsEntitiesConfiguration.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/VersionsEntitiesConfiguration.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,108 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration;
+
+import java.util.*;
+
+/**
+ * Configuration of versions entities - names of fields, entities and tables created to store versioning information.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsEntitiesConfiguration {
+    private final String versionsTablePrefix;
+    private final String versionsTableSuffix;
+
+    private final String originalIdPropName;
+
+    private final String revisionPropName;
+    private final String revisionPropPath;
+
+    private final String revisionTypePropName;
+    private final String revisionTypePropType;
+
+    private final String revisionInfoEntityName;
+
+    private final Map<String, String> customVersionsTablesNames;
+
+    public VersionsEntitiesConfiguration(Properties properties, String revisionInfoEntityName) {
+        this.revisionInfoEntityName = revisionInfoEntityName;
+
+        versionsTablePrefix = properties.getProperty("org.jboss.envers.versionsTablePrefix", "");
+        versionsTableSuffix = properties.getProperty("org.jboss.envers.versionsTableSuffix", "_versions");
+
+        originalIdPropName = "originalId";
+
+        revisionPropName = properties.getProperty("org.jboss.envers.revisionFieldName", "_revision");
+
+        revisionTypePropName = properties.getProperty("org.jboss.envers.revisionTypeFieldName", "_revision_type");
+        revisionTypePropType = "byte";
+
+        customVersionsTablesNames = new HashMap<String, String>();
+
+        revisionPropPath = originalIdPropName + "." + revisionPropName + ".id";
+    }
+
+    public String getOriginalIdPropName() {
+        return originalIdPropName;
+    }
+
+    public String getRevisionPropName() {
+        return revisionPropName;
+    }
+
+    public String getRevisionPropPath() {
+        return revisionPropPath;
+    }
+
+    public String getRevisionTypePropName() {
+        return revisionTypePropName;
+    }
+
+    public String getRevisionTypePropType() {
+        return revisionTypePropType;
+    }
+
+    public String getRevisionInfoEntityName() {
+        return revisionInfoEntityName;
+    }
+
+    //
+
+    public void addCustomVersionsTableName(String entityName, String tableName) {
+        customVersionsTablesNames.put(entityName, tableName);
+    }
+
+    //
+
+    public String getVersionsEntityName(String entityName) {
+        return versionsTablePrefix + entityName + versionsTableSuffix;
+    }
+
+    public String getVersionsTableName(String entityName, String tableName) {
+        String customHistoryTableName = customVersionsTablesNames.get(entityName);
+        if (customHistoryTableName == null) {
+            return versionsTablePrefix + tableName + versionsTableSuffix;
+        }
+
+        return customHistoryTableName;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/AnnotationsMetadataReader.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/AnnotationsMetadataReader.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/AnnotationsMetadataReader.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,181 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import javax.persistence.Version;
+import javax.persistence.MapKey;
+
+import org.jboss.envers.configuration.GlobalConfiguration;
+import org.jboss.envers.tools.reflection.YClass;
+import org.jboss.envers.tools.reflection.YProperty;
+import org.jboss.envers.tools.reflection.YReflectionManager;
+import org.jboss.envers.*;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.MappingException;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * A helper class to read versioning meta-data from annotations on a persistent class.
+ * @author Adam Warski (adam at warski dot org)
+ * @author Sebastian Komander
+ */
+public final class AnnotationsMetadataReader {
+    private final GlobalConfiguration globalCfg;
+    private final YReflectionManager reflectionManager;
+    private final PersistentClass pc;
+
+    /**
+     * This object is filled with information read from annotations and returned by the <code>getVersioningData</code>
+     * method.
+     */
+    private final PersistentClassVersioningData versioningData;
+
+    public AnnotationsMetadataReader(GlobalConfiguration globalCfg, YReflectionManager reflectionManager,
+                                     PersistentClass pc) {
+        this.globalCfg = globalCfg;
+        this.reflectionManager = reflectionManager;
+        this.pc = pc;
+
+        versioningData = new PersistentClassVersioningData();
+    }
+
+    private void addPropertyVersioned(YProperty property) {
+        Versioned ver = property.getAnnotation(Versioned.class);
+        if (ver != null) {
+            versioningData.propertyStoreInfo.propertyStores.put(property.getName(), ver.modStore());
+        }
+    }
+
+    private void addPropertyMapKey(YProperty property) {
+        MapKey mapKey = property.getAnnotation(MapKey.class);
+        if (mapKey != null) {
+            versioningData.mapKeys.put(property.getName(), mapKey.name());
+        }
+    }
+
+    private void addPropertyUnversioned(YProperty property) {
+        // check if a property is declared as unversioned to exclude it
+        // useful if a class is versioned but some properties should be excluded
+        Unversioned unVer = property.getAnnotation(Unversioned.class);
+        if (unVer != null) {
+            versioningData.unversionedProperties.add(property.getName());
+        } else {
+            // if the optimistic locking field has to be unversioned and the current property
+            // is the optimistic locking field, add it to the unversioned properties list
+            if (globalCfg.isUnversionedOptimisticLockingField()) {
+                Version jpaVer = property.getAnnotation(Version.class);
+                if (jpaVer != null) {
+                    versioningData.unversionedProperties.add(property.getName());
+                }
+            }
+        }
+    }
+
+    private void addPropertyJoinTables(YProperty property) {
+        VersionsJoinTable joinTable = property.getAnnotation(VersionsJoinTable.class);
+        if (joinTable != null) {
+            versioningData.versionsJoinTables.put(property.getName(), joinTable);
+        }
+    }
+
+    private void addFromProperties(Iterable<YProperty> properties) {
+        for (YProperty property : properties) {
+            addPropertyVersioned(property);
+            addPropertyUnversioned(property);
+            addPropertyJoinTables(property);
+            addPropertyMapKey(property);
+        }
+    }
+
+    private void addPropertiesFromClass(YClass clazz)  {
+        YClass superclazz = clazz.getSuperclass();
+        if (!"java.lang.Object".equals(superclazz.getName())) {
+            addPropertiesFromClass(superclazz);
+        }
+
+        addFromProperties(clazz.getDeclaredProperties("field"));
+        addFromProperties(clazz.getDeclaredProperties("property"));
+    }
+
+    private void addDefaultVersioned(YClass clazz) {
+        Versioned defaultVersioned = clazz.getAnnotation(Versioned.class);
+
+        if (defaultVersioned != null) {
+            versioningData.propertyStoreInfo.defaultStore = defaultVersioned.modStore();
+        }
+    }
+
+    private void addVersionsTable(YClass clazz) {
+        VersionsTable versionsTable = clazz.getAnnotation(VersionsTable.class);
+        if (versionsTable != null) {
+            versioningData.versionsTable = versionsTable;
+        } else {
+            versioningData.versionsTable = getDefaultVersionsTable();
+        }
+    }
+
+    private void addVersionsSecondaryTables(YClass clazz) {
+        // Getting information on secondary tables
+        SecondaryVersionsTable secondaryVersionsTable1 = clazz.getAnnotation(SecondaryVersionsTable.class);
+        if (secondaryVersionsTable1 != null) {
+            versioningData.secondaryTableDictionary.put(secondaryVersionsTable1.secondaryTableName(),
+                    secondaryVersionsTable1.secondaryVersionsTableName());
+        }
+
+        SecondaryVersionsTables secondaryVersionsTables = clazz.getAnnotation(SecondaryVersionsTables.class);
+        if (secondaryVersionsTables != null) {
+            for (SecondaryVersionsTable secondaryVersionsTable2 : secondaryVersionsTables.value()) {
+                versioningData.secondaryTableDictionary.put(secondaryVersionsTable2.secondaryTableName(),
+                        secondaryVersionsTable2.secondaryVersionsTableName());
+            }
+        }
+    }
+
+    public PersistentClassVersioningData getVersioningData() {
+        if (pc.getClassName() == null) {
+            return versioningData;
+        }
+
+        try {
+            YClass clazz = reflectionManager.classForName(pc.getClassName(), this.getClass());
+
+            addDefaultVersioned(clazz);
+            addPropertiesFromClass(clazz);
+            addVersionsTable(clazz);
+            addVersionsSecondaryTables(clazz);
+        } catch (ClassNotFoundException e) {
+            throw new MappingException(e);
+        }
+
+        return versioningData;
+    }
+
+    private VersionsTable getDefaultVersionsTable() {
+        return new VersionsTable() {
+            public String value() { return ""; }
+            public String schema() { return ""; }
+            public String catalog() { return ""; }
+            public Class<? extends Annotation> annotationType() { return this.getClass(); }
+        };
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/BasicMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/BasicMetadataGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/BasicMetadataGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,164 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.dom4j.Element;
+import org.hibernate.mapping.*;
+import org.hibernate.type.*;
+import org.hibernate.util.StringHelper;
+import org.jboss.envers.entities.mapper.SimpleMapperBuilder;
+import org.jboss.envers.entities.mapper.CompositeMapperBuilder;
+import org.jboss.envers.ModificationStore;
+
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * Generates metadata for basic properties: immutable types (including enums) and components
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class BasicMetadataGenerator {
+    boolean addBasic(Element parent, String name, Value value, CompositeMapperBuilder mapper,
+                     ModificationStore store, String entityName, boolean insertable, boolean key) {
+        Type type = value.getType();
+
+        if (type instanceof ComponentType) {
+            addComponent(parent, name, value, mapper, entityName, key);
+            return true;
+        } else {
+            return addBasicNoComponent(parent, name, value, mapper, store, insertable, key);
+        }
+    }
+
+    boolean addBasicNoComponent(Element parent, String name, Value value, SimpleMapperBuilder mapper,
+                                ModificationStore store, boolean insertable, boolean key) {
+        Type type = value.getType();
+
+        if (type instanceof ImmutableType || type instanceof MutableType) {
+            addSimpleValue(parent, name, value, mapper, store, insertable, key);
+        } else if (type instanceof CustomType || type instanceof CompositeCustomType) {
+            addCustomValue(parent, name, value, mapper, store, insertable, key);
+        } else if ("org.hibernate.type.PrimitiveByteArrayBlobType".equals(type.getClass().getName())) {
+            addSimpleValue(parent, name, value, mapper, store, insertable, key);
+        } else {
+            return false;
+        }
+
+        return true;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addSimpleValue(Element parent, String name, Value value, SimpleMapperBuilder mapper,
+                                ModificationStore store, boolean insertable, boolean key) {
+        if (parent != null) {
+            Element prop_mapping = MetadataTools.addProperty(parent, name,
+                    value.getType().getName(), insertable, key);
+            MetadataTools.addColumns(prop_mapping, (Iterator<Column>) value.getColumnIterator());
+        }
+
+        // A null mapper means that we only want to add xml mappings
+        if (mapper != null) {
+            mapper.add(name, store);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addCustomValue(Element parent, String name, Value value, SimpleMapperBuilder mapper,
+                                ModificationStore store, boolean insertable, boolean key) {
+        if (parent != null) {
+            Element prop_mapping = MetadataTools.addProperty(parent, name,
+                    null, insertable, key);
+
+            //CustomType propertyType = (CustomType) value.getType();
+
+            Element type_mapping = prop_mapping.addElement("type");
+            type_mapping.addAttribute("name", value.getType().getName());
+
+            if (value instanceof SimpleValue) {
+                Properties typeParameters = ((SimpleValue) value).getTypeParameters();
+                if (typeParameters != null) {
+                    for (java.util.Map.Entry paramKeyValue : typeParameters.entrySet()) {
+                        Element type_param = type_mapping.addElement("param");
+                        type_param.addAttribute("name", (String) paramKeyValue.getKey());
+                        type_param.setText((String) paramKeyValue.getValue());
+                    }
+                }
+            }
+
+            MetadataTools.addColumns(prop_mapping, (Iterator<Column>) value.getColumnIterator());
+        }
+
+        if (mapper != null) {
+            mapper.add(name, store);
+        }
+    }
+
+    private void addComponentClassName(Element any_mapping, Component comp) {
+        if (StringHelper.isNotEmpty(comp.getComponentClassName())) {
+            any_mapping.addAttribute("class", comp.getComponentClassName());
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addComponent(Element parent, String name, Value value, CompositeMapperBuilder mapper,
+                              String entityName, boolean key) {
+        Element component_mapping = null;
+        Component prop_component = (Component) value;
+
+        if (parent != null) {
+            /*
+            TODO: investigate relations inside components
+            if (!firstPass) {
+                // The required element already exists.
+                Iterator<Element> iter = parent.elementIterator("component");
+                while (iter.hasNext()) {
+                    Element child = iter.next();
+                    if (child.attribute("name").getText().equals(name)) {
+                        component_mapping = child;
+                        break;
+                    }
+                }
+
+                if (component_mapping == null) {
+                    throw new VersionsException("Element for component not found during second pass!");
+                }
+            } else {
+            */
+
+            component_mapping = parent.addElement("component");
+            component_mapping.addAttribute("name", name);
+
+            addComponentClassName(component_mapping, prop_component);
+        }
+
+        CompositeMapperBuilder componentMapper = mapper.addComposite(name);
+
+        Iterator<Property> properties = (Iterator<Property>) prop_component.getPropertyIterator();
+        while (properties.hasNext()) {
+            Property property = properties.next();
+            addBasic(component_mapping, property.getName(), property.getValue(), componentMapper,
+                    ModificationStore.FULL, entityName, property.isInsertable(), key);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/CollectionMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/CollectionMetadataGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/CollectionMetadataGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,490 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.hibernate.mapping.*;
+import org.hibernate.mapping.Collection;
+import org.hibernate.MappingException;
+import org.hibernate.type.*;
+import org.jboss.envers.entities.mapper.CompositeMapperBuilder;
+import org.jboss.envers.entities.mapper.relation.*;
+import org.jboss.envers.entities.mapper.relation.lazy.proxy.*;
+import org.jboss.envers.entities.mapper.relation.query.RelationQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.query.OneVersionsEntityQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.component.*;
+import org.jboss.envers.entities.EntityConfiguration;
+import org.jboss.envers.entities.IdMappingData;
+import org.jboss.envers.tools.Tools;
+import org.jboss.envers.tools.StringTools;
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.VersionsJoinTable;
+import org.dom4j.Element;
+
+import javax.persistence.JoinColumn;
+import java.util.*;
+import java.util.Set;
+import java.util.List;
+import java.util.Map;
+import java.lang.annotation.Annotation;
+
+/**
+ * Generates metadata for a collection-valued property.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class CollectionMetadataGenerator {
+    private final VersionsMetadataGenerator mainGenerator;
+    private final String propertyName;
+    private final Collection propertyValue;
+    private final CompositeMapperBuilder currentMapper;
+    private final String referencingEntityName;
+    private final EntityXmlMappingData xmlMappingData;
+    private final VersionsJoinTable joinTable;
+    private final String mapKey;
+
+    private final EntityConfiguration referencingEntityConfiguration;
+    /**
+     * Null if this collection isn't a relation to another entity.
+     */
+    private final String referencedEntityName;
+
+    /**
+     * @param mainGenerator Main generator, giving access to configuration and the basic mapper.
+     * @param propertyName Name of the property that references the collection in the referencing entity.
+     * @param propertyValue Value of the collection, as mapped by Hibernate.
+     * @param currentMapper Mapper, to which the appropriate {@link org.jboss.envers.entities.mapper.PropertyMapper}
+     * will be added.
+     * @param referencingEntityName Name of the entity that owns this collection.
+     * @param xmlMappingData In case this collection requires a middle table, additional mapping documents will
+     * be created using this object.
+     * @param joinTable User data for the middle (join) table. <code>null</code> if the user didn't specify it.
+     * @param mapKey The value of the name() property of the MapKey annotation on this property. Null, if this
+     * property isn't annotated with this annotation.
+     */
+    public CollectionMetadataGenerator(VersionsMetadataGenerator mainGenerator, String propertyName,
+                                       Collection propertyValue, CompositeMapperBuilder currentMapper,
+                                       String referencingEntityName, EntityXmlMappingData xmlMappingData,
+                                       VersionsJoinTable joinTable, String mapKey) {
+        this.mainGenerator = mainGenerator;
+        this.propertyName = propertyName;
+        this.propertyValue = propertyValue;
+        this.currentMapper = currentMapper;
+        this.referencingEntityName = referencingEntityName;
+        this.xmlMappingData = xmlMappingData;
+        this.joinTable = joinTable == null ? getDefaultVersionsJoinTable() : joinTable;
+        this.mapKey = mapKey;
+
+        referencingEntityConfiguration = mainGenerator.getEntitiesConfigurations().get(referencingEntityName);
+        if (referencingEntityConfiguration == null) {
+            throw new MappingException("Unable to read versioning configuration for " + referencingEntityName + "!");
+        }
+
+        referencedEntityName = getReferencedEntityName(propertyValue.getElement());
+    }
+
+    private String getReferencedEntityName(Value value) {
+        if (value instanceof ToOne) {
+            return ((ToOne) value).getReferencedEntityName();
+        } else if (value instanceof OneToMany) {
+            return ((OneToMany) value).getReferencedEntityName();
+        } else {
+            return null;
+        }
+    }
+
+    void addCollection() {
+        Type type = propertyValue.getType();
+
+        if ((type instanceof BagType || type instanceof SetType || type instanceof MapType) &&
+                (propertyValue.getElement() instanceof OneToMany) && (propertyValue.isInverse())) {
+            // A one-to-many relation mapped using @ManyToOne and @OneToMany(mappedBy="...")
+            addOneToManyAttached();
+        } else {
+            // All other kinds of relations require a middle (join) table.
+            addWithMiddleTable();
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addOneToManyAttached() {
+        String mappedBy = getMappedBy(propertyValue);
+
+        EntityConfiguration referencedEntityConfiguration = mainGenerator.getEntitiesConfigurations()
+                .get(referencedEntityName);
+
+        IdMappingData referencedIdMapping = referencedEntityConfiguration.getIdMappingData();
+        IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData();
+
+        // Generating the id mappers data for the referencing side of the relation.
+        MiddleIdData referencingIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencingIdMapping,
+                mappedBy + "_", referencingEntityName);
+
+        // And for the referenced side. The prefixed mapper won't be used (as this collection isn't persisted
+        // in a join table, so the prefix value is arbitrary).
+        MiddleIdData referencedIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencedIdMapping,
+                null, referencedEntityName);
+
+        // Generating the element mapping.
+        MiddleComponentData elementComponentData = new MiddleComponentData(
+                new MiddleRelatedComponentMapper(referencedIdData), 0);
+
+        // Generating the index mapping, if an index exists. It can only exists in case a javax.persistence.MapKey
+        // annotation is present on the entity. So the middleEntityXml will be not be used. The queryGeneratorBuilder
+        // will only be checked for nullnes.
+        MiddleComponentData indexComponentData = addIndex(null, null);
+
+        // Generating the query generator - it should read directly from the related entity.
+        RelationQueryGenerator queryGenerator = new OneVersionsEntityQueryGenerator(mainGenerator.getGlobalCfg(),
+                mainGenerator.getVerEntCfg(), referencingIdData, referencedEntityName,
+                referencedIdMapping.getIdMapper());
+
+        // Creating common mapper data.
+        CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData(
+                mainGenerator.getVerEntCfg(), referencedEntityName, propertyName,
+                referencingIdData, queryGenerator);
+
+        // Checking the type of the collection and adding an appropriate mapper.
+        addMapper(commonCollectionMapperData, elementComponentData, indexComponentData);
+
+        // Storing information about this relation.
+        referencingEntityConfiguration.addToManyNotOwningRelation(propertyName, mappedBy,
+                referencedEntityName, referencingIdData.getPrefixedMapper());
+    }
+
+    /**
+     * Adds mapping of the id of a related entity to the given xml mapping, prefixing the id with the given prefix.
+     * @param xmlMapping Mapping, to which to add the xml.
+     * @param prefix Prefix for the names of properties which will be prepended to properties that form the id.
+     * @param columnNameIterator Iterator over the column names that will be used for properties that form the id.
+     * @param relatedIdMapping Id mapping data of the related entity.
+     */
+    @SuppressWarnings({"unchecked"})
+    private void addRelatedToXmlMapping(Element xmlMapping, String prefix,
+                                        MetadataTools.ColumnNameIterator columnNameIterator,
+                                        IdMappingData relatedIdMapping) {
+        Element properties = (Element) relatedIdMapping.getXmlRelationMapping().clone();
+        MetadataTools.prefixNamesInPropertyElement(properties, prefix, columnNameIterator, true);
+        for (Element idProperty : (java.util.List<Element>) properties.elements()) {
+            xmlMapping.add((Element) idProperty.clone());
+        }
+    }
+
+    private String getMiddleTableName(Collection value, String entityName) {
+        // We check how Hibernate maps the collection.
+        if (value.getElement() instanceof OneToMany && !value.isInverse()) {
+            // This must be a @JoinColumn+ at OneToMany mapping. Generating the table name, as Hibernate doesn't use a
+            // middle table for mapping this relation.
+            return StringTools.getLastComponent(entityName) + "_" + StringTools.getLastComponent(getReferencedEntityName(value.getElement()));
+        } else {
+            // Hibernate uses a middle table for mapping this relation, so we get it's name directly. 
+            return value.getCollectionTable().getName();
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addWithMiddleTable() {
+        // Generating the name of the middle table
+        String versionsMiddleTableName;
+        String versionsMiddleEntityName;
+        if (!StringTools.isEmpty(joinTable.name())) {
+            versionsMiddleTableName = joinTable.name();
+            versionsMiddleEntityName = joinTable.name();
+        } else {
+            String middleTableName = getMiddleTableName(propertyValue, referencingEntityName);
+            versionsMiddleTableName = mainGenerator.getVerEntCfg().getVersionsTableName(null, middleTableName);
+            versionsMiddleEntityName = mainGenerator.getVerEntCfg().getVersionsEntityName(middleTableName);
+        }
+
+        // Generating the XML mapping for the middle entity, only if the relation isn't inverse.
+        // If the relation is inverse, will be later checked by comparing middleEntityXml with null.
+        Element middleEntityXml;
+        if (!propertyValue.isInverse()) {
+            middleEntityXml = createMiddleEntityXml(versionsMiddleTableName, versionsMiddleEntityName);
+        } else {
+            middleEntityXml = null;
+        }
+
+        // ******
+        // Generating the mapping for the referencing entity (it must be an entity).
+        // ******
+        // Getting the id-mapping data of the referencing entity (the entity that "owns" this collection).
+        IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData();
+
+        // Only valid for an inverse relation; null otherwise.
+        String mappedBy;
+
+        // The referencing prefix is always for a related entity. So it has always the "_" at the end added.
+        String referencingPrefixRelated;
+        String referencedPrefix;
+
+        if (propertyValue.isInverse()) {
+            // If the relation is inverse, then referencedEntityName is not null.
+            mappedBy = getMappedBy(propertyValue.getCollectionTable(), mainGenerator.getCfg().getClassMapping(referencedEntityName));
+
+            referencingPrefixRelated = mappedBy + "_";
+            referencedPrefix = StringTools.getLastComponent(referencedEntityName);
+        } else {
+            mappedBy = null;
+
+            referencingPrefixRelated = StringTools.getLastComponent(referencingEntityName) + "_";
+            referencedPrefix = referencedEntityName == null ? "element" : propertyName;
+        }
+
+        // Storing the id data of the referencing entity: original mapper, prefixed mapper and entity name.
+        MiddleIdData referencingIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencingIdMapping,
+                referencingPrefixRelated, referencingEntityName);
+
+        // Creating a query generator builder, to which additional id data will be added, in case this collection
+        // references some entities (either from the element or index). At the end, this will be used to build
+        // a query generator to read the raw data collection from the middle table.
+        QueryGeneratorBuilder queryGeneratorBuilder = new QueryGeneratorBuilder(mainGenerator.getGlobalCfg(),
+                mainGenerator.getVerEntCfg(), referencingIdData, versionsMiddleEntityName);
+
+        // Adding the XML mapping for the referencing entity, if the relation isn't inverse.
+        if (middleEntityXml != null) {
+            // Adding related-entity (in this case: the referencing's entity id) id mapping to the xml.
+            addRelatedToXmlMapping(middleEntityXml, referencingPrefixRelated,
+                    MetadataTools.getColumnNameIterator(propertyValue.getKey().getColumnIterator()),
+                    referencingIdMapping);
+        }
+
+        // ******
+        // Generating the element mapping.
+        // ******
+        MiddleComponentData elementComponentData = addValueToMiddleTable(propertyValue.getElement(), middleEntityXml,
+                queryGeneratorBuilder, referencedPrefix, joinTable.inverseJoinColumns());
+
+        // ******
+        // Generating the index mapping, if an index exists.
+        // ******
+        MiddleComponentData indexComponentData = addIndex(middleEntityXml, queryGeneratorBuilder);
+
+        // ******
+        // Generating the property mapper.
+        // ******
+        // Building the query generator.
+        RelationQueryGenerator queryGenerator = queryGeneratorBuilder.build(elementComponentData, indexComponentData);
+
+        // Creating common data
+        CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData(
+                mainGenerator.getVerEntCfg(), versionsMiddleEntityName, propertyName, referencingIdData, queryGenerator);
+
+        // Checking the type of the collection and adding an appropriate mapper.
+        addMapper(commonCollectionMapperData, elementComponentData, indexComponentData);
+
+        // ******
+        // Storing information about this relation.
+        // ******
+        storeMiddleEntityRelationInformation(mappedBy);
+    }
+
+    private MiddleComponentData addIndex(Element middleEntityXml, QueryGeneratorBuilder queryGeneratorBuilder) {
+        if (propertyValue instanceof IndexedCollection) {
+            IndexedCollection indexedValue = (IndexedCollection) propertyValue;
+            if (mapKey == null) {
+                // This entity doesn't specify a javax.persistence.MapKey. Mapping it to the middle entity.
+                return addValueToMiddleTable(indexedValue.getIndex(), middleEntityXml,
+                        queryGeneratorBuilder, "mapkey", null);
+            } else {
+                IdMappingData referencedIdMapping = mainGenerator.getEntitiesConfigurations()
+                        .get(referencedEntityName).getIdMappingData();
+                int currentIndex = queryGeneratorBuilder == null ? 0 : queryGeneratorBuilder.getCurrentIndex();
+                if ("".equals(mapKey)) {
+                    // The key of the map is the id of the entity.
+                    return new MiddleComponentData(new MiddleMapKeyIdComponentMapper(mainGenerator.getVerEntCfg(),
+                            referencedIdMapping.getIdMapper()), currentIndex);
+                } else {
+                    // The key of the map is a property of the entity.
+                    return new MiddleComponentData(new MiddleMapKeyPropertyComponentMapper(mapKey), currentIndex);
+                }
+            }
+        } else {
+            // No index - creating a dummy mapper.
+            return new MiddleComponentData(new MiddleDummyComponentMapper(), 0);
+        }
+    }
+
+    /**
+     *
+     * @param value Value, which should be mapped to the middle-table, either as a relation to another entity,
+     * or as a simple value.
+     * @param xmlMapping If not <code>null</code>, xml mapping for this value is added to this element.
+     * @param queryGeneratorBuilder In case <code>value</code> is a relation to another entity, information about it
+     * should be added to the given.
+     * @param prefix Prefix for proeprty names of related entities identifiers.
+     * @param joinColumns Names of columns to use in the xml mapping, if this array isn't null and has any elements.
+     * @return Data for mapping this component.
+     */
+    @SuppressWarnings({"unchecked"})
+    private MiddleComponentData addValueToMiddleTable(Value value, Element xmlMapping,
+                                                      QueryGeneratorBuilder queryGeneratorBuilder,
+                                                      String prefix, JoinColumn[] joinColumns) {
+        Type type = value.getType();
+        if (type instanceof ManyToOneType) {
+            String prefixRelated = prefix + "_";
+
+            String referencedEntityName = getReferencedEntityName(value);
+            IdMappingData referencedIdMapping = mainGenerator.getEntitiesConfigurations()
+                    .get(referencedEntityName).getIdMappingData();
+
+            // Adding related-entity (in this case: the referenced entities id) id mapping to the xml only if the
+            // relation isn't inverse (so when <code>xmlMapping</code> is not null).
+            if (xmlMapping != null) {
+                addRelatedToXmlMapping(xmlMapping, prefixRelated,
+                        joinColumns != null && joinColumns.length > 0
+                                ? MetadataTools.getColumnNameIterator(joinColumns)
+                                : MetadataTools.getColumnNameIterator(value.getColumnIterator()),
+                        referencedIdMapping);
+            }
+
+            // Storing the id data of the referenced entity: original mapper, prefixed mapper and entity name.
+            MiddleIdData referencedIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencedIdMapping,
+                    prefixRelated, referencedEntityName);
+            // And adding it to the generator builder.
+            queryGeneratorBuilder.addRelation(referencedIdData);
+
+            return new MiddleComponentData(new MiddleRelatedComponentMapper(referencedIdData),
+                    queryGeneratorBuilder.getCurrentIndex());
+        } else {
+            // Last but one parameter: collection components are always insertable
+            boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasicNoComponent(xmlMapping, prefix, value, null,
+                    ModificationStore.FULL, true, true);
+
+            if (mapped) {
+                // Simple values are always stored in the first item of the array returned by the query generator.
+                return new MiddleComponentData(new MiddleSimpleComponentMapper(mainGenerator.getVerEntCfg(), prefix), 0);
+            } else {
+                mainGenerator.throwUnsupportedTypeException(type, referencingEntityName, propertyName);
+                // Impossible to get here.
+                throw new AssertionError();
+            }
+        }
+    }
+
+    private void addMapper(CommonCollectionMapperData commonCollectionMapperData, MiddleComponentData elementComponentData,
+                           MiddleComponentData indexComponentData) {
+        Type type = propertyValue.getType();
+        if (type instanceof SortedSetType) {
+            currentMapper.addComposite(propertyName, new BasicCollectionMapper<Set>(commonCollectionMapperData,
+                    TreeSet.class, SortedSetProxy.class, elementComponentData));
+        } else if (type instanceof SetType) {
+            currentMapper.addComposite(propertyName, new BasicCollectionMapper<Set>(commonCollectionMapperData,
+                    HashSet.class, SetProxy.class, elementComponentData));
+        } else if (type instanceof SortedMapType) {
+            // Indexed collection, so <code>indexComponentData</code> is not null.
+            currentMapper.addComposite(propertyName, new MapCollectionMapper<Map>(commonCollectionMapperData,
+                    TreeMap.class, SortedMapProxy.class, elementComponentData, indexComponentData));
+        } else if (type instanceof MapType) {
+            // Indexed collection, so <code>indexComponentData</code> is not null.
+            currentMapper.addComposite(propertyName, new MapCollectionMapper<Map>(commonCollectionMapperData,
+                    HashMap.class, MapProxy.class, elementComponentData, indexComponentData));
+        } else if (type instanceof BagType) {
+            currentMapper.addComposite(propertyName, new BasicCollectionMapper<List>(commonCollectionMapperData,
+                    ArrayList.class, ListProxy.class, elementComponentData));
+        } else if (type instanceof ListType) {
+            // Indexed collection, so <code>indexComponentData</code> is not null.
+            currentMapper.addComposite(propertyName, new ListCollectionMapper(commonCollectionMapperData,
+                    elementComponentData, indexComponentData));
+        } else {
+            mainGenerator.throwUnsupportedTypeException(type, referencingEntityName, propertyName);
+        }
+    }
+
+    private void storeMiddleEntityRelationInformation(String mappedBy) {
+        // Only if this is a relation (when there is a referenced entity).
+        if (referencedEntityName != null) {
+            if (propertyValue.isInverse()) {
+                referencingEntityConfiguration.addToManyMiddleNotOwningRelation(propertyName, mappedBy, referencedEntityName);
+            } else {
+                referencingEntityConfiguration.addToManyMiddleRelation(propertyName, referencedEntityName);
+            }
+        }
+    }
+
+    private Element createMiddleEntityXml(String versionsMiddleTableName, String versionsMiddleEntityName) {
+        String schema = StringTools.isEmpty(joinTable.schema()) ? propertyValue.getCollectionTable().getSchema() : joinTable.schema();
+        String catalog = StringTools.isEmpty(joinTable.catalog()) ? propertyValue.getCollectionTable().getCatalog() : joinTable.catalog();
+
+        Element middleEntityXml = MetadataTools.createEntity(xmlMappingData.newAdditionalMapping(),
+                versionsMiddleEntityName, versionsMiddleTableName, schema, catalog, null);
+        Element middleEntityXmlId = middleEntityXml.addElement("composite-id");
+
+        middleEntityXmlId.addAttribute("name", mainGenerator.getVerEntCfg().getOriginalIdPropName());
+
+        // Adding the revision number as a foreign key to the revision info entity to the composite id of the
+        // middle table.
+        mainGenerator.addRevisionInfoRelation(middleEntityXmlId);
+
+        // Adding the revision type property to the entity xml.
+        mainGenerator.addRevisionType(middleEntityXml);
+
+        // All other properties should also be part of the primary key of the middle entity.
+        return middleEntityXmlId;
+    }
+
+    private VersionsJoinTable getDefaultVersionsJoinTable() {
+        return new VersionsJoinTable() {
+            public String name() { return ""; }
+            public String schema() { return ""; }
+            public String catalog() { return ""; }
+            public JoinColumn[] inverseJoinColumns() { return new JoinColumn[0]; }
+            public Class<? extends Annotation> annotationType() { return this.getClass(); }
+        };
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private String getMappedBy(Collection collectionValue) {
+        Iterator<Property> assocClassProps =
+                ((OneToMany) collectionValue.getElement()).getAssociatedClass().getPropertyIterator();
+
+        while (assocClassProps.hasNext()) {
+            Property property = assocClassProps.next();
+
+            if (Tools.iteratorsContentEqual(property.getValue().getColumnIterator(),
+                    collectionValue.getKey().getColumnIterator())) {
+                return property.getName();
+            }
+        }
+
+        throw new MappingException("Unable to read the mapped by attribute for " + propertyName + " in "
+                + referencingEntityName + "!");
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private String getMappedBy(Table collectionTable, PersistentClass referencedClass) {
+        Iterator<Property> properties = referencedClass.getPropertyIterator();
+        while (properties.hasNext()) {
+            Property property = properties.next();
+            if (property.getValue() instanceof Collection) {
+                // The equality is intentional. We want to find a collection property with the same collection table.
+                //noinspection ObjectEquality
+                if (((Collection) property.getValue()).getCollectionTable() == collectionTable) {
+                    return property.getName();
+                }
+            }
+        }
+
+        throw new MappingException("Unable to read the mapped by attribute for " + propertyName + " in "
+                + referencingEntityName + "!");
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/EntityXmlMappingData.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/EntityXmlMappingData.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/EntityXmlMappingData.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,58 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EntityXmlMappingData {
+    private Document mainXmlMapping;
+    private List<Document> additionalXmlMappings;
+
+    public EntityXmlMappingData() {
+        mainXmlMapping = DocumentHelper.createDocument();
+        additionalXmlMappings = new ArrayList<Document>();
+    }
+
+    public Document getMainXmlMapping() {
+        return mainXmlMapping;
+    }
+
+    public List<Document> getAdditionalXmlMappings() {
+        return additionalXmlMappings;
+    }
+
+    public Document newAdditionalMapping() {
+        Document additionalMapping = DocumentHelper.createDocument();
+        additionalXmlMappings.add(additionalMapping);
+
+        return additionalMapping;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/IdMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/IdMetadataGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/IdMetadataGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.dom4j.Element;
+import org.dom4j.tree.DefaultElement;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Component;
+import org.hibernate.type.Type;
+import org.hibernate.type.ImmutableType;
+import org.hibernate.MappingException;
+import org.jboss.envers.entities.mapper.SimpleMapperBuilder;
+import org.jboss.envers.entities.mapper.id.SimpleIdMapperBuilder;
+import org.jboss.envers.entities.mapper.id.MultipleIdMapper;
+import org.jboss.envers.entities.mapper.id.EmbeddedIdMapper;
+import org.jboss.envers.entities.mapper.id.SingleIdMapper;
+import org.jboss.envers.entities.IdMappingData;
+import org.jboss.envers.ModificationStore;
+
+import java.util.Iterator;
+
+/**
+ * Generates metadata for primary identifiers (ids) of versions entities.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class IdMetadataGenerator {
+    private final VersionsMetadataGenerator mainGenerator;
+
+    IdMetadataGenerator(VersionsMetadataGenerator versionsMetadataGenerator) {
+        mainGenerator = versionsMetadataGenerator;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addIdProperties(Element parent, Iterator<Property> properties, SimpleMapperBuilder mapper, boolean key) {
+        while (properties.hasNext()) {
+            Property property = properties.next();
+            Type propertyType = property.getType();
+            if (!"_identifierMapper".equals(property.getName())) {
+                if (propertyType instanceof ImmutableType) {
+                    // Last but one parameter: ids are always insertable
+                    mainGenerator.getBasicMetadataGenerator().addBasicNoComponent(parent, property.getName(),
+                            property.getValue(), mapper, ModificationStore.FULL, true, key);
+                } else {
+                    throw new MappingException("Type not supported: " + propertyType.getClass().getName());
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    IdMappingData addId(PersistentClass pc) {
+        // Xml mapping which will be used for relations
+        Element rel_id_mapping = new DefaultElement("properties");
+        // Xml mapping which will be used for the primary key of the versions table
+        Element orig_id_mapping = new DefaultElement("composite-id");
+
+        Property id_prop = pc.getIdentifierProperty();
+        Component id_mapper = pc.getIdentifierMapper();
+
+        SimpleIdMapperBuilder mapper;
+        if (id_mapper != null) {
+            // Multiple id
+
+            mapper = new MultipleIdMapper(((Component) pc.getIdentifier()).getComponentClassName());
+            addIdProperties(rel_id_mapping, (Iterator<Property>) id_mapper.getPropertyIterator(), mapper, false);
+
+            // null mapper - the mapping where already added the first time, now we only want to generate the xml
+            addIdProperties(orig_id_mapping, (Iterator<Property>) id_mapper.getPropertyIterator(), null, true);
+        } else if (id_prop.isComposite()) {
+            // Embedded id
+
+            Component id_component = (Component) id_prop.getValue();
+
+            mapper = new EmbeddedIdMapper(id_prop.getName(), id_component.getComponentClassName());
+            addIdProperties(rel_id_mapping, (Iterator<Property>) id_component.getPropertyIterator(), mapper, false);
+
+            // null mapper - the mapping where already added the first time, now we only want to generate the xml
+            addIdProperties(orig_id_mapping, (Iterator<Property>) id_component.getPropertyIterator(), null, true);
+        } else {
+            // Single id
+            
+            mapper = new SingleIdMapper();
+
+            // Last but one parameter: ids are always insertable
+            mainGenerator.getBasicMetadataGenerator().addBasicNoComponent(rel_id_mapping, id_prop.getName(),
+                    id_prop.getValue(), mapper, ModificationStore.FULL, true, false);
+
+            // null mapper - the mapping where already added the first time, now we only want to generate the xml
+            mainGenerator.getBasicMetadataGenerator().addBasicNoComponent(orig_id_mapping, id_prop.getName(),
+                    id_prop.getValue(), null, ModificationStore.FULL, true, true);
+        }
+
+        orig_id_mapping.addAttribute("name", mainGenerator.getVerEntCfg().getOriginalIdPropName());
+
+        // Adding a relation to the revision entity (effectively: the "revision number" property)
+        mainGenerator.addRevisionInfoRelation(orig_id_mapping);
+
+        return new IdMappingData(mapper, orig_id_mapping, rel_id_mapping);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/InheritanceType.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/InheritanceType.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/InheritanceType.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,60 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.hibernate.mapping.*;
+import org.hibernate.MappingException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public enum InheritanceType {
+    NONE,
+    JOINED,
+    SINGLE,
+    TABLE_PER_CLASS;
+
+    /**
+     * @param pc The class for which to get the inheritance type.
+     * @return The inheritance type of this class. NONE, if this class does not inherit from
+     * another persisten class.
+     */
+    public static InheritanceType get(PersistentClass pc) {
+        PersistentClass superclass = pc.getSuperclass();
+        if (superclass == null) {
+            return InheritanceType.NONE;
+        }
+
+        // We assume that every subclass is of the same type.
+        Subclass subclass = (Subclass) superclass.getSubclassIterator().next();
+
+        if (subclass instanceof SingleTableSubclass) {
+            return InheritanceType.SINGLE;
+        } else if (subclass instanceof JoinedSubclass) {
+            return InheritanceType.JOINED;
+        } else if (subclass instanceof UnionSubclass) {
+            return InheritanceType.TABLE_PER_CLASS;
+        }
+
+        throw new MappingException("Unknown subclass class: " + subclass.getClass());
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/MetadataTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/MetadataTools.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/MetadataTools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,207 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.dom4j.Element;
+import org.dom4j.Document;
+import org.dom4j.Attribute;
+import org.jboss.envers.tools.StringTools;
+import org.hibernate.mapping.Column;
+
+import javax.persistence.JoinColumn;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MetadataTools {
+    public static Element addNativelyGeneratedId(Element parent, String name, String type) {
+        Element id_mapping = parent.addElement("id");
+        id_mapping.addAttribute("name", name).addAttribute("type", type);
+
+        Element generator_mapping = id_mapping.addElement("generator");
+        generator_mapping.addAttribute("class", "native");
+        /*generator_mapping.addAttribute("class", "sequence");
+        generator_mapping.addElement("param").addAttribute("name", "sequence").setText("custom");*/
+
+        return id_mapping;
+    }
+
+    public static Element addProperty(Element parent, String name, String type, boolean insertable, boolean key) {
+        Element prop_mapping;
+        if (key) {
+            prop_mapping = parent.addElement("key-property");
+        } else {
+            prop_mapping = parent.addElement("property");
+        }
+
+        prop_mapping.addAttribute("name", name);
+        prop_mapping.addAttribute("insert", Boolean.toString(insertable));
+
+        if (type != null) {
+            prop_mapping.addAttribute("type", type);
+        }
+
+        return prop_mapping;
+    }
+
+    public static Element addColumn(Element parent, String name, Integer length) {
+        Element column_mapping = parent.addElement("column");
+
+        column_mapping.addAttribute("name", name);
+        if (length != null) {
+            column_mapping.addAttribute("length", length.toString());
+        }
+
+        return column_mapping;
+    }
+
+    private static Element createEntityCommon(Document document, String type, String entityName,
+                                              String tableName, String schema, String catalog,
+                                              String discriminatorValue) {
+        Element hibernate_mapping = document.addElement("hibernate-mapping");
+        hibernate_mapping.addAttribute("auto-import", "false");
+
+        Element class_mapping = hibernate_mapping.addElement(type);
+
+        if (entityName != null) {
+            class_mapping.addAttribute("entity-name", entityName);
+        }
+
+        if (discriminatorValue != null) {
+            class_mapping.addAttribute("discriminator-value", discriminatorValue);
+        }
+
+        if (!StringTools.isEmpty(tableName)) {
+            class_mapping.addAttribute("table", tableName);
+        }
+
+        if (!StringTools.isEmpty(schema)) {
+            class_mapping.addAttribute("schema", schema);
+        }
+
+        if (!StringTools.isEmpty(catalog)) {
+            class_mapping.addAttribute("catalog", catalog);
+        }
+
+        return class_mapping;
+    }
+
+    public static Element createEntity(Document document, String entityName, String tableName,
+                                       String schema, String catalog, String discriminatorValue) {
+        return createEntityCommon(document, "class", entityName, tableName, schema, catalog, discriminatorValue);
+    }
+
+    public static Element createSubclassEntity(Document document, String entityName, String tableName,
+                                               String schema, String catalog, String extendsEntityName,
+                                               String discriminatorValue) {
+        Element class_mapping = createEntityCommon(document, "subclass", entityName, tableName, schema, catalog,
+                discriminatorValue);
+
+        class_mapping.addAttribute("extends", extendsEntityName);
+
+        return class_mapping;
+    }
+
+    public static Element createJoin(Element parent, String tableName,
+                                     String schema, String catalog) {
+        Element join_mapping = parent.addElement("join");
+
+        join_mapping.addAttribute("table", tableName);
+
+        if (!StringTools.isEmpty(schema)) {
+            join_mapping.addAttribute("schema", schema);
+        }
+
+        if (!StringTools.isEmpty(catalog)) {
+            join_mapping.addAttribute("catalog", catalog);
+        }
+
+        return join_mapping;
+    }
+
+    public static void addColumns(Element any_mapping, Iterator<Column> columns) {
+        while (columns.hasNext()) {
+            Column column = columns.next();
+            addColumn(any_mapping, column.getName(), column.getLength());
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private static void changeNamesInColumnElement(Element element, ColumnNameIterator columnNameIterator) {
+        Iterator<Element> properties = element.elementIterator();
+        while (properties.hasNext()) {
+            Element property = properties.next();
+
+            if ("column".equals(property.getName())) {
+                Attribute nameAttr = property.attribute("name");
+                if (nameAttr != null) {
+                    nameAttr.setText(columnNameIterator.next());
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public static void prefixNamesInPropertyElement(Element element, String prefix, ColumnNameIterator columnNameIterator,
+                                                    boolean changeToKey) {
+        Iterator<Element> properties = element.elementIterator();
+        while (properties.hasNext()) {
+            Element property = properties.next();
+
+            if ("property".equals(property.getName())) {
+                Attribute nameAttr = property.attribute("name");
+                if (nameAttr != null) {
+                    nameAttr.setText(prefix + nameAttr.getText());
+                }
+
+                changeNamesInColumnElement(property, columnNameIterator);
+
+                if (changeToKey) {
+                    property.setName("key-property");
+                }
+            }
+        }
+    }
+
+    /**
+     * An iterator over column names.
+     */
+    public static abstract class ColumnNameIterator implements Iterator<String> { }
+
+    public static ColumnNameIterator getColumnNameIterator(final Iterator<Column> columnIterator) {
+        return new ColumnNameIterator() {
+            public boolean hasNext() { return columnIterator.hasNext(); }
+            public String next() { return columnIterator.next().getName(); }
+            public void remove() { columnIterator.remove(); }
+        };
+    }
+
+    public static ColumnNameIterator getColumnNameIterator(final JoinColumn[] joinColumns) {
+        return new ColumnNameIterator() {
+            int counter = 0;
+            public boolean hasNext() { return counter < joinColumns.length; }
+            public String next() { return joinColumns[counter++].name(); }
+            public void remove() { throw new UnsupportedOperationException(); }
+        };
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/PersistentClassVersioningData.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/PersistentClassVersioningData.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/PersistentClassVersioningData.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,65 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.VersionsJoinTable;
+import org.jboss.envers.VersionsTable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ * @author Sebastian Komander
+*/
+public class PersistentClassVersioningData {
+    public PersistentClassVersioningData() {
+        propertyStoreInfo = new PropertyStoreInfo(new HashMap<String, ModificationStore>());
+        secondaryTableDictionary = new HashMap<String, String>();
+        unversionedProperties = new ArrayList<String>();
+        versionsJoinTables = new HashMap<String, VersionsJoinTable>();
+        mapKeys = new HashMap<String, String>();
+    }
+
+    public PropertyStoreInfo propertyStoreInfo;
+    public VersionsTable versionsTable;
+    public Map<String, String> secondaryTableDictionary;
+    public List<String> unversionedProperties;
+    /**
+     * A map from property names to custom join tables definitions.
+     */
+    public Map<String, VersionsJoinTable> versionsJoinTables;
+    /**
+     * A map from property names to the value of the related property names in a map key annotation. An empty string,
+     * if the property name is not specified in the mapkey annotation.
+     */
+    public Map<String, String> mapKeys;
+
+    public boolean isVersioned() {
+        if (propertyStoreInfo.propertyStores.size() > 0) { return true; }
+        if (propertyStoreInfo.defaultStore != null) { return true; }
+        return false;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/PropertyStoreInfo.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/PropertyStoreInfo.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/PropertyStoreInfo.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,46 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.jboss.envers.ModificationStore;
+
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+*/
+public class PropertyStoreInfo {
+    // Not null if the whole class is versioned
+    public ModificationStore defaultStore;
+
+    // Maps property names to their stores defined in per-field versioned annotations
+    public Map<String, ModificationStore> propertyStores;
+
+    public PropertyStoreInfo(Map<String, ModificationStore> propertyStores) {
+        this.propertyStores = propertyStores;
+    }
+
+    public PropertyStoreInfo(ModificationStore defaultStore, Map<String, ModificationStore> propertyStores) {
+        this.defaultStore = defaultStore;
+        this.propertyStores = propertyStores;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/QueryGeneratorBuilder.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/QueryGeneratorBuilder.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/QueryGeneratorBuilder.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,86 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.jboss.envers.entities.mapper.relation.MiddleIdData;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.entities.mapper.relation.query.RelationQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.query.OneEntityQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.query.ThreeEntityQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.query.TwoEntityQueryGenerator;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.configuration.GlobalConfiguration;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Builds query generators, for reading collection middle tables, along with any related entities.
+ * The related entities information can be added gradually, and when complete, the query generator can be built.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class QueryGeneratorBuilder {
+    private final GlobalConfiguration globalCfg;
+    private final VersionsEntitiesConfiguration verEntCfg;
+    private final MiddleIdData referencingIdData;
+    private final String versionsMiddleEntityName;
+    private final List<MiddleIdData> idDatas;
+
+    QueryGeneratorBuilder(GlobalConfiguration globalCfg, VersionsEntitiesConfiguration verEntCfg,
+                          MiddleIdData referencingIdData, String versionsMiddleEntityName) {
+        this.globalCfg = globalCfg;
+        this.verEntCfg = verEntCfg;
+        this.referencingIdData = referencingIdData;
+        this.versionsMiddleEntityName = versionsMiddleEntityName;
+
+        idDatas = new ArrayList<MiddleIdData>();
+    }
+
+    void addRelation(MiddleIdData idData) {
+        idDatas.add(idData);
+    }
+
+    RelationQueryGenerator build(MiddleComponentData... componentDatas) {
+        if (idDatas.size() == 0) {
+            return new OneEntityQueryGenerator(verEntCfg, versionsMiddleEntityName, referencingIdData,
+                    componentDatas);
+        } else if (idDatas.size() == 1) {
+            return new TwoEntityQueryGenerator(globalCfg, verEntCfg, versionsMiddleEntityName, referencingIdData,
+                    idDatas.get(0), componentDatas);
+        } else if (idDatas.size() == 2) {
+            return new ThreeEntityQueryGenerator(globalCfg, verEntCfg, versionsMiddleEntityName, referencingIdData, 
+                    idDatas.get(0), idDatas.get(1), componentDatas);
+        } else {
+            throw new IllegalStateException("Illegal number of related entities.");
+        }
+    }
+
+    /**
+     * @return Current index of data in the array, which will be the element of a list, returned when executing a query
+     * generated by the built query generator.
+     */
+    int getCurrentIndex() {
+        return idDatas.size();
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/ToOneRelationMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/ToOneRelationMetadataGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/ToOneRelationMetadataGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,111 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.dom4j.Element;
+import org.hibernate.mapping.Value;
+import org.hibernate.mapping.ToOne;
+import org.hibernate.mapping.OneToOne;
+import org.hibernate.MappingException;
+import org.jboss.envers.entities.mapper.CompositeMapperBuilder;
+import org.jboss.envers.entities.mapper.relation.ToOneIdMapper;
+import org.jboss.envers.entities.mapper.relation.OneToOneNotOwningMapper;
+import org.jboss.envers.entities.mapper.id.IdMapper;
+import org.jboss.envers.entities.EntityConfiguration;
+import org.jboss.envers.entities.IdMappingData;
+
+/**
+ * Generates metadata for to-one relations (reference-valued properties).
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class ToOneRelationMetadataGenerator {
+    private final VersionsMetadataGenerator mainGenerator;
+
+    ToOneRelationMetadataGenerator(VersionsMetadataGenerator versionsMetadataGenerator) {
+        mainGenerator = versionsMetadataGenerator;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    void addToOne(Element parent, String name, Value value, CompositeMapperBuilder mapper, String entityName) {
+        String referencedEntityName = ((ToOne) value).getReferencedEntityName();
+
+        EntityConfiguration configuration = mainGenerator.getEntitiesConfigurations().get(referencedEntityName);
+        if (configuration == null) {
+            throw new MappingException("A versioned relation to a non-versioned entity " + referencedEntityName + "!");
+        }
+
+        IdMappingData idMapping = configuration.getIdMappingData();
+
+        String lastPropertyPrefix = name + "_";
+
+        // Generating the id mapper for the relation
+        IdMapper relMapper = idMapping.getIdMapper().prefixMappedProperties(lastPropertyPrefix);
+
+        // Storing information about this relation
+        mainGenerator.getEntitiesConfigurations().get(entityName).addToOneRelation(name, referencedEntityName, relMapper);
+
+        // Adding an element to the mapping corresponding to the references entity id's
+        Element properties = (Element) idMapping.getXmlRelationMapping().clone();
+        properties.addAttribute("name", name);
+
+        MetadataTools.prefixNamesInPropertyElement(properties, lastPropertyPrefix,
+                MetadataTools.getColumnNameIterator(value.getColumnIterator()), false);
+        parent.add(properties);
+
+        // Adding mapper for the id
+        mapper.addComposite(name, new ToOneIdMapper(relMapper, name, referencedEntityName));
+    }
+
+    @SuppressWarnings({"unchecked"})
+    void addOneToOneNotOwning(String name, Value value, CompositeMapperBuilder mapper, String entityName) {
+        OneToOne propertyValue = (OneToOne) value;
+
+        String owningReferencePropertyName = propertyValue.getReferencedPropertyName(); // mappedBy
+
+        EntityConfiguration configuration = mainGenerator.getEntitiesConfigurations().get(entityName);
+        if (configuration == null) {
+            throw new MappingException("A versioned relation to a non-versioned entity " + entityName + "!");
+        }
+
+        IdMappingData ownedIdMapping = configuration.getIdMappingData();
+
+        if (ownedIdMapping == null) {
+            throw new MappingException("A versioned relation to a non-versioned entity " + entityName + "!");
+        }
+
+        String lastPropertyPrefix = owningReferencePropertyName + "_";
+        String referencedEntityName = propertyValue.getReferencedEntityName();
+
+        // Generating the id mapper for the relation
+        IdMapper ownedIdMapper = ownedIdMapping.getIdMapper().prefixMappedProperties(lastPropertyPrefix);
+
+        // Storing information about this relation
+        mainGenerator.getEntitiesConfigurations().get(entityName).addToOneNotOwningRelation(name, owningReferencePropertyName,
+                referencedEntityName, ownedIdMapper);
+
+        // Adding mapper for the id
+        mapper.addComposite(name, new OneToOneNotOwningMapper(owningReferencePropertyName,
+                referencedEntityName, name));
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/VersionsMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/VersionsMetadataGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/configuration/metadata/VersionsMetadataGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,372 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.configuration.metadata;
+
+import org.hibernate.type.*;
+import org.hibernate.MappingException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.*;
+import org.hibernate.mapping.Collection;
+import org.dom4j.Element;
+import org.jboss.envers.entities.EntityConfiguration;
+import org.jboss.envers.entities.IdMappingData;
+import org.jboss.envers.configuration.GlobalConfiguration;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.entities.mapper.*;
+import org.jboss.envers.tools.StringTools;
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.VersionsJoinTable;
+import org.jboss.envers.entity.VersionsInheritanceEntityPersister;
+import org.jboss.envers.tools.log.YLog;
+import org.jboss.envers.tools.log.YLogManager;
+
+import java.util.*;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ * @author Sebastian Komander
+ */
+public final class VersionsMetadataGenerator {
+    private final Configuration cfg;
+    private final GlobalConfiguration globalCfg;
+    private final VersionsEntitiesConfiguration verEntCfg;
+    private final Element revisionInfoRelationMapping;
+
+    private final BasicMetadataGenerator basicMetadataGenerator;
+    private final IdMetadataGenerator idMetadataGenerator;
+    private final ToOneRelationMetadataGenerator toOneRelationMetadataGenerator;
+
+    private final Map<String, EntityConfiguration> entitiesConfigurations;
+
+    // Map entity name -> (join descriptor -> element describing the "versioned" join)
+    private final Map<String, Map<Join, Element>> entitiesJoins;
+
+    private YLog log = YLogManager.getLogManager().getLog(VersionsMetadataGenerator.class);
+
+    public VersionsMetadataGenerator(Configuration cfg, GlobalConfiguration globalCfg,
+                                     VersionsEntitiesConfiguration verEntCfg,
+                                     Element revisionInfoRelationMapping) {
+        this.cfg = cfg;
+        this.globalCfg = globalCfg;
+        this.verEntCfg = verEntCfg;
+        this.revisionInfoRelationMapping = revisionInfoRelationMapping;
+
+        this.basicMetadataGenerator = new BasicMetadataGenerator();
+        this.idMetadataGenerator = new IdMetadataGenerator(this);
+        this.toOneRelationMetadataGenerator = new ToOneRelationMetadataGenerator(this);
+
+        entitiesConfigurations = new HashMap<String, EntityConfiguration>();
+        entitiesJoins = new HashMap<String, Map<Join, Element>>();
+    }
+
+    void addRevisionInfoRelation(Element any_mapping) {
+        Element rev_mapping = (Element) revisionInfoRelationMapping.clone();
+        rev_mapping.addAttribute("name", verEntCfg.getRevisionPropName());
+        MetadataTools.addColumn(rev_mapping, verEntCfg.getRevisionPropName(), null);
+
+        any_mapping.add(rev_mapping);
+    }
+
+    void addRevisionType(Element any_mapping) {
+        Element revTypeProperty = MetadataTools.addProperty(any_mapping, verEntCfg.getRevisionTypePropName(),
+                verEntCfg.getRevisionTypePropType(), true, false);
+        revTypeProperty.addAttribute("type", "org.jboss.envers.entities.RevisionTypeType");
+    }
+
+    private ModificationStore getStoreForProperty(Property property, PropertyStoreInfo propertyStoreInfo,
+                                                  List<String> unversionedProperties) {
+        /*
+         * Checks if a property is versioned, which is when:
+         * - the property isn't unversioned
+         * - the whole entity is versioned, then the default store is not null
+         * - there is a store defined for this entity, which is when this property is annotated 
+         */
+
+        if (unversionedProperties.contains(property.getName())) {
+            return null;
+        }
+
+        ModificationStore store = propertyStoreInfo.propertyStores.get(property.getName());
+
+        if (store == null) {
+            return propertyStoreInfo.defaultStore;
+        }
+
+        return store;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    void addValue(Element parent, String name, Value value, CompositeMapperBuilder currentMapper,
+                  ModificationStore store, String entityName, EntityXmlMappingData xmlMappingData,
+                  VersionsJoinTable joinTable, String mapKey, boolean insertable, boolean firstPass) {
+        Type type = value.getType();
+
+        // only first pass
+        if (firstPass) {
+            if (basicMetadataGenerator.addBasic(parent, name, value, currentMapper, store, entityName, insertable,
+                    false)) {
+                // The property was mapped by the basic generator.
+                return;
+            }
+        }
+
+        if (type instanceof ManyToOneType) {
+            // only second pass
+            if (!firstPass) {
+                toOneRelationMetadataGenerator.addToOne(parent, name, value, currentMapper, entityName);
+            }
+        } else if (type instanceof OneToOneType) {
+            // only second pass
+            if (!firstPass) {
+                toOneRelationMetadataGenerator.addOneToOneNotOwning(name, value, currentMapper, entityName);
+            }
+        } else if (type instanceof CollectionType) {
+            // only second pass
+            if (!firstPass) {
+                CollectionMetadataGenerator collectionMetadataGenerator = new CollectionMetadataGenerator(this,
+                        name, (Collection) value, currentMapper, entityName, xmlMappingData, joinTable, mapKey);
+                collectionMetadataGenerator.addCollection();
+            }
+        } else {
+            if (firstPass) {
+                // If we got here in the first pass, it means the basic mapper didn't map it, and none of the
+                // above branches either.
+                throwUnsupportedTypeException(type, entityName, name);
+            }
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addProperties(Element parent, Iterator<Property> properties, CompositeMapperBuilder currentMapper,
+                               PersistentClassVersioningData versioningData, String entityName, EntityXmlMappingData xmlMappingData,
+                               boolean firstPass) {
+        while (properties.hasNext()) {
+            Property property = properties.next();
+            if (!"_identifierMapper".equals(property.getName())) {
+                ModificationStore store = getStoreForProperty(property, versioningData.propertyStoreInfo,
+                        versioningData.unversionedProperties);
+
+                if (store != null) {
+                    addValue(parent, property.getName(), property.getValue(), currentMapper, store, entityName,
+                            xmlMappingData, versioningData.versionsJoinTables.get(property.getName()),
+                            versioningData.mapKeys.get(property.getName()), property.isInsertable(), firstPass);
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void createJoins(PersistentClass pc, Element parent, PersistentClassVersioningData versioningData) {
+        Iterator<Join> joins = pc.getJoinIterator();
+
+        Map<Join, Element> joinElements = new HashMap<Join, Element>();
+        entitiesJoins.put(pc.getEntityName(), joinElements);
+
+        while (joins.hasNext()) {
+            Join join = joins.next();
+
+            // Determining the table name. If there is no entry in the dictionary, just constructing the table name
+            // as if it was an entity (by appending/prepending configured strings).
+            String originalTableName = join.getTable().getName();
+            String versionedTableName = versioningData.secondaryTableDictionary.get(originalTableName);
+            if (versionedTableName == null) {
+                versionedTableName = verEntCfg.getVersionsEntityName(originalTableName);
+            }
+
+            String schema = versioningData.versionsTable.schema();
+            if (StringTools.isEmpty(schema)) {
+                schema = join.getTable().getSchema();
+            }
+
+            String catalog = versioningData.versionsTable.catalog();
+            if (StringTools.isEmpty(catalog)) {
+                catalog = join.getTable().getCatalog();
+            }
+
+            Element joinElement = MetadataTools.createJoin(parent, versionedTableName, schema, catalog);
+            joinElements.put(join, joinElement);
+
+            Element joinKey = joinElement.addElement("key");
+            MetadataTools.addColumns(joinKey, join.getKey().getColumnIterator());
+            MetadataTools.addColumn(joinKey, verEntCfg.getRevisionPropName(), null);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void addJoins(PersistentClass pc, CompositeMapperBuilder currentMapper, PersistentClassVersioningData versioningData,
+                          String entityName, EntityXmlMappingData xmlMappingData,boolean firstPass) {
+        Iterator<Join> joins = pc.getJoinIterator();
+
+        while (joins.hasNext()) {
+            Join join = joins.next();
+            Element joinElement = entitiesJoins.get(entityName).get(join);
+
+            addProperties(joinElement, join.getPropertyIterator(), currentMapper, versioningData, entityName,
+                    xmlMappingData, firstPass);
+        }
+    }
+
+    private void addPersisterHack(Element class_mapping) {
+        class_mapping.addAttribute("persister", VersionsInheritanceEntityPersister.class.getName() );
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public void generateFirstPass(PersistentClass pc, PersistentClassVersioningData versioningData,
+                                  EntityXmlMappingData xmlMappingData) {
+        String schema = versioningData.versionsTable.schema();
+        if (StringTools.isEmpty(schema)) {
+            schema = pc.getTable().getSchema();
+        }
+
+        String catalog = versioningData.versionsTable.catalog();
+        if (StringTools.isEmpty(catalog)) {
+            catalog = pc.getTable().getCatalog();
+        }
+
+        String entityName = pc.getEntityName();
+        String versionsEntityName = verEntCfg.getVersionsEntityName(entityName);
+        String versionsTableName = verEntCfg.getVersionsTableName(entityName, pc.getTable().getName());
+
+        // Generating a mapping for the id
+        IdMappingData idMapper = idMetadataGenerator.addId(pc);
+
+        Element class_mapping;
+        ExtendedPropertyMapper propertyMapper;
+
+        InheritanceType inheritanceType = InheritanceType.get(pc);
+        String parentEntityName = null;
+
+        switch (inheritanceType) {
+            case NONE:
+                class_mapping = MetadataTools.createEntity(xmlMappingData.getMainXmlMapping(), versionsEntityName, versionsTableName,
+                        schema, catalog, pc.getDiscriminatorValue());
+                propertyMapper = new MultiPropertyMapper();
+
+                // Checking if there is a discriminator column
+                if (pc.getDiscriminator() != null) {
+                    Element discriminator_element = class_mapping.addElement("discriminator");
+                    MetadataTools.addColumns(discriminator_element, pc.getDiscriminator().getColumnIterator());
+                    discriminator_element.addAttribute("type", pc.getDiscriminator().getType().getName());
+
+                    // If so, there is some inheritance scheme -> using the persister hack.
+                    addPersisterHack(class_mapping);
+                }
+
+                // Adding the id mapping
+                class_mapping.add((Element) idMapper.getXmlMapping().clone());
+
+                // Adding the "revision type" property
+                addRevisionType(class_mapping);
+
+                break;
+            case SINGLE:
+                String extendsEntityName = verEntCfg.getVersionsEntityName(pc.getSuperclass().getEntityName());
+                class_mapping = MetadataTools.createSubclassEntity(xmlMappingData.getMainXmlMapping(), versionsEntityName,
+                        versionsTableName, schema, catalog, extendsEntityName, pc.getDiscriminatorValue());
+
+                addPersisterHack(class_mapping);
+
+                // The id and revision type is already mapped in the parent
+
+                // Getting the property mapper of the parent - when mapping properties, they need to be included
+                parentEntityName = pc.getSuperclass().getEntityName();
+                ExtendedPropertyMapper parentPropertyMapper = entitiesConfigurations.get(parentEntityName).getPropertyMapper();
+                propertyMapper = new SubclassPropertyMapper(new MultiPropertyMapper(), parentPropertyMapper);
+
+                break;
+            case JOINED:
+                throw new MappingException("Joined inheritance strategy not supported for versioning!");
+            case TABLE_PER_CLASS:
+                throw new MappingException("Table-per-class inheritance strategy not supported for versioning!");
+            default:
+                throw new AssertionError("Impossible enum value.");
+        }
+
+        // Mapping unjoined properties
+        addProperties(class_mapping, (Iterator<Property>) pc.getUnjoinedPropertyIterator(), propertyMapper,
+                versioningData, pc.getEntityName(), xmlMappingData,
+                true);
+
+        // Creating and mapping joins (first pass)
+        createJoins(pc, class_mapping, versioningData);
+        addJoins(pc, propertyMapper, versioningData, pc.getEntityName(), xmlMappingData, true);
+
+        // Storing the generated configuration
+        EntityConfiguration entityCfg = new EntityConfiguration(versionsEntityName, idMapper,
+                propertyMapper, parentEntityName);
+        entitiesConfigurations.put(pc.getEntityName(), entityCfg);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public void generateSecondPass(PersistentClass pc, PersistentClassVersioningData versioningData,
+                                   EntityXmlMappingData xmlMappingData) {
+        String entityName = pc.getEntityName();
+
+        CompositeMapperBuilder propertyMapper = entitiesConfigurations.get(entityName).getPropertyMapper();
+
+        // Mapping unjoined properties
+        Element parent = xmlMappingData.getMainXmlMapping().getRootElement().element("class");
+        if (parent == null) {
+            parent = xmlMappingData.getMainXmlMapping().getRootElement().element("subclass");
+        }
+
+        addProperties(parent, (Iterator<Property>) pc.getUnjoinedPropertyIterator(),
+                propertyMapper, versioningData, entityName, xmlMappingData, false);
+
+        // Mapping joins (second pass)
+        addJoins(pc, propertyMapper, versioningData, entityName, xmlMappingData, false);
+    }
+
+    public Map<String, EntityConfiguration> getEntitiesConfigurations() {
+        return entitiesConfigurations;
+    }
+
+    // Getters for generators and configuration
+
+    BasicMetadataGenerator getBasicMetadataGenerator() {
+        return basicMetadataGenerator;
+    }
+
+    Configuration getCfg() {
+        return cfg;
+    }
+
+    GlobalConfiguration getGlobalCfg() {
+        return globalCfg;
+    }
+
+    VersionsEntitiesConfiguration getVerEntCfg() {
+        return verEntCfg;
+    }
+
+    void throwUnsupportedTypeException(Type type, String entityName, String propertyName) {
+        String message = "Type not supported for versioning: " + type.getClass().getName() +
+                ", on entity " + entityName + ", property '" + propertyName + "'.";
+        if (globalCfg.isWarnOnUnsupportedTypes()) {
+            log.warn(message);
+        } else {
+            throw new MappingException(message);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/EntitiesConfigurations.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/EntitiesConfigurations.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/EntitiesConfigurations.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,99 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Configuration of the user entities: property mapping of the entities, relations, inheritance.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EntitiesConfigurations {
+    private Map<String, EntityConfiguration> entitiesConfigurations;
+
+    // Map versions entity name -> entity name
+    private Map<String, String> entityNamesForVersionsEntityNames = new HashMap<String, String>();
+
+    public EntitiesConfigurations(Map<String, EntityConfiguration> entitiesConfigurations) {
+        this.entitiesConfigurations = entitiesConfigurations;
+
+        generateBidirectionRelationInfo();
+        generateVersionsEntityToEntityNames();
+    }
+
+    private void generateVersionsEntityToEntityNames() {
+        entityNamesForVersionsEntityNames = new HashMap<String, String>();
+
+        for (String entityName : entitiesConfigurations.keySet()) {
+            entityNamesForVersionsEntityNames.put(entitiesConfigurations.get(entityName).getVersionsEntityName(),
+                    entityName);
+        }
+    }
+
+    private void generateBidirectionRelationInfo() {
+        // Checking each relation if it is bidirectional. If so, storing that information.
+        for (String entityName : entitiesConfigurations.keySet()) {
+            EntityConfiguration entCfg = entitiesConfigurations.get(entityName);
+            // Iterating over all relations from that entity
+            for (RelationDescription relDesc : entCfg.getRelationsIterator()) {
+                // If this is an "owned" relation, checking the related entity, if it has a relation that has
+                // a mapped-by attribute to the currently checked. If so, this is a bidirectional relation.
+                if (relDesc.getRelationType() == RelationType.TO_ONE ||
+                        relDesc.getRelationType() == RelationType.TO_MANY_MIDDLE) {
+                    for (RelationDescription other : entitiesConfigurations.get(relDesc.getToEntityName()).getRelationsIterator()) {
+                        if (relDesc.getFromPropertyName().equals(other.getMappedByPropertyName()) &&
+                                (entityName.equals(other.getToEntityName()))) {
+                            relDesc.setBidirectional(true);
+                            other.setBidirectional(true);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public EntityConfiguration get(String entityName) {
+        return entitiesConfigurations.get(entityName);
+    }
+
+    public String getEntityNameForVersionsEntityName(String versionsEntityName) {
+        return entityNamesForVersionsEntityNames.get(versionsEntityName);
+    }
+
+    public boolean isVersioned(String entityName) {
+        return get(entityName) != null;
+    }
+
+    public RelationDescription getRelationDescription(String entityName, String propertyName) {
+        EntityConfiguration entCfg = entitiesConfigurations.get(entityName);
+        RelationDescription relDesc = entCfg.getRelationDescription(propertyName);
+        if (relDesc != null) {
+            return relDesc;
+        } else if (entCfg.getParentEntityName() != null) {
+            // The field may be declared in a superclass ...
+            return getRelationDescription(entCfg.getParentEntityName(), propertyName);
+        } else {
+            return null;
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/EntityConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/EntityConfiguration.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/EntityConfiguration.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,111 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities;
+
+import org.jboss.envers.entities.mapper.ExtendedPropertyMapper;
+import org.jboss.envers.entities.mapper.id.IdMapper;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EntityConfiguration {
+    private String versionsEntityName;
+    private IdMappingData idMappingData;
+    private ExtendedPropertyMapper propertyMapper;
+    // Maps from property name
+    private Map<String, RelationDescription> relations;
+    private String parentEntityName;
+
+    public EntityConfiguration(String versionsEntityName, IdMappingData idMappingData,
+                               ExtendedPropertyMapper propertyMapper, String parentEntityName) {
+        this.versionsEntityName = versionsEntityName;
+        this.idMappingData = idMappingData;
+        this.propertyMapper = propertyMapper;
+        this.parentEntityName = parentEntityName;
+
+        this.relations = new HashMap<String, RelationDescription>();
+    }
+
+    public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper) {
+        relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE,
+                toEntityName, null, idMapper));
+    }
+
+    public void addToOneNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
+                                    IdMapper idMapper) {
+        relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE_NOT_OWNING,
+                toEntityName, mappedByPropertyName, idMapper));
+    }
+
+    public void addToManyNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
+                                     IdMapper idMapper) {
+        relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_NOT_OWNING,
+                toEntityName, mappedByPropertyName, idMapper));
+    }
+
+    public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) {
+        relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE,
+                toEntityName, null, null));
+    }
+
+    public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) {
+        relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING,
+                toEntityName, mappedByPropertyName, null));
+    }
+
+    public boolean isRelation(String propertyName) {
+        return relations.get(propertyName) != null;
+    }
+    
+    public RelationDescription getRelationDescription(String propertyName) {
+        return relations.get(propertyName);
+    }
+
+    public IdMappingData getIdMappingData() {
+        return idMappingData;
+    }
+
+    public IdMapper getIdMapper() {
+        return idMappingData.getIdMapper();
+    }
+
+    public ExtendedPropertyMapper getPropertyMapper() {
+        return propertyMapper;
+    }
+
+    // For use by EntitiesConfigurations
+
+    String getParentEntityName() {
+        return parentEntityName;
+    }
+
+    String getVersionsEntityName() {
+        return versionsEntityName;
+    }
+
+    Iterable<RelationDescription> getRelationsIterator() {
+        return relations.values();
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/EntityInstantiator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/EntityInstantiator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/EntityInstantiator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,103 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities;
+
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.entities.mapper.id.IdMapper;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+
+import java.util.Map;
+import java.util.List;
+import java.util.Collection;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EntityInstantiator {
+    private final VersionsConfiguration verCfg;
+    private final VersionsReaderImplementor versionsReader;
+
+    public EntityInstantiator(VersionsConfiguration verCfg, VersionsReaderImplementor versionsReader) {
+        this.verCfg = verCfg;
+        this.versionsReader = versionsReader;
+    }
+
+    /**
+     * Creates an entity instance based on an entry from the versions table.
+     * @param entityName Name of the entity, which instances should be read
+     * @param versionsEntity An entry in the versions table, from which data should be mapped.
+     * @param revision Revision at which this entity was read.
+     * @return An entity instance, with versioned properties set as in the versionsEntity map, and proxies
+     * created for collections.
+     */
+    public Object createInstanceFromVersionsEntity(String entityName, Map versionsEntity, Number revision) {
+        if (versionsEntity == null) {
+            return null;
+        }
+
+        // The $type$ property holds the name of the (versions) entity
+        String type = verCfg.getEntCfg().getEntityNameForVersionsEntityName((String) versionsEntity.get("$type$"));
+
+        if (type != null) {
+            entityName = type;
+        }
+
+        // First mapping the primary key
+        IdMapper idMapper = verCfg.getEntCfg().get(entityName).getIdMapper();
+        Map originalId = (Map) versionsEntity.get(verCfg.getVerEntCfg().getOriginalIdPropName());
+
+        Object primaryKey = idMapper.mapToIdFromMap(originalId);
+
+        // Checking if the entity is in cache
+        if (versionsReader.getFirstLevelCache().contains(entityName, revision, primaryKey)) {
+            return versionsReader.getFirstLevelCache().get(entityName, revision, primaryKey);
+        }
+
+        // If it is not in the cache, creating a new entity instance
+        Object ret;
+        try {
+            Class<?> cls = ReflectionTools.loadClass(entityName);
+            ret = cls.newInstance();
+        } catch (Exception e) {
+            throw new VersionsException(e);
+        }
+
+        // Putting the newly created entity instance into the first level cache, in case a one-to-one bidirectional
+        // relation is present (which is eagerly loaded).
+        versionsReader.getFirstLevelCache().put(entityName, revision, primaryKey, ret);
+
+        verCfg.getEntCfg().get(entityName).getPropertyMapper().mapToEntityFromMap(verCfg, ret, versionsEntity, primaryKey,
+                versionsReader, revision);
+        idMapper.mapToEntityFromMap(ret, originalId);
+
+        return ret;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public void addInstancesFromVersionsEntities(String entityName, Collection addTo, List<Map> versionsEntities, Number revision) {
+        for (Map versionsEntity : versionsEntities) {
+            addTo.add(createInstanceFromVersionsEntity(entityName, versionsEntity, revision));
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/IdMappingData.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/IdMappingData.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/IdMappingData.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,54 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities;
+
+import org.jboss.envers.entities.mapper.id.IdMapper;
+import org.dom4j.Element;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class IdMappingData {
+    private final IdMapper idMapper;
+    // Mapping which will be used to generate the entity
+    private final Element xmlMapping;
+    // Mapping which will be used to generate references to the entity in related entities
+    private final Element xmlRelationMapping;
+
+    public IdMappingData(IdMapper idMapper, Element xmlMapping, Element xmlRelationMapping) {
+        this.idMapper = idMapper;
+        this.xmlMapping = xmlMapping;
+        this.xmlRelationMapping = xmlRelationMapping;
+    }
+
+    public IdMapper getIdMapper() {
+        return idMapper;
+    }
+
+    public Element getXmlMapping() {
+        return xmlMapping;
+    }
+
+    public Element getXmlRelationMapping() {
+        return xmlRelationMapping;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/RelationDescription.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/RelationDescription.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/RelationDescription.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,75 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities;
+
+import org.jboss.envers.entities.mapper.id.IdMapper;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+*/
+public class RelationDescription {
+    private final String fromPropertyName;
+    private final RelationType relationType;
+    private final String toEntityName;
+    private final String mappedByPropertyName;
+    private final IdMapper idMapper;
+    private boolean bidirectional;
+
+    public RelationDescription(String fromPropertyName, RelationType relationType, String toEntityName,
+                               String mappedByPropertyName, IdMapper idMapper) {
+        this.fromPropertyName = fromPropertyName;
+        this.relationType = relationType;
+        this.toEntityName = toEntityName;
+        this.mappedByPropertyName = mappedByPropertyName;
+        this.idMapper = idMapper;
+
+        this.bidirectional = false;
+    }
+
+    public String getFromPropertyName() {
+        return fromPropertyName;
+    }
+
+    public RelationType getRelationType() {
+        return relationType;
+    }
+
+    public String getToEntityName() {
+        return toEntityName;
+    }
+
+    public String getMappedByPropertyName() {
+        return mappedByPropertyName;
+    }
+
+    public IdMapper getIdMapper() {
+        return idMapper;
+    }
+
+    public boolean isBidirectional() {
+        return bidirectional;
+    }
+
+    void setBidirectional(boolean bidirectional) {
+        this.bidirectional = bidirectional;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/RelationType.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/RelationType.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/RelationType.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities;
+
+/**
+ * Type of a relation between two entities.
+ * @author Adam Warski (adam at warski dot org)
+*/
+public enum RelationType {
+    /**
+     * A single-reference-valued relation. The entity owns the relation.
+     */
+    TO_ONE,
+    /**
+     * A single-reference-valued relation. The entity doesn't own the relation. It is directly mapped in the related
+     * entity.
+     */
+    TO_ONE_NOT_OWNING,
+    /**
+     * A collection-of-references-valued relation. The entity doesn't own the relation. It is directly mapped in the
+     * related entity.
+     */
+    TO_MANY_NOT_OWNING,
+    /**
+     * A collection-of-references-valued relation. The entity owns the relation. It is mapped using a middle table.
+     */
+    TO_MANY_MIDDLE,
+    /**
+     * A collection-of-references-valued relation. The entity doesn't own the relation. It is mapped using a middle
+     * table.
+     */
+    TO_MANY_MIDDLE_NOT_OWNING
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/RevisionTypeType.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/RevisionTypeType.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/RevisionTypeType.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,108 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities;
+
+import org.hibernate.usertype.UserType;
+import org.hibernate.HibernateException;
+import org.jboss.envers.RevisionType;
+
+import java.sql.ResultSet;
+import java.sql.Types;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.io.Serializable;
+
+/**
+ * A hibernate type for the {@link RevisionType} enum.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionTypeType implements UserType {
+    private static final int[] SQL_TYPES = { Types.TINYINT };
+
+    public int[] sqlTypes() {
+        return SQL_TYPES;
+    }
+
+    public Class returnedClass() {
+        return RevisionType.class;
+    }
+
+    public RevisionType nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
+        byte representation = (byte) resultSet.getInt(names[0]);
+        RevisionType result = null;
+
+        if (!resultSet.wasNull()) {
+            result = RevisionType.fromRepresentation(representation);
+        }
+
+        return result;
+    }
+
+    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException, SQLException {
+        if (null == value) {
+            preparedStatement.setNull(index, Types.TINYINT);
+        } else {
+            preparedStatement.setInt(index, ((RevisionType) value).getRepresentation());
+        }
+    }
+
+    public Object deepCopy(Object value) throws HibernateException{
+        return value;
+    }
+
+    public boolean isMutable() {
+        return false;
+    }
+
+    public Object assemble(Serializable cached, Object owner) throws HibernateException {
+        return cached;
+    }
+
+    public Serializable disassemble(Object value) throws HibernateException {
+        return (Serializable)value;
+    }
+
+    public Object replace(Object original, Object target, Object owner) throws HibernateException {
+        return original;
+    }
+
+    public int hashCode(Object x) throws HibernateException {
+        return x.hashCode();
+    }
+
+    public boolean equals(Object x, Object y) throws HibernateException {
+        //noinspection ObjectEquality
+        if (x == y) {
+            return true;
+        }
+
+        if (null == x || null == y) {
+            return false;
+        }
+
+        return x.equals(y);
+    }
+}
+
+

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/CompositeMapperBuilder.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/CompositeMapperBuilder.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/CompositeMapperBuilder.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,30 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface CompositeMapperBuilder extends SimpleMapperBuilder {    
+    public CompositeMapperBuilder addComposite(String propertyName);
+    public void addComposite(String propertyName, PropertyMapper propertyMapper);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/ExtendedPropertyMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/ExtendedPropertyMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/ExtendedPropertyMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,31 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface ExtendedPropertyMapper extends PropertyMapper, CompositeMapperBuilder {
+    public boolean map(Map<String, Object> data, String[] propertyNames, Object[] newState, Object[] oldState);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/MapPropertyMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/MapPropertyMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/MapPropertyMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,94 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.exception.VersionsException;
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.collection.PersistentCollection;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MapPropertyMapper implements PropertyMapper, CompositeMapperBuilder {
+    private String propertyName;
+    private ExtendedPropertyMapper delegate;
+
+    public MapPropertyMapper(String propertyName) {
+        this.propertyName = propertyName;
+        this.delegate = new MultiPropertyMapper();
+    }
+
+    public void add(String propertyName, ModificationStore modStore) {
+        delegate.add(propertyName, modStore);
+    }
+
+    public CompositeMapperBuilder addComposite(String propertyName) {
+        return delegate.addComposite(propertyName);
+    }
+
+    public void addComposite(String propertyName, PropertyMapper propertyMapper) {
+        delegate.addComposite(propertyName, propertyMapper);
+    }
+
+    public boolean mapToMapFromEntity(Map<String, Object> data, Object newObj, Object oldObj) {
+        Map<String, Object> newData = new HashMap<String, Object>();
+        data.put(propertyName, newData);
+
+        return delegate.mapToMapFromEntity(newData, newObj, oldObj);
+    }
+
+    public void mapToEntityFromMap(VersionsConfiguration verCfg, Object obj, Map data, Object primaryKey, VersionsReaderImplementor versionsReader, Number revision) {
+        if (data == null || obj == null) {
+            return;
+        }
+
+        Getter getter = ReflectionTools.getGetter(obj.getClass(), propertyName);
+        Setter setter = ReflectionTools.getSetter(obj.getClass(), propertyName);
+
+        try {
+            Object subObj = ReflectHelper.getDefaultConstructor(getter.getReturnType()).newInstance();
+            setter.set(obj, subObj, null);
+            delegate.mapToEntityFromMap(verCfg, subObj, (Map) data.get(propertyName), primaryKey, versionsReader, revision);
+        } catch (Exception e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
+                                                                                    PersistentCollection newColl,
+                                                                                    Serializable oldColl,
+                                                                                    Serializable id) {
+        return delegate.mapCollectionChanges(referencingPropertyName, newColl, oldColl, id);
+    }
+
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/MultiPropertyMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/MultiPropertyMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/MultiPropertyMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,122 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+import org.hibernate.property.Getter;
+import org.hibernate.MappingException;
+import org.hibernate.collection.PersistentCollection;
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MultiPropertyMapper implements ExtendedPropertyMapper {
+    protected Map<String, PropertyMapper> properties;
+
+    public MultiPropertyMapper() {
+        properties = new HashMap<String, PropertyMapper>();
+    }
+
+    public void add(String propertyName, ModificationStore modStore) {
+        SinglePropertyMapper single = new SinglePropertyMapper();
+        single.add(propertyName,  modStore);
+        properties.put(propertyName, single);
+    }
+
+    public CompositeMapperBuilder addComposite(String propertyName) {
+        if (properties.get(propertyName) != null) {
+            throw new MappingException("Mapping for " + propertyName + " already added!");
+        }
+
+        MapPropertyMapper mapperBuilder = new MapPropertyMapper(propertyName);
+        properties.put(propertyName, mapperBuilder);
+
+        return mapperBuilder;
+    }
+
+    public void addComposite(String propertyName, PropertyMapper propertyMapper) {
+        properties.put(propertyName, propertyMapper);
+    }
+
+    private Object getAtIndexOrNull(Object[] array, int index) { return array == null ? null : array[index]; }
+
+    public boolean map(Map<String, Object> data, String[] propertyNames, Object[] newState, Object[] oldState) {
+        boolean ret = false;
+        for (int i=0; i<propertyNames.length; i++) {
+            String propertyName = propertyNames[i];
+
+            if (properties.containsKey(propertyName)) {
+                ret |= properties.get(propertyName).mapToMapFromEntity(data,
+                        getAtIndexOrNull(newState, i),
+                        getAtIndexOrNull(oldState, i));
+            }
+        }
+
+        return ret;
+    }
+
+    public boolean mapToMapFromEntity(Map<String, Object> data, Object newObj, Object oldObj) {
+        boolean ret = false;
+        for (String propertyName : properties.keySet()) {
+            Getter getter;
+            if (newObj != null) {
+                getter = ReflectionTools.getGetter(newObj.getClass(), propertyName);
+            } else if (oldObj != null) {
+                getter = ReflectionTools.getGetter(oldObj.getClass(), propertyName);
+            } else {
+                return false;
+            }
+
+            ret |= properties.get(propertyName).mapToMapFromEntity(data,
+                    newObj == null ? null : getter.get(newObj),
+                    oldObj == null ? null : getter.get(oldObj));
+        }
+
+        return ret;
+    }
+
+    public void mapToEntityFromMap(VersionsConfiguration verCfg, Object obj, Map data, Object primaryKey, VersionsReaderImplementor versionsReader, Number revision) {
+        for (String propertyName : properties.keySet()) {
+            properties.get(propertyName).mapToEntityFromMap(verCfg, obj, data, primaryKey, versionsReader, revision);
+        }
+    }
+
+    public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
+                                                                                    PersistentCollection newColl,
+                                                                                    Serializable oldColl,
+                                                                                    Serializable id) {
+        PropertyMapper mapper = properties.get(referencingPropertyName);
+        if (mapper != null) {
+            return mapper.mapCollectionChanges(referencingPropertyName, newColl, oldColl, id);
+        } else {
+            return null;
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/PersistentCollectionChangeData.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/PersistentCollectionChangeData.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/PersistentCollectionChangeData.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+import java.util.Map;
+
+/**
+ * Data describing the change of a single object in a persisten collection (when the object was added, removed or
+ * modified in the collection).
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PersistentCollectionChangeData {
+    private final String entityName;
+    private final Map<String, Object> data;
+    private final Object changedElement;
+
+    public PersistentCollectionChangeData(String entityName, Map<String, Object> data, Object changedElement) {
+        this.entityName = entityName;
+        this.data = data;
+        this.changedElement = changedElement;
+    }
+
+    /**
+     *
+     * @return Name of the (middle) entity that holds the collection data.
+     */
+    public String getEntityName() {
+        return entityName;
+    }
+
+    public Map<String, Object> getData() {
+        return data;
+    }
+
+    /**
+     * For use by bi-directional associations.
+     * @return The affected element, which was changed (added, removed, modified) in the collection.
+     */
+    public Object getChangedElement() {
+        return changedElement;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/PropertyMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/PropertyMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/PropertyMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,68 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.hibernate.collection.PersistentCollection;
+
+import java.util.Map;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface PropertyMapper {
+    /**
+     * Maps properties to the given map, basing on differences between properties of new and old objects.
+     * @param data Data to map to.
+     * @param newObj New state of the entity.
+     * @param oldObj Old state of the entity.
+     * @return True if there are any differences between the states represented by newObj and oldObj.
+     */
+    boolean mapToMapFromEntity(Map<String, Object> data, Object newObj, Object oldObj);
+
+    /**
+     * Maps properties from the given map to the given object.
+     * @param verCfg Versions configuration.
+     * @param obj Object to map to.
+     * @param data Data to map from.
+     * @param primaryKey Primary key of the object to which we map (for relations)
+     * @param versionsReader VersionsReader for reading relations
+     * @param revision Revision at which the object is read, for reading relations
+     */
+    void mapToEntityFromMap(VersionsConfiguration verCfg, Object obj, Map data, Object primaryKey,
+                            VersionsReaderImplementor versionsReader, Number revision);
+
+    /**
+     * Maps collection changes
+     * @param referencingPropertyName Name of the field, which holds the collection in the entity.
+     * @param newColl New collection, after updates.
+     * @param oldColl Old collection, before updates.
+     * @param id Id of the object owning the collection.
+     * @return List of changes that need to be performed on the persistent store.
+     */
+    List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
+                                                              PersistentCollection newColl,
+                                                              Serializable oldColl, Serializable id);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SimpleMapperBuilder.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SimpleMapperBuilder.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SimpleMapperBuilder.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,31 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+import org.jboss.envers.ModificationStore;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface SimpleMapperBuilder {    
+    public void add(String propertyName, ModificationStore modStore);
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SinglePropertyMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SinglePropertyMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SinglePropertyMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,76 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.tools.Tools;
+import org.jboss.envers.exception.VersionsException;
+import org.hibernate.property.Setter;
+import org.hibernate.collection.PersistentCollection;
+
+import java.util.Map;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * TODO: diff
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SinglePropertyMapper implements PropertyMapper, SimpleMapperBuilder {
+    private String propertyName;
+
+    public SinglePropertyMapper() { }
+
+    public void add(String propertyName, ModificationStore modStore) {
+        if (this.propertyName != null) {
+            throw new VersionsException("Only one property can be added!");
+        }
+
+        this.propertyName = propertyName;
+    }
+
+    public boolean mapToMapFromEntity(Map<String, Object> data, Object newObj, Object oldObj) {
+        data.put(propertyName, newObj);
+
+        return !Tools.objectsEqual(newObj, oldObj);
+    }
+
+    public void mapToEntityFromMap(VersionsConfiguration verCfg, Object obj, Map data, Object primaryKey, VersionsReaderImplementor versionsReader, Number revision) {
+        if (data == null || obj == null) {
+            return;
+        }
+
+        Setter setter = ReflectionTools.getSetter(obj.getClass(), propertyName);
+        setter.set(obj, data.get(propertyName), null);
+    }
+
+    public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
+                                                                                    PersistentCollection newColl,
+                                                                                    Serializable oldColl,
+                                                                                    Serializable id) {
+        return null;
+    }
+
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SubclassPropertyMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SubclassPropertyMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/SubclassPropertyMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,91 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper;
+
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.hibernate.collection.PersistentCollection;
+
+import java.util.Map;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * A mapper which maps from a parent mapper and a "main" one, but adds only to the "main". The "main" mapper
+ * should be the mapper of the subclass.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SubclassPropertyMapper implements ExtendedPropertyMapper {
+    private ExtendedPropertyMapper main;
+    private ExtendedPropertyMapper parentMapper;
+
+    public SubclassPropertyMapper(ExtendedPropertyMapper main, ExtendedPropertyMapper parentMapper) {
+        this.main = main;
+        this.parentMapper = parentMapper;
+    }
+
+    public boolean map(Map<String, Object> data, String[] propertyNames, Object[] newState, Object[] oldState) {
+        boolean parentDiffs = parentMapper.map(data, propertyNames, newState, oldState);
+        boolean mainDiffs = main.map(data, propertyNames, newState, oldState);
+
+        return parentDiffs || mainDiffs;
+    }
+
+    public boolean mapToMapFromEntity(Map<String, Object> data, Object newObj, Object oldObj) {
+        boolean parentDiffs = parentMapper.mapToMapFromEntity(data, newObj, oldObj);
+        boolean mainDiffs = main.mapToMapFromEntity(data, newObj, oldObj);
+
+        return parentDiffs || mainDiffs;
+    }
+
+    public void mapToEntityFromMap(VersionsConfiguration verCfg, Object obj, Map data, Object primaryKey, VersionsReaderImplementor versionsReader, Number revision) {
+        parentMapper.mapToEntityFromMap(verCfg, obj, data, primaryKey, versionsReader, revision);
+        main.mapToEntityFromMap(verCfg, obj, data, primaryKey, versionsReader, revision);
+    }
+
+    public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
+                                                                                    PersistentCollection newColl, 
+                                                                                    Serializable oldColl,
+                                                                                    Serializable id) {
+        List<PersistentCollectionChangeData> collectionChanges = parentMapper.mapCollectionChanges(
+                referencingPropertyName, newColl, oldColl, id);
+
+        if (collectionChanges == null) {
+            return main.mapCollectionChanges(referencingPropertyName, newColl, oldColl, id);
+        } else {
+            return collectionChanges;
+        }
+    }
+
+    public CompositeMapperBuilder addComposite(String propertyName) {
+        return main.addComposite(propertyName);
+    }
+
+    public void addComposite(String propertyName, PropertyMapper propertyMapper) {
+        main.addComposite(propertyName, propertyMapper);
+    }
+
+    public void add(String propertyName, ModificationStore modStore) {
+        main.add(propertyName, modStore);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/AbstractCompositeIdMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/AbstractCompositeIdMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/AbstractCompositeIdMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,61 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.id;
+
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.exception.VersionsException;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractCompositeIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder {
+    protected Map<String, SingleIdMapper> ids;
+    protected String compositeIdClass;
+
+    protected AbstractCompositeIdMapper(String compositeIdClass) {
+        ids = new LinkedHashMap<String, SingleIdMapper>();
+        
+        this.compositeIdClass = compositeIdClass;
+    }
+
+    public void add(String propertyName, ModificationStore modStore) {
+        ids.put(propertyName, new SingleIdMapper(propertyName));
+    }
+
+    public Object mapToIdFromMap(Map data) {
+        Object ret;
+        try {
+            ret = Thread.currentThread().getContextClassLoader().loadClass(compositeIdClass).newInstance();
+        } catch (Exception e) {
+            throw new VersionsException(e);
+        }
+
+        for (SingleIdMapper mapper : ids.values()) {
+            mapper.mapToEntityFromMap(ret, data);
+        }
+
+        return ret;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/AbstractIdMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/AbstractIdMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/AbstractIdMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,86 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.id;
+
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractIdMapper implements IdMapper {
+    private Parameters getParametersToUse(Parameters parameters, List<QueryParameterData> paramDatas) {
+         if (paramDatas.size() > 1) {
+            return parameters.addSubParameters("and");
+        } else {
+            return parameters;
+        }
+    }
+
+    public void addIdsEqualToQuery(Parameters parameters, String prefix1, String prefix2) {
+        List<QueryParameterData> paramDatas = mapToQueryParametersFromId(null);
+
+        Parameters parametersToUse = getParametersToUse(parameters, paramDatas);
+
+        for (QueryParameterData paramData : paramDatas) {
+            parametersToUse.addWhere(paramData.getProperty(prefix1), false, "=", paramData.getProperty(prefix2), false);
+        }
+    }
+
+    public void addIdsEqualToQuery(Parameters parameters, String prefix1, IdMapper mapper2, String prefix2) {
+        List<QueryParameterData> paramDatas1 = mapToQueryParametersFromId(null);
+        List<QueryParameterData> paramDatas2 = mapper2.mapToQueryParametersFromId(null);
+
+        Parameters parametersToUse = getParametersToUse(parameters, paramDatas1);
+
+        Iterator<QueryParameterData> paramDataIter1 = paramDatas1.iterator();
+        Iterator<QueryParameterData> paramDataIter2 = paramDatas2.iterator();
+        while (paramDataIter1.hasNext()) {
+            QueryParameterData paramData1 = paramDataIter1.next();
+            QueryParameterData paramData2 = paramDataIter2.next();
+
+            parametersToUse.addWhere(paramData1.getProperty(prefix1), false, "=", paramData2.getProperty(prefix2), false);
+        }
+    }
+
+    public void addIdEqualsToQuery(Parameters parameters, Object id, String prefix, boolean equals) {
+        List<QueryParameterData> paramDatas = mapToQueryParametersFromId(id);
+
+        Parameters parametersToUse = getParametersToUse(parameters, paramDatas);
+
+        for (QueryParameterData paramData : paramDatas) {
+            parametersToUse.addWhereWithParam(paramData.getProperty(prefix), equals ? "=" : "<>", paramData.getValue());
+        }
+    }
+
+    public void addNamedIdEqualsToQuery(Parameters parameters, String prefix, boolean equals) {
+        List<QueryParameterData> paramDatas = mapToQueryParametersFromId(null);
+
+        Parameters parametersToUse = getParametersToUse(parameters, paramDatas);
+
+        for (QueryParameterData paramData : paramDatas) {
+            parametersToUse.addWhereWithNamedParam(paramData.getProperty(prefix), equals ? "=" : "<>", paramData.getQueryParameterName());
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/EmbeddedIdMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/EmbeddedIdMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/EmbeddedIdMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,110 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.id;
+
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+import org.hibernate.util.ReflectHelper;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EmbeddedIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder {
+    private String idPropertyName;
+
+    public EmbeddedIdMapper(String idPropertyName, String compositeIdClass) {
+        super(compositeIdClass);
+        
+        this.idPropertyName = idPropertyName;
+    }
+
+    public void mapToMapFromId(Map<String, Object> data, Object obj) {
+        for (IdMapper idMapper : ids.values()) {
+            idMapper.mapToMapFromEntity(data, obj);
+        }
+    }
+
+    public void mapToMapFromEntity(Map<String, Object> data, Object obj) {
+        if (obj == null) {
+            return;
+        }
+
+        Getter getter = ReflectionTools.getGetter(obj.getClass(), idPropertyName);
+        mapToMapFromId(data, getter.get(obj));
+    }
+
+    public void mapToEntityFromMap(Object obj, Map data) {
+        if (data == null || obj == null) {
+            return;
+        }
+
+        Getter getter = ReflectionTools.getGetter(obj.getClass(), idPropertyName);
+        Setter setter = ReflectionTools.getSetter(obj.getClass(), idPropertyName);
+
+        try {
+            Object subObj = ReflectHelper.getDefaultConstructor(getter.getReturnType()).newInstance();
+            setter.set(obj, subObj, null);
+
+            for (IdMapper idMapper : ids.values()) {
+                idMapper.mapToEntityFromMap(subObj, data);
+            }
+        } catch (Exception e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public IdMapper prefixMappedProperties(String prefix) {
+        EmbeddedIdMapper ret = new EmbeddedIdMapper(idPropertyName, compositeIdClass);
+
+        for (String propertyName : ids.keySet()) {
+            ret.ids.put(propertyName, new SingleIdMapper(propertyName, prefix + propertyName));
+        }
+
+        return ret;
+    }
+
+    public Object mapToIdFromEntity(Object data) {
+        if (data == null) {
+            return null;
+        }
+
+        Getter getter = ReflectionTools.getGetter(data.getClass(), idPropertyName);
+        return getter.get(data);
+    }
+
+    public List<QueryParameterData> mapToQueryParametersFromId(Object obj) {
+        Map<String, Object> data = new LinkedHashMap<String, Object>();
+        mapToMapFromId(data, obj);
+
+        List<QueryParameterData> ret = new ArrayList<QueryParameterData>();
+
+        for (Map.Entry<String, Object> propertyData : data.entrySet()) {
+            ret.add(new QueryParameterData(propertyData.getKey(), propertyData.getValue()));
+        }
+
+        return ret;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/IdMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/IdMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/IdMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,97 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.id;
+
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.Map;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface IdMapper {
+    void mapToMapFromId(Map<String, Object> data, Object obj);
+
+    void mapToMapFromEntity(Map<String, Object> data, Object obj);
+
+    void mapToEntityFromMap(Object obj, Map data);
+
+    Object mapToIdFromEntity(Object data);
+
+    Object mapToIdFromMap(Map data);
+
+    /**
+     * Creates a mapper with all mapped properties prefixed. A mapped property is a property which
+     * is directly mapped to values (not composite).
+     * @param prefix Prefix to add to mapped properties
+     * @return A copy of the current property mapper, with mapped properties prefixed.
+     */
+    IdMapper prefixMappedProperties(String prefix);
+
+    /**
+     * @param obj Id from which to map.
+     * @return A set parameter data, needed to build a query basing on the given id.
+     */
+    List<QueryParameterData> mapToQueryParametersFromId(Object obj);
+
+    /**
+     * Adds query statements, which contains restrictions, which express the property that the id of the entity
+     * with alias prefix1, is equal to the id of the entity with alias prefix2 (the entity is the same).
+     * @param parameters Parameters, to which to add the statements.
+     * @param prefix1 First alias of the entity + prefix to add to the properties.
+     * @param prefix2 Second alias of the entity + prefix to add to the properties.
+     */
+    void addIdsEqualToQuery(Parameters parameters, String prefix1, String prefix2);
+
+    /**
+     * Adds query statements, which contains restrictions, which express the property that the id of the entity
+     * with alias prefix1, is equal to the id of the entity with alias prefix2 mapped by the second mapper
+     * (the second mapper must be for the same entity, but it can have, for example, prefixed properties).
+     * @param parameters Parameters, to which to add the statements.
+     * @param prefix1 First alias of the entity + prefix to add to the properties.
+     * @param mapper2 Second mapper for the same entity, which will be used to get properties for the right side
+     * of the equation.
+     * @param prefix2 Second alias of the entity + prefix to add to the properties.
+     */
+    void addIdsEqualToQuery(Parameters parameters, String prefix1, IdMapper mapper2, String prefix2);
+
+    /**
+     * Adds query statements, which contains restrictions, which express the property that the id of the entity
+     * with alias prefix, is equal to the given object.
+     * @param parameters Parameters, to which to add the statements.
+     * @param id Value of id.
+     * @param prefix Prefix to add to the properties (may be null).
+     * @param equals Should this query express the "=" relation or the "<>" relation.
+     */
+    void addIdEqualsToQuery(Parameters parameters, Object id, String prefix, boolean equals);
+
+    /**
+     * Adds query statements, which contains named parameters, which express the property that the id of the entity
+     * with alias prefix, is equal to the given object. It is the responsibility of the using method to read
+     * parameter values from the id and specify them on the final query object.
+     * @param parameters Parameters, to which to add the statements.
+     * @param prefix Prefix to add to the properties (may be null).
+     * @param equals Should this query express the "=" relation or the "<>" relation.
+     */
+    void addNamedIdEqualsToQuery(Parameters parameters, String prefix, boolean equals);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/MultipleIdMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/MultipleIdMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/MultipleIdMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,93 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.id;
+
+import org.jboss.envers.exception.VersionsException;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MultipleIdMapper extends AbstractCompositeIdMapper implements SimpleIdMapperBuilder {
+    public MultipleIdMapper(String compositeIdClass) {
+        super(compositeIdClass);
+    }
+
+    public void mapToMapFromId(Map<String, Object> data, Object obj) {
+        for (IdMapper idMapper : ids.values()) {
+            idMapper.mapToMapFromEntity(data, obj);
+        }
+    }
+
+    public void mapToMapFromEntity(Map<String, Object> data, Object obj) {
+        mapToMapFromId(data, obj);
+    }
+
+    public void mapToEntityFromMap(Object obj, Map data) {
+        for (IdMapper idMapper : ids.values()) {
+            idMapper.mapToEntityFromMap(obj, data);
+        }
+    }
+
+    public IdMapper prefixMappedProperties(String prefix) {
+        MultipleIdMapper ret = new MultipleIdMapper(compositeIdClass);
+
+        for (String propertyName : ids.keySet()) {
+            ret.ids.put(propertyName, new SingleIdMapper(propertyName, prefix + propertyName));
+        }
+
+        return ret;
+    }
+
+    public Object mapToIdFromEntity(Object data) {
+        if (data == null) {
+            return null;
+        }
+
+        Object ret;
+        try {
+            ret = Thread.currentThread().getContextClassLoader().loadClass(compositeIdClass).newInstance();
+        } catch (Exception e) {
+            throw new VersionsException(e);
+        }
+
+        for (SingleIdMapper mapper : ids.values()) {
+            mapper.mapToEntityFromEntity(ret, data);
+        }
+
+        return ret;
+    }
+
+    public List<QueryParameterData> mapToQueryParametersFromId(Object obj) {
+        Map<String, Object> data = new LinkedHashMap<String, Object>();
+        mapToMapFromId(data, obj);
+
+        List<QueryParameterData> ret = new ArrayList<QueryParameterData>();
+
+        for (Map.Entry<String, Object> propertyData : data.entrySet()) {
+            ret.add(new QueryParameterData(propertyData.getKey(), propertyData.getValue()));
+        }
+
+        return ret;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/QueryParameterData.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/QueryParameterData.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/QueryParameterData.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,73 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.id;
+
+import org.hibernate.Query;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class QueryParameterData {
+    private String flatEntityPropertyName;
+    private Object value;
+
+    public QueryParameterData(String flatEntityPropertyName, Object value) {
+        this.flatEntityPropertyName = flatEntityPropertyName;
+        this.value = value;
+    }
+
+    public String getProperty(String prefix) {
+        if (prefix != null) {
+            return prefix + "." + flatEntityPropertyName;
+        } else {
+            return flatEntityPropertyName;
+        }
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setParameterValue(Query query) {
+        query.setParameter(flatEntityPropertyName, value);
+    }
+
+    public String getQueryParameterName() {
+        return flatEntityPropertyName;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QueryParameterData)) return false;
+
+        QueryParameterData that = (QueryParameterData) o;
+
+        if (flatEntityPropertyName != null ? !flatEntityPropertyName.equals(that.flatEntityPropertyName) : that.flatEntityPropertyName != null)
+            return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (flatEntityPropertyName != null ? flatEntityPropertyName.hashCode() : 0);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/SimpleIdMapperBuilder.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/SimpleIdMapperBuilder.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/SimpleIdMapperBuilder.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,30 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.id;
+
+import org.jboss.envers.entities.mapper.SimpleMapperBuilder;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface SimpleIdMapperBuilder extends IdMapper, SimpleMapperBuilder {
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/SingleIdMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/SingleIdMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/id/SingleIdMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,123 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.id;
+
+import org.jboss.envers.ModificationStore;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.jboss.envers.exception.VersionsException;
+import org.hibernate.property.Setter;
+import org.hibernate.property.Getter;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SingleIdMapper extends AbstractIdMapper implements SimpleIdMapperBuilder {
+    private String beanPropertyName;
+    private String propertyName;
+
+    public SingleIdMapper() {
+    }
+
+    public SingleIdMapper(String beanPropertyName, String propertyName) {
+        this.beanPropertyName = beanPropertyName;
+        this.propertyName = propertyName;
+    }
+
+    public SingleIdMapper(String propertyName) {
+        this.beanPropertyName = propertyName;
+        this.propertyName = propertyName;
+    }
+
+    public void add(String propertyName, ModificationStore modStore) {
+        if (this.propertyName != null) {
+            throw new VersionsException("Only one property can be added!");
+        }
+
+        this.propertyName = propertyName;
+        this.beanPropertyName = propertyName;
+    }
+
+    public void mapToEntityFromMap(Object obj, Map data) {
+        if (data == null || obj == null) {
+            return;
+        }
+
+        Setter setter = ReflectionTools.getSetter(obj.getClass(), beanPropertyName);
+        setter.set(obj, data.get(propertyName), null);
+    }
+
+    public Object mapToIdFromMap(Map data) {
+        if (data == null) {
+            return null;
+        }
+
+        return data.get(propertyName);
+    }
+
+    public Object mapToIdFromEntity(Object data) {
+        if (data == null) {
+            return null;
+        }
+
+        Getter getter = ReflectionTools.getGetter(data.getClass(), beanPropertyName);
+        return getter.get(data);
+    }
+
+    public void mapToMapFromId(Map<String, Object> data, Object obj) {
+        if (data != null) {
+            data.put(propertyName, obj);
+        }
+    }
+
+    public void mapToMapFromEntity(Map<String, Object> data, Object obj) {
+        if (obj == null) {
+            data.put(propertyName, null);
+        } else {
+            Getter getter = ReflectionTools.getGetter(obj.getClass(), beanPropertyName);
+            data.put(propertyName, getter.get(obj));
+        }
+    }
+
+    public void mapToEntityFromEntity(Object objTo, Object objFrom) {
+        if (objTo == null || objFrom == null) {
+            return;
+        }
+
+        Getter getter = ReflectionTools.getGetter(objFrom.getClass(), beanPropertyName);
+        Setter setter = ReflectionTools.getSetter(objTo.getClass(), beanPropertyName);
+        setter.set(objTo, getter.get(objFrom), null);
+    }
+
+    public IdMapper prefixMappedProperties(String prefix) {
+        return new SingleIdMapper(propertyName, prefix + propertyName);
+    }
+
+    public List<QueryParameterData> mapToQueryParametersFromId(Object obj) {
+        List<QueryParameterData> ret = new ArrayList<QueryParameterData>();
+
+        ret.add(new QueryParameterData(propertyName, obj));
+
+        return ret;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/AbstractCollectionMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/AbstractCollectionMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/AbstractCollectionMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,145 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.entities.mapper.PersistentCollectionChangeData;
+import org.jboss.envers.entities.mapper.PropertyMapper;
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.hibernate.collection.PersistentCollection;
+import org.hibernate.property.Setter;
+
+import java.util.*;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractCollectionMapper<T> implements PropertyMapper {
+    protected final CommonCollectionMapperData commonCollectionMapperData;    
+    protected final Class<? extends T> collectionClass;
+
+    private final Constructor<? extends T> proxyConstructor;
+
+    protected AbstractCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
+                                       Class<? extends T> collectionClass, Class<? extends T> proxyClass) {
+        this.commonCollectionMapperData = commonCollectionMapperData;
+        this.collectionClass = collectionClass;
+
+        try {
+            proxyConstructor = proxyClass.getConstructor(Initializor.class);
+        } catch (NoSuchMethodException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    protected abstract Collection getNewCollectionContent(PersistentCollection newCollection);
+    protected abstract Collection getOldCollectionContent(Serializable oldCollection);
+    protected abstract Object getElement(Object changedObject);
+
+    /**
+     * Maps the changed collection element to the given map.
+     * @param data Where to map the data.
+     * @param changed The changed collection element to map.
+     */
+    protected abstract void mapToMapFromObject(Map<String, Object> data, Object changed);
+
+    private void addCollectionChanges(List<PersistentCollectionChangeData> collectionChanges, Set<Object> changed,
+                                      RevisionType revisionType, Serializable id) {
+        for (Object changedObj : changed) {
+            Map<String, Object> entityData = new HashMap<String, Object>();
+            Map<String, Object> originalId = new HashMap<String, Object>();
+            entityData.put(commonCollectionMapperData.getVerEntCfg().getOriginalIdPropName(), originalId);
+
+            collectionChanges.add(new PersistentCollectionChangeData(
+                    commonCollectionMapperData.getVersionsMiddleEntityName(), entityData, getElement(changedObj)));
+            // Mapping the collection owner's id.
+            commonCollectionMapperData.getReferencingIdData().getPrefixedMapper().mapToMapFromId(originalId, id);
+
+            // Mapping collection element and index (if present).
+            mapToMapFromObject(originalId, changedObj);
+
+            entityData.put(commonCollectionMapperData.getVerEntCfg().getRevisionTypePropName(), revisionType);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
+                                                                     PersistentCollection newColl,
+                                                                     Serializable oldColl, Serializable id) {
+        if (!commonCollectionMapperData.getCollectionReferencingPropertyName().equals(referencingPropertyName)) {
+            return null;
+        }
+
+        List<PersistentCollectionChangeData> collectionChanges = new ArrayList<PersistentCollectionChangeData>();
+
+        // Comparing new and old collection content.
+        Collection newCollection = getNewCollectionContent(newColl);
+        Collection oldCollection = getOldCollectionContent(oldColl);
+
+        Set<Object> added = new HashSet<Object>();
+        if (newColl != null) { added.addAll(newCollection); }
+        if (oldColl != null) { added.removeAll(oldCollection); }
+
+        addCollectionChanges(collectionChanges, added, RevisionType.ADD, id);
+
+        Set<Object> deleted = new HashSet<Object>();
+        if (oldColl != null) { deleted.addAll(oldCollection); }
+        if (newColl != null) { deleted.removeAll(newCollection); }
+
+        addCollectionChanges(collectionChanges, deleted, RevisionType.DEL, id);
+
+        return collectionChanges;
+    }
+
+    public boolean mapToMapFromEntity(Map<String, Object> data, Object newObj, Object oldObj) {
+        // Changes are mapped in the "mapCollectionChanges" method.
+        return false;
+    }
+
+    protected abstract Initializor<T> getInitializor(VersionsConfiguration verCfg,
+                                                     VersionsReaderImplementor versionsReader, Object primaryKey,
+                                                     Number revision);
+
+    public void mapToEntityFromMap(VersionsConfiguration verCfg, Object obj, Map data, Object primaryKey,
+                                   VersionsReaderImplementor versionsReader, Number revision) {
+        Setter setter = ReflectionTools.getSetter(obj.getClass(),
+                commonCollectionMapperData.getCollectionReferencingPropertyName());
+        try {
+            setter.set(obj, proxyConstructor.newInstance(getInitializor(verCfg, versionsReader, primaryKey, revision)), null);
+        } catch (InstantiationException e) {
+            throw new VersionsException(e);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/BasicCollectionMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/BasicCollectionMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/BasicCollectionMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,77 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.entities.mapper.PropertyMapper;
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor;
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.BasicCollectionInitializor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.hibernate.collection.PersistentCollection;
+
+import java.util.Map;
+import java.util.Collection;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class BasicCollectionMapper<T extends Collection> extends AbstractCollectionMapper<T> implements PropertyMapper {
+    private final MiddleComponentData elementComponentData;
+
+    public BasicCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
+                                 Class<? extends T> collectionClass, Class<? extends T> proxyClass,
+                                 MiddleComponentData elementComponentData) {
+        super(commonCollectionMapperData, collectionClass, proxyClass);
+        this.elementComponentData = elementComponentData;
+    }
+
+    protected Initializor<T> getInitializor(VersionsConfiguration verCfg, VersionsReaderImplementor versionsReader,
+                                            Object primaryKey, Number revision) {
+        return new BasicCollectionInitializor<T>(verCfg, versionsReader, commonCollectionMapperData.getQueryGenerator(),
+                primaryKey, revision, collectionClass, elementComponentData);
+    }
+
+    protected Collection getNewCollectionContent(PersistentCollection newCollection) {
+        return (Collection) newCollection;
+    }
+
+    protected Collection getOldCollectionContent(Serializable oldCollection) {
+        if (oldCollection == null) {
+            return null;
+        } else if (oldCollection instanceof Map) {
+            return ((Map) oldCollection).keySet();
+        } else {
+            return (Collection) oldCollection;
+        }
+    }
+
+    protected void mapToMapFromObject(Map<String, Object> data, Object changed) {
+        elementComponentData.getComponentMapper().mapToMapFromObject(data, changed);
+    }
+
+    protected Object getElement(Object changedObject) {
+        return changedObject;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/CommonCollectionMapperData.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/CommonCollectionMapperData.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/CommonCollectionMapperData.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,69 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.entities.mapper.relation.query.RelationQueryGenerator;
+
+/**
+ * Data that is used by all collection mappers, regardless of the type.  
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class CommonCollectionMapperData {
+    private final VersionsEntitiesConfiguration verEntCfg;
+    private final String versionsMiddleEntityName;
+    private final String collectionReferencingPropertyName;
+    private final MiddleIdData referencingIdData;
+    private final RelationQueryGenerator queryGenerator;
+
+    public CommonCollectionMapperData(VersionsEntitiesConfiguration verEntCfg, String versionsMiddleEntityName,
+                                      String collectionReferencingPropertyName, MiddleIdData referencingIdData,
+                                      RelationQueryGenerator queryGenerator) {
+        this.verEntCfg = verEntCfg;
+        this.versionsMiddleEntityName = versionsMiddleEntityName;
+        this.collectionReferencingPropertyName = collectionReferencingPropertyName;
+        this.referencingIdData = referencingIdData;
+        this.queryGenerator = queryGenerator;
+    }
+
+    public VersionsEntitiesConfiguration getVerEntCfg() {
+        return verEntCfg;
+    }
+
+    public String getVersionsMiddleEntityName() {
+        return versionsMiddleEntityName;
+    }
+
+    public String getCollectionReferencingPropertyName() {
+        return collectionReferencingPropertyName;
+    }
+
+    public MiddleIdData getReferencingIdData() {
+        return referencingIdData;
+    }
+
+    public RelationQueryGenerator getQueryGenerator() {
+        return queryGenerator;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/ListCollectionMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/ListCollectionMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/ListCollectionMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,90 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.entities.mapper.PropertyMapper;
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor;
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.ListCollectionInitializor;
+import org.jboss.envers.entities.mapper.relation.lazy.proxy.ListProxy;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.tools.Tools;
+import org.jboss.envers.tools.Pair;
+import org.hibernate.collection.PersistentCollection;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class ListCollectionMapper extends AbstractCollectionMapper<List> implements PropertyMapper {
+    private final MiddleComponentData elementComponentData;
+    private final MiddleComponentData indexComponentData;
+
+    public ListCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
+                                MiddleComponentData elementComponentData, MiddleComponentData indexComponentData) {
+        super(commonCollectionMapperData, List.class, ListProxy.class);
+        this.elementComponentData = elementComponentData;
+        this.indexComponentData = indexComponentData;
+    }
+
+    protected Initializor<List> getInitializor(VersionsConfiguration verCfg, VersionsReaderImplementor versionsReader,
+                                               Object primaryKey, Number revision) {
+        return new ListCollectionInitializor(verCfg, versionsReader, commonCollectionMapperData.getQueryGenerator(),
+                primaryKey, revision, elementComponentData, indexComponentData);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected Collection getNewCollectionContent(PersistentCollection newCollection) {
+        if (newCollection == null) {
+            return null;
+        } else {
+            return Tools.listToIndexElementPairList((List<Object>) newCollection);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected Collection getOldCollectionContent(Serializable oldCollection) {
+        if (oldCollection == null) {
+            return null;
+        } else {
+            return Tools.listToIndexElementPairList((List<Object>) oldCollection);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected void mapToMapFromObject(Map<String, Object> data, Object changed) {
+        Pair<Integer, Object> indexValuePair = (Pair<Integer, Object>) changed;
+        elementComponentData.getComponentMapper().mapToMapFromObject(data, indexValuePair.getSecond());
+        indexComponentData.getComponentMapper().mapToMapFromObject(data, indexValuePair.getFirst());
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected Object getElement(Object changedObject) {
+        return ((Pair<Integer, Object>) changedObject).getFirst();
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MapCollectionMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MapCollectionMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MapCollectionMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.entities.mapper.PropertyMapper;
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor;
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.MapCollectionInitializor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.hibernate.collection.PersistentCollection;
+
+import java.util.Map;
+import java.util.Collection;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class MapCollectionMapper<T extends Map> extends AbstractCollectionMapper<T> implements PropertyMapper {
+    private final MiddleComponentData elementComponentData;
+    private final MiddleComponentData indexComponentData;
+
+    public MapCollectionMapper(CommonCollectionMapperData commonCollectionMapperData,
+                               Class<? extends T> collectionClass, Class<? extends T> proxyClass,
+                               MiddleComponentData elementComponentData, MiddleComponentData indexComponentData) {
+        super(commonCollectionMapperData, collectionClass, proxyClass);
+        this.elementComponentData = elementComponentData;
+        this.indexComponentData = indexComponentData;
+    }
+
+    protected Initializor<T> getInitializor(VersionsConfiguration verCfg, VersionsReaderImplementor versionsReader,
+                                            Object primaryKey, Number revision) {
+        return new MapCollectionInitializor<T>(verCfg, versionsReader, commonCollectionMapperData.getQueryGenerator(),
+                primaryKey, revision, collectionClass, elementComponentData, indexComponentData);
+    }
+
+    protected Collection getNewCollectionContent(PersistentCollection newCollection) {
+        if (newCollection == null) {
+            return null;
+        } else {
+            return ((Map) newCollection).entrySet();
+        }
+    }
+
+    protected Collection getOldCollectionContent(Serializable oldCollection) {
+        if (oldCollection == null) {
+            return null;
+        } else {
+            return ((Map) oldCollection).entrySet();
+        }
+    }
+
+    protected void mapToMapFromObject(Map<String, Object> data, Object changed) {
+        elementComponentData.getComponentMapper().mapToMapFromObject(data, ((Map.Entry) changed).getValue());
+        indexComponentData.getComponentMapper().mapToMapFromObject(data, ((Map.Entry) changed).getKey());
+    }
+
+    protected Object getElement(Object changedObject) {
+        return ((Map.Entry) changedObject).getValue();
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MiddleComponentData.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MiddleComponentData.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MiddleComponentData.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.entities.mapper.relation.component.MiddleComponentMapper;
+
+/**
+ * A data holder for a middle relation component (which is either the collection element or index):
+ * - component mapper used to map the component to and from versions entities
+ * - an index, which specifies in which element of the array returned by the query for reading the collection the data
+ * of the component is
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class MiddleComponentData {
+    private final MiddleComponentMapper componentMapper;
+    private final int componentIndex;
+
+    public MiddleComponentData(MiddleComponentMapper componentMapper, int componentIndex) {
+        this.componentMapper = componentMapper;
+        this.componentIndex = componentIndex;
+    }
+
+    public MiddleComponentMapper getComponentMapper() {
+        return componentMapper;
+    }
+
+    public int getComponentIndex() {
+        return componentIndex;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MiddleIdData.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MiddleIdData.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/MiddleIdData.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,76 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.entities.mapper.id.IdMapper;
+import org.jboss.envers.entities.IdMappingData;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+
+/**
+ * A class holding information about ids, which form a virtual "relation" from a middle-table. Middle-tables are used
+ * when mapping collections.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class MiddleIdData {
+    /**
+     * Original id mapper of the related entity.
+     */
+    private final IdMapper originalMapper;
+    /**
+     * Prefixed id mapper (with the names for the id fields that are used in the middle table) of the related entity.
+     */
+    private final IdMapper prefixedMapper;
+    /**
+     * Name of the related entity.
+     */
+    private final String entityName;
+    /**
+     * Versions name of the related entity.
+     */
+    private final String versionsEntityName;
+
+    public MiddleIdData(VersionsEntitiesConfiguration verEntCfg, IdMappingData mappingData, String prefix,
+                        String entityName) {
+        this.originalMapper = mappingData.getIdMapper();
+        this.prefixedMapper = mappingData.getIdMapper().prefixMappedProperties(prefix);
+        this.entityName = entityName;
+        this.versionsEntityName = verEntCfg.getVersionsEntityName(entityName);
+    }
+
+    public IdMapper getOriginalMapper() {
+        return originalMapper;
+    }
+
+    public IdMapper getPrefixedMapper() {
+        return prefixedMapper;
+    }
+
+    public String getEntityName() {
+        return entityName;
+    }
+
+    public String getVersionsEntityName() {
+        return versionsEntityName;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/OneToOneNotOwningMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/OneToOneNotOwningMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/OneToOneNotOwningMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,87 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.entities.mapper.PropertyMapper;
+import org.jboss.envers.entities.mapper.PersistentCollectionChangeData;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.hibernate.property.Setter;
+import org.hibernate.NonUniqueResultException;
+import org.hibernate.collection.PersistentCollection;
+
+import javax.persistence.NoResultException;
+import java.util.Map;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class OneToOneNotOwningMapper implements PropertyMapper {
+    private String owningReferencePropertyName;
+    private String owningEntityName;
+    private String propertyName;
+
+    public OneToOneNotOwningMapper(String owningReferencePropertyName, String owningEntityName, String propertyName) {
+        this.owningReferencePropertyName = owningReferencePropertyName;
+        this.owningEntityName = owningEntityName;
+        this.propertyName = propertyName;
+    }
+
+    public boolean mapToMapFromEntity(Map<String, Object> data, Object newObj, Object oldObj) {
+        return false;
+    }
+
+    public void mapToEntityFromMap(VersionsConfiguration verCfg, Object obj, Map data, Object primaryKey, VersionsReaderImplementor versionsReader, Number revision) {
+        if (obj == null) {
+            return;
+        }
+
+        Class<?> entityClass = ReflectionTools.loadClass(owningEntityName);
+
+        Object value;
+
+        try {
+            value = versionsReader.createQuery().forEntitiesAtRevision(entityClass, revision)
+                    .add(VersionsRestrictions.relatedIdEq(owningReferencePropertyName, primaryKey)).getSingleResult();
+        } catch (NoResultException e) {
+            value = null;
+        } catch (NonUniqueResultException e) {
+            throw new VersionsException("Many versions results for one-to-one relationship: (" + owningEntityName +
+                    ", " + owningReferencePropertyName + ")");
+        }
+
+        Setter setter = ReflectionTools.getSetter(obj.getClass(), propertyName);
+        setter.set(obj, value, null);
+    }
+
+    public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
+                                                                                    PersistentCollection newColl,
+                                                                                    Serializable oldColl,
+                                                                                    Serializable id) {
+        return null;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/ToOneIdMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/ToOneIdMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/ToOneIdMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,94 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation;
+
+import org.jboss.envers.entities.mapper.PropertyMapper;
+import org.jboss.envers.entities.mapper.PersistentCollectionChangeData;
+import org.jboss.envers.entities.mapper.id.IdMapper;
+import org.jboss.envers.tools.Tools;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.entities.mapper.relation.lazy.ToOneDelegateSessionImplementor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.hibernate.property.Setter;
+import org.hibernate.collection.PersistentCollection;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ToOneIdMapper implements PropertyMapper {
+    private final IdMapper delegate;
+    private final String propertyName;
+    private final String referencedEntityName;
+
+    public ToOneIdMapper(IdMapper delegate, String propertyName, String referencedEntityName) {
+        this.delegate = delegate;
+        this.propertyName = propertyName;
+        this.referencedEntityName = referencedEntityName;
+    }
+
+    public boolean mapToMapFromEntity(Map<String, Object> data, Object newObj, Object oldObj) {
+        HashMap<String, Object> newData = new HashMap<String, Object>();
+        data.put(propertyName, newData);
+
+        delegate.mapToMapFromEntity(newData, newObj);
+
+        return !Tools.objectsEqual(newObj, oldObj);
+    }
+
+    public void mapToEntityFromMap(VersionsConfiguration verCfg, Object obj, Map data, Object primaryKey,
+                                   VersionsReaderImplementor versionsReader, Number revision) {
+        if (obj == null) {
+            return;
+        }
+
+        Object entityId = delegate.mapToIdFromMap((Map) data.get(propertyName));
+        Object value;
+        if (entityId == null) {
+            value = null;
+        } else {
+            if (versionsReader.getFirstLevelCache().contains(referencedEntityName, revision, entityId)) {
+                value = versionsReader.getFirstLevelCache().get(referencedEntityName, revision, entityId);
+            } else {
+                Class<?> entityClass = ReflectionTools.loadClass(referencedEntityName);
+
+                value = versionsReader.getSessionImplementor().getFactory().getEntityPersister(referencedEntityName).
+                        createProxy(null, new ToOneDelegateSessionImplementor(versionsReader, entityClass, entityId, revision));
+            }
+        }
+
+        Setter setter = ReflectionTools.getSetter(obj.getClass(), propertyName);
+        setter.set(obj, value, null);
+    }
+
+    public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
+                                                                     PersistentCollection newColl,
+                                                                     Serializable oldColl,
+                                                                     Serializable id) {
+        return null;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleComponentMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleComponentMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleComponentMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.component;
+
+import org.jboss.envers.entities.EntityInstantiator;
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface MiddleComponentMapper {
+    /**
+     * Maps from full object data, contained in the given map (or object representation of the map, if
+     * available), to an object.
+     * @param entityInstantiator An entity instatiator bound with an open versions reader.
+     * @param data Full object data.
+     * @param dataObject An optional object representation of the data.
+     * @param revision Revision at which the data is read.
+     * @return An object with data corresponding to the one found in the given map.
+     */
+    Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map<String, Object> data,
+                                  Object dataObject, Number revision);
+
+    /**
+     * Maps from an object to the object's map representation (for an entity - only its id).
+     * @param data Map to which data should be added.
+     * @param obj Object to map from.
+     */
+    void mapToMapFromObject(Map<String, Object> data, Object obj);
+
+    /**
+     * Adds query statements, which contains restrictions, which express the property that part of the middle
+     * entity with alias prefix1, is equal to part of the middle entity with alias prefix2 (the entity is the same).
+     * The part is the component's representation in the middle entity.
+     * @param parameters Parameters, to which to add the statements.
+     * @param prefix1 First alias of the entity + prefix to add to the properties.
+     * @param prefix2 Second alias of the entity + prefix to add to the properties.
+     */
+    void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleDummyComponentMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleDummyComponentMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleDummyComponentMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,45 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.component;
+
+import org.jboss.envers.entities.EntityInstantiator;
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class MiddleDummyComponentMapper implements MiddleComponentMapper {
+    public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map<String, Object> data,
+                                         Object dataObject, Number revision) {
+        return null;
+    }
+
+    public void mapToMapFromObject(Map<String, Object> data, Object obj) {
+    }
+
+    public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) {
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleMapKeyIdComponentMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,60 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.component;
+
+import org.jboss.envers.entities.EntityInstantiator;
+import org.jboss.envers.entities.mapper.id.IdMapper;
+import org.jboss.envers.tools.query.Parameters;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+
+import java.util.Map;
+
+/**
+ * A component mapper for the @MapKey mapping: the value of the map's key is the id of the entity. This
+ * doesn't have an effect on the data stored in the versions tables, so <code>mapToMapFromObject</code> is
+ * empty.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class MiddleMapKeyIdComponentMapper implements MiddleComponentMapper {
+    private final VersionsEntitiesConfiguration verEntCfg;
+    private final IdMapper relatedIdMapper;
+
+    public MiddleMapKeyIdComponentMapper(VersionsEntitiesConfiguration verEntCfg, IdMapper relatedIdMapper) {
+        this.verEntCfg = verEntCfg;
+        this.relatedIdMapper = relatedIdMapper;
+    }
+
+    public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map<String, Object> data,
+                                         Object dataObject, Number revision) {
+        return relatedIdMapper.mapToIdFromMap((Map) data.get(verEntCfg.getOriginalIdPropName()));
+    }
+
+    public void mapToMapFromObject(Map<String, Object> data, Object obj) {
+        // Doing nothing.
+    }
+
+    public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) {
+        // Doing nothing.
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleMapKeyPropertyComponentMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,58 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.component;
+
+import org.jboss.envers.entities.EntityInstantiator;
+import org.jboss.envers.tools.query.Parameters;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+
+import java.util.Map;
+
+/**
+ * A component mapper for the @MapKey mapping with the name parameter specified: the value of the map's key
+ * is a property of the entity. This doesn't have an effect on the data stored in the versions tables,
+ * so <code>mapToMapFromObject</code> is empty.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class MiddleMapKeyPropertyComponentMapper implements MiddleComponentMapper {
+    private final String propertyName;
+
+    public MiddleMapKeyPropertyComponentMapper(String propertyName) {
+        this.propertyName = propertyName;
+    }
+
+    public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map<String, Object> data,
+                                         Object dataObject, Number revision) {
+        // dataObject is not null, as this mapper can only be used in an index.
+        return ReflectionTools.getGetter(dataObject.getClass(), propertyName).get(dataObject);
+    }
+
+    public void mapToMapFromObject(Map<String, Object> data, Object obj) {
+        // Doing nothing.
+    }
+
+    public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) {
+        // Doing nothing.
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleRelatedComponentMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleRelatedComponentMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleRelatedComponentMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.component;
+
+import org.jboss.envers.entities.EntityInstantiator;
+import org.jboss.envers.entities.mapper.relation.MiddleIdData;
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class MiddleRelatedComponentMapper implements MiddleComponentMapper {
+    private final MiddleIdData relatedIdData;
+
+    public MiddleRelatedComponentMapper(MiddleIdData relatedIdData) {
+        this.relatedIdData = relatedIdData;
+    }
+
+    public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map<String, Object> data,
+                                         Object dataObject, Number revision) {
+        return entityInstantiator.createInstanceFromVersionsEntity(relatedIdData.getEntityName(), data, revision);
+    }
+
+    public void mapToMapFromObject(Map<String, Object> data, Object obj) {
+        relatedIdData.getPrefixedMapper().mapToMapFromEntity(data, obj);
+    }
+
+    public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) {
+        relatedIdData.getPrefixedMapper().addIdsEqualToQuery(parameters, prefix1, prefix2);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleSimpleComponentMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleSimpleComponentMapper.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/component/MiddleSimpleComponentMapper.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,57 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.component;
+
+import org.jboss.envers.entities.EntityInstantiator;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class MiddleSimpleComponentMapper implements MiddleComponentMapper {
+    private final String propertyName;
+    private final VersionsEntitiesConfiguration verEntCfg;
+
+    public MiddleSimpleComponentMapper(VersionsEntitiesConfiguration verEntCfg, String propertyName) {
+        this.propertyName = propertyName;
+        this.verEntCfg = verEntCfg;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public Object mapToObjectFromFullMap(EntityInstantiator entityInstantiator, Map<String, Object> data,
+                                         Object dataObject, Number revision) {
+        return ((Map<String, Object>) data.get(verEntCfg.getOriginalIdPropName())).get(propertyName);
+    }
+
+    public void mapToMapFromObject(Map<String, Object> data, Object obj) {
+        data.put(propertyName, obj);
+    }
+
+    public void addMiddleEqualToQuery(Parameters parameters, String prefix1, String prefix2) {
+        parameters.addWhere(prefix1 + "." + propertyName, false, "=", prefix2 + "." + propertyName, false);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/AbstractDelegateSessionImplementor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/AbstractDelegateSessionImplementor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/AbstractDelegateSessionImplementor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,271 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy;
+
+import org.hibernate.engine.*;
+import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
+import org.hibernate.*;
+import org.hibernate.event.EventListeners;
+import org.hibernate.type.Type;
+import org.hibernate.loader.custom.CustomQuery;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.impl.CriteriaImpl;
+import org.hibernate.jdbc.Batcher;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.collection.PersistentCollection;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.sql.Connection;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractDelegateSessionImplementor implements SessionImplementor {
+    private SessionImplementor delegate;
+
+    public AbstractDelegateSessionImplementor(SessionImplementor delegate) {
+        this.delegate = delegate;
+    }
+
+    public abstract Object doImmediateLoad(String entityName);
+
+    public Object immediateLoad(String entityName, Serializable id) throws HibernateException {
+        return doImmediateLoad(entityName);
+    }
+
+    // Delegate methods
+
+	public LoadQueryInfluencers getLoadQueryInfluencers() {
+		return delegate.getLoadQueryInfluencers();
+	}
+
+	public Interceptor getInterceptor() {
+        return delegate.getInterceptor();
+    }
+
+    public void setAutoClear(boolean enabled) {
+        delegate.setAutoClear(enabled);
+    }
+
+    public boolean isTransactionInProgress() {
+        return delegate.isTransactionInProgress();
+    }
+
+    public void initializeCollection(PersistentCollection collection, boolean writing) throws HibernateException {
+        delegate.initializeCollection(collection, writing);
+    }
+
+    public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException {
+        return delegate.internalLoad(entityName, id, eager, nullable);
+    }
+
+    public long getTimestamp() {
+        return delegate.getTimestamp();
+    }
+
+    public SessionFactoryImplementor getFactory() {
+        return delegate.getFactory();
+    }
+
+    public Batcher getBatcher() {
+        return delegate.getBatcher();
+    }
+
+    public List list(String query, QueryParameters queryParameters) throws HibernateException {
+        return delegate.list(query, queryParameters);
+    }
+
+    public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException {
+        return delegate.iterate(query, queryParameters);
+    }
+
+    public ScrollableResults scroll(String query, QueryParameters queryParameters) throws HibernateException {
+        return delegate.scroll(query, queryParameters);
+    }
+
+    public ScrollableResults scroll(CriteriaImpl criteria, ScrollMode scrollMode) {
+        return delegate.scroll(criteria, scrollMode);
+    }
+
+    public List list(CriteriaImpl criteria) {
+        return delegate.list(criteria);
+    }
+
+    public List listFilter(Object collection, String filter, QueryParameters queryParameters) throws HibernateException {
+        return delegate.listFilter(collection, filter, queryParameters);
+    }
+
+    public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters) throws HibernateException {
+        return delegate.iterateFilter(collection, filter, queryParameters);
+    }
+
+    public EntityPersister getEntityPersister(String entityName, Object object) throws HibernateException {
+        return delegate.getEntityPersister(entityName, object);
+    }
+
+    public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
+        return delegate.getEntityUsingInterceptor(key);
+    }
+
+    public void afterTransactionCompletion(boolean successful, Transaction tx) {
+        delegate.afterTransactionCompletion(successful, tx);
+    }
+
+    public void beforeTransactionCompletion(Transaction tx) {
+        delegate.beforeTransactionCompletion(tx);
+    }
+
+    public Serializable getContextEntityIdentifier(Object object) {
+        return delegate.getContextEntityIdentifier(object);
+    }
+
+    public String bestGuessEntityName(Object object) {
+        return delegate.bestGuessEntityName(object);
+    }
+
+    public String guessEntityName(Object entity) throws HibernateException {
+        return delegate.guessEntityName(entity);
+    }
+
+    public Object instantiate(String entityName, Serializable id) throws HibernateException {
+        return delegate.instantiate(entityName, id);
+    }
+
+    public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException {
+        return delegate.listCustomQuery(customQuery, queryParameters);
+    }
+
+    public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException {
+        return delegate.scrollCustomQuery(customQuery, queryParameters);
+    }
+
+    public List list(NativeSQLQuerySpecification spec, QueryParameters queryParameters) throws HibernateException {
+        return delegate.list(spec, queryParameters);
+    }
+
+    public ScrollableResults scroll(NativeSQLQuerySpecification spec, QueryParameters queryParameters) throws HibernateException {
+        return delegate.scroll(spec, queryParameters);
+    }
+
+    public Object getFilterParameterValue(String filterParameterName) {
+        return delegate.getFilterParameterValue(filterParameterName);
+    }
+
+    public Type getFilterParameterType(String filterParameterName) {
+        return delegate.getFilterParameterType(filterParameterName);
+    }
+
+    public Map getEnabledFilters() {
+        return delegate.getEnabledFilters();
+    }
+
+    public int getDontFlushFromFind() {
+        return delegate.getDontFlushFromFind();
+    }
+
+    public EventListeners getListeners() {
+        return delegate.getListeners();
+    }
+
+    public PersistenceContext getPersistenceContext() {
+        return delegate.getPersistenceContext();
+    }
+
+    public int executeUpdate(String query, QueryParameters queryParameters) throws HibernateException {
+        return delegate.executeUpdate(query, queryParameters);
+    }
+
+    public int executeNativeUpdate(NativeSQLQuerySpecification specification, QueryParameters queryParameters) throws HibernateException {
+        return delegate.executeNativeUpdate(specification, queryParameters);
+    }
+
+    public EntityMode getEntityMode() {
+        return delegate.getEntityMode();
+    }
+
+    public CacheMode getCacheMode() {
+        return delegate.getCacheMode();
+    }
+
+    public void setCacheMode(CacheMode cm) {
+        delegate.setCacheMode(cm);
+    }
+
+    public boolean isOpen() {
+        return delegate.isOpen();
+    }
+
+    public boolean isConnected() {
+        return delegate.isConnected();
+    }
+
+    public FlushMode getFlushMode() {
+        return delegate.getFlushMode();
+    }
+
+    public void setFlushMode(FlushMode fm) {
+        delegate.setFlushMode(fm);
+    }
+
+    public Connection connection() {
+        return delegate.connection();
+    }
+
+    public void flush() {
+        delegate.flush();
+    }
+
+    public Query getNamedQuery(String name) {
+        return delegate.getNamedQuery(name);
+    }
+
+    public Query getNamedSQLQuery(String name) {
+        return delegate.getNamedSQLQuery(name);
+    }
+
+    public boolean isEventSource() {
+        return delegate.isEventSource();
+    }
+
+    public void afterScrollOperation() {
+        delegate.afterScrollOperation();
+    }
+
+    public void setFetchProfile(String name) {
+        delegate.setFetchProfile(name);
+    }
+
+    public String getFetchProfile() {
+        return delegate.getFetchProfile();
+    }
+
+    public JDBCContext getJDBCContext() {
+        return delegate.getJDBCContext();
+    }
+
+    public boolean isClosed() {
+        return delegate.isClosed();
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/ToOneDelegateSessionImplementor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/ToOneDelegateSessionImplementor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/ToOneDelegateSessionImplementor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,48 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy;
+
+import org.hibernate.HibernateException;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ToOneDelegateSessionImplementor extends AbstractDelegateSessionImplementor {
+    private final VersionsReaderImplementor versionsReader;
+    private final Class<?> entityClass;
+    private final Object entityId;
+    private final Number revision;
+
+    public ToOneDelegateSessionImplementor(VersionsReaderImplementor versionsReader,
+                                           Class<?> entityClass, Object entityId, Number revision) {
+        super(versionsReader.getSessionImplementor());
+        this.versionsReader = versionsReader;
+        this.entityClass = entityClass;
+        this.entityId = entityId;
+        this.revision = revision;
+    }
+
+    public Object doImmediateLoad(String entityName) throws HibernateException {
+        return versionsReader.find(entityClass, entityId, revision);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/AbstractCollectionInitializor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/AbstractCollectionInitializor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/AbstractCollectionInitializor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.initializor;
+
+import org.jboss.envers.entities.mapper.relation.query.RelationQueryGenerator;
+import org.jboss.envers.entities.EntityInstantiator;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+import java.util.List;
+
+/**
+ * Initializes a persistent collection.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractCollectionInitializor<T> implements Initializor<T> {
+    private final VersionsReaderImplementor versionsReader;
+    private final RelationQueryGenerator queryGenerator;
+    private final Object primaryKey;
+    
+    protected final Number revision;
+    protected final EntityInstantiator entityInstantiator;
+
+    public AbstractCollectionInitializor(VersionsConfiguration verCfg,
+                                         VersionsReaderImplementor versionsReader,
+                                         RelationQueryGenerator queryGenerator,
+                                         Object primaryKey, Number revision) {
+        this.versionsReader = versionsReader;
+        this.queryGenerator = queryGenerator;
+        this.primaryKey = primaryKey;
+        this.revision = revision;
+
+        entityInstantiator = new EntityInstantiator(verCfg, versionsReader);
+    }
+
+    protected abstract T initializeCollection(int size);
+
+    protected abstract void addToCollection(T collection, Object collectionRow);
+
+    public T initialize() {
+        List<?> collectionContent = queryGenerator.getQuery(versionsReader, primaryKey, revision).list();
+
+        T collection = initializeCollection(collectionContent.size());
+
+        for (Object collectionRow : collectionContent) {
+            addToCollection(collection, collectionRow);
+        }
+
+        return collection;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/ArrayCollectionInitializor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/ArrayCollectionInitializor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/ArrayCollectionInitializor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,70 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.initializor;
+
+import org.jboss.envers.entities.mapper.relation.query.RelationQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+import java.util.*;
+
+/**
+ * Initializes a map.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ArrayCollectionInitializor extends AbstractCollectionInitializor<Object[]> {
+    private final MiddleComponentData elementComponentData;
+    private final MiddleComponentData indexComponentData;
+
+    public ArrayCollectionInitializor(VersionsConfiguration verCfg,
+                                    VersionsReaderImplementor versionsReader,
+                                    RelationQueryGenerator queryGenerator,
+                                    Object primaryKey, Number revision,
+                                    MiddleComponentData elementComponentData,
+                                    MiddleComponentData indexComponentData) {
+        super(verCfg, versionsReader, queryGenerator, primaryKey, revision);
+
+        this.elementComponentData = elementComponentData;
+        this.indexComponentData = indexComponentData;
+    }
+
+    protected Object[] initializeCollection(int size) {
+        return new Object[size];
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected void addToCollection(Object[] collection, Object collectionRow) {
+        Object elementData = ((List) collectionRow).get(elementComponentData.getComponentIndex());
+        Object element = elementComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
+                (Map<String, Object>) elementData, null, revision);
+
+        Object indexData = ((List) collectionRow).get(indexComponentData.getComponentIndex());
+        Object indexObj = indexComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
+                (Map<String, Object>) indexData, element, revision);
+        int index = ((Number) indexObj).intValue();
+
+        collection[index] = element;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/BasicCollectionInitializor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/BasicCollectionInitializor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/BasicCollectionInitializor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,71 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.initializor;
+
+import org.jboss.envers.entities.mapper.relation.query.RelationQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+import java.util.*;
+
+/**
+ * Initializes a non-indexed java collection (set or list, eventually sorted).
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicCollectionInitializor<T extends Collection> extends AbstractCollectionInitializor<T> {
+    private final Class<? extends T> collectionClass;
+    private final MiddleComponentData elementComponentData;
+
+    public BasicCollectionInitializor(VersionsConfiguration verCfg,
+                                       VersionsReaderImplementor versionsReader,
+                                       RelationQueryGenerator queryGenerator,
+                                       Object primaryKey, Number revision,
+                                       Class<? extends T> collectionClass,
+                                       MiddleComponentData elementComponentData) {
+        super(verCfg, versionsReader, queryGenerator, primaryKey, revision);
+
+        this.collectionClass = collectionClass;
+        this.elementComponentData = elementComponentData;
+    }
+
+    protected T initializeCollection(int size) {
+        try {
+            return collectionClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new VersionsException(e);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected void addToCollection(T collection, Object collectionRow) {
+        Object elementData = ((List) collectionRow).get(elementComponentData.getComponentIndex());
+        Object element = elementComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
+                (Map<String, Object>) elementData, null, revision);
+        collection.add(element);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/Initializor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/Initializor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/Initializor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,29 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.initializor;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface Initializor<T> {
+    T initialize();
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/ListCollectionInitializor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/ListCollectionInitializor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/ListCollectionInitializor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,75 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.initializor;
+
+import org.jboss.envers.entities.mapper.relation.query.RelationQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+import java.util.*;
+
+/**
+ * Initializes a map.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ListCollectionInitializor extends AbstractCollectionInitializor<List> {
+    private final MiddleComponentData elementComponentData;
+    private final MiddleComponentData indexComponentData;
+
+    public ListCollectionInitializor(VersionsConfiguration verCfg,
+                                    VersionsReaderImplementor versionsReader,
+                                    RelationQueryGenerator queryGenerator,
+                                    Object primaryKey, Number revision,
+                                    MiddleComponentData elementComponentData,
+                                    MiddleComponentData indexComponentData) {
+        super(verCfg, versionsReader, queryGenerator, primaryKey, revision);
+
+        this.elementComponentData = elementComponentData;
+        this.indexComponentData = indexComponentData;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected List initializeCollection(int size) {
+        // Creating a list of the given capacity with all elements null initially. This ensures that we can then
+        // fill the elements safely using the <code>List.set</code> method.
+        List list = new ArrayList(size);
+        for (int i=0; i<size; i++) { list.add(null); }
+        return list;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected void addToCollection(List collection, Object collectionRow) {
+        Object elementData = ((List) collectionRow).get(elementComponentData.getComponentIndex());
+        Object element = elementComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
+                (Map<String, Object>) elementData, null, revision);
+
+        Object indexData = ((List) collectionRow).get(indexComponentData.getComponentIndex());
+        Object indexObj = indexComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
+                (Map<String, Object>) indexData, element, revision);
+        int index = ((Number) indexObj).intValue();
+
+        collection.set(index, element);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/MapCollectionInitializor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/MapCollectionInitializor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/initializor/MapCollectionInitializor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,79 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.initializor;
+
+import org.jboss.envers.entities.mapper.relation.query.RelationQueryGenerator;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+import java.util.*;
+
+/**
+ * Initializes a map.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MapCollectionInitializor<T extends Map> extends AbstractCollectionInitializor<T> {
+    private final Class<? extends T> collectionClass;
+    private final MiddleComponentData elementComponentData;
+    private final MiddleComponentData indexComponentData;
+
+    public MapCollectionInitializor(VersionsConfiguration verCfg,
+                                    VersionsReaderImplementor versionsReader,
+                                    RelationQueryGenerator queryGenerator,
+                                    Object primaryKey, Number revision,
+                                    Class<? extends T> collectionClass,
+                                    MiddleComponentData elementComponentData,
+                                    MiddleComponentData indexComponentData) {
+        super(verCfg, versionsReader, queryGenerator, primaryKey, revision);
+
+        this.collectionClass = collectionClass;
+        this.elementComponentData = elementComponentData;
+        this.indexComponentData = indexComponentData;
+    }
+
+    protected T initializeCollection(int size) {
+        try {
+            return collectionClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new VersionsException(e);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    protected void addToCollection(T collection, Object collectionRow) {
+        Object elementData = ((List) collectionRow).get(elementComponentData.getComponentIndex());
+        Object element = elementComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
+                (Map<String, Object>) elementData, null, revision);
+
+        Object indexData = ((List) collectionRow).get(indexComponentData.getComponentIndex());
+        Object index = indexComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
+                (Map<String, Object>) indexData, element, revision);
+
+        collection.put(index, element);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/CollectionProxy.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/CollectionProxy.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/CollectionProxy.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,129 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.proxy;
+
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class CollectionProxy<U, T extends Collection<U>> implements Collection<U> {
+    private org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor<T> initializor;
+    protected T delegate;
+
+    public CollectionProxy(Initializor<T> initializor) {
+        this.initializor = initializor;
+    }
+
+    protected void checkInit() {
+        if (delegate == null) {
+            delegate = initializor.initialize();
+        }
+    }
+
+    public int size() {
+        checkInit();
+        return delegate.size();
+    }
+
+    public boolean isEmpty() {
+        checkInit();
+        return delegate.isEmpty();
+    }
+
+    public boolean contains(Object o) {
+        checkInit();
+        return delegate.contains(o);
+    }
+
+    public Iterator<U> iterator() {
+        checkInit();
+        return delegate.iterator();
+    }
+
+    public Object[] toArray() {
+        checkInit();
+        return delegate.toArray();
+    }
+
+    public <V> V[] toArray(V[] a) {
+        checkInit();
+        return delegate.toArray(a);
+    }
+
+    public boolean add(U o) {
+        checkInit();
+        return delegate.add(o);
+    }
+
+    public boolean remove(Object o) {
+        checkInit();
+        return delegate.remove(o);
+    }
+
+    public boolean containsAll(Collection<?> c) {
+        checkInit();
+        return delegate.containsAll(c);
+    }
+
+    public boolean addAll(Collection<? extends U> c) {
+        checkInit();
+        return delegate.addAll(c);
+    }
+
+    public boolean removeAll(Collection<?> c) {
+        checkInit();
+        return delegate.removeAll(c);
+    }
+
+    public boolean retainAll(Collection<?> c) {
+        checkInit();
+        return delegate.retainAll(c);
+    }
+
+    public void clear() {
+        checkInit();
+        delegate.clear();
+    }
+
+    @Override
+    public String toString() {
+        checkInit();
+        return delegate.toString();
+    }
+
+    @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
+    @Override
+    public boolean equals(Object obj) {
+        checkInit();
+        return delegate.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        checkInit();
+        return delegate.hashCode();
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/ListProxy.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/ListProxy.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/ListProxy.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.proxy;
+
+import java.util.List;
+import java.util.Collection;
+import java.util.ListIterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ListProxy<U> extends CollectionProxy<U, List<U>> implements List<U> {
+    public ListProxy(org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor<List<U>> initializor) {
+        super(initializor);
+    }
+
+    public boolean addAll(int index, Collection<? extends U> c) {
+        checkInit();
+        return delegate.addAll(index, c);
+    }
+
+    public U get(int index) {
+        checkInit();
+        return delegate.get(index);
+    }
+
+    public U set(int index, U element) {
+        checkInit();
+        return delegate.set(index, element);
+    }
+
+    public void add(int index, U element) {
+        checkInit();
+        delegate.add(index, element);
+    }
+
+    public U remove(int index) {
+        checkInit();
+        return delegate.remove(index);
+    }
+
+    public int indexOf(Object o) {
+        checkInit();
+        return delegate.indexOf(o);
+    }
+
+    public int lastIndexOf(Object o) {
+        checkInit();
+        return delegate.lastIndexOf(o);
+    }
+
+    public ListIterator<U> listIterator() {
+        checkInit();
+        return delegate.listIterator();
+    }
+
+    public ListIterator<U> listIterator(int index) {
+        checkInit();
+        return delegate.listIterator(index);
+    }
+
+    public List<U> subList(int fromIndex, int toIndex) {
+        checkInit();
+        return delegate.subList(fromIndex, toIndex);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/MapProxy.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/MapProxy.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/MapProxy.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,127 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.proxy;
+
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Collection;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MapProxy<K, V> implements Map<K, V> {
+    private Initializor<Map<K, V>> initializor;
+    protected Map<K, V> delegate;
+
+    public MapProxy(org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor<Map<K, V>> initializor) {
+        this.initializor = initializor;
+    }
+
+    private void checkInit() {
+        if (delegate == null) {
+            delegate = initializor.initialize();
+        }
+    }
+
+    public int size() {
+        checkInit();
+        return delegate.size();
+    }
+
+    public boolean isEmpty() {
+        checkInit();
+        return delegate.isEmpty();
+    }
+
+    public boolean containsKey(Object o) {
+        checkInit();
+        return delegate.containsKey(o);
+    }
+
+    public boolean containsValue(Object o) {
+        checkInit();
+        return delegate.containsValue(o);
+    }
+
+    public V get(Object o) {
+        checkInit();
+        return delegate.get(o);
+    }
+
+    public V put(K k, V v) {
+        checkInit();
+        return delegate.put(k, v);
+    }
+
+    public V remove(Object o) {
+        checkInit();
+        return delegate.remove(o);
+    }
+
+    public void putAll(Map<? extends K, ? extends V> map) {
+        checkInit();
+        delegate.putAll(map);
+    }
+
+    public void clear() {
+        checkInit();
+        delegate.clear();
+    }
+
+    public Set<K> keySet() {
+        checkInit();
+        return delegate.keySet();
+    }
+
+    public Collection<V> values() {
+        checkInit();
+        return delegate.values();
+    }
+
+    public Set<Entry<K, V>> entrySet() {
+        checkInit();
+        return delegate.entrySet();
+    }
+
+    @Override
+    public String toString() {
+        checkInit();
+        return delegate.toString();
+    }
+
+    @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
+    @Override
+    public boolean equals(Object obj) {
+        checkInit();
+        return delegate.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        checkInit();
+        return delegate.hashCode();
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SetProxy.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SetProxy.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SetProxy.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,33 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.proxy;
+
+import java.util.Set;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SetProxy<U> extends CollectionProxy<U, Set<U>> implements Set<U> {
+    public SetProxy(org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor<Set<U>> initializor) {
+        super(initializor);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SortedMapProxy.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SortedMapProxy.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SortedMapProxy.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,147 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.proxy;
+
+import org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SortedMapProxy<K, V> implements SortedMap<K, V> {
+    private Initializor<SortedMap<K, V>> initializor;
+    protected SortedMap<K, V> delegate;
+
+    public SortedMapProxy(org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor<SortedMap<K, V>> initializor) {
+        this.initializor = initializor;
+    }
+
+    private void checkInit() {
+        if (delegate == null) {
+            delegate = initializor.initialize();
+        }
+    }
+
+    public int size() {
+        checkInit();
+        return delegate.size();
+    }
+
+    public boolean isEmpty() {
+        checkInit();
+        return delegate.isEmpty();
+    }
+
+    public boolean containsKey(Object o) {
+        checkInit();
+        return delegate.containsKey(o);
+    }
+
+    public boolean containsValue(Object o) {
+        checkInit();
+        return delegate.containsValue(o);
+    }
+
+    public V get(Object o) {
+        checkInit();
+        return delegate.get(o);
+    }
+
+    public V put(K k, V v) {
+        checkInit();
+        return delegate.put(k, v);
+    }
+
+    public V remove(Object o) {
+        checkInit();
+        return delegate.remove(o);
+    }
+
+    public void putAll(Map<? extends K, ? extends V> map) {
+        checkInit();
+        delegate.putAll(map);
+    }
+
+    public void clear() {
+        checkInit();
+        delegate.clear();
+    }
+
+    public Set<K> keySet() {
+        checkInit();
+        return delegate.keySet();
+    }
+
+    public Collection<V> values() {
+        checkInit();
+        return delegate.values();
+    }
+
+    public Set<Entry<K, V>> entrySet() {
+        checkInit();
+        return delegate.entrySet();
+    }
+
+    public Comparator<? super K> comparator() {
+        checkInit();
+        return delegate.comparator();
+    }
+
+    public SortedMap<K, V> subMap(K k, K k1) {
+        checkInit();
+        return delegate.subMap(k, k1);
+    }
+
+    public SortedMap<K, V> headMap(K k) {
+        checkInit();
+        return delegate.headMap(k);
+    }
+
+    public SortedMap<K, V> tailMap(K k) {
+        checkInit();
+        return delegate.tailMap(k);
+    }
+
+    public K firstKey() {
+        checkInit();
+        return delegate.firstKey();
+    }
+
+    public K lastKey() {
+        checkInit();
+        return delegate.lastKey();
+    }
+
+    @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
+    public boolean equals(Object o) {
+        checkInit();
+        return delegate.equals(o);
+    }
+
+    public int hashCode() {
+        checkInit();
+        return delegate.hashCode();
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SortedSetProxy.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SortedSetProxy.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/lazy/proxy/SortedSetProxy.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.lazy.proxy;
+
+import java.util.SortedSet;
+import java.util.Comparator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SortedSetProxy<U> extends CollectionProxy<U, SortedSet<U>> implements SortedSet<U> {
+    public SortedSetProxy(org.jboss.envers.entities.mapper.relation.lazy.initializor.Initializor<SortedSet<U>> initializor) {
+        super(initializor);
+    }
+
+    public Comparator<? super U> comparator() {
+        checkInit();
+        return delegate.comparator();
+    }
+
+    public SortedSet<U> subSet(U u, U u1) {
+        checkInit();
+        return delegate.subSet(u, u1);
+    }
+
+    public SortedSet<U> headSet(U u) {
+        checkInit();
+        return delegate.headSet(u);
+    }
+
+    public SortedSet<U> tailSet(U u) {
+        checkInit();
+        return delegate.tailSet(u);
+    }
+
+    public U first() {
+        checkInit();
+        return delegate.first();
+    }
+
+    public U last() {
+        checkInit();
+        return delegate.last();
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/OneEntityQueryGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/OneEntityQueryGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/OneEntityQueryGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,107 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.query;
+
+import org.jboss.envers.entities.mapper.relation.MiddleIdData;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.entities.mapper.id.QueryParameterData;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+import org.hibernate.Query;
+
+import java.util.Collections;
+
+/**
+ * Selects data from a relation middle-table only.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class OneEntityQueryGenerator implements RelationQueryGenerator {
+    private final String queryString;
+    private final MiddleIdData referencingIdData;
+
+    public OneEntityQueryGenerator(VersionsEntitiesConfiguration verEntCfg,
+                                   String versionsMiddleEntityName,
+                                   MiddleIdData referencingIdData,
+                                   MiddleComponentData... componentDatas) {
+        this.referencingIdData = referencingIdData;
+
+        /*
+         * The query that we need to create:
+         *   SELECT new list(ee) FROM middleEntity ee WHERE
+         * (only entities referenced by the association; id_ref_ing = id of the referencing entity)
+         *     ee.originalId.id_ref_ing = :id_ref_ing AND
+         * (the association at revision :revision)
+         *     ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
+         *       WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) AND
+         * (only non-deleted entities and associations)
+         *     ee.revision_type != DEL
+         */
+        String revisionPropertyPath = verEntCfg.getRevisionPropPath();
+        String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
+
+        // SELECT new list(ee) FROM middleEntity ee
+        QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee");
+        qb.addProjection("new list", "ee", false, false);
+        // WHERE
+        Parameters rootParameters = qb.getRootParameters();
+        // ee.originalId.id_ref_ing = :id_ref_ing
+        referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
+        // SELECT max(ee2.revision) FROM middleEntity ee2
+        QueryBuilder maxRevQb = qb.newSubQueryBuilder(versionsMiddleEntityName, "ee2");
+        maxRevQb.addProjection("max", revisionPropertyPath, false);
+        // WHERE
+        Parameters maxRevQbParameters = maxRevQb.getRootParameters();
+        // ee2.revision <= :revision
+        maxRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
+        // ee2.originalId.* = ee.originalId.*        
+        String eeOriginalIdPropertyPath = "ee." + originalIdPropertyName;
+        String ee2OriginalIdPropertyPath = "ee2." + originalIdPropertyName;
+        referencingIdData.getPrefixedMapper().addIdsEqualToQuery(maxRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
+        for (MiddleComponentData componentData : componentDatas) {
+            componentData.getComponentMapper().addMiddleEqualToQuery(maxRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
+        }
+        // ee.revision = (SELECT max(...) ...)
+        rootParameters.addWhere(revisionPropertyPath, "=", maxRevQb);       
+        // ee.revision_type != DEL
+        rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", "delrevisiontype");
+
+        StringBuilder sb = new StringBuilder();
+        qb.build(sb, Collections.<String, Object>emptyMap());
+        queryString = sb.toString();
+    }
+
+    public Query getQuery(VersionsReaderImplementor versionsReader, Object primaryKey, Number revision) {
+        Query query = versionsReader.getSession().createQuery(queryString);
+        query.setParameter("revision", revision);
+        query.setParameter("delrevisiontype", RevisionType.DEL);
+        for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) {
+            paramData.setParameterValue(query);
+        }
+
+        return query;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/OneVersionsEntityQueryGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/OneVersionsEntityQueryGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/OneVersionsEntityQueryGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.query;
+
+import org.jboss.envers.entities.mapper.id.QueryParameterData;
+import org.jboss.envers.entities.mapper.id.IdMapper;
+import org.jboss.envers.entities.mapper.relation.MiddleIdData;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.configuration.GlobalConfiguration;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+import org.hibernate.Query;
+
+import java.util.Collections;
+
+/**
+ * Selects data from a versions entity.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class OneVersionsEntityQueryGenerator implements RelationQueryGenerator {
+    private final String queryString;
+    private final MiddleIdData referencingIdData;
+
+    public OneVersionsEntityQueryGenerator(GlobalConfiguration globalCfg, VersionsEntitiesConfiguration verEntCfg,
+                                           MiddleIdData referencingIdData, String referencedEntityName,
+                                           IdMapper referencedIdMapper) {
+        this.referencingIdData = referencingIdData;
+
+        /*
+         * The query that we need to create:
+         *   SELECT new list(e) FROM versionsReferencedEntity e
+         *   WHERE
+         * (only entities referenced by the association; id_ref_ing = id of the referencing entity)
+         *     e.id_ref_ing = :id_ref_ing AND
+         * (selecting e entities at revision :revision)
+         *     e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
+         *       WHERE e2.revision <= :revision AND e2.id = e.id) AND
+         * (only non-deleted entities)
+         *     e.revision_type != DEL
+         */
+        String revisionPropertyPath = verEntCfg.getRevisionPropPath();
+        String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
+
+        String versionsReferencedEntityName = verEntCfg.getVersionsEntityName(referencedEntityName);
+
+        // SELECT new list(e) FROM versionsEntity e
+        QueryBuilder qb = new QueryBuilder(versionsReferencedEntityName, "e");
+        qb.addProjection("new list", "e", false, false);
+        // WHERE
+        Parameters rootParameters = qb.getRootParameters();
+        // e.id_ref_ed = :id_ref_ed
+        referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, null, true);
+
+        // SELECT max(e.revision) FROM versionsReferencedEntity e2
+        QueryBuilder maxERevQb = qb.newSubQueryBuilder(versionsReferencedEntityName, "e2");
+        maxERevQb.addProjection("max", revisionPropertyPath, false);
+        // WHERE
+        Parameters maxERevQbParameters = maxERevQb.getRootParameters();
+        // e2.revision <= :revision
+        maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
+        // e2.id = e.id
+        referencedIdMapper.addIdsEqualToQuery(maxERevQbParameters,
+                "e." + originalIdPropertyName, "e2." + originalIdPropertyName);
+
+        // e.revision = (SELECT max(...) ...)
+        rootParameters.addWhere(revisionPropertyPath, false, globalCfg.getCorrelatedSubqueryOperator(), maxERevQb);
+
+        // e.revision_type != DEL
+        rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), false, "!=", "delrevisiontype");
+
+        StringBuilder sb = new StringBuilder();
+        qb.build(sb, Collections.<String, Object>emptyMap());
+        queryString = sb.toString();
+    }
+
+    public Query getQuery(VersionsReaderImplementor versionsReader, Object primaryKey, Number revision) {
+        Query query = versionsReader.getSession().createQuery(queryString);
+        query.setParameter("revision", revision);
+        query.setParameter("delrevisiontype", RevisionType.DEL);
+        for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) {
+            paramData.setParameterValue(query);
+        }
+
+        return query;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/QueryGeneratorTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/QueryGeneratorTools.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/QueryGeneratorTools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,75 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.query;
+
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+import org.jboss.envers.entities.mapper.relation.MiddleIdData;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.configuration.GlobalConfiguration;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class QueryGeneratorTools {
+    public static void addEntityAtRevision(GlobalConfiguration globalCfg, QueryBuilder qb, Parameters rootParameters,
+                                           MiddleIdData idData, String revisionPropertyPath, String originalIdPropertyName,
+                                           String alias1, String alias2) {
+        // SELECT max(e.revision) FROM versionsReferencedEntity e2
+        QueryBuilder maxERevQb = qb.newSubQueryBuilder(idData.getVersionsEntityName(), alias2);
+        maxERevQb.addProjection("max", revisionPropertyPath, false);
+        // WHERE
+        Parameters maxERevQbParameters = maxERevQb.getRootParameters();
+        // e2.revision <= :revision
+        maxERevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
+        // e2.id_ref_ed = e.id_ref_ed
+        idData.getOriginalMapper().addIdsEqualToQuery(maxERevQbParameters,
+                alias1 + "." + originalIdPropertyName, alias2 +"." + originalIdPropertyName);
+
+        // e.revision = (SELECT max(...) ...)
+        rootParameters.addWhere("e." + revisionPropertyPath, false, globalCfg.getCorrelatedSubqueryOperator(), maxERevQb);
+    }
+
+    public static void addAssociationAtRevision(QueryBuilder qb, Parameters rootParameters,
+                                                MiddleIdData referencingIdData, String versionsMiddleEntityName,
+                                                String eeOriginalIdPropertyPath, String revisionPropertyPath,
+                                                String originalIdPropertyName, MiddleComponentData... componentDatas) {
+        // SELECT max(ee2.revision) FROM middleEntity ee2
+        QueryBuilder maxEeRevQb = qb.newSubQueryBuilder(versionsMiddleEntityName, "ee2");
+        maxEeRevQb.addProjection("max", revisionPropertyPath, false);
+        // WHERE
+        Parameters maxEeRevQbParameters = maxEeRevQb.getRootParameters();
+        // ee2.revision <= :revision
+        maxEeRevQbParameters.addWhereWithNamedParam(revisionPropertyPath, "<=", "revision");
+        // ee2.originalId.* = ee.originalId.*
+        String ee2OriginalIdPropertyPath = "ee2." + originalIdPropertyName;
+        referencingIdData.getPrefixedMapper().addIdsEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
+        for (MiddleComponentData componentData : componentDatas) {
+            componentData.getComponentMapper().addMiddleEqualToQuery(maxEeRevQbParameters, eeOriginalIdPropertyPath, ee2OriginalIdPropertyPath);
+        }
+
+        // ee.revision = (SELECT max(...) ...)
+        rootParameters.addWhere(revisionPropertyPath, "=", maxEeRevQb);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/RelationQueryGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/RelationQueryGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/RelationQueryGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,37 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.query;
+
+import org.hibernate.Query;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+
+/**
+ * Implementations of this interface provide a method to generate queries on a relation table (a table used
+ * for mapping relations). The query can select, apart from selecting the content of the relation table, also data of
+ * other "related" entities.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RelationQueryGenerator {
+    Query getQuery(VersionsReaderImplementor versionsReader, Object primaryKey, Number revision);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/ThreeEntityQueryGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/ThreeEntityQueryGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/ThreeEntityQueryGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,135 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.query;
+
+import org.jboss.envers.entities.mapper.id.QueryParameterData;
+import org.jboss.envers.entities.mapper.relation.MiddleIdData;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.configuration.GlobalConfiguration;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+import org.hibernate.Query;
+
+import java.util.Collections;
+
+/**
+ * Selects data from a relation middle-table and a two related versions entity.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
+    private final String queryString;
+    private final MiddleIdData referencingIdData;
+
+    public ThreeEntityQueryGenerator(GlobalConfiguration globalCfg,
+                                     VersionsEntitiesConfiguration verEntCfg,
+                                     String versionsMiddleEntityName,
+                                     MiddleIdData referencingIdData,
+                                     MiddleIdData referencedIdData,
+                                     MiddleIdData indexIdData,
+                                     MiddleComponentData... componentDatas) {
+        this.referencingIdData = referencingIdData;
+
+        /*
+         * The query that we need to create:
+         *   SELECT new list(ee, e, f) FROM versionsReferencedEntity e, versionsIndexEntity f, middleEntity ee
+         *   WHERE
+         * (entities referenced by the middle table; id_ref_ed = id of the referenced entity)
+         *     ee.id_ref_ed = e.id_ref_ed AND
+         * (entities referenced by the middle table; id_ref_ind = id of the index entity)
+         *     ee.id_ref_ind = f.id_ref_ind AND
+         * (only entities referenced by the association; id_ref_ing = id of the referencing entity)
+         *     ee.id_ref_ing = :id_ref_ing AND
+         * (selecting e entities at revision :revision)
+         *     e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
+         *       WHERE e2.revision <= :revision AND e2.id_ref_ed = e.id_ref_ed) AND
+         * (selecting f entities at revision :revision)
+         *     f.revision = (SELECT max(f2.revision) FROM versionsIndexEntity f2
+         *       WHERE f2.revision <= :revision AND f2.id_ref_ed = f.id_ref_ed) AND
+         * (the association at revision :revision)
+         *     ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
+         *       WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) AND
+         * (only non-deleted entities and associations)
+         *     ee.revision_type != DEL AND
+         *     e.revision_type != DEL AND
+         *     f.revision_type != DEL
+         */
+        String revisionPropertyPath = verEntCfg.getRevisionPropPath();
+        String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
+
+        String eeOriginalIdPropertyPath = "ee." + originalIdPropertyName;
+
+        // SELECT new list(ee) FROM middleEntity ee
+        QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee");
+        qb.addFrom(referencedIdData.getVersionsEntityName(), "e");
+        qb.addFrom(indexIdData.getVersionsEntityName(), "f");
+        qb.addProjection("new list", "ee, e, f", false, false);
+        // WHERE
+        Parameters rootParameters = qb.getRootParameters();
+        // ee.id_ref_ed = e.id_ref_ed
+        referencedIdData.getPrefixedMapper().addIdsEqualToQuery(rootParameters, eeOriginalIdPropertyPath,
+                referencedIdData.getOriginalMapper(), "e." + originalIdPropertyName);
+        // ee.id_ref_ind = f.id_ref_ind
+        indexIdData.getPrefixedMapper().addIdsEqualToQuery(rootParameters, eeOriginalIdPropertyPath,
+                indexIdData.getOriginalMapper(), "f." + originalIdPropertyName);
+        // ee.originalId.id_ref_ing = :id_ref_ing
+        referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
+
+        // e.revision = (SELECT max(...) ...)
+        QueryGeneratorTools.addEntityAtRevision(globalCfg, qb, rootParameters, referencedIdData, revisionPropertyPath,
+                originalIdPropertyName, "e", "e2");
+
+        // f.revision = (SELECT max(...) ...)
+        QueryGeneratorTools.addEntityAtRevision(globalCfg, qb, rootParameters, indexIdData, revisionPropertyPath,
+                originalIdPropertyName, "f", "f2");
+
+        // ee.revision = (SELECT max(...) ...)
+        QueryGeneratorTools.addAssociationAtRevision(qb, rootParameters, referencingIdData, versionsMiddleEntityName,
+                eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas);
+
+        // ee.revision_type != DEL
+        rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", "delrevisiontype");
+        // e.revision_type != DEL
+        rootParameters.addWhereWithNamedParam("e." + verEntCfg.getRevisionTypePropName(), false, "!=", "delrevisiontype");
+        // f.revision_type != DEL
+        rootParameters.addWhereWithNamedParam("f." + verEntCfg.getRevisionTypePropName(), false, "!=", "delrevisiontype");
+
+        StringBuilder sb = new StringBuilder();
+        qb.build(sb, Collections.<String, Object>emptyMap());
+        queryString = sb.toString();
+    }
+
+    public Query getQuery(VersionsReaderImplementor versionsReader, Object primaryKey, Number revision) {
+        Query query = versionsReader.getSession().createQuery(queryString);
+        query.setParameter("revision", revision);
+        query.setParameter("delrevisiontype", RevisionType.DEL);
+        for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) {
+            paramData.setParameterValue(query);
+        }
+
+        return query;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/TwoEntityQueryGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/TwoEntityQueryGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entities/mapper/relation/query/TwoEntityQueryGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,118 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entities.mapper.relation.query;
+
+import org.jboss.envers.entities.mapper.id.QueryParameterData;
+import org.jboss.envers.entities.mapper.relation.MiddleIdData;
+import org.jboss.envers.entities.mapper.relation.MiddleComponentData;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.configuration.GlobalConfiguration;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+import org.hibernate.Query;
+
+import java.util.Collections;
+
+/**
+ * Selects data from a relation middle-table and a related versions entity.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public final class TwoEntityQueryGenerator implements RelationQueryGenerator {
+    private final String queryString;
+    private final MiddleIdData referencingIdData;
+
+    public TwoEntityQueryGenerator(GlobalConfiguration globalCfg,
+                                   VersionsEntitiesConfiguration verEntCfg,
+                                   String versionsMiddleEntityName,
+                                   MiddleIdData referencingIdData,
+                                   MiddleIdData referencedIdData,
+                                   MiddleComponentData... componentDatas) {
+        this.referencingIdData = referencingIdData;
+
+        /*
+         * The query that we need to create:
+         *   SELECT new list(ee, e) FROM versionsReferencedEntity e, middleEntity ee
+         *   WHERE
+         * (entities referenced by the middle table; id_ref_ed = id of the referenced entity)         
+         *     ee.id_ref_ed = e.id_ref_ed AND
+         * (only entities referenced by the association; id_ref_ing = id of the referencing entity)
+         *     ee.id_ref_ing = :id_ref_ing AND
+         * (selecting e entities at revision :revision)
+         *     e.revision = (SELECT max(e2.revision) FROM versionsReferencedEntity e2
+         *       WHERE e2.revision <= :revision AND e2.id_ref_ed = e.id_ref_ed) AND
+         * (the association at revision :revision)
+         *     ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
+         *       WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) AND
+         * (only non-deleted entities and associations)
+         *     ee.revision_type != DEL AND
+         *     e.revision_type != DEL
+         */
+        String revisionPropertyPath = verEntCfg.getRevisionPropPath();
+        String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
+
+        String eeOriginalIdPropertyPath = "ee." + originalIdPropertyName;
+
+        // SELECT new list(ee) FROM middleEntity ee
+        QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee");
+        qb.addFrom(referencedIdData.getVersionsEntityName(), "e");
+        qb.addProjection("new list", "ee, e", false, false);
+        // WHERE
+        Parameters rootParameters = qb.getRootParameters();
+        // ee.id_ref_ed = e.id_ref_ed
+        referencedIdData.getPrefixedMapper().addIdsEqualToQuery(rootParameters, eeOriginalIdPropertyPath,
+                referencedIdData.getOriginalMapper(), "e." + originalIdPropertyName);
+        // ee.originalId.id_ref_ing = :id_ref_ing
+        referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
+
+        // e.revision = (SELECT max(...) ...)
+        QueryGeneratorTools.addEntityAtRevision(globalCfg, qb, rootParameters, referencedIdData, revisionPropertyPath,
+                originalIdPropertyName, "e", "e2");
+
+        // ee.revision = (SELECT max(...) ...)
+        QueryGeneratorTools.addAssociationAtRevision(qb, rootParameters, referencingIdData, versionsMiddleEntityName,
+                eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas);
+
+        // ee.revision_type != DEL
+        rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", "delrevisiontype");
+        // e.revision_type != DEL
+        rootParameters.addWhereWithNamedParam("e." + verEntCfg.getRevisionTypePropName(), false, "!=", "delrevisiontype");
+
+        StringBuilder sb = new StringBuilder();
+        qb.build(sb, Collections.<String, Object>emptyMap());
+        queryString = sb.toString();
+    }
+
+    public Query getQuery(VersionsReaderImplementor versionsReader, Object primaryKey, Number revision) {
+        Query query = versionsReader.getSession().createQuery(queryString);
+        query.setParameter("revision", revision);
+        query.setParameter("delrevisiontype", RevisionType.DEL);
+        for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) {
+            paramData.setParameterValue(query);
+        }
+
+        return query;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/entity/VersionsInheritanceEntityPersister.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/entity/VersionsInheritanceEntityPersister.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/entity/VersionsInheritanceEntityPersister.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,45 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.entity;
+
+import org.hibernate.persister.entity.SingleTableEntityPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.Mapping;
+import org.hibernate.HibernateException;
+import org.hibernate.EntityMode;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsInheritanceEntityPersister extends SingleTableEntityPersister {
+    public VersionsInheritanceEntityPersister(PersistentClass persistentClass, EntityRegionAccessStrategy entityRegionAccessStrategy, SessionFactoryImplementor factory, Mapping mapping) throws HibernateException {
+        super(persistentClass, entityRegionAccessStrategy, factory, mapping);
+    }
+
+    // A hack for http://opensource.atlassian.com/projects/hibernate/browse/HHH-3351
+    public EntityPersister getSubclassEntityPersister(Object instance, SessionFactoryImplementor factory, EntityMode entityMode) {
+        return this;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/event/VersionsEventListener.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/event/VersionsEventListener.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/event/VersionsEventListener.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,198 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.event;
+
+import org.hibernate.event.*;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.engine.CollectionEntry;
+import org.hibernate.collection.PersistentCollection;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.entities.RelationDescription;
+import org.jboss.envers.entities.RelationType;
+import org.jboss.envers.synchronization.VersionsSync;
+import org.jboss.envers.synchronization.work.*;
+import org.jboss.envers.tools.Tools;
+import org.jboss.envers.entities.mapper.id.IdMapper;
+import org.jboss.envers.entities.mapper.PersistentCollectionChangeData;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsEventListener implements PostInsertEventListener, PostUpdateEventListener,
+        PostDeleteEventListener, PreCollectionUpdateEventListener, PreCollectionRemoveEventListener,
+        PostCollectionRecreateEventListener, Initializable {
+    private VersionsConfiguration verCfg;
+
+    private void generateBidirectionalCollectionChangeWorkUnits(VersionsSync verSync, EntityPersister entityPersister,
+                                                                String entityName, Object[] newState, Object[] oldState) {
+        // Checking if this is enabled in configuration ...
+        if (!verCfg.getGlobalCfg().isGenerateRevisionsForCollections()) {
+            return;
+        }
+
+        // Checks every property of the entity, if it is an "owned" to-one relation to another entity.
+        // If the value of that property changed, and the relation is bi-directional, a new revision
+        // for the related entity is generated.
+        String[] propertyNames = entityPersister.getPropertyNames();
+
+        for (int i=0; i<propertyNames.length; i++) {
+            String propertyName = propertyNames[i];
+            RelationDescription relDesc = verCfg.getEntCfg().getRelationDescription(entityName, propertyName);
+            if (relDesc != null && relDesc.isBidirectional() && relDesc.getRelationType() == RelationType.TO_ONE) {
+                // Checking for changes
+                Object oldValue = oldState == null ? null : oldState[i];
+                Object newValue = newState == null ? null : newState[i];
+
+                if (!Tools.objectsEqual(oldValue, newValue)) {
+                    IdMapper idMapper = verCfg.getEntCfg().get(relDesc.getToEntityName()).getIdMapper();
+
+                    // We have to generate changes both in the old collection (size decreses) and new collection
+                    // (size increases).
+                    if (newValue != null) {
+                        Serializable id = (Serializable) idMapper.mapToIdFromEntity(newValue);
+                        verSync.addWorkUnit(new CollectionChangeWorkUnit(relDesc.getToEntityName(), verCfg, id, newValue));
+                    }
+
+                    if (oldValue != null) {
+                        Serializable id = (Serializable) idMapper.mapToIdFromEntity(oldValue);
+                        verSync.addWorkUnit(new CollectionChangeWorkUnit(relDesc.getToEntityName(), verCfg, id, oldValue));
+                    }
+                }
+            }
+        }
+    }
+
+    public void onPostInsert(PostInsertEvent event) {
+        String entityName = event.getPersister().getEntityName();
+
+        if (verCfg.getEntCfg().isVersioned(entityName)) {
+            VersionsSync verSync = verCfg.getSyncManager().get(event.getSession());
+
+            verSync.addWorkUnit(new AddWorkUnit(event.getPersister().getEntityName(), verCfg, event.getId(),
+                    event.getPersister(), event.getState()));
+
+            generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(), null);
+        }
+    }
+
+    public void onPostUpdate(PostUpdateEvent event) {
+        String entityName = event.getPersister().getEntityName();
+
+        if (verCfg.getEntCfg().isVersioned(entityName)) {
+            VersionsSync verSync = verCfg.getSyncManager().get(event.getSession());
+
+            verSync.addWorkUnit(new ModWorkUnit(event.getPersister().getEntityName(), verCfg, event.getId(),
+                    event.getPersister(), event.getState(), event.getOldState()));
+
+            generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(), event.getOldState());
+        }
+    }
+
+    public void onPostDelete(PostDeleteEvent event) {
+        String entityName = event.getPersister().getEntityName();
+
+        if (verCfg.getEntCfg().isVersioned(entityName)) {
+            VersionsSync verSync = verCfg.getSyncManager().get(event.getSession());
+
+            verSync.addWorkUnit(new DelWorkUnit(event.getPersister().getEntityName(), verCfg, event.getId()));
+
+            generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, null, event.getDeletedState());
+        }
+    }
+
+    private void generateBidirectionalCollectionChangeWorkUnits(VersionsSync verSync, AbstractCollectionEvent event,
+                                                                PersistentCollectionChangeWorkUnit workUnit) {
+        // Checking if this is enabled in configuration ...
+        if (!verCfg.getGlobalCfg().isGenerateRevisionsForCollections()) {
+            return;
+        }
+
+        // Checking if this is not a bidirectional relation - then, a revision needs also be generated for
+        // the other side of the relation.
+        RelationDescription relDesc = verCfg.getEntCfg().getRelationDescription(event.getAffectedOwnerEntityName(),
+                workUnit.getReferencingPropertyName());
+
+        // relDesc can be null if this is a collection of simple values (not a relation).
+        if (relDesc != null && relDesc.isBidirectional()) {
+            String relatedEntityName = relDesc.getToEntityName();
+            IdMapper relatedIdMapper = verCfg.getEntCfg().get(relatedEntityName).getIdMapper();
+            
+            for (PersistentCollectionChangeData changeData : workUnit.getCollectionChanges()) {
+                Object relatedObj = changeData.getChangedElement();
+                Serializable relatedId = (Serializable) relatedIdMapper.mapToIdFromEntity(relatedObj);
+
+                verSync.addWorkUnit(new CollectionChangeWorkUnit(relatedEntityName, verCfg, relatedId, relatedObj));
+            }
+        }
+    }
+
+    private void onCollectionAction(AbstractCollectionEvent event, PersistentCollection newColl, Serializable oldColl,
+                                    CollectionEntry collectionEntry) {
+        String entityName = event.getAffectedOwnerEntityName();
+
+        if (verCfg.getEntCfg().isVersioned(entityName)) {
+            VersionsSync verSync = verCfg.getSyncManager().get(event.getSession());
+
+            PersistentCollectionChangeWorkUnit workUnit = new PersistentCollectionChangeWorkUnit(entityName, verCfg,
+                    newColl, collectionEntry.getRole(), oldColl, event.getAffectedOwnerIdOrNull());
+            verSync.addWorkUnit(workUnit);
+
+            if (workUnit.containsWork()) {
+                // There are some changes: a revision needs also be generated for the collection owner
+                verSync.addWorkUnit(new CollectionChangeWorkUnit(event.getAffectedOwnerEntityName(), verCfg,
+                        event.getAffectedOwnerIdOrNull(), event.getAffectedOwnerOrNull()));
+
+                generateBidirectionalCollectionChangeWorkUnits(verSync, event, workUnit);
+            }
+        }
+    }
+
+    private CollectionEntry getCollectionEntry(AbstractCollectionEvent event) {
+        return event.getSession().getPersistenceContext().getCollectionEntry(event.getCollection());
+    }
+
+    public void onPreUpdateCollection(PreCollectionUpdateEvent event) {
+        CollectionEntry collectionEntry = getCollectionEntry(event);
+        onCollectionAction(event, event.getCollection(), collectionEntry.getSnapshot(), collectionEntry);
+    }
+
+    public void onPreRemoveCollection(PreCollectionRemoveEvent event) {
+        CollectionEntry collectionEntry = getCollectionEntry(event);
+        onCollectionAction(event, null, collectionEntry.getSnapshot(), collectionEntry);
+    }
+
+    public void onPostRecreateCollection(PostCollectionRecreateEvent event) {
+        onCollectionAction(event, event.getCollection(), null, getCollectionEntry(event));
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public void initialize(Configuration cfg) {
+        verCfg = VersionsConfiguration.getFor(cfg);
+    }
+
+    public VersionsConfiguration getVerCfg() {
+        return verCfg;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/exception/NotVersionedException.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/exception/NotVersionedException.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/exception/NotVersionedException.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,38 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.exception;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NotVersionedException extends VersionsException {
+    private final String entityName;
+
+    public NotVersionedException(String entityName, String message) {
+        super(message);
+        this.entityName = entityName;        
+    }
+
+    public String getEntityName() {
+        return entityName;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/exception/RevisionDoesNotExistException.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/exception/RevisionDoesNotExistException.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/exception/RevisionDoesNotExistException.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,50 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.exception;
+
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionDoesNotExistException extends VersionsException {
+    private Number revision;
+    private Date date;
+
+    public RevisionDoesNotExistException(Number revision) {
+        super("Revision " + revision + " does not exist.");
+        this.revision = revision;
+    }
+
+    public RevisionDoesNotExistException(Date date) {
+        super("There is no revision before or at " + date + ".");
+        this.date = date;
+    }
+
+    public Number getRevision() {
+        return revision;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/exception/VersionsException.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/exception/VersionsException.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/exception/VersionsException.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,41 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.exception;
+
+import org.hibernate.HibernateException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsException extends HibernateException {
+    public VersionsException(String message) {
+        super(message);
+    }
+
+    public VersionsException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public VersionsException(Throwable cause) {
+        super(cause);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/RevisionProperty.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/RevisionProperty.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/RevisionProperty.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,127 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query;
+
+import org.jboss.envers.query.criteria.VersionsCriterion;
+import org.jboss.envers.query.criteria.RevisionVersionsExpression;
+import org.jboss.envers.query.order.VersionsOrder;
+import org.jboss.envers.query.order.RevisionVersionsOrder;
+import org.jboss.envers.query.projection.VersionsProjection;
+import org.jboss.envers.query.projection.RevisionVersionsProjection;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.Triple;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"JavaDoc"})
+public class RevisionProperty implements VersionsProjection {
+    private RevisionProperty() { }
+
+    /**
+     * Apply a "greater than" constraint on the revision number
+     */
+    public static VersionsCriterion gt(Integer revision) {
+        return new RevisionVersionsExpression(revision, ">");
+    }
+
+    /**
+     * Apply a "greater than or equal" constraint on the revision number
+     */
+    public static VersionsCriterion ge(Integer revision) {
+        return new RevisionVersionsExpression(revision, ">=");
+    }
+
+    /**
+     * Apply a "less than" constraint on the revision number
+     */
+    public static VersionsCriterion lt(Integer revision) {
+        return new RevisionVersionsExpression(revision, "<");
+    }
+
+    /**
+     * Apply a "less than or equal" constraint on the revision number
+     */
+    public static VersionsCriterion le(Integer revision) {
+        return new RevisionVersionsExpression(revision, "<=");
+    }
+
+    /**
+     * Sort the results by revision in ascending order
+     */
+    public static VersionsOrder asc() {
+        return new RevisionVersionsOrder(true);
+    }
+
+    /**
+     * Sort the results by revision in descending order
+     */
+    public static VersionsOrder desc() {
+        return new RevisionVersionsOrder(false);
+    }
+
+    /**
+     * Select the maximum revision
+     */
+    public static VersionsProjection max() {
+        return new RevisionVersionsProjection(RevisionVersionsProjection.ProjectionType.MAX);
+    }
+
+    /**
+     * Select the minimum revision
+     */
+    public static VersionsProjection min() {
+        return new RevisionVersionsProjection(RevisionVersionsProjection.ProjectionType.MIN);
+    }
+
+    /**
+     * Count revisions
+     */
+    public static VersionsProjection count() {
+        return new RevisionVersionsProjection(RevisionVersionsProjection.ProjectionType.COUNT);
+    }
+
+    /**
+     * Count distinct revisions
+     */
+    public static VersionsProjection countDistinct() {
+        return new RevisionVersionsProjection(RevisionVersionsProjection.ProjectionType.COUNT_DISTINCT);
+    }
+
+    /**
+     * Distinct revisions
+     */
+    public static VersionsProjection distinct() {
+        return new RevisionVersionsProjection(RevisionVersionsProjection.ProjectionType.DISTINCT);
+    }
+
+    /**
+     * Projection the revision number
+     */
+    public static VersionsProjection revisionNumber() {
+        return new RevisionProperty();
+    }
+
+    public Triple<String, String, Boolean> getData(VersionsConfiguration verCfg) {
+        return Triple.make(null, verCfg.getVerEntCfg().getRevisionPropPath(), false);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/RevisionTypeProperty.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/RevisionTypeProperty.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/RevisionTypeProperty.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,45 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query;
+
+import org.jboss.envers.query.projection.VersionsProjection;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.Triple;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"JavaDoc"})
+public class RevisionTypeProperty implements VersionsProjection {
+    private RevisionTypeProperty() { }
+
+    /**
+     * Projection on the revision type
+     */
+    public static VersionsProjection revisionType() {
+        return new RevisionTypeProperty();
+    }
+
+    public Triple<String, String, Boolean> getData(VersionsConfiguration verCfg) {
+        return Triple.make(null, verCfg.getVerEntCfg().getRevisionTypePropName(), false);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsQuery.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsQuery.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,72 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query;
+
+import org.jboss.envers.query.criteria.VersionsCriterion;
+import org.jboss.envers.query.projection.VersionsProjection;
+import org.jboss.envers.query.order.VersionsOrder;
+import org.jboss.envers.exception.VersionsException;
+import org.hibernate.FlushMode;
+import org.hibernate.CacheMode;
+import org.hibernate.LockMode;
+
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.NoResultException;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ * @see org.hibernate.Criteria
+ */
+public interface VersionsQuery {
+    List getResultList() throws VersionsException;
+
+    Object getSingleResult() throws VersionsException, NonUniqueResultException, NoResultException;
+
+    VersionsQuery add(VersionsCriterion criterion);
+
+    VersionsQuery addProjection(String function, String propertyName);
+
+    VersionsQuery addProjection(VersionsProjection projection);
+
+    VersionsQuery addOrder(String propertyName, boolean asc);
+
+    VersionsQuery addOrder(VersionsOrder order);
+
+    VersionsQuery setMaxResults(int maxResults);
+
+	VersionsQuery setFirstResult(int firstResult);
+
+    VersionsQuery setCacheable(boolean cacheable);
+
+    VersionsQuery setCacheRegion(String cacheRegion);
+
+    VersionsQuery setComment(String comment);
+
+    VersionsQuery setFlushMode(FlushMode flushMode);
+
+    VersionsQuery setCacheMode(CacheMode cacheMode);
+
+    VersionsQuery setTimeout(int timeout);
+
+    VersionsQuery setLockMode(LockMode lockMode);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsQueryCreator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsQueryCreator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsQueryCreator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,80 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query;
+
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.query.impl.EntitiesAtRevisionQuery;
+import org.jboss.envers.query.impl.RevisionsOfEntityQuery;
+import static org.jboss.envers.tools.ArgumentsTools.*;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsQueryCreator {
+    private final VersionsConfiguration verCfg;
+    private final VersionsReaderImplementor versionsReaderImplementor;
+
+    public VersionsQueryCreator(VersionsConfiguration verCfg, VersionsReaderImplementor versionsReaderImplementor) {
+        this.verCfg = verCfg;
+        this.versionsReaderImplementor = versionsReaderImplementor;
+    }
+
+    /**
+     * Creates a query, which will return entities satisfying some conditions (specified later),
+     * at a given revision.
+     * @param c Class of the entities for which to query.
+     * @param revision Revision number at which to execute the query.
+     * @return A query for entities at a given revision, to which conditions can be added and which
+     * can then be executed. The result of the query will be a list of entities (beans), unless a
+     * projection is added.
+     */
+    public VersionsQuery forEntitiesAtRevision(Class<?> c, Number revision) {
+        checkNotNull(revision, "Entity revision");
+        checkPositive(revision, "Entity revision");
+        return new EntitiesAtRevisionQuery(verCfg, versionsReaderImplementor, c, revision);
+    }
+
+    /**
+     * Creates a query, which selects the revisions, at which the given entity was modified.
+     * Unless an explicit projection is set, the result will be a list of three-element arrays, containing:
+     * <ol>
+     * <li>the entity instance</li>
+     * <li>revision entity, corresponding to the revision at which the entity was modified. If no custom
+     * revision entity is used, this will be an instance of {@link org.jboss.envers.DefaultRevisionEntity}</li>
+     * <li>type of the revision (an enum instance of class {@link org.jboss.envers.RevisionType})</li>.
+     * </ol>
+     * Additional conditions that the results must satisfy may be specified.
+     * @param c Class of the entities for which to query.
+     * @param selectEntitiesOnly If true, instead of a list of three-element arrays, a list of entites will be
+     * returned as a result of executing this query.
+     * @param selectDeletedEntities If true, also revisions where entities were deleted will be returned. The additional
+     * entities will have revision type "delete", and contain no data (all fields null), except for the id field.
+     * @return A query for revisions at which instances of the given entity were modified, to which
+     * conditions can be added (for example - a specific id of an entity of class <code>c</code>), and which
+     * can then be executed. The results of the query will be sorted in ascending order by the revision number,
+     * unless an order or projection is added.
+     */
+    public VersionsQuery forRevisionsOfEntity(Class<?> c, boolean selectEntitiesOnly, boolean selectDeletedEntities) {
+        return new RevisionsOfEntityQuery(verCfg, versionsReaderImplementor, c, selectEntitiesOnly,selectDeletedEntities);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsRestrictions.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsRestrictions.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/VersionsRestrictions.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,242 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query;
+
+import org.jboss.envers.query.criteria.*;
+import org.hibernate.criterion.MatchMode;
+
+import java.util.Collection;
+
+/**
+ * TODO: ilike
+ * @author Adam Warski (adam at warski dot org)
+ * @see org.hibernate.criterion.Restrictions
+ */
+ at SuppressWarnings({"JavaDoc"})
+public class VersionsRestrictions {
+    private VersionsRestrictions() { }
+
+	/**
+	 * Apply an "equal" constraint to the identifier property.
+	 */
+	public static VersionsCriterion idEq(Object value) {
+		return new IdentifierEqVersionsExpression(value);
+	}
+    
+    /**
+	 * Apply an "equal" constraint to the named property
+	 */
+	public static VersionsCriterion eq(String propertyName, Object value) {
+		return new SimpleVersionsExpression(propertyName, value, "=");
+	}
+
+    /**
+	 * Apply a "not equal" constraint to the named property
+	 */
+	public static VersionsCriterion ne(String propertyName, Object value) {
+		return new SimpleVersionsExpression(propertyName, value, "<>");
+	}
+
+    /**
+	 * Apply an "equal" constraint on an id of a related entity
+	 */
+	public static VersionsCriterion relatedIdEq(String propertyName, Object id) {
+		return new RelatedVersionsExpression(propertyName, id, true);
+	}
+
+    /**
+	 * Apply a "not equal" constraint to the named property
+	 */
+	public static VersionsCriterion relatedIdNe(String propertyName, Object id) {
+		return new RelatedVersionsExpression(propertyName, id, false);
+	}
+
+    /**
+	 * Apply a "like" constraint to the named property
+	 */
+	public static VersionsCriterion like(String propertyName, Object value) {
+		return new SimpleVersionsExpression(propertyName, value, " like ");
+	}
+
+    /**
+	 * Apply a "like" constraint to the named property
+	 */
+	public static VersionsCriterion like(String propertyName, String value, MatchMode matchMode) {
+		return new SimpleVersionsExpression(propertyName, matchMode.toMatchString(value), " like " );
+	}
+
+    /**
+	 * Apply a "greater than" constraint to the named property
+	 */
+	public static VersionsCriterion gt(String propertyName, Object value) {
+		return new SimpleVersionsExpression(propertyName, value, ">");
+	}
+
+    /**
+	 * Apply a "less than" constraint to the named property
+	 */
+	public static VersionsCriterion lt(String propertyName, Object value) {
+		return new SimpleVersionsExpression(propertyName, value, "<");
+	}
+
+    /**
+	 * Apply a "less than or equal" constraint to the named property
+	 */
+	public static VersionsCriterion le(String propertyName, Object value) {
+		return new SimpleVersionsExpression(propertyName, value, "<=");
+	}
+
+    /**
+	 * Apply a "greater than or equal" constraint to the named property
+	 */
+	public static VersionsCriterion ge(String propertyName, Object value) {
+		return new SimpleVersionsExpression(propertyName, value, ">=");
+	}
+
+    /**
+	 * Apply a "between" constraint to the named property
+	 */
+	public static VersionsCriterion between(String propertyName, Object lo, Object hi) {
+		return new BetweenVersionsExpression(propertyName, lo, hi);
+	}
+
+    /**
+	 * Apply an "in" constraint to the named property
+	 */
+	public static VersionsCriterion in(String propertyName, Object[] values) {
+		return new InVersionsExpression(propertyName, values);
+	}
+
+    /**
+	 * Apply an "in" constraint to the named property
+	 */
+	public static VersionsCriterion in(String propertyName, Collection values) {
+		return new InVersionsExpression(propertyName, values.toArray());
+	}
+
+    /**
+	 * Apply an "is null" constraint to the named property
+	 */
+	public static VersionsCriterion isNull(String propertyName) {
+		return new NullVersionsExpression(propertyName);
+	}
+
+    /**
+	 * Apply an "equal" constraint to two properties
+	 */
+	public static VersionsCriterion eqProperty(String propertyName, String otherPropertyName) {
+		return new PropertyVersionsExpression(propertyName, otherPropertyName, "=");
+	}
+
+    /**
+	 * Apply a "not equal" constraint to two properties
+	 */
+	public static VersionsCriterion neProperty(String propertyName, String otherPropertyName) {
+		return new PropertyVersionsExpression(propertyName, otherPropertyName, "<>");
+	}
+    
+    /**
+	 * Apply a "less than" constraint to two properties
+	 */
+	public static VersionsCriterion ltProperty(String propertyName, String otherPropertyName) {
+		return new PropertyVersionsExpression(propertyName, otherPropertyName, "<");
+	}
+
+    /**
+	 * Apply a "less than or equal" constraint to two properties
+	 */
+	public static VersionsCriterion leProperty(String propertyName, String otherPropertyName) {
+		return new PropertyVersionsExpression(propertyName, otherPropertyName, "<=");
+	}
+
+    /**
+	 * Apply a "greater than" constraint to two properties
+	 */
+	public static VersionsCriterion gtProperty(String propertyName, String otherPropertyName) {
+		return new PropertyVersionsExpression(propertyName, otherPropertyName, ">");
+	}
+
+    /**
+	 * Apply a "greater than or equal" constraint to two properties
+	 */
+	public static VersionsCriterion geProperty(String propertyName, String otherPropertyName) {
+		return new PropertyVersionsExpression(propertyName, otherPropertyName, ">=");
+	}
+
+    /**
+	 * Apply an "is not null" constraint to the named property
+	 */
+	public static VersionsCriterion isNotNull(String propertyName) {
+		return new NotNullVersionsExpression(propertyName);
+	}
+
+    /**
+	 * Return the conjuction of two expressions
+	 */
+	public static VersionsCriterion and(VersionsCriterion lhs, VersionsCriterion rhs) {
+		return new LogicalVersionsExpression(lhs, rhs, "and");
+	}
+
+    /**
+	 * Return the disjuction of two expressions
+	 */
+	public static VersionsCriterion or(VersionsCriterion lhs, VersionsCriterion rhs) {
+		return new LogicalVersionsExpression(lhs, rhs, "or");
+	}
+
+    /**
+	 * Return the negation of an expression
+	 */
+	public static VersionsCriterion not(VersionsCriterion expression) {
+		return new NotVersionsExpression(expression);
+	}
+
+	/**
+	 * Group expressions together in a single conjunction (A and B and C...)
+	 */
+	public static VersionsConjunction conjunction() {
+		return new VersionsConjunction();
+	}
+
+	/**
+	 * Group expressions together in a single disjunction (A or B or C...)
+	 */
+	public static VersionsDisjunction disjunction() {
+		return new VersionsDisjunction();
+	}
+
+    /**
+     * Apply a "maximalize property" constraint.
+     */
+    public static AggregatedFieldVersionsExpression maximizeProperty(String propertyName) {
+        return new AggregatedFieldVersionsExpression(propertyName,
+                AggregatedFieldVersionsExpression.AggregatedMode.MAX);
+    }
+
+    /**
+     * Apply a "minimize property" constraint.
+     */
+    public static AggregatedFieldVersionsExpression minimizeProperty(String propertyName) {
+        return new AggregatedFieldVersionsExpression(propertyName,
+                AggregatedFieldVersionsExpression.AggregatedMode.MIN);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/AggregatedFieldVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/AggregatedFieldVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/AggregatedFieldVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,80 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class AggregatedFieldVersionsExpression implements VersionsCriterion, ExtendableCriterion {
+    public static enum AggregatedMode {
+        MAX,
+        MIN
+    }
+
+    private String propertyName;
+    private AggregatedMode mode;
+    private List<VersionsCriterion> criterions;
+
+    public AggregatedFieldVersionsExpression(String propertyName, AggregatedMode mode) {
+        this.propertyName = propertyName;
+        this.mode = mode;
+        criterions = new ArrayList<VersionsCriterion>();
+    }
+
+    public AggregatedFieldVersionsExpression add(VersionsCriterion criterion) {
+        criterions.add(criterion);
+        return this;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        CriteriaTools.checkPropertyNotARelation(verCfg, entityName, propertyName);
+
+        // This will be the aggregated query, containing all the specified conditions
+        QueryBuilder subQb = qb.newSubQueryBuilder();
+
+        // Adding all specified conditions both to the main query, as well as to the
+        // aggregated one.
+        for (VersionsCriterion versionsCriteria : criterions) {
+            versionsCriteria.addToQuery(verCfg, entityName, qb, parameters);
+            versionsCriteria.addToQuery(verCfg, entityName, subQb, subQb.getRootParameters());
+        }
+
+        // Setting the desired projection of the aggregated query
+        switch (mode) {
+            case MIN:
+                subQb.addProjection("min", propertyName, false);
+                break;
+            case MAX:
+                subQb.addProjection("max", propertyName, false);
+        }
+
+        // Adding the constrain on the result of the aggregated criteria
+        parameters.addWhere(propertyName, "=", subQb);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/BetweenVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/BetweenVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/BetweenVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,47 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BetweenVersionsExpression implements VersionsCriterion {
+    private String propertyName;
+    private Object lo;
+    private Object hi;
+
+    public BetweenVersionsExpression(String propertyName, Object lo, Object hi) {
+        this.propertyName = propertyName;
+        this.lo = lo;
+        this.hi = hi;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        CriteriaTools.checkPropertyNotARelation(verCfg, entityName, propertyName);
+        parameters.addWhereWithParam(propertyName, ">=", lo);
+        parameters.addWhereWithParam(propertyName, "<=", hi);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/CriteriaTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/CriteriaTools.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/CriteriaTools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,58 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.entities.RelationDescription;
+import org.jboss.envers.entities.RelationType;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class CriteriaTools {
+    private CriteriaTools() { }
+
+    public static void checkPropertyNotARelation(VersionsConfiguration verCfg, String entityName,
+                                                 String propertyName) throws VersionsException {
+        if (verCfg.getEntCfg().get(entityName).isRelation(propertyName)) {
+            throw new VersionsException("This criterion cannot be used on a property that is " +
+                    "a relation to another property.");
+        }
+    }
+
+    public static RelationDescription getRelatedEntity(VersionsConfiguration verCfg, String entityName,
+                                                       String propertyName) throws VersionsException {
+        RelationDescription relationDesc = verCfg.getEntCfg().getRelationDescription(entityName, propertyName);
+
+        if (relationDesc == null) {
+            return null;
+        }
+
+        if (relationDesc.getRelationType() == RelationType.TO_ONE) {
+            return relationDesc;
+        }
+
+        throw new VersionsException("This type of relation (" + entityName + "." + propertyName +
+                ") isn't supported and can't be used in queries.");
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/ExtendableCriterion.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/ExtendableCriterion.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/ExtendableCriterion.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,29 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface ExtendableCriterion {
+    public ExtendableCriterion add(VersionsCriterion criterion);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/IdentifierEqVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/IdentifierEqVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/IdentifierEqVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,42 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class IdentifierEqVersionsExpression implements VersionsCriterion {
+    private Object id;
+
+    public IdentifierEqVersionsExpression(Object id) {
+        this.id = id;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        verCfg.getEntCfg().get(entityName).getIdMapper()
+                .addIdEqualsToQuery(parameters, id, verCfg.getVerEntCfg().getOriginalIdPropName(), true);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/InVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/InVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/InVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,44 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class InVersionsExpression implements VersionsCriterion {
+    private String propertyName;
+    private Object[] values;
+
+    public InVersionsExpression(String propertyName, Object[] values) {
+        this.propertyName = propertyName;
+        this.values = values;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        CriteriaTools.checkPropertyNotARelation(verCfg, entityName, propertyName);
+        parameters.addWhereWithParams(propertyName, "in (", values, ")");
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/LogicalVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/LogicalVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/LogicalVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,48 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class LogicalVersionsExpression implements VersionsCriterion {
+    private VersionsCriterion lhs;
+    private VersionsCriterion rhs;
+    private String op;
+
+    public LogicalVersionsExpression(VersionsCriterion lhs, VersionsCriterion rhs, String op) {
+        this.lhs = lhs;
+        this.rhs = rhs;
+        this.op = op;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        Parameters opParameters = parameters.addSubParameters(op);
+
+        lhs.addToQuery(verCfg, entityName, qb, opParameters.addSubParameters("and"));
+        rhs.addToQuery(verCfg, entityName, qb, opParameters.addSubParameters("and"));
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NotNullVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NotNullVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NotNullVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,48 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.entities.RelationDescription;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NotNullVersionsExpression implements VersionsCriterion {
+    private String propertyName;
+
+    public NotNullVersionsExpression(String propertyName) {
+        this.propertyName = propertyName;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        RelationDescription relatedEntity = CriteriaTools.getRelatedEntity(verCfg, entityName, propertyName);
+
+        if (relatedEntity == null) {
+            parameters.addWhereWithParam(propertyName, "<>", null);
+        } else {
+            relatedEntity.getIdMapper().addIdEqualsToQuery(parameters, null, propertyName, false);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NotVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NotVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NotVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,41 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NotVersionsExpression implements VersionsCriterion {
+    private VersionsCriterion criterion;
+
+    public NotVersionsExpression(VersionsCriterion criterion) {
+        this.criterion = criterion;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        criterion.addToQuery(verCfg, entityName, qb, parameters.addNegatedParameters());
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NullVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NullVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/NullVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,48 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.entities.RelationDescription;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NullVersionsExpression implements VersionsCriterion {
+    private String propertyName;
+
+    public NullVersionsExpression(String propertyName) {
+        this.propertyName = propertyName;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        RelationDescription relatedEntity = CriteriaTools.getRelatedEntity(verCfg, entityName, propertyName);
+
+        if (relatedEntity == null) {
+            parameters.addWhereWithParam(propertyName, "=", null);
+        } else {
+            relatedEntity.getIdMapper().addIdEqualsToQuery(parameters, null, propertyName, true);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/PropertyVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/PropertyVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/PropertyVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,47 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PropertyVersionsExpression implements VersionsCriterion {
+    private String propertyName;
+    private String otherPropertyName;
+    private String op;
+
+    public PropertyVersionsExpression(String propertyName, String otherPropertyName, String op) {
+        this.propertyName = propertyName;
+        this.otherPropertyName = otherPropertyName;
+        this.op = op;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        CriteriaTools.checkPropertyNotARelation(verCfg, entityName, propertyName);
+        CriteriaTools.checkPropertyNotARelation(verCfg, entityName, otherPropertyName);
+        parameters.addWhere(propertyName, op, otherPropertyName);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/RelatedVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/RelatedVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/RelatedVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,54 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.entities.RelationDescription;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RelatedVersionsExpression implements VersionsCriterion {
+    private String propertyName;
+    private Object id;
+    private boolean equals;
+
+    public RelatedVersionsExpression(String propertyName, Object id, boolean equals) {
+        this.propertyName = propertyName;
+        this.id = id;
+        this.equals = equals;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        RelationDescription relatedEntity = CriteriaTools.getRelatedEntity(verCfg, entityName, propertyName);
+
+        if (relatedEntity == null) {
+            throw new VersionsException("This criterion can only be used on a property that is " +
+                    "a relation to another property.");
+        } else {
+            relatedEntity.getIdMapper().addIdEqualsToQuery(parameters, id, propertyName, equals);
+        }
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/RevisionVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/RevisionVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/RevisionVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,43 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionVersionsExpression implements VersionsCriterion {
+    private Object value;
+    private String op;
+
+    public RevisionVersionsExpression(Object value, String op) {
+        this.value = value;
+        this.op = op;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        parameters.addWhereWithParam(verCfg.getVerEntCfg().getRevisionPropPath(), op, value);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/SimpleVersionsExpression.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/SimpleVersionsExpression.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/SimpleVersionsExpression.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,60 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.entities.RelationDescription;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SimpleVersionsExpression implements VersionsCriterion {
+    private String propertyName;
+    private Object value;
+    private String op;
+
+    public SimpleVersionsExpression(String propertyName, Object value, String op) {
+        this.propertyName = propertyName;
+        this.value = value;
+        this.op = op;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        RelationDescription relatedEntity = CriteriaTools.getRelatedEntity(verCfg, entityName, propertyName);
+
+        if (relatedEntity == null) {
+            parameters.addWhereWithParam(propertyName, op, value);
+        } else {
+            if (!"=".equals(op) && !"<>".equals(op)) {
+                throw new VersionsException("This type of operation: " + op + " (" + entityName + "." + propertyName +
+                        ") isn't supported and can't be used in queries.");
+            }
+
+            Object id = relatedEntity.getIdMapper().mapToIdFromEntity(value);
+
+            relatedEntity.getIdMapper().addIdEqualsToQuery(parameters, id, propertyName, "=".equals(op));
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsConjunction.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsConjunction.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsConjunction.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,53 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsConjunction implements VersionsCriterion, ExtendableCriterion {
+    private List<VersionsCriterion> criterions;
+
+    public VersionsConjunction() {
+        criterions = new ArrayList<VersionsCriterion>();
+    }
+
+    public VersionsConjunction add(VersionsCriterion criterion) {
+        criterions.add(criterion);
+        return this;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        Parameters andParameters = parameters.addSubParameters(Parameters.AND);
+
+        for (VersionsCriterion criterion : criterions) {
+            criterion.addToQuery(verCfg, entityName, qb, andParameters);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsCriterion.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsCriterion.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsCriterion.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,33 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface VersionsCriterion {
+    void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsDisjunction.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsDisjunction.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/criteria/VersionsDisjunction.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,53 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.criteria;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.tools.query.Parameters;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsDisjunction implements VersionsCriterion, ExtendableCriterion {
+    private List<VersionsCriterion> criterions;
+
+    public VersionsDisjunction() {
+        criterions = new ArrayList<VersionsCriterion>();
+    }
+
+    public VersionsDisjunction add(VersionsCriterion criterion) {
+        criterions.add(criterion);
+        return this;
+    }
+
+    public void addToQuery(VersionsConfiguration verCfg, String entityName, QueryBuilder qb, Parameters parameters) {
+        Parameters orParameters = parameters.addSubParameters(Parameters.OR);
+
+        for (VersionsCriterion criterion : criterions) {
+            criterion.addToQuery(verCfg, entityName, qb, orParameters);
+        }
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/impl/AbstractVersionsQuery.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/impl/AbstractVersionsQuery.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/impl/AbstractVersionsQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,210 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.impl;
+
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.entities.EntityInstantiator;
+import org.jboss.envers.query.criteria.VersionsCriterion;
+import org.jboss.envers.query.VersionsQuery;
+import org.jboss.envers.query.order.VersionsOrder;
+import org.jboss.envers.query.projection.VersionsProjection;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.Pair;
+import org.jboss.envers.tools.Triple;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.hibernate.*;
+
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.NoResultException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractVersionsQuery implements VersionsQuery {
+    protected EntityInstantiator entityInstantiator;
+    protected List<VersionsCriterion> criterions;
+
+    protected String entityName;
+    protected String versionsEntityName;
+    protected QueryBuilder qb;
+
+    protected boolean hasProjection;
+    protected boolean hasOrder;
+
+    protected final VersionsConfiguration verCfg;
+    private final VersionsReaderImplementor versionsReader;
+
+    protected AbstractVersionsQuery(VersionsConfiguration verCfg, VersionsReaderImplementor versionsReader,
+                                    Class<?> cls) {
+        this.verCfg = verCfg;
+        this.versionsReader = versionsReader;
+
+        criterions = new ArrayList<VersionsCriterion>();
+        entityInstantiator = new EntityInstantiator(verCfg, versionsReader);
+
+        entityName = cls.getName();
+        versionsEntityName = verCfg.getVerEntCfg().getVersionsEntityName(entityName);
+
+        qb = new QueryBuilder(versionsEntityName, "e");
+    }
+
+    protected List buildAndExecuteQuery() {
+        StringBuilder querySb = new StringBuilder();
+        Map<String, Object> queryParamValues = new HashMap<String, Object>();
+
+        qb.build(querySb, queryParamValues);
+
+        Query query = versionsReader.getSession().createQuery(querySb.toString());
+        for (Map.Entry<String, Object> paramValue : queryParamValues.entrySet()) {
+            query.setParameter(paramValue.getKey(), paramValue.getValue());
+        }
+
+        setQueryProperties(query);
+
+        return query.list();
+    }
+
+    public abstract List list() throws VersionsException;
+
+    public List getResultList() throws VersionsException {
+        return list();
+    }
+
+    public Object getSingleResult() throws VersionsException, NonUniqueResultException, NoResultException {
+        List result = list();
+
+        if (result == null || result.size() == 0) {
+            throw new NoResultException();
+        }
+
+        if (result.size() > 1) {
+            throw new NonUniqueResultException();
+        }
+
+        return result.get(0);
+    }
+
+    public VersionsQuery add(VersionsCriterion criterion) {
+        criterions.add(criterion);
+        return this;
+    }
+
+    // Projection and order
+
+    public VersionsQuery addProjection(String function, String propertyName) {
+        hasProjection = true;
+        qb.addProjection(function, propertyName, false);
+        return this;
+    }
+
+    public VersionsQuery addProjection(VersionsProjection projection) {
+        Triple<String, String, Boolean> projectionData = projection.getData(verCfg);
+        hasProjection = true;
+        qb.addProjection(projectionData.getFirst(), projectionData.getSecond(), projectionData.getThird());
+        return this;
+    }
+
+    public VersionsQuery addOrder(String propertyName, boolean asc) {
+        hasOrder = true;
+        qb.addOrder(propertyName, asc);
+        return this;
+    }
+
+    public VersionsQuery addOrder(VersionsOrder order) {
+        Pair<String, Boolean> orderData = order.getData(verCfg);
+        return addOrder(orderData.getFirst(), orderData.getSecond());
+    }
+
+    // Query properties
+
+    private Integer maxResults;
+    private Integer firstResult;
+    private Boolean cacheable;
+    private String cacheRegion;
+    private String comment;
+    private FlushMode flushMode;
+    private CacheMode cacheMode;
+    private Integer timeout;
+    private LockMode lockMode;
+
+    public VersionsQuery setMaxResults(int maxResults) {
+        this.maxResults = maxResults;
+        return this;
+    }
+
+    public VersionsQuery setFirstResult(int firstResult) {
+        this.firstResult = firstResult;
+        return this;
+    }
+
+    public VersionsQuery setCacheable(boolean cacheable) {
+        this.cacheable = cacheable;
+        return this;
+    }
+
+    public VersionsQuery setCacheRegion(String cacheRegion) {
+        this.cacheRegion = cacheRegion;
+        return this;
+    }
+
+    public VersionsQuery setComment(String comment) {
+        this.comment = comment;
+        return this;
+    }
+
+    public VersionsQuery setFlushMode(FlushMode flushMode) {
+        this.flushMode = flushMode;
+        return this;
+    }
+
+    public VersionsQuery setCacheMode(CacheMode cacheMode) {
+        this.cacheMode = cacheMode;
+        return this;
+    }
+
+    public VersionsQuery setTimeout(int timeout) {
+        this.timeout = timeout;
+        return this;
+    }
+
+    public VersionsQuery setLockMode(LockMode lockMode) {
+        this.lockMode = lockMode;
+        return this;
+    }
+
+    protected void setQueryProperties(Query query) {
+        if (maxResults != null) query.setMaxResults(maxResults);
+        if (firstResult != null) query.setFirstResult(firstResult);
+        if (cacheable != null) query.setCacheable(cacheable);
+        if (cacheRegion != null) query.setCacheRegion(cacheRegion);
+        if (comment != null) query.setComment(comment);
+        if (flushMode != null) query.setFlushMode(flushMode);
+        if (cacheMode != null) query.setCacheMode(cacheMode);
+        if (timeout != null) query.setTimeout(timeout);
+        if (lockMode != null) query.setLockMode("e", lockMode);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/impl/EntitiesAtRevisionQuery.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/impl/EntitiesAtRevisionQuery.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/impl/EntitiesAtRevisionQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,93 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.impl;
+
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.query.criteria.VersionsCriterion;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.tools.query.QueryBuilder;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.configuration.VersionsConfiguration;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EntitiesAtRevisionQuery extends AbstractVersionsQuery {
+    private final Number revision;
+
+    public EntitiesAtRevisionQuery(VersionsConfiguration verCfg,
+                                   VersionsReaderImplementor versionsReader, Class<?> cls,
+                                   Number revision) {
+        super(verCfg, versionsReader, cls);
+        this.revision = revision;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public List list() {
+        /*
+        The query that should be executed in the versions table:
+        SELECT e FROM ent_ver e WHERE
+          (all specified conditions, transformed, on the "e" entity) AND
+          e.revision_type != DEL AND
+          e.revision = (SELECT max(e2.revision) FROM ent_ver e2 WHERE
+            e2.revision <= :revision AND e2.originalId.id = e.originalId.id)
+         */
+
+        QueryBuilder maxRevQb = qb.newSubQueryBuilder(versionsEntityName, "e2");
+
+        VersionsEntitiesConfiguration verEntCfg = verCfg.getVerEntCfg();
+
+        String revisionPropertyPath = verEntCfg.getRevisionPropPath();
+        String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
+
+        // SELECT max(e2.revision)
+        maxRevQb.addProjection("max", revisionPropertyPath, false);
+        // e2.revision <= :revision
+        maxRevQb.getRootParameters().addWhereWithParam(revisionPropertyPath, "<=", revision);
+        // e2.id = e.id
+        verCfg.getEntCfg().get(entityName).getIdMapper().addIdsEqualToQuery(maxRevQb.getRootParameters(),
+                "e." + originalIdPropertyName, "e2." + originalIdPropertyName);
+
+        // e.revision_type != DEL AND
+        qb.getRootParameters().addWhereWithParam(verEntCfg.getRevisionTypePropName(), "<>", RevisionType.DEL);
+        // e.revision = (SELECT max(...) ...)
+        qb.getRootParameters().addWhere(revisionPropertyPath, verCfg.getGlobalCfg().getCorrelatedSubqueryOperator(), maxRevQb);
+        // all specified conditions
+        for (VersionsCriterion criterion : criterions) {
+            criterion.addToQuery(verCfg, entityName, qb, qb.getRootParameters());
+        }
+
+        List queryResult = buildAndExecuteQuery();
+
+        if (hasProjection) {
+            return queryResult;
+        } else {
+            List result = new ArrayList();
+            entityInstantiator.addInstancesFromVersionsEntities(entityName, result, queryResult, revision);
+
+            return result;
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/impl/RevisionsOfEntityQuery.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/impl/RevisionsOfEntityQuery.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/impl/RevisionsOfEntityQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,135 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.impl;
+
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.reader.VersionsReaderImplementor;
+import org.jboss.envers.query.criteria.VersionsCriterion;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.hibernate.proxy.HibernateProxy;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionsOfEntityQuery extends AbstractVersionsQuery {
+    private final boolean selectEntitiesOnly;
+    private final boolean selectDeletedEntities;
+
+    public RevisionsOfEntityQuery(VersionsConfiguration verCfg,
+                                  VersionsReaderImplementor versionsReader,
+                                  Class<?> cls, boolean selectEntitiesOnly,
+                                  boolean selectDeletedEntities) {
+        super(verCfg, versionsReader, cls);
+
+        this.selectEntitiesOnly = selectEntitiesOnly;
+        this.selectDeletedEntities = selectDeletedEntities;
+    }
+
+    private Number getRevisionNumber(Map versionsEntity) {
+        VersionsEntitiesConfiguration verEntCfg = verCfg.getVerEntCfg();
+
+        String originalId = verEntCfg.getOriginalIdPropName();
+        String revisionPropertyName = verEntCfg.getRevisionPropName();
+
+        Object revisionInfoObject = ((Map) versionsEntity.get(originalId)).get(revisionPropertyName);
+
+        if (revisionInfoObject instanceof HibernateProxy) {
+            return (Number) ((HibernateProxy) revisionInfoObject).getHibernateLazyInitializer().getIdentifier();
+        } else {
+            // Not a proxy - must be read from cache or with a join
+            return verCfg.getRevisionInfoNumberReader().getRevisionNumber(revisionInfoObject);   
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public List list() throws VersionsException {
+        VersionsEntitiesConfiguration verEntCfg = verCfg.getVerEntCfg();
+
+        /*
+        The query that should be executed in the versions table:
+        SELECT e (unless another projection is specified) FROM ent_ver e, rev_entity r WHERE
+          e.revision_type != DEL (if selectDeletedEntities == false) AND
+          e.revision = r.revision AND
+          (all specified conditions, transformed, on the "e" entity)
+          ORDER BY e.revision ASC (unless another order or projection is specified)
+         */      
+        if (!selectDeletedEntities) {
+            // e.revision_type != DEL AND
+            qb.getRootParameters().addWhereWithParam(verEntCfg.getRevisionTypePropName(), "<>", RevisionType.DEL);
+        }
+
+        // all specified conditions, transformed
+        for (VersionsCriterion criterion : criterions) {
+            criterion.addToQuery(verCfg, entityName, qb, qb.getRootParameters());
+        }
+
+        if (!hasProjection && !hasOrder) {
+            String revisionPropertyPath = verEntCfg.getRevisionPropPath();
+            qb.addOrder(revisionPropertyPath, true);
+        }
+
+        if (!selectEntitiesOnly) {
+            qb.addFrom(verCfg.getVerEntCfg().getRevisionInfoEntityName(), "r");
+            qb.getRootParameters().addWhere(verCfg.getVerEntCfg().getRevisionPropPath(), true, "=", "r.id", false);
+        }
+
+        List<Object> queryResult = buildAndExecuteQuery();
+        if (hasProjection) {
+            return queryResult;
+        } else {
+            List entities = new ArrayList();
+            String revisionTypePropertyName = verEntCfg.getRevisionTypePropName();
+
+            for (Object resultRow : queryResult) {
+                Map versionsEntity;
+                Object revisionData;
+
+                if (selectEntitiesOnly) {
+                    versionsEntity = (Map) resultRow;
+                    revisionData = null;
+                } else {
+                    Object[] arrayResultRow = (Object[]) resultRow;
+                    versionsEntity = (Map) arrayResultRow[0];
+                    revisionData = arrayResultRow[1];
+                }
+
+                Number revision = getRevisionNumber(versionsEntity);
+                
+                Object entity = entityInstantiator.createInstanceFromVersionsEntity(entityName, versionsEntity, revision);
+
+                if (!selectEntitiesOnly) {
+                    entities.add(new Object[] { entity, revisionData, versionsEntity.get(revisionTypePropertyName) });
+                } else {
+                    entities.add(entity);
+                }
+            }
+
+            return entities;
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/order/RevisionVersionsOrder.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/order/RevisionVersionsOrder.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/order/RevisionVersionsOrder.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,41 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.order;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.Pair;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionVersionsOrder implements VersionsOrder {
+    private final boolean asc;
+
+    public RevisionVersionsOrder(boolean asc) {
+        this.asc = asc;
+    }
+
+    public Pair<String, Boolean> getData(VersionsConfiguration verCfg) {
+        String revisionPropPath = verCfg.getVerEntCfg().getRevisionPropPath();
+        return Pair.make(revisionPropPath, asc);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/order/VersionsOrder.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/order/VersionsOrder.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/order/VersionsOrder.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,36 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.order;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.Pair;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface VersionsOrder {
+    /**
+     * @param verCfg Configuration.
+     * @return A pair: (property name, ascending?).
+     */
+    Pair<String, Boolean> getData(VersionsConfiguration verCfg);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/projection/RevisionVersionsProjection.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/projection/RevisionVersionsProjection.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/projection/RevisionVersionsProjection.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,58 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.projection;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.Triple;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionVersionsProjection implements VersionsProjection {
+    public static enum ProjectionType {
+        MAX,
+        MIN,
+        COUNT,
+        COUNT_DISTINCT,
+        DISTINCT
+    }
+
+    private final ProjectionType type;
+
+    public RevisionVersionsProjection(ProjectionType type) {
+        this.type = type;
+    }
+
+    public Triple<String, String, Boolean> getData(VersionsConfiguration verCfg) {
+        String revisionPropPath = verCfg.getVerEntCfg().getRevisionPropPath();
+
+        switch (type) {
+            case MAX: return Triple.make("max", revisionPropPath, false);
+            case MIN: return Triple.make("min", revisionPropPath, false);
+            case COUNT: return Triple.make("count", revisionPropPath, false);
+            case COUNT_DISTINCT: return Triple.make("count", revisionPropPath, true); 
+            case DISTINCT: return Triple.make(null, revisionPropPath, true);
+        }
+
+        throw new IllegalArgumentException("Unknown type " + type + ".");
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/query/projection/VersionsProjection.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/query/projection/VersionsProjection.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/query/projection/VersionsProjection.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,38 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.query.projection;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.tools.Pair;
+import org.jboss.envers.tools.Triple;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface VersionsProjection {
+    /**
+     *
+     * @param verCfg Configuration.
+     * @return A triple: (function name - possibly null, property name, add distinct?).
+     */
+    Triple<String, String, Boolean> getData(VersionsConfiguration verCfg);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/reader/FirstLevelCache.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/reader/FirstLevelCache.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/reader/FirstLevelCache.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,32 @@
+package org.jboss.envers.reader;
+
+import org.jboss.envers.tools.Triple;
+import static org.jboss.envers.tools.Triple.*;
+import static org.jboss.envers.tools.Tools.newHashMap;
+
+import java.util.Map;
+
+/**
+ * First level cache for versioned entities, versions reader-scoped. Each entity is uniquely identified by a
+ * revision number and entity id.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FirstLevelCache {
+    private final Map<Triple<String, Number, Object>, Object> cache;
+
+    public FirstLevelCache() {
+        cache = newHashMap();
+    }
+
+    public Object get(String entityName, Number revision, Object id) {
+        return cache.get(make(entityName, revision, id));
+    }
+
+    public void put(String entityName, Number revision, Object id, Object entity) {
+        cache.put(make(entityName, revision, id), entity);
+    }
+
+    public boolean contains(String entityName, Number revision, Object id) {
+        return cache.containsKey(make(entityName, revision, id));
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/reader/VersionsReaderImpl.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/reader/VersionsReaderImpl.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/reader/VersionsReaderImpl.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,194 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.reader;
+
+import org.hibernate.NonUniqueResultException;
+import org.hibernate.Session;
+import org.hibernate.Query;
+import org.hibernate.engine.SessionImplementor;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.exception.NotVersionedException;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+
+import javax.persistence.NoResultException;
+
+import static org.jboss.envers.tools.ArgumentsTools.*;
+import org.jboss.envers.query.VersionsQueryCreator;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.jboss.envers.query.RevisionProperty;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsReaderImpl implements VersionsReaderImplementor {
+    private final VersionsConfiguration verCfg;
+    private final SessionImplementor sessionImplementor;
+    private final Session session;
+    private final FirstLevelCache firstLevelCache;
+
+    public VersionsReaderImpl(VersionsConfiguration verCfg, Session session,
+                              SessionImplementor sessionImplementor) {
+        this.verCfg = verCfg;
+        this.sessionImplementor = sessionImplementor;
+        this.session = session;
+
+        firstLevelCache = new FirstLevelCache();
+    }
+
+    private void checkSession() {
+        if (!session.isOpen()) {
+            throw new IllegalStateException("The associated entity manager is closed!");
+        }
+    }
+
+    public SessionImplementor getSessionImplementor() {
+        return sessionImplementor;
+    }
+
+    public Session getSession() {
+        return session;
+    }
+
+    public FirstLevelCache getFirstLevelCache() {
+        return firstLevelCache;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T find(Class<T> cls, Object primaryKey, Number revision) throws
+            IllegalArgumentException, NotVersionedException, IllegalStateException {
+        checkNotNull(cls, "Entity class");
+        checkNotNull(primaryKey, "Primary key");
+        checkNotNull(revision, "Entity revision");
+        checkPositive(revision, "Entity revision");
+        checkSession();
+
+        String entityName = cls.getName();
+
+        if (!verCfg.getEntCfg().isVersioned(entityName)) {
+            throw new NotVersionedException(entityName, entityName + " is not versioned!");
+        }
+
+        if (firstLevelCache.contains(entityName, revision, primaryKey)) {
+            return (T) firstLevelCache.get(entityName, revision, primaryKey);
+        }
+
+        Object result;
+        try {
+            // The result is put into the cache by the entity instantiator called from the query
+            result = createQuery().forEntitiesAtRevision(cls, revision)
+                .add(VersionsRestrictions.idEq(primaryKey)).getSingleResult();
+        } catch (NoResultException e) {
+            result = null;
+        } catch (NonUniqueResultException e) {
+            throw new VersionsException(e);
+        }
+
+        return (T) result;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public List<Number> getRevisions(Class<?> cls, Object primaryKey)
+            throws IllegalArgumentException, NotVersionedException, IllegalStateException {
+        // todo: if a class is not versioned from the beginning, there's a missing ADD rev - what then?
+        checkNotNull(cls, "Entity class");
+        checkNotNull(primaryKey, "Primary key");
+        checkSession();
+
+        String entityName = cls.getName();
+
+        if (!verCfg.getEntCfg().isVersioned(entityName)) {
+            throw new NotVersionedException(entityName, entityName + " is not versioned!");
+        }
+
+        return createQuery().forRevisionsOfEntity(cls, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(VersionsRestrictions.idEq(primaryKey))
+                .getResultList();
+    }
+
+    public Date getRevisionDate(Number revision) throws IllegalArgumentException, RevisionDoesNotExistException,
+            IllegalStateException{
+        checkNotNull(revision, "Entity revision");
+        checkPositive(revision, "Entity revision");
+        checkSession();
+
+        Query query = verCfg.getRevisionInfoQueryCreator().getRevisionDateQuery(session, revision);
+
+        try {
+            Long timestamp = (Long) query.uniqueResult();
+            if (timestamp == null) {
+                throw new RevisionDoesNotExistException(revision);
+            }
+
+            return new Date(timestamp);
+        } catch (NonUniqueResultException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public Number getRevisionNumberForDate(Date date) {
+        checkNotNull(date, "Date of revision");
+        checkSession();
+
+        Query query = verCfg.getRevisionInfoQueryCreator().getRevisionNumberForDateQuery(session, date);
+
+        try {
+            Number res = (Number) query.uniqueResult();
+            if (res == null) {
+                throw new RevisionDoesNotExistException(date);
+            }
+
+            return res;
+        } catch (NonUniqueResultException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T findRevision(Class<T> revisionEntityClass, Number revision) throws IllegalArgumentException,
+            RevisionDoesNotExistException, IllegalStateException {
+        checkNotNull(revision, "Entity revision");
+        checkPositive(revision, "Entity revision");
+        checkSession();
+
+        Query query = verCfg.getRevisionInfoQueryCreator().getRevisionQuery(session, revision);
+
+        try {
+            T revisionData = (T) query.uniqueResult();
+
+            if (revisionData == null) {
+                throw new RevisionDoesNotExistException(revision);
+            }
+
+            return revisionData;
+        } catch (NonUniqueResultException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public VersionsQueryCreator createQuery() {
+        return new VersionsQueryCreator(verCfg, this);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/reader/VersionsReaderImplementor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/reader/VersionsReaderImplementor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/reader/VersionsReaderImplementor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,36 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.reader;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.Session;
+import org.jboss.envers.VersionsReader;
+
+/**
+ * An interface exposed by a VersionsReader to library-facing classes.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface VersionsReaderImplementor extends VersionsReader {
+    SessionImplementor getSessionImplementor();
+    Session getSession();
+    FirstLevelCache getFirstLevelCache();
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/DefaultRevisionInfoGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/DefaultRevisionInfoGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/DefaultRevisionInfoGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.revisioninfo;
+
+import org.jboss.envers.RevisionListener;
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.hibernate.MappingException;
+import org.hibernate.Session;
+import org.hibernate.property.Setter;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DefaultRevisionInfoGenerator implements RevisionInfoGenerator {
+    private final String revisionInfoEntityName;
+    private final RevisionListener listener;
+    private final Setter revisionTimestampSetter;
+    private final Class<?> revisionInfoClass;
+
+    public DefaultRevisionInfoGenerator(String revisionInfoEntityName, Class<?> revisionInfoClass,
+                                       Class<? extends RevisionListener> listenerClass,
+                                       String revisionInfoTimestampName) {
+        this.revisionInfoEntityName = revisionInfoEntityName;
+        this.revisionInfoClass = revisionInfoClass;
+
+        revisionTimestampSetter = ReflectionTools.getSetter(revisionInfoClass, revisionInfoTimestampName);
+
+        if (!listenerClass.equals(RevisionListener.class)) {
+            // This is not the default value.
+            try {
+                listener = listenerClass.newInstance();
+            } catch (InstantiationException e) {
+                throw new MappingException(e);
+            } catch (IllegalAccessException e) {
+                throw new MappingException(e);
+            }
+        } else {
+            // Default listener - none
+            listener = null;
+        }
+    }
+
+    private Object newRevision() {
+        Object revisionInfo;
+        try {
+            revisionInfo = revisionInfoClass.newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        
+        revisionTimestampSetter.set(revisionInfo, System.currentTimeMillis(), null);
+
+        if (listener != null) {
+            listener.newRevision(revisionInfo);
+        }
+
+        return revisionInfo;
+    }
+
+    public Object generate(Session session) {
+        Object revisionData = newRevision();
+        session.save(revisionInfoEntityName, revisionData);
+        return revisionData;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoGenerator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoGenerator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,31 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.revisioninfo;
+
+import org.hibernate.Session;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RevisionInfoGenerator {
+    Object generate(Session session);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoNumberReader.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoNumberReader.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoNumberReader.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,40 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.revisioninfo;
+
+import org.jboss.envers.tools.reflection.ReflectionTools;
+import org.hibernate.property.Getter;
+
+/**
+ * Gets a revision number from a persisted revision info entity.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionInfoNumberReader {
+    private final Getter revisionIdGetter;
+    public RevisionInfoNumberReader(Class<?> revisionInfoClass, String revisionInfoIdName) {
+        revisionIdGetter = ReflectionTools.getGetter(revisionInfoClass, revisionInfoIdName);
+    }
+
+    public Number getRevisionNumber(Object revision) {
+        return (Number) revisionIdGetter.get(revision);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoQueryCreator.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoQueryCreator.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/revisioninfo/RevisionInfoQueryCreator.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,71 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.revisioninfo;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionInfoQueryCreator {
+    private final String revisionDateQuery;
+    private final String revisionNumberForDateQuery;
+    private final String revisionQuery;
+
+    public RevisionInfoQueryCreator(String revisionInfoEntityName, String revisionInfoIdName,
+                                    String revisionInfoTimestampName) {
+        revisionDateQuery = new StringBuilder()
+                .append("select rev.").append(revisionInfoTimestampName)
+                .append(" from ").append(revisionInfoEntityName)
+                .append(" rev where ").append(revisionInfoIdName).append(" = :_revision_number")
+                .toString();
+
+        revisionNumberForDateQuery = new StringBuilder()
+                .append("select max(rev.").append(revisionInfoIdName)
+                .append(") from ").append(revisionInfoEntityName)
+                .append(" rev where ").append(revisionInfoTimestampName).append(" <= :_revision_date")
+                .toString();
+
+        revisionQuery = new StringBuilder()
+                .append("select rev from ").append(revisionInfoEntityName)
+                .append(" rev where ").append(revisionInfoIdName)
+                .append(" = :_revision_number")
+                .toString();
+    }
+
+    public Query getRevisionDateQuery(Session session, Number revision) {
+        return session.createQuery(revisionDateQuery).setParameter("_revision_number", revision);
+    }
+
+    public Query getRevisionNumberForDateQuery(Session session, Date date) {
+        return session.createQuery(revisionNumberForDateQuery).setParameter("_revision_date", date.getTime());
+    }
+
+    public Query getRevisionQuery(Session session, Number revision) {
+        return session.createQuery(revisionQuery).setParameter("_revision_number", revision);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/VersionsSync.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/VersionsSync.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/VersionsSync.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,154 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.FlushMode;
+import org.hibernate.event.EventSource;
+import org.jboss.envers.synchronization.work.VersionsWorkUnit;
+import org.jboss.envers.tools.Pair;
+import org.jboss.envers.revisioninfo.RevisionInfoGenerator;
+
+import javax.transaction.Synchronization;
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsSync implements Synchronization {
+    private final RevisionInfoGenerator revisionInfoGenerator;
+    private final VersionsSyncManager manager;
+    private final EventSource session;
+
+    private final Transaction transaction;
+    private final LinkedList<VersionsWorkUnit> workUnits;
+    private final Queue<VersionsWorkUnit> undoQueue;
+    private final Map<Pair<String, Object>, VersionsWorkUnit> usedIds;
+
+    private Object revisionData;
+
+    public VersionsSync(VersionsSyncManager manager, EventSource session, RevisionInfoGenerator revisionInfoGenerator) {
+        this.manager = manager;
+        this.session = session;
+        this.revisionInfoGenerator = revisionInfoGenerator;
+
+        transaction = session.getTransaction();
+        workUnits = new LinkedList<VersionsWorkUnit>();
+        undoQueue = new LinkedList<VersionsWorkUnit>();
+        usedIds = new HashMap<Pair<String, Object>, VersionsWorkUnit>();
+    }
+
+    private void removeWorkUnit(VersionsWorkUnit vwu) {
+        workUnits.remove(vwu);
+        if (vwu.isPerformed()) {
+            // If this work unit has already been performed, it must be deleted (undone) first.
+            undoQueue.offer(vwu);
+        }
+    }
+
+    public void addWorkUnit(VersionsWorkUnit vwu) {
+        if (vwu.containsWork()) {
+            Object entityId = vwu.getEntityId();
+
+            if (entityId == null) {
+                // Just adding the work unit - it's not associated with any persistent entity.
+                workUnits.offer(vwu);
+            } else {
+                String entityName = vwu.getEntityName();
+                Pair<String, Object> usedIdsKey = Pair.make(entityName, entityId);
+
+                if (usedIds.containsKey(usedIdsKey)) {
+                    VersionsWorkUnit other = usedIds.get(usedIdsKey);
+
+                    // The entity with entityId has two work units; checking which one should be kept.
+                    switch (vwu.dispatch(other)) {
+                        case FIRST:
+                            // Simply not adding the second
+                            break;
+
+                        case SECOND:
+                            removeWorkUnit(other);
+                            usedIds.put(usedIdsKey, vwu);
+                            workUnits.offer(vwu);
+                            break;
+
+                        case NONE:
+                            removeWorkUnit(other);
+                            break;
+                    }
+                } else {
+                    usedIds.put(usedIdsKey, vwu);
+                    workUnits.offer(vwu);
+                }
+            }
+        }
+    }
+
+    private void executeInSession(Session session) {
+        if (revisionData == null) {
+            revisionData = revisionInfoGenerator.generate(session);
+        }
+
+        VersionsWorkUnit vwu;
+
+        // First undoing any performed work units
+        while ((vwu = undoQueue.poll()) != null) {
+            vwu.undo(session);
+        }
+
+        while ((vwu = workUnits.poll()) != null) {
+            vwu.perform(session, revisionData);
+        }
+    }
+
+    public void beforeCompletion() {
+        if (workUnits.size() == 0 && undoQueue.size() == 0) {
+            return;
+        }
+
+        // see: http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4178431
+        if (FlushMode.isManualFlushMode(session.getFlushMode()) || session.isClosed()) {
+            Session temporarySession = null;
+            try {
+                temporarySession = session.getFactory().openTemporarySession();
+
+                executeInSession(temporarySession);
+
+                temporarySession.flush();
+            } finally {
+                if (temporarySession != null) {
+                    temporarySession.close();
+                }
+            }
+        } else {
+            executeInSession(session);
+
+            // Explicity flushing the session, as the auto-flush may have already happened.
+            session.flush();
+        }
+    }
+
+    public void afterCompletion(int i) {
+        manager.remove(transaction);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/VersionsSyncManager.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/VersionsSyncManager.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/VersionsSyncManager.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,63 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization;
+
+import org.jboss.envers.tools.ConcurrentReferenceHashMap;
+import org.jboss.envers.revisioninfo.RevisionInfoGenerator;
+import org.hibernate.Transaction;
+import org.hibernate.event.EventSource;
+
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsSyncManager {
+    private final Map<Transaction, VersionsSync> versionsSyncs;
+    private final RevisionInfoGenerator revisionInfoGenerator;
+
+    public VersionsSyncManager(RevisionInfoGenerator revisionInfoGenerator) {
+        versionsSyncs = new ConcurrentReferenceHashMap<Transaction, VersionsSync>(10,
+                ConcurrentReferenceHashMap.ReferenceType.WEAK,
+                ConcurrentReferenceHashMap.ReferenceType.STRONG);
+
+        this.revisionInfoGenerator = revisionInfoGenerator;
+    }
+
+    public VersionsSync get(EventSource session) {
+        Transaction transaction = session.getTransaction();
+
+        VersionsSync verSync = versionsSyncs.get(transaction);
+        if (verSync == null) {
+            verSync = new VersionsSync(this, session, revisionInfoGenerator);
+            versionsSyncs.put(transaction, verSync);
+
+            transaction.registerSynchronization(verSync);
+        }
+
+        return verSync;
+    }
+
+    public void remove(Transaction transaction) {
+        versionsSyncs.remove(transaction);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/AbstractVersionsWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/AbstractVersionsWorkUnit.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/AbstractVersionsWorkUnit.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,83 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.RevisionType;
+import org.hibernate.Session;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractVersionsWorkUnit implements VersionsWorkUnit {
+    protected final VersionsConfiguration verCfg;
+    protected final Serializable id;
+
+    private final String entityName;
+
+    private Object performedData;
+
+    protected AbstractVersionsWorkUnit(String entityName, VersionsConfiguration verCfg, Serializable id) {
+        this.verCfg = verCfg;
+        this.id = id;
+        this.entityName = entityName;
+    }
+
+    protected void fillDataWithId(Map<String, Object> data, Object revision, RevisionType revisionType) {
+        VersionsEntitiesConfiguration entitiesCfg = verCfg.getVerEntCfg();
+
+        Map<String, Object> originalId = new HashMap<String, Object>();
+        originalId.put(entitiesCfg.getRevisionPropName(), revision);
+
+        verCfg.getEntCfg().get(getEntityName()).getIdMapper().mapToMapFromId(originalId, id);
+        data.put(entitiesCfg.getRevisionTypePropName(), revisionType);
+        data.put(entitiesCfg.getOriginalIdPropName(), originalId);
+    }
+
+    public Object getEntityId() {
+        return id;
+    }
+
+    public boolean isPerformed() {
+        return performedData != null;
+    }
+
+    public String getEntityName() {
+        return entityName;
+    }
+
+    protected void setPerformed(Object performedData) {
+        this.performedData = performedData;
+    }
+
+    public void undo(Session session) {
+        if (isPerformed()) {
+            session.delete(verCfg.getVerEntCfg().getVersionsEntityName(getEntityName()), performedData);
+            session.flush();
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/AddWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/AddWorkUnit.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/AddWorkUnit.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.RevisionType;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.Session;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class AddWorkUnit extends AbstractVersionsWorkUnit implements VersionsWorkUnit {
+    private final Object[] state;
+    private final String[] propertyNames;
+
+    public AddWorkUnit(String entityName, VersionsConfiguration verCfg, Serializable id,
+                       EntityPersister entityPersister, Object[] state) {
+        super(entityName, verCfg, id);
+
+        this.state = state;
+        this.propertyNames = entityPersister.getPropertyNames();
+    }
+
+    public boolean containsWork() {
+        return true;
+    }
+
+    public void perform(Session session, Object revisionData) {
+        Map<String, Object> data = new HashMap<String, Object>();
+        fillDataWithId(data, revisionData, RevisionType.ADD);
+
+        verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(data, propertyNames, state, null);
+
+        session.save(verCfg.getVerEntCfg().getVersionsEntityName(getEntityName()), data);
+
+        setPerformed(data);
+    }
+
+    public KeepCheckResult check(AddWorkUnit second) {
+        return KeepCheckResult.FIRST;
+    }
+
+    public KeepCheckResult check(ModWorkUnit second) {
+        return KeepCheckResult.SECOND;
+    }
+
+    public KeepCheckResult check(DelWorkUnit second) {
+        return KeepCheckResult.NONE;
+    }
+
+    public KeepCheckResult check(CollectionChangeWorkUnit second) {
+        return KeepCheckResult.FIRST;
+    }
+
+    public KeepCheckResult dispatch(KeepCheckVisitor first) {
+        return first.check(this);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/CollectionChangeWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/CollectionChangeWorkUnit.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/CollectionChangeWorkUnit.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,78 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+import org.hibernate.Session;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.RevisionType;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class CollectionChangeWorkUnit extends AbstractVersionsWorkUnit implements VersionsWorkUnit {
+    private final Object entity;
+
+    public CollectionChangeWorkUnit(String entityName, VersionsConfiguration verCfg, Serializable id, Object entity) {
+        super(entityName, verCfg, id);
+
+        this.entity = entity;
+    }
+
+    public boolean containsWork() {
+        return true;
+    }
+
+    public void perform(Session session, Object revisionData) {
+        Map<String, Object> data = new HashMap<String, Object>();
+        fillDataWithId(data, revisionData, RevisionType.MOD);
+
+        verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().mapToMapFromEntity(data, entity, null);
+
+        session.save(verCfg.getVerEntCfg().getVersionsEntityName(getEntityName()), data);
+
+        setPerformed(data);
+    }
+
+    public KeepCheckResult check(AddWorkUnit second) {
+        return KeepCheckResult.SECOND;
+    }
+
+    public KeepCheckResult check(ModWorkUnit second) {
+        return KeepCheckResult.SECOND;
+    }
+
+    public KeepCheckResult check(DelWorkUnit second) {
+        return KeepCheckResult.SECOND;
+    }
+
+    public KeepCheckResult check(CollectionChangeWorkUnit second) {
+        return KeepCheckResult.FIRST;
+    }
+
+    public KeepCheckResult dispatch(KeepCheckVisitor first) {
+        return first.check(this);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/DelWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/DelWorkUnit.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/DelWorkUnit.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,72 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.RevisionType;
+import org.hibernate.Session;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DelWorkUnit extends AbstractVersionsWorkUnit implements VersionsWorkUnit {
+    public DelWorkUnit(String entityName, VersionsConfiguration verCfg, Serializable id) {
+        super(entityName, verCfg, id);
+    }
+
+    public boolean containsWork() {
+        return true;
+    }
+
+    public void perform(Session session, Object revisionData) {
+        Map<String, Object> data = new HashMap<String, Object>();
+        fillDataWithId(data, revisionData, RevisionType.DEL);
+
+        session.save(verCfg.getVerEntCfg().getVersionsEntityName(getEntityName()), data);
+
+        setPerformed(data);
+    }
+
+    public KeepCheckResult check(AddWorkUnit second) {
+        return KeepCheckResult.NONE;
+    }
+
+    public KeepCheckResult check(ModWorkUnit second) {
+        return KeepCheckResult.NONE;
+    }
+
+    public KeepCheckResult check(DelWorkUnit second) {
+        return KeepCheckResult.FIRST;
+    }
+
+    public KeepCheckResult check(CollectionChangeWorkUnit second) {
+        return KeepCheckResult.FIRST;
+    }
+
+    public KeepCheckResult dispatch(KeepCheckVisitor first) {
+        return first.check(this);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckDispatcher.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckDispatcher.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckDispatcher.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,35 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+/**
+ * Visitor patter dispatcher.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface KeepCheckDispatcher {
+    /**
+     * Shuold be invoked on the second work unit.
+     * @param first First work unit (that is, the one added earlier).
+     * @return Which work unit should be kept.
+     */
+    KeepCheckResult dispatch(KeepCheckVisitor first);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckResult.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckResult.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckResult.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,33 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+/**
+ * Possible outcomes of selecting which work unit to keep, in case there are two work units for the same entity
+ * with the same id.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public enum KeepCheckResult {
+    FIRST,
+    SECOND,
+    NONE
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckVisitor.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckVisitor.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/KeepCheckVisitor.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,33 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+/**
+ * Visitor pattern visitor. All methods should be invoked on the first work unit.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface KeepCheckVisitor {
+    KeepCheckResult check(AddWorkUnit second);
+    KeepCheckResult check(ModWorkUnit second);
+    KeepCheckResult check(DelWorkUnit second);
+    KeepCheckResult check(CollectionChangeWorkUnit second);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/ModWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/ModWorkUnit.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/ModWorkUnit.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,80 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.RevisionType;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.Session;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ModWorkUnit extends AbstractVersionsWorkUnit implements VersionsWorkUnit {
+    private final Map<String, Object> data;
+    private final boolean changes;        
+
+    public ModWorkUnit(String entityName, VersionsConfiguration verCfg, Serializable id,
+                       EntityPersister entityPersister, Object[] newState, Object[] oldState) {
+        super(entityName, verCfg, id);
+
+        data = new HashMap<String, Object>();
+        changes = verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(data, entityPersister.getPropertyNames(),
+                newState, oldState);
+    }
+
+    public boolean containsWork() {
+        return changes;
+    }
+
+    public void perform(Session session, Object revisionData) {
+        fillDataWithId(data, revisionData, RevisionType.MOD);
+
+        session.save(verCfg.getVerEntCfg().getVersionsEntityName(getEntityName()), data);
+
+        setPerformed(data);
+    }
+
+    public KeepCheckResult check(AddWorkUnit second) {
+        return KeepCheckResult.FIRST;
+    }
+
+    public KeepCheckResult check(ModWorkUnit second) {
+        return KeepCheckResult.SECOND;
+    }
+
+    public KeepCheckResult check(DelWorkUnit second) {
+        return KeepCheckResult.SECOND;
+    }
+
+    public KeepCheckResult check(CollectionChangeWorkUnit second) {
+        return KeepCheckResult.FIRST;
+    }
+
+    public KeepCheckResult dispatch(KeepCheckVisitor first) {
+        return first.check(this);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,98 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+import org.hibernate.Session;
+import org.hibernate.collection.PersistentCollection;
+import org.jboss.envers.configuration.VersionsConfiguration;
+import org.jboss.envers.configuration.VersionsEntitiesConfiguration;
+import org.jboss.envers.entities.mapper.PersistentCollectionChangeData;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PersistentCollectionChangeWorkUnit extends AbstractVersionsWorkUnit implements VersionsWorkUnit {
+    private final List<PersistentCollectionChangeData> collectionChanges;
+    private final String referencingPropertyName;
+
+    public PersistentCollectionChangeWorkUnit(String entityName, VersionsConfiguration verCfg,
+                                              PersistentCollection collection, String role,
+                                              Serializable snapshot, Serializable id) {
+        super(entityName, verCfg, null);
+
+        referencingPropertyName = role.substring(entityName.length() + 1);
+
+        collectionChanges = verCfg.getEntCfg().get(getEntityName()).getPropertyMapper()
+                .mapCollectionChanges(referencingPropertyName, collection, snapshot, id);
+    }
+
+    public boolean containsWork() {
+        return collectionChanges != null && collectionChanges.size() != 0;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public void perform(Session session, Object revisionData) {
+        VersionsEntitiesConfiguration entitiesCfg = verCfg.getVerEntCfg();
+
+        for (PersistentCollectionChangeData persistentCollectionChangeData : collectionChanges) {
+            // Setting the revision number
+            ((Map<String, Object>) persistentCollectionChangeData.getData().get(entitiesCfg.getOriginalIdPropName()))
+                    .put(entitiesCfg.getRevisionPropName(), revisionData);
+
+            session.save(persistentCollectionChangeData.getEntityName(), persistentCollectionChangeData.getData());
+        }
+    }
+
+    public String getReferencingPropertyName() {
+        return referencingPropertyName;
+    }
+
+    public List<PersistentCollectionChangeData> getCollectionChanges() {
+        return collectionChanges;
+    }
+
+    public KeepCheckResult check(AddWorkUnit second) {
+        return null;
+    }
+
+    public KeepCheckResult check(ModWorkUnit second) {
+        return null;
+    }
+
+    public KeepCheckResult check(DelWorkUnit second) {
+        return null;
+    }
+
+    public KeepCheckResult check(CollectionChangeWorkUnit second) {
+        return null;
+    }
+
+    public KeepCheckResult dispatch(KeepCheckVisitor first) {
+        return null;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/VersionsWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/VersionsWorkUnit.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/synchronization/work/VersionsWorkUnit.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,39 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.synchronization.work;
+
+import org.hibernate.Session;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface VersionsWorkUnit extends KeepCheckVisitor, KeepCheckDispatcher {
+    Object getEntityId();
+    String getEntityName();
+    
+    boolean containsWork();
+
+    boolean isPerformed();
+    
+    void perform(Session session, Object revisionData);
+    void undo(Session session);
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/ArgumentsTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/ArgumentsTools.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/ArgumentsTools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,39 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ArgumentsTools {
+    public static void checkNotNull(Object o, String paramName) {
+        if (o == null) {
+            throw new IllegalArgumentException(paramName + " cannot be null.");
+        }
+    }
+
+    public static void checkPositive(Number i, String paramName) {
+        if (i.longValue() <= 0l) {
+            throw new IllegalArgumentException(paramName + " has to be greater than 0.");
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/ArraysTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/ArraysTools.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/ArraysTools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,37 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ArraysTools {
+    public static <T> boolean arrayIncludesInstanceOf(T[] array, Class<?> cls) {
+        for (T obj : array) {
+            if (cls.isAssignableFrom(obj.getClass())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/ConcurrentReferenceHashMap.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/ConcurrentReferenceHashMap.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/ConcurrentReferenceHashMap.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,1710 @@
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
+ */
+
+package org.jboss.envers.tools;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * An advanced hash table supporting configurable garbage collection semantics
+ * of keys and values, optional referential-equality, full concurrency of
+ * retrievals, and adjustable expected concurrency for updates.
+ *
+ * This table is designed around specific advanced use-cases. If there is any
+ * doubt whether this table is for you, you most likely should be using
+ * {@link java.util.concurrent.ConcurrentHashMap} instead.
+ *
+ * This table supports strong, weak, and soft keys and values. By default keys
+ * are weak, and values are strong. Such a configuration offers similar behavior
+ * to {@link java.util.WeakHashMap}, entries of this table are periodically
+ * removed once their corresponding keys are no longer referenced outside of
+ * this table. In other words, this table will not prevent a key from being
+ * discarded by the garbage collector. Once a key has been discarded by the
+ * collector, the corresponding entry is no longer visible to this table;
+ * however, the entry may occupy space until a future table operation decides to
+ * reclaim it. For this reason, summary functions such as <tt>size</tt> and
+ * <tt>isEmpty</tt> might return a value greater than the observed number of
+ * entries. In order to support a high level of concurrency, stale entries are
+ * only reclaimed during blocking (usually mutating) operations.
+ *
+ * Enabling soft keys allows entries in this table to remain until their space
+ * is absolutely needed by the garbage collector. This is unlike weak keys which
+ * can be reclaimed as soon as they are no longer referenced by a normal strong
+ * reference. The primary use case for soft keys is a cache, which ideally
+ * occupies memory that is not in use for as long as possible.
+ *
+ * By default, values are held using a normal strong reference. This provides
+ * the commonly desired guarantee that a value will always have at least the
+ * same life-span as it's key. For this reason, care should be taken to ensure
+ * that a value never refers, either directly or indirectly, to its key, thereby
+ * preventing reclamation. If this is unavoidable, then it is recommended to use
+ * the same reference type in use for the key. However, it should be noted that
+ * non-strong values may disappear before their corresponding key.
+ *
+ * While this table does allow the use of both strong keys and values, it is
+ * recommended to use {@link java.util.concurrent.ConcurrentHashMap} for such a
+ * configuration, since it is optimized for that case.
+ *
+ * Just like {@link java.util.concurrent.ConcurrentHashMap}, this class obeys
+ * the same functional specification as {@link java.util.Hashtable}, and
+ * includes versions of methods corresponding to each method of
+ * <tt>Hashtable</tt>. However, even though all operations are thread-safe,
+ * retrieval operations do <em>not</em> entail locking, and there is
+ * <em>not</em> any support for locking the entire table in a way that
+ * prevents all access. This class is fully interoperable with
+ * <tt>Hashtable</tt> in programs that rely on its thread safety but not on
+ * its synchronization details.
+ *
+ * <p>
+ * Retrieval operations (including <tt>get</tt>) generally do not block, so
+ * may overlap with update operations (including <tt>put</tt> and
+ * <tt>remove</tt>). Retrievals reflect the results of the most recently
+ * <em>completed</em> update operations holding upon their onset. For
+ * aggregate operations such as <tt>putAll</tt> and <tt>clear</tt>,
+ * concurrent retrievals may reflect insertion or removal of only some entries.
+ * Similarly, Iterators and Enumerations return elements reflecting the state of
+ * the hash table at some point at or since the creation of the
+ * iterator/enumeration. They do <em>not</em> throw
+ * {@link ConcurrentModificationException}. However, iterators are designed to
+ * be used by only one thread at a time.
+ *
+ * <p>
+ * The allowed concurrency among update operations is guided by the optional
+ * <tt>concurrencyLevel</tt> constructor argument (default <tt>16</tt>),
+ * which is used as a hint for internal sizing. The table is internally
+ * partitioned to try to permit the indicated number of concurrent updates
+ * without contention. Because placement in hash tables is essentially random,
+ * the actual concurrency will vary. Ideally, you should choose a value to
+ * accommodate as many threads as will ever concurrently modify the table. Using
+ * a significantly higher value than you need can waste space and time, and a
+ * significantly lower value can lead to thread contention. But overestimates
+ * and underestimates within an order of magnitude do not usually have much
+ * noticeable impact. A value of one is appropriate when it is known that only
+ * one thread will modify and all others will only read. Also, resizing this or
+ * any other kind of hash table is a relatively slow operation, so, when
+ * possible, it is a good idea to provide estimates of expected table sizes in
+ * constructors.
+ *
+ * <p>
+ * This class and its views and iterators implement all of the <em>optional</em>
+ * methods of the {@link Map} and {@link Iterator} interfaces.
+ *
+ * <p>
+ * Like {@link Hashtable} but unlike {@link HashMap}, this class does
+ * <em>not</em> allow <tt>null</tt> to be used as a key or value.
+ *
+ * <p>
+ * This class is a member of the <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * Java Collections Framework</a>.
+ *
+ * @author Doug Lea
+ * @author Jason T. Greene
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
+ */
+public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V>
+        implements java.util.concurrent.ConcurrentMap<K, V>, Serializable {
+    private static final long serialVersionUID = 7249069246763182397L;
+
+    /*
+     * The basic strategy is to subdivide the table among Segments,
+     * each of which itself is a concurrently readable hash table.
+     */
+
+    /**
+     * An option specifying which Java reference type should be used to refer
+     * to a key and/or value.
+     */
+    public static enum ReferenceType {
+        /** Indicates a normal Java strong reference should be used */
+        STRONG,
+        /** Indicates a {@link WeakReference} should be used */
+        WEAK,
+        /** Indicates a {@link SoftReference} should be used */
+        SOFT
+    };
+
+
+    public static enum Option {
+        /** Indicates that referential-equality (== instead of .equals()) should
+         * be used when locating keys. This offers similar behavior to {@link IdentityHashMap} */
+        IDENTITY_COMPARISONS
+    };
+
+    /* ---------------- Constants -------------- */
+
+    static final ReferenceType DEFAULT_KEY_TYPE = ReferenceType.WEAK;
+
+    static final ReferenceType DEFAULT_VALUE_TYPE = ReferenceType.STRONG;
+
+
+    /**
+     * The default initial capacity for this table,
+     * used when not otherwise specified in a constructor.
+     */
+    static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+    /**
+     * The default load factor for this table, used when not
+     * otherwise specified in a constructor.
+     */
+    static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+    /**
+     * The default concurrency level for this table, used when not
+     * otherwise specified in a constructor.
+     */
+    static final int DEFAULT_CONCURRENCY_LEVEL = 16;
+
+    /**
+     * The maximum capacity, used if a higher value is implicitly
+     * specified by either of the constructors with arguments.  MUST
+     * be a power of two <= 1<<30 to ensure that entries are indexable
+     * using ints.
+     */
+    static final int MAXIMUM_CAPACITY = 1 << 30;
+
+    /**
+     * The maximum number of segments to allow; used to bound
+     * constructor arguments.
+     */
+    static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
+
+    /**
+     * Number of unsynchronized retries in size and containsValue
+     * methods before resorting to locking. This is used to avoid
+     * unbounded retries if tables undergo continuous modification
+     * which would make it impossible to obtain an accurate result.
+     */
+    static final int RETRIES_BEFORE_LOCK = 2;
+
+    /* ---------------- Fields -------------- */
+
+    /**
+     * Mask value for indexing into segments. The upper bits of a
+     * key's hash code are used to choose the segment.
+     */
+    final int segmentMask;
+
+    /**
+     * Shift value for indexing within segments.
+     */
+    final int segmentShift;
+
+    /**
+     * The segments, each of which is a specialized hash table
+     */
+    final Segment<K,V>[] segments;
+
+    boolean identityComparisons;
+
+    transient Set<K> keySet;
+    transient Set<Map.Entry<K,V>> entrySet;
+    transient Collection<V> values;
+
+    /* ---------------- Small Utilities -------------- */
+
+    /**
+     * Applies a supplemental hash function to a given hashCode, which
+     * defends against poor quality hash functions.  This is critical
+     * because ConcurrentReferenceHashMap uses power-of-two length hash tables,
+     * that otherwise encounter collisions for hashCodes that do not
+     * differ in lower or upper bits.
+     */
+    private static int hash(int h) {
+        // Spread bits to regularize both segment and index locations,
+        // using variant of single-word Wang/Jenkins hash.
+        h += (h <<  15) ^ 0xffffcd7d;
+        h ^= (h >>> 10);
+        h += (h <<   3);
+        h ^= (h >>>  6);
+        h += (h <<   2) + (h << 14);
+        return h ^ (h >>> 16);
+    }
+
+    /**
+     * Returns the segment that should be used for key with given hash
+     * @param hash the hash code for the key
+     * @return the segment
+     */
+    final Segment<K,V> segmentFor(int hash) {
+        return segments[(hash >>> segmentShift) & segmentMask];
+    }
+
+    private int hashOf(Object key) {
+        return hash(identityComparisons ?
+                System.identityHashCode(key) : key.hashCode());
+    }
+
+    /* ---------------- Inner Classes -------------- */
+
+    static interface KeyReference {
+        int keyHash();
+        Object keyRef();
+    }
+
+    /**
+     * A weak-key reference which stores the key hash needed for reclamation.
+     */
+    static final class WeakKeyReference<K> extends WeakReference<K>  implements KeyReference {
+        final int hash;
+        WeakKeyReference(K key, int hash, ReferenceQueue<Object> refQueue) {
+            super(key, refQueue);
+            this.hash = hash;
+        }
+        public final int keyHash() {
+            return hash;
+        }
+
+        public final Object keyRef() {
+            return this;
+        }
+    }
+
+    /**
+     * A soft-key reference which stores the key hash needed for reclamation.
+     */
+    static final class SoftKeyReference<K> extends SoftReference<K> implements KeyReference {
+        final int hash;
+        SoftKeyReference(K key, int hash, ReferenceQueue<Object> refQueue) {
+            super(key, refQueue);
+            this.hash = hash;
+        }
+        public final int keyHash() {
+            return hash;
+        }
+
+        public final Object keyRef() {
+            return this;
+        }
+    }
+
+    static final class WeakValueReference<V> extends WeakReference<V>  implements KeyReference {
+        final Object keyRef;
+        final int hash;
+        WeakValueReference(V value, Object keyRef, int hash, ReferenceQueue<Object> refQueue) {
+            super(value, refQueue);
+            this.keyRef = keyRef;
+            this.hash = hash;
+        }
+
+        public final int keyHash() {
+            return hash;
+        }
+
+        public final Object keyRef() {
+            return keyRef;
+        }
+    }
+
+    static final class SoftValueReference<V> extends SoftReference<V>  implements KeyReference {
+        final Object keyRef;
+        final int hash;
+        SoftValueReference(V value, Object keyRef, int hash, ReferenceQueue<Object> refQueue) {
+            super(value, refQueue);
+            this.keyRef = keyRef;
+            this.hash = hash;
+        }
+        public final int keyHash() {
+            return hash;
+        }
+
+        public final Object keyRef() {
+            return keyRef;
+        }
+    }
+
+    /**
+     * ConcurrentReferenceHashMap list entry. Note that this is never exported
+     * out as a user-visible Map.Entry.
+     *
+     * Because the value field is volatile, not final, it is legal wrt
+     * the Java Memory Model for an unsynchronized reader to see null
+     * instead of initial value when read via a data race.  Although a
+     * reordering leading to this is not likely to ever actually
+     * occur, the Segment.readValueUnderLock method is used as a
+     * backup in case a null (pre-initialized) value is ever seen in
+     * an unsynchronized access method.
+     */
+    static final class HashEntry<K,V> {
+        final Object keyRef;
+        final int hash;
+        volatile Object valueRef;
+        final HashEntry<K,V> next;
+
+        HashEntry(K key, int hash,  HashEntry<K,V> next, V value,
+                ReferenceType keyType, ReferenceType valueType,
+                ReferenceQueue<Object> refQueue) {
+            this.hash = hash;
+            this.next = next;
+            this.keyRef = newKeyReference(key, keyType, refQueue);
+            this.valueRef = newValueReference(value, valueType, refQueue);
+        }
+
+        final Object newKeyReference(K key, ReferenceType keyType,
+                ReferenceQueue<Object> refQueue) {
+            if (keyType == ReferenceType.WEAK)
+                return new WeakKeyReference<K>(key, hash, refQueue);
+            if (keyType == ReferenceType.SOFT)
+                return new SoftKeyReference<K>(key, hash, refQueue);
+
+            return key;
+        }
+
+        final Object newValueReference(V value, ReferenceType valueType,
+                ReferenceQueue<Object> refQueue) {
+            if (valueType == ReferenceType.WEAK)
+                return new WeakValueReference<V>(value, keyRef, hash, refQueue);
+            if (valueType == ReferenceType.SOFT)
+                return new SoftValueReference<V>(value, keyRef, hash, refQueue);
+
+            return value;
+        }
+
+        @SuppressWarnings("unchecked")
+        final K key() {
+            if (keyRef instanceof KeyReference)
+                return ((Reference<K>)keyRef).get();
+
+            return (K) keyRef;
+        }
+
+        final V value() {
+            return dereferenceValue(valueRef);
+        }
+
+        @SuppressWarnings("unchecked")
+        final V dereferenceValue(Object value) {
+            if (value instanceof KeyReference)
+                return ((Reference<V>)value).get();
+
+            return (V) value;
+        }
+
+        final void setValue(V value, ReferenceType valueType, ReferenceQueue<Object> refQueue) {
+            this.valueRef = newValueReference(value, valueType, refQueue);
+        }
+
+        @SuppressWarnings("unchecked")
+        static final <K,V> HashEntry<K,V>[] newArray(int i) {
+            return new HashEntry[i];
+        }
+    }
+
+    /**
+     * Segments are specialized versions of hash tables.  This
+     * subclasses from ReentrantLock opportunistically, just to
+     * simplify some locking and avoid separate construction.
+     */
+    static final class Segment<K,V> extends ReentrantLock implements Serializable {
+        /*
+         * Segments maintain a table of entry lists that are ALWAYS
+         * kept in a consistent state, so can be read without locking.
+         * Next fields of nodes are immutable (final).  All list
+         * additions are performed at the front of each bin. This
+         * makes it easy to check changes, and also fast to traverse.
+         * When nodes would otherwise be changed, new nodes are
+         * created to replace them. This works well for hash tables
+         * since the bin lists tend to be short. (The average length
+         * is less than two for the default load factor threshold.)
+         *
+         * Read operations can thus proceed without locking, but rely
+         * on selected uses of volatiles to ensure that completed
+         * write operations performed by other threads are
+         * noticed. For most purposes, the "count" field, tracking the
+         * number of elements, serves as that volatile variable
+         * ensuring visibility.  This is convenient because this field
+         * needs to be read in many read operations anyway:
+         *
+         *   - All (unsynchronized) read operations must first read the
+         *     "count" field, and should not look at table entries if
+         *     it is 0.
+         *
+         *   - All (synchronized) write operations should write to
+         *     the "count" field after structurally changing any bin.
+         *     The operations must not take any action that could even
+         *     momentarily cause a concurrent read operation to see
+         *     inconsistent data. This is made easier by the nature of
+         *     the read operations in Map. For example, no operation
+         *     can reveal that the table has grown but the threshold
+         *     has not yet been updated, so there are no atomicity
+         *     requirements for this with respect to reads.
+         *
+         * As a guide, all critical volatile reads and writes to the
+         * count field are marked in code comments.
+         */
+
+        private static final long serialVersionUID = 2249069246763182397L;
+
+        /**
+         * The number of elements in this segment's region.
+         */
+        transient volatile int count;
+
+        /**
+         * Number of updates that alter the size of the table. This is
+         * used during bulk-read methods to make sure they see a
+         * consistent snapshot: If modCounts change during a traversal
+         * of segments computing size or checking containsValue, then
+         * we might have an inconsistent view of state so (usually)
+         * must retry.
+         */
+        transient int modCount;
+
+        /**
+         * The table is rehashed when its size exceeds this threshold.
+         * (The value of this field is always <tt>(int)(capacity *
+         * loadFactor)</tt>.)
+         */
+        transient int threshold;
+
+        /**
+         * The per-segment table.
+         */
+        transient volatile HashEntry<K,V>[] table;
+
+        /**
+         * The load factor for the hash table.  Even though this value
+         * is same for all segments, it is replicated to avoid needing
+         * links to outer object.
+         * @serial
+         */
+        final float loadFactor;
+
+        /**
+         * The collected weak-key reference queue for this segment.
+         * This should be (re)initialized whenever table is assigned,
+         */
+        transient volatile ReferenceQueue<Object> refQueue;
+
+        final ReferenceType keyType;
+
+        final ReferenceType valueType;
+
+        final boolean identityComparisons;
+
+        Segment(int initialCapacity, float lf, ReferenceType keyType,
+                ReferenceType valueType, boolean identityComparisons) {
+            loadFactor = lf;
+            this.keyType = keyType;
+            this.valueType = valueType;
+            this.identityComparisons = identityComparisons;
+            setTable(HashEntry.<K,V>newArray(initialCapacity));
+        }
+
+        @SuppressWarnings("unchecked")
+        static final <K,V> Segment<K,V>[] newArray(int i) {
+            return new Segment[i];
+        }
+
+        private boolean keyEq(Object src, Object dest) {
+            return identityComparisons ? src == dest : src.equals(dest);
+        }
+
+        /**
+         * Sets table to new HashEntry array.
+         * Call only while holding lock or in constructor.
+         */
+        void setTable(HashEntry<K,V>[] newTable) {
+            threshold = (int)(newTable.length * loadFactor);
+            table = newTable;
+            refQueue = new ReferenceQueue<Object>();
+        }
+
+        /**
+         * Returns properly casted first entry of bin for given hash.
+         */
+        HashEntry<K,V> getFirst(int hash) {
+            HashEntry<K,V>[] tab = table;
+            return tab[hash & (tab.length - 1)];
+        }
+
+        HashEntry<K,V> newHashEntry(K key, int hash, HashEntry<K, V> next, V value) {
+            return new HashEntry<K,V>(key, hash, next, value, keyType, valueType, refQueue);
+        }
+
+        /**
+         * Reads value field of an entry under lock. Called if value
+         * field ever appears to be null. This is possible only if a
+         * compiler happens to reorder a HashEntry initialization with
+         * its table assignment, which is legal under memory model
+         * but is not known to ever occur.
+         */
+        V readValueUnderLock(HashEntry<K,V> e) {
+            lock();
+            try {
+                removeStale();
+                return e.value();
+            } finally {
+                unlock();
+            }
+        }
+
+        /* Specialized implementations of map methods */
+
+        V get(Object key, int hash) {
+            if (count != 0) { // read-volatile
+                HashEntry<K,V> e = getFirst(hash);
+                while (e != null) {
+                    if (e.hash == hash && keyEq(key, e.key())) {
+                        Object opaque = e.valueRef;
+                        if (opaque != null)
+                            return e.dereferenceValue(opaque);
+
+                        return readValueUnderLock(e);  // recheck
+                    }
+                    e = e.next;
+                }
+            }
+            return null;
+        }
+
+        boolean containsKey(Object key, int hash) {
+            if (count != 0) { // read-volatile
+                HashEntry<K,V> e = getFirst(hash);
+                while (e != null) {
+                    if (e.hash == hash && keyEq(key, e.key()))
+                        return true;
+                    e = e.next;
+                }
+            }
+            return false;
+        }
+
+        boolean containsValue(Object value) {
+            if (count != 0) { // read-volatile
+                HashEntry<K,V>[] tab = table;
+                int len = tab.length;
+                for (int i = 0 ; i < len; i++) {
+                    for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
+                        Object opaque = e.valueRef;
+                        V v;
+
+                        if (opaque == null)
+                            v = readValueUnderLock(e); // recheck
+                        else
+                            v = e.dereferenceValue(opaque);
+
+                        if (value.equals(v))
+                            return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        boolean replace(K key, int hash, V oldValue, V newValue) {
+            lock();
+            try {
+                removeStale();
+                HashEntry<K,V> e = getFirst(hash);
+                while (e != null && (e.hash != hash || !keyEq(key, e.key())))
+                    e = e.next;
+
+                boolean replaced = false;
+                if (e != null && oldValue.equals(e.value())) {
+                    replaced = true;
+                    e.setValue(newValue, valueType, refQueue);
+                }
+                return replaced;
+            } finally {
+                unlock();
+            }
+        }
+
+        V replace(K key, int hash, V newValue) {
+            lock();
+            try {
+                removeStale();
+                HashEntry<K,V> e = getFirst(hash);
+                while (e != null && (e.hash != hash || !keyEq(key, e.key())))
+                    e = e.next;
+
+                V oldValue = null;
+                if (e != null) {
+                    oldValue = e.value();
+                    e.setValue(newValue, valueType, refQueue);
+                }
+                return oldValue;
+            } finally {
+                unlock();
+            }
+        }
+
+
+        V put(K key, int hash, V value, boolean onlyIfAbsent) {
+            lock();
+            try {
+                removeStale();
+                int c = count;
+                if (c++ > threshold) {// ensure capacity
+                    int reduced = rehash();
+                    if (reduced > 0)  // adjust from possible weak cleanups
+                        count = (c -= reduced) - 1; // write-volatile
+                }
+
+                HashEntry<K,V>[] tab = table;
+                int index = hash & (tab.length - 1);
+                HashEntry<K,V> first = tab[index];
+                HashEntry<K,V> e = first;
+                while (e != null && (e.hash != hash || !keyEq(key, e.key())))
+                    e = e.next;
+
+                V oldValue;
+                if (e != null) {
+                    oldValue = e.value();
+                    if (!onlyIfAbsent)
+                        e.setValue(value, valueType, refQueue);
+                }
+                else {
+                    oldValue = null;
+                    ++modCount;
+                    tab[index] = newHashEntry(key, hash, first, value);
+                    count = c; // write-volatile
+                }
+                return oldValue;
+            } finally {
+                unlock();
+            }
+        }
+
+        int rehash() {
+            HashEntry<K,V>[] oldTable = table;
+            int oldCapacity = oldTable.length;
+            if (oldCapacity >= MAXIMUM_CAPACITY)
+                return 0;
+
+            /*
+             * Reclassify nodes in each list to new Map.  Because we are
+             * using power-of-two expansion, the elements from each bin
+             * must either stay at same index, or move with a power of two
+             * offset. We eliminate unnecessary node creation by catching
+             * cases where old nodes can be reused because their next
+             * fields won't change. Statistically, at the default
+             * threshold, only about one-sixth of them need cloning when
+             * a table doubles. The nodes they replace will be garbage
+             * collectable as soon as they are no longer referenced by any
+             * reader thread that may be in the midst of traversing table
+             * right now.
+             */
+
+            HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1);
+            threshold = (int)(newTable.length * loadFactor);
+            int sizeMask = newTable.length - 1;
+            int reduce = 0;
+            for (int i = 0; i < oldCapacity ; i++) {
+                // We need to guarantee that any existing reads of old Map can
+                //  proceed. So we cannot yet null out each bin.
+                HashEntry<K,V> e = oldTable[i];
+
+                if (e != null) {
+                    HashEntry<K,V> next = e.next;
+                    int idx = e.hash & sizeMask;
+
+                    //  Single node on list
+                    if (next == null)
+                        newTable[idx] = e;
+
+                    else {
+                        // Reuse trailing consecutive sequence at same slot
+                        HashEntry<K,V> lastRun = e;
+                        int lastIdx = idx;
+                        for (HashEntry<K,V> last = next;
+                             last != null;
+                             last = last.next) {
+                            int k = last.hash & sizeMask;
+                            if (k != lastIdx) {
+                                lastIdx = k;
+                                lastRun = last;
+                            }
+                        }
+                        newTable[lastIdx] = lastRun;
+                        // Clone all remaining nodes
+                        for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
+                            // Skip GC'd weak refs
+                            K key = p.key();
+                            if (key == null) {
+                                reduce++;
+                                continue;
+                            }
+                            int k = p.hash & sizeMask;
+                            HashEntry<K,V> n = newTable[k];
+                            newTable[k] = newHashEntry(key, p.hash, n, p.value());
+                        }
+                    }
+                }
+            }
+            table = newTable;
+            return reduce;
+        }
+
+        /**
+         * Remove; match on key only if value null, else match both.
+         */
+        V remove(Object key, int hash, Object value, boolean refRemove) {
+            lock();
+            try {
+                if (!refRemove)
+                    removeStale();
+                int c = count - 1;
+                HashEntry<K,V>[] tab = table;
+                int index = hash & (tab.length - 1);
+                HashEntry<K,V> first = tab[index];
+                HashEntry<K,V> e = first;
+                // a ref remove operation compares the Reference instance
+                while (e != null && key != e.keyRef
+                                 && (refRemove || hash != e.hash || !keyEq(key, e.key())))
+                    e = e.next;
+
+                V oldValue = null;
+                if (e != null) {
+                    V v = e.value();
+                    if (value == null || value.equals(v)) {
+                        oldValue = v;
+                        // All entries following removed node can stay
+                        // in list, but all preceding ones need to be
+                        // cloned.
+                        ++modCount;
+                        HashEntry<K,V> newFirst = e.next;
+                        for (HashEntry<K,V> p = first; p != e; p = p.next) {
+                            K pKey = p.key();
+                            if (pKey == null) { // Skip GC'd keys
+                                c--;
+                                continue;
+                            }
+
+                            newFirst = newHashEntry(pKey, p.hash, newFirst, p.value());
+                        }
+                        tab[index] = newFirst;
+                        count = c; // write-volatile
+                    }
+                }
+                return oldValue;
+            } finally {
+                unlock();
+            }
+        }
+
+        final void removeStale() {
+            KeyReference ref;
+            while ((ref = (KeyReference) refQueue.poll()) != null) {
+                remove(ref.keyRef(), ref.keyHash(), null, true);
+            }
+        }
+
+        void clear() {
+            if (count != 0) {
+                lock();
+                try {
+                    HashEntry<K,V>[] tab = table;
+                    for (int i = 0; i < tab.length ; i++)
+                        tab[i] = null;
+                    ++modCount;
+                    // replace the reference queue to avoid unnecessary stale cleanups
+                    refQueue = new ReferenceQueue<Object>();
+                    count = 0; // write-volatile
+                } finally {
+                    unlock();
+                }
+            }
+        }
+    }
+
+
+
+    /* ---------------- Public operations -------------- */
+
+    /**
+     * Creates a new, empty map with the specified initial
+     * capacity, reference types, load factor and concurrency level.
+     *
+     * Behavioral changing options such as {@link Option#IDENTITY_COMPARISONS}
+     * can also be specified.
+     *
+     * @param initialCapacity the initial capacity. The implementation
+     * performs internal sizing to accommodate this many elements.
+     * @param loadFactor  the load factor threshold, used to control resizing.
+     * Resizing may be performed when the average number of elements per
+     * bin exceeds this threshold.
+     * @param concurrencyLevel the estimated number of concurrently
+     * updating threads. The implementation performs internal sizing
+     * to try to accommodate this many threads.
+     * @param keyType the reference type to use for keys
+     * @param valueType the reference type to use for values
+     * @param options the behavioral options
+     * @throws IllegalArgumentException if the initial capacity is
+     * negative or the load factor or concurrencyLevel are
+     * nonpositive.
+     */
+    public ConcurrentReferenceHashMap(int initialCapacity,
+                             float loadFactor, int concurrencyLevel,
+                             ReferenceType keyType, ReferenceType valueType,
+                             EnumSet<Option> options) {
+        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
+            throw new IllegalArgumentException();
+
+        if (concurrencyLevel > MAX_SEGMENTS)
+            concurrencyLevel = MAX_SEGMENTS;
+
+        // Find power-of-two sizes best matching arguments
+        int sshift = 0;
+        int ssize = 1;
+        while (ssize < concurrencyLevel) {
+            ++sshift;
+            ssize <<= 1;
+        }
+        segmentShift = 32 - sshift;
+        segmentMask = ssize - 1;
+        this.segments = Segment.newArray(ssize);
+
+        if (initialCapacity > MAXIMUM_CAPACITY)
+            initialCapacity = MAXIMUM_CAPACITY;
+        int c = initialCapacity / ssize;
+        if (c * ssize < initialCapacity)
+            ++c;
+        int cap = 1;
+        while (cap < c)
+            cap <<= 1;
+
+        identityComparisons = options != null && options.contains(Option.IDENTITY_COMPARISONS);
+
+        for (int i = 0; i < this.segments.length; ++i)
+            this.segments[i] = new Segment<K,V>(cap, loadFactor,
+                    keyType, valueType, identityComparisons);
+    }
+
+    /**
+     * Creates a new, empty map with the specified initial
+     * capacity, load factor and concurrency level.
+     *
+     * @param initialCapacity the initial capacity. The implementation
+     * performs internal sizing to accommodate this many elements.
+     * @param loadFactor  the load factor threshold, used to control resizing.
+     * Resizing may be performed when the average number of elements per
+     * bin exceeds this threshold.
+     * @param concurrencyLevel the estimated number of concurrently
+     * updating threads. The implementation performs internal sizing
+     * to try to accommodate this many threads.
+     * @throws IllegalArgumentException if the initial capacity is
+     * negative or the load factor or concurrencyLevel are
+     * nonpositive.
+     */
+    public ConcurrentReferenceHashMap(int initialCapacity,
+                             float loadFactor, int concurrencyLevel) {
+        this(initialCapacity, loadFactor, concurrencyLevel,
+                DEFAULT_KEY_TYPE, DEFAULT_VALUE_TYPE, null);
+    }
+
+    /**
+     * Creates a new, empty map with the specified initial capacity
+     * and load factor and with the default reference types (weak keys,
+     * strong values), and concurrencyLevel (16).
+     *
+     * @param initialCapacity The implementation performs internal
+     * sizing to accommodate this many elements.
+     * @param loadFactor  the load factor threshold, used to control resizing.
+     * Resizing may be performed when the average number of elements per
+     * bin exceeds this threshold.
+     * @throws IllegalArgumentException if the initial capacity of
+     * elements is negative or the load factor is nonpositive
+     *
+     * @since 1.6
+     */
+    public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor) {
+        this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
+    }
+
+
+    /**
+     * Creates a new, empty map with the specified initial capacity,
+     * reference types and with default load factor (0.75) and concurrencyLevel (16).
+     *
+     * @param initialCapacity the initial capacity. The implementation
+     * performs internal sizing to accommodate this many elements.
+     * @param keyType the reference type to use for keys
+     * @param valueType the reference type to use for values
+     * @throws IllegalArgumentException if the initial capacity of
+     * elements is negative.
+     */
+    public ConcurrentReferenceHashMap(int initialCapacity,
+            ReferenceType keyType, ReferenceType valueType) {
+        this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL,
+                keyType, valueType, null);
+    }
+
+    /**
+     * Creates a new, empty map with the specified initial capacity,
+     * and with default reference types (weak keys, strong values),
+     * load factor (0.75) and concurrencyLevel (16).
+     *
+     * @param initialCapacity the initial capacity. The implementation
+     * performs internal sizing to accommodate this many elements.
+     * @throws IllegalArgumentException if the initial capacity of
+     * elements is negative.
+     */
+    public ConcurrentReferenceHashMap(int initialCapacity) {
+        this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
+    }
+
+    /**
+     * Creates a new, empty map with a default initial capacity (16),
+     * reference types (weak keys, strong values), default
+     * load factor (0.75) and concurrencyLevel (16).
+     */
+    public ConcurrentReferenceHashMap() {
+        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
+    }
+
+    /**
+     * Creates a new map with the same mappings as the given map.
+     * The map is created with a capacity of 1.5 times the number
+     * of mappings in the given map or 16 (whichever is greater),
+     * and a default load factor (0.75) and concurrencyLevel (16).
+     *
+     * @param m the map
+     */
+    public ConcurrentReferenceHashMap(Map<? extends K, ? extends V> m) {
+        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
+                      DEFAULT_INITIAL_CAPACITY),
+             DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
+        putAll(m);
+    }
+
+    /**
+     * Returns <tt>true</tt> if this map contains no key-value mappings.
+     *
+     * @return <tt>true</tt> if this map contains no key-value mappings
+     */
+    public boolean isEmpty() {
+        final Segment<K,V>[] segments = this.segments;
+        /*
+         * We keep track of per-segment modCounts to avoid ABA
+         * problems in which an element in one segment was added and
+         * in another removed during traversal, in which case the
+         * table was never actually empty at any point. Note the
+         * similar use of modCounts in the size() and containsValue()
+         * methods, which are the only other methods also susceptible
+         * to ABA problems.
+         */
+        int[] mc = new int[segments.length];
+        int mcsum = 0;
+        for (int i = 0; i < segments.length; ++i) {
+            if (segments[i].count != 0)
+                return false;
+            else
+                mcsum += mc[i] = segments[i].modCount;
+        }
+        // If mcsum happens to be zero, then we know we got a snapshot
+        // before any modifications at all were made.  This is
+        // probably common enough to bother tracking.
+        if (mcsum != 0) {
+            for (int i = 0; i < segments.length; ++i) {
+                if (segments[i].count != 0 ||
+                    mc[i] != segments[i].modCount)
+                    return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns the number of key-value mappings in this map.  If the
+     * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
+     * <tt>Integer.MAX_VALUE</tt>.
+     *
+     * @return the number of key-value mappings in this map
+     */
+    public int size() {
+        final Segment<K,V>[] segments = this.segments;
+        long sum = 0;
+        long check = 0;
+        int[] mc = new int[segments.length];
+        // Try a few times to get accurate count. On failure due to
+        // continuous async changes in table, resort to locking.
+        for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
+            check = 0;
+            sum = 0;
+            int mcsum = 0;
+            for (int i = 0; i < segments.length; ++i) {
+                sum += segments[i].count;
+                mcsum += mc[i] = segments[i].modCount;
+            }
+            if (mcsum != 0) {
+                for (int i = 0; i < segments.length; ++i) {
+                    check += segments[i].count;
+                    if (mc[i] != segments[i].modCount) {
+                        check = -1; // force retry
+                        break;
+                    }
+                }
+            }
+            if (check == sum)
+                break;
+        }
+        if (check != sum) { // Resort to locking all segments
+            sum = 0;
+            for (int i = 0; i < segments.length; ++i)
+                segments[i].lock();
+            for (int i = 0; i < segments.length; ++i)
+                sum += segments[i].count;
+            for (int i = 0; i < segments.length; ++i)
+                segments[i].unlock();
+        }
+        if (sum > Integer.MAX_VALUE)
+            return Integer.MAX_VALUE;
+        else
+            return (int)sum;
+    }
+
+    /**
+     * Returns the value to which the specified key is mapped,
+     * or {@code null} if this map contains no mapping for the key.
+     *
+     * <p>More formally, if this map contains a mapping from a key
+     * {@code k} to a value {@code v} such that {@code key.equals(k)},
+     * then this method returns {@code v}; otherwise it returns
+     * {@code null}.  (There can be at most one such mapping.)
+     *
+     * @throws NullPointerException if the specified key is null
+     */
+    public V get(Object key) {
+        int hash = hashOf(key);
+        return segmentFor(hash).get(key, hash);
+    }
+
+    /**
+     * Tests if the specified object is a key in this table.
+     *
+     * @param  key   possible key
+     * @return <tt>true</tt> if and only if the specified object
+     *         is a key in this table, as determined by the
+     *         <tt>equals</tt> method; <tt>false</tt> otherwise.
+     * @throws NullPointerException if the specified key is null
+     */
+    public boolean containsKey(Object key) {
+        int hash = hashOf(key);
+        return segmentFor(hash).containsKey(key, hash);
+    }
+
+    /**
+     * Returns <tt>true</tt> if this map maps one or more keys to the
+     * specified value. Note: This method requires a full internal
+     * traversal of the hash table, and so is much slower than
+     * method <tt>containsKey</tt>.
+     *
+     * @param value value whose presence in this map is to be tested
+     * @return <tt>true</tt> if this map maps one or more keys to the
+     *         specified value
+     * @throws NullPointerException if the specified value is null
+     */
+    public boolean containsValue(Object value) {
+        if (value == null)
+            throw new NullPointerException();
+
+        // See explanation of modCount use above
+
+        final Segment<K,V>[] segments = this.segments;
+        int[] mc = new int[segments.length];
+
+        // Try a few times without locking
+        for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
+            int sum = 0;
+            int mcsum = 0;
+            for (int i = 0; i < segments.length; ++i) {
+                int c = segments[i].count;
+                mcsum += mc[i] = segments[i].modCount;
+                if (segments[i].containsValue(value))
+                    return true;
+            }
+            boolean cleanSweep = true;
+            if (mcsum != 0) {
+                for (int i = 0; i < segments.length; ++i) {
+                    int c = segments[i].count;
+                    if (mc[i] != segments[i].modCount) {
+                        cleanSweep = false;
+                        break;
+                    }
+                }
+            }
+            if (cleanSweep)
+                return false;
+        }
+        // Resort to locking all segments
+        for (int i = 0; i < segments.length; ++i)
+            segments[i].lock();
+        boolean found = false;
+        try {
+            for (int i = 0; i < segments.length; ++i) {
+                if (segments[i].containsValue(value)) {
+                    found = true;
+                    break;
+                }
+            }
+        } finally {
+            for (int i = 0; i < segments.length; ++i)
+                segments[i].unlock();
+        }
+        return found;
+    }
+
+    /**
+     * Legacy method testing if some key maps into the specified value
+     * in this table.  This method is identical in functionality to
+     * {@link #containsValue}, and exists solely to ensure
+     * full compatibility with class {@link java.util.Hashtable},
+     * which supported this method prior to introduction of the
+     * Java Collections framework.
+
+     * @param  value a value to search for
+     * @return <tt>true</tt> if and only if some key maps to the
+     *         <tt>value</tt> argument in this table as
+     *         determined by the <tt>equals</tt> method;
+     *         <tt>false</tt> otherwise
+     * @throws NullPointerException if the specified value is null
+     */
+    public boolean contains(Object value) {
+        return containsValue(value);
+    }
+
+    /**
+     * Maps the specified key to the specified value in this table.
+     * Neither the key nor the value can be null.
+     *
+     * <p> The value can be retrieved by calling the <tt>get</tt> method
+     * with a key that is equal to the original key.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param value value to be associated with the specified key
+     * @return the previous value associated with <tt>key</tt>, or
+     *         <tt>null</tt> if there was no mapping for <tt>key</tt>
+     * @throws NullPointerException if the specified key or value is null
+     */
+    public V put(K key, V value) {
+        if (value == null)
+            throw new NullPointerException();
+        int hash = hashOf(key);
+        return segmentFor(hash).put(key, hash, value, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the previous value associated with the specified key,
+     *         or <tt>null</tt> if there was no mapping for the key
+     * @throws NullPointerException if the specified key or value is null
+     */
+    public V putIfAbsent(K key, V value) {
+        if (value == null)
+            throw new NullPointerException();
+        int hash = hashOf(key);
+        return segmentFor(hash).put(key, hash, value, true);
+    }
+
+    /**
+     * Copies all of the mappings from the specified map to this one.
+     * These mappings replace any mappings that this map had for any of the
+     * keys currently in the specified map.
+     *
+     * @param m mappings to be stored in this map
+     */
+    public void putAll(Map<? extends K, ? extends V> m) {
+        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
+            put(e.getKey(), e.getValue());
+    }
+
+    /**
+     * Removes the key (and its corresponding value) from this map.
+     * This method does nothing if the key is not in the map.
+     *
+     * @param  key the key that needs to be removed
+     * @return the previous value associated with <tt>key</tt>, or
+     *         <tt>null</tt> if there was no mapping for <tt>key</tt>
+     * @throws NullPointerException if the specified key is null
+     */
+    public V remove(Object key) {
+        int hash = hashOf(key);
+        return segmentFor(hash).remove(key, hash, null, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws NullPointerException if the specified key is null
+     */
+    public boolean remove(Object key, Object value) {
+        int hash = hashOf(key);
+        if (value == null)
+            return false;
+        return segmentFor(hash).remove(key, hash, value, false) != null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws NullPointerException if any of the arguments are null
+     */
+    public boolean replace(K key, V oldValue, V newValue) {
+        if (oldValue == null || newValue == null)
+            throw new NullPointerException();
+        int hash = hashOf(key);
+        return segmentFor(hash).replace(key, hash, oldValue, newValue);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the previous value associated with the specified key,
+     *         or <tt>null</tt> if there was no mapping for the key
+     * @throws NullPointerException if the specified key or value is null
+     */
+    public V replace(K key, V value) {
+        if (value == null)
+            throw new NullPointerException();
+        int hash = hashOf(key);
+        return segmentFor(hash).replace(key, hash, value);
+    }
+
+    /**
+     * Removes all of the mappings from this map.
+     */
+    public void clear() {
+        for (int i = 0; i < segments.length; ++i)
+            segments[i].clear();
+    }
+
+    /**
+     * Removes any stale entries whose keys have been finalized. Use of this
+     * method is normally not necessary since stale entries are automatically
+     * removed lazily, when blocking operations are required. However, there
+     * are some cases where this operation should be performed eagerly, such
+     * as cleaning up old references to a ClassLoader in a multi-classloader
+     * environment.
+     *
+     * Note: this method will acquire locks, one at a time, across all segments
+     * of this table, so if it is to be used, it should be used sparingly.
+     */
+    public void purgeStaleEntries() {
+        for (int i = 0; i < segments.length; ++i)
+            segments[i].removeStale();
+    }
+
+
+    /**
+     * Returns a {@link Set} view of the keys contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  The set supports element
+     * removal, which removes the corresponding mapping from this map,
+     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+     * operations.  It does not support the <tt>add</tt> or
+     * <tt>addAll</tt> operations.
+     *
+     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     */
+    public Set<K> keySet() {
+        Set<K> ks = keySet;
+        return (ks != null) ? ks : (keySet = new KeySet());
+    }
+
+    /**
+     * Returns a {@link Collection} view of the values contained in this map.
+     * The collection is backed by the map, so changes to the map are
+     * reflected in the collection, and vice-versa.  The collection
+     * supports element removal, which removes the corresponding
+     * mapping from this map, via the <tt>Iterator.remove</tt>,
+     * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
+     * <tt>retainAll</tt>, and <tt>clear</tt> operations.  It does not
+     * support the <tt>add</tt> or <tt>addAll</tt> operations.
+     *
+     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     */
+    public Collection<V> values() {
+        Collection<V> vs = values;
+        return (vs != null) ? vs : (values = new Values());
+    }
+
+    /**
+     * Returns a {@link Set} view of the mappings contained in this map.
+     * The set is backed by the map, so changes to the map are
+     * reflected in the set, and vice-versa.  The set supports element
+     * removal, which removes the corresponding mapping from the map,
+     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+     * operations.  It does not support the <tt>add</tt> or
+     * <tt>addAll</tt> operations.
+     *
+     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
+     * that will never throw {@link ConcurrentModificationException},
+     * and guarantees to traverse elements as they existed upon
+     * construction of the iterator, and may (but is not guaranteed to)
+     * reflect any modifications subsequent to construction.
+     */
+    public Set<Map.Entry<K,V>> entrySet() {
+        Set<Map.Entry<K,V>> es = entrySet;
+        return (es != null) ? es : (entrySet = new EntrySet());
+    }
+
+    /**
+     * Returns an enumeration of the keys in this table.
+     *
+     * @return an enumeration of the keys in this table
+     * @see #keySet()
+     */
+    public Enumeration<K> keys() {
+        return new KeyIterator();
+    }
+
+    /**
+     * Returns an enumeration of the values in this table.
+     *
+     * @return an enumeration of the values in this table
+     * @see #values()
+     */
+    public Enumeration<V> elements() {
+        return new ValueIterator();
+    }
+
+    /* ---------------- Iterator Support -------------- */
+
+    abstract class HashIterator {
+        int nextSegmentIndex;
+        int nextTableIndex;
+        HashEntry<K,V>[] currentTable;
+        HashEntry<K, V> nextEntry;
+        HashEntry<K, V> lastReturned;
+        K currentKey; // Strong reference to weak key (prevents gc)
+
+        HashIterator() {
+            nextSegmentIndex = segments.length - 1;
+            nextTableIndex = -1;
+            advance();
+        }
+
+        public boolean hasMoreElements() { return hasNext(); }
+
+        final void advance() {
+            if (nextEntry != null && (nextEntry = nextEntry.next) != null)
+                return;
+
+            while (nextTableIndex >= 0) {
+                if ( (nextEntry = currentTable[nextTableIndex--]) != null)
+                    return;
+            }
+
+            while (nextSegmentIndex >= 0) {
+                Segment<K,V> seg = segments[nextSegmentIndex--];
+                if (seg.count != 0) {
+                    currentTable = seg.table;
+                    for (int j = currentTable.length - 1; j >= 0; --j) {
+                        if ( (nextEntry = currentTable[j]) != null) {
+                            nextTableIndex = j - 1;
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+
+        public boolean hasNext() {
+            while (nextEntry != null) {
+                if (nextEntry.key() != null)
+                    return true;
+                advance();
+            }
+
+            return false;
+        }
+
+        HashEntry<K,V> nextEntry() {
+            do {
+                if (nextEntry == null)
+                    throw new NoSuchElementException();
+
+                lastReturned = nextEntry;
+                currentKey = lastReturned.key();
+                advance();
+            } while (currentKey == null); // Skip GC'd keys
+
+            return lastReturned;
+        }
+
+        public void remove() {
+            if (lastReturned == null)
+                throw new IllegalStateException();
+            ConcurrentReferenceHashMap.this.remove(currentKey);
+            lastReturned = null;
+        }
+    }
+
+    final class KeyIterator
+        extends HashIterator
+        implements Iterator<K>, Enumeration<K>
+    {
+        public K next()        { return super.nextEntry().key(); }
+        public K nextElement() { return super.nextEntry().key(); }
+    }
+
+    final class ValueIterator
+        extends HashIterator
+        implements Iterator<V>, Enumeration<V>
+    {
+        public V next()        { return super.nextEntry().value(); }
+        public V nextElement() { return super.nextEntry().value(); }
+    }
+
+     /*
+      * This class is needed for JDK5 compatibility.
+      */
+     static class SimpleEntry<K, V> implements Entry<K, V>,
+            java.io.Serializable {
+        private static final long serialVersionUID = -8499721149061103585L;
+
+        private final K key;
+        private V value;
+
+        public SimpleEntry(K key, V value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        public SimpleEntry(Entry<? extends K, ? extends V> entry) {
+            this.key = entry.getKey();
+            this.value = entry.getValue();
+        }
+
+        public K getKey() {
+            return key;
+        }
+
+        public V getValue() {
+            return value;
+        }
+
+        public V setValue(V value) {
+            V oldValue = this.value;
+            this.value = value;
+            return oldValue;
+        }
+
+        public boolean equals(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            @SuppressWarnings("unchecked")
+            Map.Entry e = (Map.Entry) o;
+            return eq(key, e.getKey()) && eq(value, e.getValue());
+        }
+
+        public int hashCode() {
+            return (key == null ? 0 : key.hashCode())
+                    ^ (value == null ? 0 : value.hashCode());
+        }
+
+        public String toString() {
+            return key + "=" + value;
+        }
+
+        private static boolean eq(Object o1, Object o2) {
+            return o1 == null ? o2 == null : o1.equals(o2);
+        }
+    }
+
+
+    /**
+     * Custom Entry class used by EntryIterator.next(), that relays setValue
+     * changes to the underlying map.
+     */
+    final class WriteThroughEntry extends SimpleEntry<K,V>
+    {
+        private static final long serialVersionUID = -7900634345345313646L;
+
+        WriteThroughEntry(K k, V v) {
+            super(k,v);
+        }
+
+        /**
+         * Set our entry's value and write through to the map. The
+         * value to return is somewhat arbitrary here. Since a
+         * WriteThroughEntry does not necessarily track asynchronous
+         * changes, the most recent "previous" value could be
+         * different from what we return (or could even have been
+         * removed in which case the put will re-establish). We do not
+         * and cannot guarantee more.
+         */
+        public V setValue(V value) {
+            if (value == null) throw new NullPointerException();
+            V v = super.setValue(value);
+            ConcurrentReferenceHashMap.this.put(getKey(), value);
+            return v;
+        }
+    }
+
+    final class EntryIterator
+        extends HashIterator
+        implements Iterator<Entry<K,V>>
+    {
+        public Map.Entry<K,V> next() {
+            HashEntry<K,V> e = super.nextEntry();
+            return new WriteThroughEntry(e.key(), e.value());
+        }
+    }
+
+    final class KeySet extends AbstractSet<K> {
+        public Iterator<K> iterator() {
+            return new KeyIterator();
+        }
+        public int size() {
+            return ConcurrentReferenceHashMap.this.size();
+        }
+        public boolean isEmpty() {
+            return ConcurrentReferenceHashMap.this.isEmpty();
+        }
+        public boolean contains(Object o) {
+            return ConcurrentReferenceHashMap.this.containsKey(o);
+        }
+        public boolean remove(Object o) {
+            return ConcurrentReferenceHashMap.this.remove(o) != null;
+        }
+        public void clear() {
+            ConcurrentReferenceHashMap.this.clear();
+        }
+    }
+
+    final class Values extends AbstractCollection<V> {
+        public Iterator<V> iterator() {
+            return new ValueIterator();
+        }
+        public int size() {
+            return ConcurrentReferenceHashMap.this.size();
+        }
+        public boolean isEmpty() {
+            return ConcurrentReferenceHashMap.this.isEmpty();
+        }
+        public boolean contains(Object o) {
+            return ConcurrentReferenceHashMap.this.containsValue(o);
+        }
+        public void clear() {
+            ConcurrentReferenceHashMap.this.clear();
+        }
+    }
+
+    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
+        public Iterator<Map.Entry<K,V>> iterator() {
+            return new EntryIterator();
+        }
+        public boolean contains(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+            V v = ConcurrentReferenceHashMap.this.get(e.getKey());
+            return v != null && v.equals(e.getValue());
+        }
+        public boolean remove(Object o) {
+            if (!(o instanceof Map.Entry))
+                return false;
+            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+            return ConcurrentReferenceHashMap.this.remove(e.getKey(), e.getValue());
+        }
+        public int size() {
+            return ConcurrentReferenceHashMap.this.size();
+        }
+        public boolean isEmpty() {
+            return ConcurrentReferenceHashMap.this.isEmpty();
+        }
+        public void clear() {
+            ConcurrentReferenceHashMap.this.clear();
+        }
+    }
+
+    /* ---------------- Serialization Support -------------- */
+
+    /**
+     * Save the state of the <tt>ConcurrentReferenceHashMap</tt> instance to a
+     * stream (i.e., serialize it).
+     * @param s the stream
+     * @serialData
+     * the key (Object) and value (Object)
+     * for each key-value mapping, followed by a null pair.
+     * The key-value mappings are emitted in no particular order.
+     */
+    private void writeObject(java.io.ObjectOutputStream s) throws IOException  {
+        s.defaultWriteObject();
+
+        for (int k = 0; k < segments.length; ++k) {
+            Segment<K,V> seg = segments[k];
+            seg.lock();
+            try {
+                HashEntry<K,V>[] tab = seg.table;
+                for (int i = 0; i < tab.length; ++i) {
+                    for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) {
+                        K key = e.key();
+                        if (key == null) // Skip GC'd keys
+                            continue;
+
+                        s.writeObject(key);
+                        s.writeObject(e.value());
+                    }
+                }
+            } finally {
+                seg.unlock();
+            }
+        }
+        s.writeObject(null);
+        s.writeObject(null);
+    }
+
+    /**
+     * Reconstitute the <tt>ConcurrentReferenceHashMap</tt> instance from a
+     * stream (i.e., deserialize it).
+     * @param s the stream
+     */
+    @SuppressWarnings("unchecked")
+    private void readObject(java.io.ObjectInputStream s)
+        throws IOException, ClassNotFoundException  {
+        s.defaultReadObject();
+
+        // Initialize each segment to be minimally sized, and let grow.
+        for (int i = 0; i < segments.length; ++i) {
+            segments[i].setTable(new HashEntry[1]);
+        }
+
+        // Read the keys and values, and put the mappings in the table
+        for (;;) {
+            K key = (K) s.readObject();
+            V value = (V) s.readObject();
+            if (key == null)
+                break;
+            put(key, value);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/HibernateVersion.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/HibernateVersion.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/HibernateVersion.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,46 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+import org.hibernate.cfg.Environment;
+import org.hibernate.MappingException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class HibernateVersion {
+    private static String version;
+
+    public static String get() {
+        if (version == null) {
+            try {
+                version = (String) Environment.class.getField("VERSION").get(null);
+            } catch (IllegalAccessException e) {
+                throw new MappingException(e);
+            } catch (NoSuchFieldException e) {
+                throw new MappingException(e);
+            }
+        }
+
+        return version;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/MutableBoolean.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/MutableBoolean.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/MutableBoolean.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,48 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MutableBoolean {
+    private boolean value;
+
+    public MutableBoolean() {
+    }
+
+    public MutableBoolean(boolean value) {
+        this.value = value;
+    }
+
+    public boolean isSet() {
+        return value;
+    }
+
+    public void set() {
+        value = true;
+    }
+
+    public void unset() {
+        value = false;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/MutableInteger.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/MutableInteger.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/MutableInteger.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,36 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MutableInteger {
+    private int value;
+
+    public MutableInteger() {
+    }
+
+    public int getAndIncrease() {
+        return value++;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/Pair.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/Pair.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/Pair.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,69 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+/**
+ * A pair of objects.
+ * @param <T1>
+ * @param <T2>
+ * @author Adam Warski (adamw at aster.pl)
+ */
+public class Pair<T1, T2> {
+    private T1 obj1;
+    private T2 obj2;
+
+    public Pair(T1 obj1, T2 obj2) {
+        this.obj1 = obj1;
+        this.obj2 = obj2;
+    }
+
+    public T1 getFirst() {
+        return obj1;
+    }
+
+    public T2 getSecond() {
+        return obj2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Pair)) return false;
+
+        Pair pair = (Pair) o;
+
+        if (obj1 != null ? !obj1.equals(pair.obj1) : pair.obj1 != null) return false;
+        if (obj2 != null ? !obj2.equals(pair.obj2) : pair.obj2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (obj1 != null ? obj1.hashCode() : 0);
+        result = 31 * result + (obj2 != null ? obj2.hashCode() : 0);
+        return result;
+    }
+
+    public static <T1, T2> Pair<T1, T2> make(T1 obj1, T2 obj2) {
+        return new Pair<T1, T2>(obj1, obj2);
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/StringTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/StringTools.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/StringTools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,72 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class StringTools {
+    public static boolean isEmpty(String s) {
+        return s == null || "".equals(s);
+    }
+
+    /**
+     * @param s String, from which to get the last component.
+     * @return The last component of the dot-separated string <code>s</code>. For example, for a string
+     * "a.b.c", the result is "c".
+     */
+    public static String getLastComponent(String s) {
+        if (s == null) {
+            return null;
+        }
+
+        int lastDot = s.lastIndexOf(".");
+        if (lastDot == -1) {
+            return s;
+        } else {
+            return s.substring(lastDot + 1);
+        }
+    }
+
+    /**
+     * To the given string builder, appends all strings in the given iterator, separating them with the given
+     * separator. For example, for an interator "a" "b" "c" and separator ":" the output is "a:b:c".
+     * @param sb String builder, to which to append.
+     * @param contents Strings to be appended.
+     * @param separator Separator between subsequent content.
+     */
+    public static void append(StringBuilder sb, Iterator<String> contents, String separator) {
+        boolean isFirst = true;
+
+        while (contents.hasNext()) {
+            if (!isFirst) {
+                sb.append(separator);
+            }
+
+            sb.append(contents.next());
+            
+            isFirst = false;
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/Tools.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/Tools.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/Tools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,79 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Tools {
+    public static <K,V> Map<K,V> newHashMap() {
+        return new HashMap<K,V>();
+    }
+
+    public static boolean objectsEqual(Object obj1, Object obj2) {
+        if (obj1 == null) {
+            return obj2 == null;
+        }
+
+        return obj1.equals(obj2);
+    }
+
+    public static <T> List<T> iteratorToList(Iterator<T> iter) {
+        List<T> ret = new ArrayList<T>();
+        while (iter.hasNext()) {
+            ret.add(iter.next());
+        }
+
+        return ret;
+    }
+
+    public static boolean iteratorsContentEqual(Iterator iter1, Iterator iter2) {
+        while (iter1.hasNext() && iter2.hasNext()) {
+            if (!iter1.next().equals(iter2.next())) {
+                return false;
+            }
+        }
+
+        if (iter1.hasNext() || iter2.hasNext()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Transforms a list of arbitrary elements to a list of index-element pairs. 
+     * @param list List to transform.
+     * @return A list of pairs: ((0, element_at_index_0), (1, element_at_index_1), ...)
+     */
+    public static <T> List<Pair<Integer, T>> listToIndexElementPairList(List<T> list) {
+        List<Pair<Integer, T>> ret = new ArrayList<Pair<Integer, T>>();
+        Iterator<T> listIter = list.iterator();
+        for (int i=0; i<list.size(); i++) {
+            ret.add(Pair.make(i, listIter.next()));
+        }
+
+        return ret;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/Triple.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/Triple.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/Triple.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,78 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools;
+
+/**
+ * A triple of objects.
+ * @param <T1>
+ * @param <T2>
+ * @param <T3>
+ * @author Adam Warski (adamw at aster.pl)
+ */
+public class Triple<T1, T2, T3> {
+    private T1 obj1;
+    private T2 obj2;
+    private T3 obj3;
+
+    public Triple(T1 obj1, T2 obj2, T3 obj3) {
+        this.obj1 = obj1;
+        this.obj2 = obj2;
+        this.obj3 = obj3;
+    }
+
+    public T1 getFirst() {
+        return obj1;
+    }
+
+    public T2 getSecond() {
+        return obj2;
+    }
+
+    public T3 getThird() {
+        return obj3;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Triple)) return false;
+
+        Triple triple = (Triple) o;
+
+        if (obj1 != null ? !obj1.equals(triple.obj1) : triple.obj1 != null) return false;
+        if (obj2 != null ? !obj2.equals(triple.obj2) : triple.obj2 != null) return false;
+        if (obj3 != null ? !obj3.equals(triple.obj3) : triple.obj3 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (obj1 != null ? obj1.hashCode() : 0);
+        result = 31 * result + (obj2 != null ? obj2.hashCode() : 0);
+        result = 31 * result + (obj3 != null ? obj3.hashCode() : 0);
+        return result;
+    }
+
+    public static <T1, T2, T3> Triple<T1, T2, T3> make(T1 obj1, T2 obj2, T3 obj3) {
+        return new Triple<T1, T2, T3>(obj1, obj2, obj3);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/GraphDefiner.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/GraphDefiner.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/GraphDefiner.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,36 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.graph;
+
+import java.util.List;
+
+/**
+ * Defines a graph, where each vertex has a representation, which identifies uniquely a value.
+ * Representations are comparable, values - not.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface GraphDefiner<V, R> {
+    R getRepresentation(V v);
+    V getValue(R r);
+    List<V> getNeighbours(V v);
+    List<V> getValues();
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/GraphTopologicalSort.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/GraphTopologicalSort.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/GraphTopologicalSort.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,66 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.graph;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class GraphTopologicalSort {
+    /**
+     * Sorts a graph topologically.
+     * @param definer Defines a graph (values and representations) to sort.
+     * @return Values of the graph, sorted topologically.
+     */
+    public static <V, R> List<V> sort(GraphDefiner<V, R> definer) {
+        List<V> values = definer.getValues();
+        Map<R, Vertex<R>> vertices = new HashMap<R, Vertex<R>>();
+
+        // Creating a vertex for each representation
+        for (V v : values) {
+            R rep = definer.getRepresentation(v);
+            vertices.put(rep, new Vertex<R>(rep));
+        }
+
+        // Connecting neighbourhooding vertices
+        for (V v : values) {
+            for (V vn : definer.getNeighbours(v)) {
+                vertices.get(definer.getRepresentation(v)).addNeighbour(vertices.get(definer.getRepresentation(vn)));
+            }
+        }
+
+        // Sorting the representations
+        List<R> sortedReps = new TopologicalSort<R>().sort(vertices.values());
+
+        // Transforming the sorted representations to sorted values 
+        List<V> sortedValues = new ArrayList<V>(sortedReps.size());
+        for (R rep : sortedReps) {
+            sortedValues.add(definer.getValue(rep));
+        }
+
+        return sortedValues;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/TopologicalSort.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/TopologicalSort.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/TopologicalSort.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,66 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.graph;
+
+import java.util.*;
+
+/**
+ * Topological sorting of a graph - based on DFS.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class TopologicalSort<R> {
+    private List<R> sorted;
+    private int time;
+
+    private void process(Vertex<R> v) {
+        if (v.getStartTime() != 0) {
+            // alread processed
+            return;
+        }
+
+        v.setStartTime(time++);
+
+        for (Vertex<R> n : v.getNeighbours()) {
+            process(n);
+        }
+
+        v.setEndTime(time++);
+
+        sorted.add(v.getRepresentation());
+    }
+
+    public List<R> sort(Collection<Vertex<R>> vertices) {
+        sorted = new ArrayList<R>(vertices.size());
+        
+        time = 1;
+
+        for (Vertex<R> v : vertices) {
+            if (v.getEndTime() == 0) {
+                process(v);
+            }
+        }
+
+        Collections.reverse(sorted);
+
+        return sorted;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/Vertex.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/Vertex.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/graph/Vertex.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,72 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.graph;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A graph vertex - stores its representation, neighbours, start and end time in (D|B)FS.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Vertex<R> {
+    private final R representation;
+    private final List<Vertex<R>> neighbours;
+    
+    private int startTime;
+    private int endTime;
+
+    public Vertex(R representation) {
+        this.representation = representation;
+        this.neighbours = new ArrayList<Vertex<R>>();
+        this.startTime = 0;
+        this.endTime = 0;
+    }
+
+    public R getRepresentation() {
+        return representation;
+    }
+
+    public List<Vertex<R>> getNeighbours() {
+        return neighbours;
+    }
+
+    public void addNeighbour(Vertex<R> n) {
+        neighbours.add(n);
+    }
+
+    public int getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(int startTime) {
+        this.startTime = startTime;
+    }
+
+    public int getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(int endTime) {
+        this.endTime = endTime;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/log/YLog.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/log/YLog.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/log/YLog.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,90 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.log;
+
+import org.jboss.envers.exception.VersionsException;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A simple logger facade which delegates through reflection to a logging delegate.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class YLog {
+    private final Object delegate;
+    private final Method errorMethod;
+    private final Method warnMethod;
+    private final Method infoMethod;
+
+    public YLog(Object delegate, Class<?> argClass) {
+        this.delegate = delegate;
+
+        try {
+            errorMethod = delegate.getClass().getMethod("error", argClass);
+        } catch (NoSuchMethodException e) {
+            throw new VersionsException(e);
+        }
+
+        try {
+            warnMethod = delegate.getClass().getMethod("warn", argClass);
+        } catch (NoSuchMethodException e) {
+            throw new VersionsException(e);
+        }
+
+        try {
+            infoMethod = delegate.getClass().getMethod("info", argClass);
+        } catch (NoSuchMethodException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public void error(String message) {
+        try {
+            errorMethod.invoke(delegate, message);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public void warn(String message) {
+        try {
+            warnMethod.invoke(delegate, message);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public void info(String message) {
+        try {
+            infoMethod.invoke(delegate, message);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/log/YLogManager.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/log/YLogManager.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/log/YLogManager.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,86 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.log;
+
+import org.jboss.envers.exception.VersionsException;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A class for creating logging facades either to loggers obtained from
+ * <code>org.apache.commons.logging.LogFactory</code> (in Hibernate 3.2.x) or from
+ * <code>org.slf4j.LoggerFactory</code> (in Hibernate 3.3.x).
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class YLogManager {
+    private Method getLogMethod;
+    private Class argClass;
+
+    private YLogManager() {
+        ClassLoader cl = YLogManager.class.getClassLoader();
+
+        try {
+            Class<?> logFactoryClass = cl.loadClass("org.apache.commons.logging.LogFactory");
+            try {
+                getLogMethod = logFactoryClass.getMethod("getLog", Class.class);
+                argClass = Object.class;
+            } catch (NoSuchMethodException e) {
+                throw new VersionsException("No 'getLog' method in org.apache.commons.logging.LogFactory.");
+            }
+        } catch (ClassNotFoundException e) {
+            try {
+                Class<?> loggerFactoryClass = cl.loadClass("org.slf4j.LoggerFactory");
+                try {
+                    getLogMethod = loggerFactoryClass.getMethod("getLogger", Class.class);
+                    argClass = String.class;
+                } catch (NoSuchMethodException e1) {
+                    throw new VersionsException("No 'getLogger' method in org.slf4j.LoggerFactory.");
+                }
+            } catch (ClassNotFoundException e1) {
+                throw new VersionsException("No org.apache.commons.logging.LogFactory or org.slf4j.LoggerFactory found.");
+            }
+        }
+    }
+
+    public YLog getLog(Class<?> cls) {
+        try {
+            return new YLog(getLogMethod.invoke(null, cls), argClass);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    //
+
+    private static YLogManager instance;
+
+    public static synchronized YLogManager getLogManager() {
+        if (instance == null) {
+            instance = new YLogManager();
+        }
+
+        return instance;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/query/Parameters.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/query/Parameters.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/query/Parameters.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,242 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.query;
+
+import org.jboss.envers.tools.MutableInteger;
+import org.jboss.envers.tools.MutableBoolean;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Parameters of a query, built using {@link QueryBuilder}.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Parameters {
+    public final static String AND = "and";
+    public final static String OR = "or";
+    
+    /**
+     * Main alias of the entity.
+     */
+    private final String alias;
+    /**
+     * Connective between these parameters - "and" or "or".
+     */
+    private final String connective;
+    /**
+     * For use by the parameter generator. Must be the same in all "child" (and parent) parameters.
+     */
+    private final MutableInteger queryParamCounter;
+
+    /**
+     * A list of sub-parameters (parameters with a different connective).
+     */
+    private final List<Parameters> subParameters;
+    /**
+     * A list of negated parameters.
+     */
+    private final List<Parameters> negatedParameters;
+    /**
+     * A list of complete where-expressions.
+     */
+    private final List<String> expressions;
+    /**
+     * Values of parameters used in expressions.
+     */
+    private final Map<String, Object> localQueryParamValues;
+
+    Parameters(String alias, String connective, MutableInteger queryParamCounter) {
+        this.alias = alias;
+        this.connective = connective;
+        this.queryParamCounter = queryParamCounter;
+
+        subParameters = new ArrayList<Parameters>();
+        negatedParameters = new ArrayList<Parameters>();
+        expressions = new ArrayList<String>();
+        localQueryParamValues = new HashMap<String, Object>();
+    }
+
+    private String generateQueryParam() {
+        return "_p" + queryParamCounter.getAndIncrease();
+    }
+
+    /**
+     * Adds sub-parameters with a new connective. That is, the parameters will be grouped in parentheses in the
+     * generated query, e.g.: ... and (exp1 or exp2) and ..., assuming the old connective is "and", and the
+     * new connective is "or".
+     * @param newConnective New connective of the parameters.
+     * @return Sub-parameters with the given connective.
+     */
+    public Parameters addSubParameters(String newConnective) {
+        if (connective.equals(newConnective)) {
+            return this;
+        } else {
+            Parameters newParams = new Parameters(alias, newConnective, queryParamCounter);
+            subParameters.add(newParams);
+            return newParams;
+        }
+    }
+
+    /**
+     * Adds negated parameters, by default with the "and" connective. These paremeters will be grouped in parentheses
+     * in the generated query and negated, e.g. ... not (exp1 and exp2) ...
+     * @return Negated sub paremters.
+     */
+    public Parameters addNegatedParameters() {
+        Parameters newParams = new Parameters(alias, AND, queryParamCounter);
+        negatedParameters.add(newParams);
+        return newParams;
+    }
+
+    public void addWhere(String left, String op, String right) {
+        addWhere(left, true, op, right, true);
+    }
+
+    public void addWhere(String left, boolean addAliasLeft, String op, String right, boolean addAliasRight) {
+        StringBuilder expression = new StringBuilder();
+
+        if (addAliasLeft) { expression.append(alias).append("."); }
+        expression.append(left);
+
+        expression.append(" ").append(op).append(" ");
+
+        if (addAliasRight) { expression.append(alias).append("."); }
+        expression.append(right);
+
+        expressions.add(expression.toString());
+    }
+
+    public void addWhereWithParam(String left, String op, Object paramValue) {
+        addWhereWithParam(left, true, op, paramValue);
+    }
+
+    public void addWhereWithParam(String left, boolean addAlias, String op, Object paramValue) {
+        String paramName = generateQueryParam();
+        localQueryParamValues.put(paramName, paramValue);
+
+        addWhereWithNamedParam(left, addAlias, op, paramName);
+    }
+
+    public void addWhereWithNamedParam(String left, String op, String paramName) {
+        addWhereWithNamedParam(left, true, op, paramName);
+    }
+
+    public void addWhereWithNamedParam(String left, boolean addAlias, String op, String paramName) {
+        StringBuilder expression = new StringBuilder();
+
+        if (addAlias) { expression.append(alias).append("."); }
+        expression.append(left);
+        expression.append(" ").append(op).append(" ");
+        expression.append(":").append(paramName);
+
+        expressions.add(expression.toString());
+    }
+
+    public void addWhereWithParams(String left, String opStart, Object[] paramValues, String opEnd) {
+        StringBuilder expression = new StringBuilder();
+
+        expression.append(alias).append(".").append(left).append(" ").append(opStart);
+
+        for (int i=0; i<paramValues.length; i++) {
+            Object paramValue = paramValues[i];
+            String paramName = generateQueryParam();
+            localQueryParamValues.put(paramName, paramValue);
+            expression.append(":").append(paramName);
+
+            if (i != paramValues.length-1) {
+                expression.append(", ");
+            }
+        }
+
+        expression.append(opEnd);
+
+        expressions.add(expression.toString());
+    }
+
+    public void addWhere(String left, String op, QueryBuilder right) {
+        addWhere(left, true, op, right);
+    }
+
+    public void addWhere(String left, boolean addAlias, String op, QueryBuilder right) {
+        StringBuilder expression = new StringBuilder();
+
+        if (addAlias) {
+            expression.append(alias).append(".");
+        }
+
+        expression.append(left);
+        
+        expression.append(" ").append(op).append(" ");
+
+        expression.append("(");
+        right.build(expression, localQueryParamValues);
+        expression.append(")");        
+
+        expressions.add(expression.toString());
+    }
+
+    private void append(StringBuilder sb, String toAppend, MutableBoolean isFirst) {
+        if (!isFirst.isSet()) {
+            sb.append(" ").append(connective).append(" ");
+        }
+
+        sb.append(toAppend);
+
+        isFirst.unset();
+    }
+
+    boolean isEmpty() {
+        return expressions.size() == 0 && subParameters.size() == 0 && negatedParameters.size() == 0;
+    }
+
+    void build(StringBuilder sb, Map<String, Object> queryParamValues) {
+        MutableBoolean isFirst = new MutableBoolean(true);
+
+        for (String expression : expressions) {
+            append(sb, expression, isFirst);
+        }
+
+        for (Parameters sub : subParameters) {
+            if (!subParameters.isEmpty()) {
+                append(sb, "(", isFirst);
+                sub.build(sb, queryParamValues);
+                sb.append(")");
+            }
+        }
+
+        for (Parameters negated : negatedParameters) {
+            if (!negatedParameters.isEmpty()) {
+                append(sb, "not (", isFirst);
+                negated.build(sb, queryParamValues);
+                sb.append(")");
+            }
+        }
+
+        queryParamValues.putAll(localQueryParamValues);
+    }
+}
+

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/query/QueryBuilder.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/query/QueryBuilder.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/query/QueryBuilder.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,200 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
+ * by the @authors tag. All rights reserved.
+ *
+ * See the copyright.txt in the distribution for a  full listing of individual
+ * contributors. This copyrighted material is made available to anyone wishing
+ * to use,  modify, copy, or redistribute it subject to the terms and
+ * conditions of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT A WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.query;
+
+import org.jboss.envers.tools.MutableInteger;
+import org.jboss.envers.tools.Pair;
+import org.jboss.envers.tools.StringTools;
+
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A class for incrementaly building a HQL query.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class QueryBuilder {
+    private final String entityName;
+    private final String alias;
+
+    /**
+     * For use by alias generator (in case an alias is not provided by the user).
+     */
+    private final MutableInteger aliasCounter;
+    /**
+     * For use by parameter generator, in {@link Parameters}. This counter must be
+     * the same in all parameters and sub-queries of this query.
+     */
+    private final MutableInteger paramCounter;
+    /**
+     * Main "where" parameters for this query.
+     */
+    private final Parameters rootParameters;
+
+    /**
+     * A list of pairs (from entity name, alias name).
+     */
+    private final List<Pair<String, String>> froms;
+    /**
+     * A list of pairs (property name, order ascending?).
+     */
+    private final List<Pair<String, Boolean>> orders;
+    /**
+     * A list of complete projection definitions: either a sole property name, or a function(property name).
+     */
+    private final List<String> projections;
+
+    /**
+     *
+     * @param entityName Main entity which should be selected.
+     * @param alias Alias of the entity
+     */
+    public QueryBuilder(String entityName, String alias) {
+        this(entityName, alias, new MutableInteger(), new MutableInteger());
+    }
+
+    private QueryBuilder(String entityName, String alias, MutableInteger aliasCounter, MutableInteger paramCounter) {
+        this.entityName = entityName;
+        this.alias = alias;
+        this.aliasCounter = aliasCounter;
+        this.paramCounter = paramCounter;
+
+        rootParameters = new Parameters(alias, "and", paramCounter);
+
+        froms = new ArrayList<Pair<String, String>>();
+        orders = new ArrayList<Pair<String, Boolean>>();
+        projections = new ArrayList<String>();
+
+        addFrom(entityName, alias);
+    }
+
+    /**
+     * Add an entity from which to select.
+     * @param entityName Name of the entity from which to select.
+     * @param alias Alias of the entity. Should be different than all other aliases.
+     */
+    public void addFrom(String entityName, String alias) {
+        froms.add(Pair.make(entityName, alias));
+    }
+
+    private String generateAlias() {
+        return "_e" + aliasCounter.getAndIncrease();
+    }
+
+    /**
+     * @return A sub-query builder for the same entity (with an auto-generated alias). The sub-query can
+     * be later used as a value of a parameter.
+     */
+    public QueryBuilder newSubQueryBuilder() {
+        return newSubQueryBuilder(entityName, generateAlias());
+    }
+
+    /**
+     * @param entityName Entity name, which will be the main entity for the sub-query.
+     * @param alias Alias of the entity, which can later be used in parameters.
+     * @return A sub-query builder for the given entity, with the given alias. The sub-query can
+     * be later used as a value of a parameter.
+     */
+    public QueryBuilder newSubQueryBuilder(String entityName, String alias) {
+        return new QueryBuilder(entityName, alias, aliasCounter, paramCounter);
+    }
+
+    public Parameters getRootParameters() {
+        return rootParameters;
+    }
+
+    public void addOrder(String propertyName, boolean ascending) {
+        orders.add(Pair.make(propertyName, ascending));
+    }
+
+    public void addProjection(String function, String propertyName, boolean distinct) {
+        addProjection(function, propertyName, distinct, true);
+    }
+
+    public void addProjection(String function, String propertyName, boolean distinct, boolean addAlias) {
+        if (function == null) {
+            projections.add((distinct ? "distinct " : "") + (addAlias ? alias+ "." : "") + propertyName);
+        } else {
+            projections.add(function + "(" + (distinct ? "distinct " : "") + (addAlias ? alias + "." : "") + propertyName + ")");
+        }
+    }
+
+    /**
+     * Builds the given query, appending results to the given string buffer, and adding all query parameter values
+     * that are used to the map provided.
+     * @param sb String builder to which the query will be appended.
+     * @param queryParamValues Map to which name and values of parameters used in the query should be added.
+     */
+    public void build(StringBuilder sb, Map<String, Object> queryParamValues) {
+        sb.append("select ");
+        if (projections.size() > 0) {
+            // all projections separated with commas
+            StringTools.append(sb, projections.iterator(), ", ");
+        } else {
+            // all aliases separated with commas
+            StringTools.append(sb, getAliasList().iterator(), ", ");
+        }
+        sb.append(" from ");
+        // all from entities with aliases, separated with commas
+        StringTools.append(sb, getFromList().iterator(), ", ");
+        // where part - rootParameters
+        if (!rootParameters.isEmpty()) {
+            sb.append(" where ");
+            rootParameters.build(sb, queryParamValues);
+        }
+        // orders
+        if (orders.size() > 0) {
+            sb.append(" order by ");
+            StringTools.append(sb, getOrderList().iterator(), ", ");
+        }
+    }
+
+    private List<String> getAliasList() {
+        List<String> aliasList = new ArrayList<String>();
+        for (Pair<String, String> from : froms) {
+            aliasList.add(from.getSecond());
+        }
+
+        return aliasList;
+    }
+
+    private List<String> getFromList() {
+        List<String> fromList = new ArrayList<String>();
+        for (Pair<String, String> from : froms) {
+            fromList.add(from.getFirst() + " " + from.getSecond());
+        }
+
+        return fromList;
+    }
+
+    private List<String> getOrderList() {
+        List<String> orderList = new ArrayList<String>();
+        for (Pair<String, Boolean> order : orders) {
+            orderList.add(alias + "." + order.getFirst() + " " + (order.getSecond() ? "asc" : "desc"));
+        }
+
+        return orderList;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/ReflectionTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/ReflectionTools.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/ReflectionTools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,83 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.reflection;
+
+import org.hibernate.property.PropertyAccessor;
+import org.hibernate.property.BasicPropertyAccessor;
+import org.hibernate.property.Getter;
+import org.hibernate.property.Setter;
+import org.hibernate.util.ReflectHelper;
+import org.jboss.envers.exception.VersionsException;
+import org.jboss.envers.tools.Pair;
+import org.jboss.envers.tools.ConcurrentReferenceHashMap;
+
+import java.util.Map;
+
+import static org.jboss.envers.tools.Pair.make;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ReflectionTools {
+    private static final Map<Pair<Class, String>, Getter> getterCache =
+            new ConcurrentReferenceHashMap<Pair<Class, String>, Getter>(10,
+                ConcurrentReferenceHashMap.ReferenceType.SOFT,
+                ConcurrentReferenceHashMap.ReferenceType.SOFT);
+    private static final Map<Pair<Class, String>, Setter> setterCache =
+            new ConcurrentReferenceHashMap<Pair<Class, String>, Setter>(10,
+                ConcurrentReferenceHashMap.ReferenceType.SOFT,
+                ConcurrentReferenceHashMap.ReferenceType.SOFT);
+
+    private static final PropertyAccessor BASIC_PROPERTY_ACCESSOR = new BasicPropertyAccessor();
+
+    public static Class<?> loadClass(String name) {
+        try {
+            return Thread.currentThread().getContextClassLoader().loadClass(name);
+        } catch (ClassNotFoundException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public static Getter getGetter(Class cls, String propertyName) {
+        Pair<Class, String> key = make(cls, propertyName);
+        Getter value = getterCache.get(key);
+        if (value == null) {
+            value = ReflectHelper.getGetter(cls, propertyName);
+            // It's ok if two getters are generated concurrently
+            getterCache.put(key, value);
+        }
+
+        return value;
+    }
+
+    public static Setter getSetter(Class cls, String propertyName) {
+        Pair<Class, String> key = make(cls, propertyName);
+        Setter value = setterCache.get(key);
+        if (value == null) {
+            value = BASIC_PROPERTY_ACCESSOR.getSetter(cls, propertyName);
+            // It's ok if two setters are generated concurrently
+            setterCache.put(key, value);
+        }
+
+        return value;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YClass.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YClass.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YClass.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,96 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.reflection;
+
+import org.jboss.envers.exception.VersionsException;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class YClass {
+    private final YMethodsAndClasses ymc;
+    private final Object delegate;
+
+    public YClass(YMethodsAndClasses ymc, Object delegate) {
+        this.ymc = ymc;
+        this.delegate = delegate;
+    }
+
+    Object getDelegate() {
+        return delegate;
+    }
+
+    public String getName() {
+        try {
+            return (String) ymc.getXClass_getNameMethod().invoke(delegate);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public YClass getSuperclass() {
+        try {
+            return new YClass(ymc, ymc.getXClass_getSuperclassMethod().invoke(delegate));
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public List<YProperty> getDeclaredProperties(String accessMode) {
+        List delegates;
+
+        try {
+            delegates = (List) ymc.getXClass_getDeclaredPropertiesMethod().invoke(delegate, accessMode);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+
+        List<YProperty> ret = new ArrayList<YProperty>();
+        for (Object delegate : delegates) {
+            ret.add(new YProperty(ymc, delegate));
+        }
+
+        return ret;
+    }
+
+    public <T extends Annotation> T getAnnotation(Class<T> annotation) {
+        try {
+            //noinspection unchecked
+            return (T) ymc.getXClass_getAnnotationMethod().invoke(delegate, annotation);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YMethodsAndClasses.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YMethodsAndClasses.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YMethodsAndClasses.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,119 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.reflection;
+
+import org.jboss.envers.exception.VersionsException;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class YMethodsAndClasses {
+    private final Method reflectionManager_classForNameMethod;
+    private final Method reflectionManager_equalsMethod;
+
+    private final Method xClass_getNameMethod;
+    private final Method xClass_getSuperclassMethod;
+    private final Method xClass_getDeclaredPropertiesMethod;
+    private final Method xClass_getAnnotationMethod;
+
+    private final Method xProperty_getNameMethod;
+    private final Method xProperty_getAnnotationMethod;
+    private final Method xProperty_getTypeMethod;
+
+    public YMethodsAndClasses(Class<?> delegateClass) throws Exception {
+        // Initializing classes
+        ClassLoader cl = YMethodsAndClasses.class.getClassLoader();
+
+        Class xClassClass;
+        try {
+            xClassClass = cl.loadClass("org.hibernate.annotations.common.reflection.XClass");
+        } catch (ClassNotFoundException e) {
+            try {
+                xClassClass = cl.loadClass("org.hibernate.reflection.XClass");
+            } catch (ClassNotFoundException e1) {
+                throw new VersionsException("No XClass found.");
+            }
+        }
+
+        Class xPropertyClass;
+        try {
+            xPropertyClass = cl.loadClass("org.hibernate.annotations.common.reflection.XProperty");
+        } catch (ClassNotFoundException e) {
+            try {
+                xPropertyClass = cl.loadClass("org.hibernate.reflection.XProperty");
+            } catch (ClassNotFoundException e1) {
+                throw new VersionsException("No XProperty found.");
+            }
+        }
+
+        // Initializing methods
+        reflectionManager_classForNameMethod = delegateClass.getMethod("classForName", String.class, Class.class);
+        reflectionManager_equalsMethod = delegateClass.getMethod("equals", xClassClass, Class.class);
+
+        xClass_getNameMethod = xClassClass.getMethod("getName");
+        xClass_getSuperclassMethod = xClassClass.getMethod("getSuperclass");
+        xClass_getDeclaredPropertiesMethod = xClassClass.getMethod("getDeclaredProperties", String.class);
+        xClass_getAnnotationMethod = xClassClass.getMethod("getAnnotation", Class.class);
+
+        xProperty_getNameMethod = xPropertyClass.getMethod("getName");
+        xProperty_getTypeMethod = xPropertyClass.getMethod("getType");
+        xProperty_getAnnotationMethod = xPropertyClass.getMethod("getAnnotation", Class.class);
+    }
+
+    public Method getXClass_getNameMethod() {
+        return xClass_getNameMethod;
+    }
+
+    public Method getXClass_getSuperclassMethod() {
+        return xClass_getSuperclassMethod;
+    }
+
+    public Method getXClass_getDeclaredPropertiesMethod() {
+        return xClass_getDeclaredPropertiesMethod;
+    }
+
+    public Method getXClass_getAnnotationMethod() {
+        return xClass_getAnnotationMethod;
+    }
+
+    public Method getXProperty_getNameMethod() {
+        return xProperty_getNameMethod;
+    }
+
+    public Method getXProperty_getAnnotationMethod() {
+        return xProperty_getAnnotationMethod;
+    }
+
+    public Method getXProperty_getTypeMethod() {
+        return xProperty_getTypeMethod;
+    }
+
+    public Method getReflectionManager_classForNameMethod() {
+        return reflectionManager_classForNameMethod;
+    }
+
+    public Method getReflectionManager_equalsMethod() {
+        return reflectionManager_equalsMethod;
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YProperty.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YProperty.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YProperty.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,71 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.reflection;
+
+import org.jboss.envers.exception.VersionsException;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class YProperty {
+    private final YMethodsAndClasses ymc;
+    private final Object delegate;
+
+    public YProperty(YMethodsAndClasses ymc, Object delegate) {
+        this.ymc = ymc;
+        this.delegate = delegate;
+    }
+
+    public String getName() {
+        try {
+            return (String) ymc.getXProperty_getNameMethod().invoke(delegate);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public <T extends Annotation> T getAnnotation(Class<T> annotation) {
+        try {
+            //noinspection unchecked
+            return (T) ymc.getXProperty_getAnnotationMethod().invoke(delegate, annotation);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public YClass getType() {
+        try {
+            return new YClass(ymc, ymc.getXProperty_getTypeMethod().invoke(delegate));
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+}

Added: core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YReflectionManager.java
===================================================================
--- core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YReflectionManager.java	                        (rev 0)
+++ core/trunk/envers/src/main/java/org/jboss/envers/tools/reflection/YReflectionManager.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,80 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.tools.reflection;
+
+import org.hibernate.cfg.Configuration;
+import org.hibernate.MappingException;
+import org.jboss.envers.exception.VersionsException;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A reflection manager which proxies either to the old classes from package org.hibernate.reflection,
+ * or to the new classed from package org.hibernate.annotations.common.reflection.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class YReflectionManager {
+    private final Object delegate;
+    private final YMethodsAndClasses ymc;
+
+    public YReflectionManager(Configuration cfg) throws Exception {
+        Object delegateTemp;
+        try {
+            delegateTemp = cfg.getClass().getMethod("getReflectionManager").invoke(cfg);
+        } catch (NoSuchMethodException e) {
+            // TODO - what if there it's a pure Hibernate envirionment?
+            delegateTemp = ReflectionTools
+                    .loadClass("org.hibernate.cfg.annotations.reflection.EJB3ReflectionManager")
+                    .newInstance();
+        }
+        delegate = delegateTemp;
+        ymc = new YMethodsAndClasses(delegate.getClass());
+    }
+
+    public YClass classForName(String className, Class<?> caller) throws ClassNotFoundException {
+        try {
+            return new YClass(ymc, ymc.getReflectionManager_classForNameMethod().invoke(delegate, className, caller));
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public <T> boolean equals(YClass class1, java.lang.Class<T> class2) {
+        try {
+            return (Boolean) ymc.getReflectionManager_equalsMethod().invoke(delegate, class1.getDelegate(), class2);
+        } catch (IllegalAccessException e) {
+            throw new VersionsException(e);
+        } catch (InvocationTargetException e) {
+            throw new VersionsException(e);
+        }
+    }
+
+    public static YReflectionManager get(Configuration cfg) {
+        try {
+            return new YReflectionManager(cfg);
+        } catch (Exception e) {
+            throw new MappingException(e);
+        }
+    }
+}

Added: core/trunk/envers/src/main/resources/META-INF/COPYING.txt
===================================================================
--- core/trunk/envers/src/main/resources/META-INF/COPYING.txt	                        (rev 0)
+++ core/trunk/envers/src/main/resources/META-INF/COPYING.txt	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,466 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
\ No newline at end of file

Added: core/trunk/envers/src/main/resources/META-INF/copyright.txt
===================================================================
--- core/trunk/envers/src/main/resources/META-INF/copyright.txt	                        (rev 0)
+++ core/trunk/envers/src/main/resources/META-INF/copyright.txt	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1 @@
+(c) 2008, Adam Warski, JBoss Inc.
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/AbstractEntityTest.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/AbstractEntityTest.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/AbstractEntityTest.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,55 @@
+package org.jboss.envers.test;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.jboss.envers.VersionsReader;
+import org.jboss.envers.VersionsReaderFactory;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractEntityTest {
+    private EntityManagerFactory emf;
+    private EntityManager entityManager;
+    private VersionsReader versionsReader;
+    private Ejb3Configuration cfg;
+
+    public abstract void configure(Ejb3Configuration cfg);
+
+    @BeforeMethod
+    public void newEntityManager() {
+        if (entityManager != null) {
+            entityManager.close();
+        }
+        
+        entityManager = emf.createEntityManager();
+        versionsReader = VersionsReaderFactory.get(entityManager);
+    }
+
+    @BeforeClass
+    public void init() throws IOException {
+        cfg = new Ejb3Configuration();
+        cfg.configure("hibernate.test.cfg.xml");
+        configure(cfg);
+        emf = cfg.buildEntityManagerFactory();
+
+        newEntityManager();
+    }
+
+    public EntityManager getEntityManager() {
+        return entityManager;
+    }
+
+    public VersionsReader getVersionsReader() {
+        return versionsReader;
+    }
+
+    public Ejb3Configuration getCfg() {
+        return cfg;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/IntTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/IntTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/IntTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,71 @@
+package org.jboss.envers.test.entities;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class IntTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    private Integer number;
+
+    public IntTestEntity() {
+    }
+
+    public IntTestEntity(Integer number, Integer id) {
+        this.id = id;
+        this.number = number;
+    }
+
+    public IntTestEntity(Integer number) {
+        this.number = number;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof IntTestEntity)) return false;
+
+        IntTestEntity that = (IntTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (number != null ? !number.equals(that.number) : that.number != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (number != null ? number.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ITE(id = " + id + ", number = " + number + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/StrIntTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/StrIntTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/StrIntTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,86 @@
+package org.jboss.envers.test.entities;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class StrIntTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    private String str1;
+
+    @Versioned
+    private Integer number;
+
+    public StrIntTestEntity() {
+    }
+
+    public StrIntTestEntity(String str1, Integer number, Integer id) {
+        this.id = id;
+        this.str1 = str1;
+        this.number = number;
+    }
+
+    public StrIntTestEntity(String str1, Integer number) {
+        this.str1 = str1;
+        this.number = number;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof StrIntTestEntity)) return false;
+
+        StrIntTestEntity that = (StrIntTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (number != null ? !number.equals(that.number) : that.number != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        result = 31 * result + (number != null ? number.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SITE(id = " + id + ", str1 = " + str1 + ", number = " + number + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/StrTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/StrTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/StrTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,71 @@
+package org.jboss.envers.test.entities;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class StrTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    private String str;
+
+    public StrTestEntity() {
+    }
+
+    public StrTestEntity(String str, Integer id) {
+        this.str = str;
+        this.id = id;
+    }
+
+    public StrTestEntity(String str) {
+        this.str = str;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr() {
+        return str;
+    }
+
+    public void setStr(String str) {
+        this.str = str;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof StrTestEntity)) return false;
+
+        StrTestEntity that = (StrTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str != null ? !str.equals(that.str) : that.str != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str != null ? str.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "STE(id = " + id + ", str = " + str + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/UnversionedEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/UnversionedEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/UnversionedEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,86 @@
+package org.jboss.envers.test.entities;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.Unversioned;
+
+import javax.persistence.Id;
+import javax.persistence.Basic;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Versioned
+public class UnversionedEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Basic
+    private String data1;
+
+    @Basic
+    @Unversioned
+    private String data2;
+
+    public UnversionedEntity() {
+    }
+
+    public UnversionedEntity(String data1, String data2) {
+        this.data1 = data1;
+        this.data2 = data2;
+    }
+
+    public UnversionedEntity(Integer id, String data1, String data2) {
+        this.id = id;
+        this.data1 = data1;
+        this.data2 = data2;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData1() {
+        return data1;
+    }
+
+    public void setData1(String data1) {
+        this.data1 = data1;
+    }
+
+    public String getData2() {
+        return data2;
+    }
+
+    public void setData2(String data2) {
+        this.data2 = data2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UnversionedEntity)) return false;
+
+        UnversionedEntity that = (UnversionedEntity) o;
+
+        if (data1 != null ? !data1.equals(that.data1) : that.data1 != null) return false;
+        if (data2 != null ? !data2.equals(that.data2) : that.data2 != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data1 != null ? data1.hashCode() : 0);
+        result = 31 * result + (data2 != null ? data2.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/UnversionedStrTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/UnversionedStrTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/UnversionedStrTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,70 @@
+package org.jboss.envers.test.entities;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class UnversionedStrTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    private String str;
+
+    public UnversionedStrTestEntity() {
+    }
+
+    public UnversionedStrTestEntity(String str, Integer id) {
+        this.str = str;
+        this.id = id;
+    }
+
+    public UnversionedStrTestEntity(String str) {
+        this.str = str;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr() {
+        return str;
+    }
+
+    public void setStr(String str) {
+        this.str = str;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UnversionedStrTestEntity)) return false;
+
+        UnversionedStrTestEntity that = (UnversionedStrTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str != null ? !str.equals(that.str) : that.str != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str != null ? str.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "USTE(id = " + id + ", str = " + str + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/EnumSetEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/EnumSetEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/EnumSetEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,79 @@
+package org.jboss.envers.test.entities.collection;
+
+import org.jboss.envers.Versioned;
+import org.hibernate.annotations.CollectionOfElements;
+
+import javax.persistence.*;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class EnumSetEntity {
+    public static enum E1 { X, Y }
+    public static enum E2 { A, B }
+
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @CollectionOfElements
+    @Enumerated(EnumType.STRING)
+    private Set<E1> enums1;
+
+    @Versioned
+    @CollectionOfElements
+    @Enumerated(EnumType.ORDINAL)
+    private Set<E2> enums2;
+
+    public EnumSetEntity() {
+        enums1 = new HashSet<E1>();
+        enums2 = new HashSet<E2>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Set<E1> getEnums1() {
+        return enums1;
+    }
+
+    public void setEnums1(Set<E1> enums1) {
+        this.enums1 = enums1;
+    }
+
+    public Set<E2> getEnums2() {
+        return enums2;
+    }
+
+    public void setEnums2(Set<E2> enums2) {
+        this.enums2 = enums2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EnumSetEntity)) return false;
+
+        EnumSetEntity that = (EnumSetEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "ESE(id = " + id + ", enums1 = " + enums1 + ", enums2 = " + enums2 + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringListEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringListEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringListEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,65 @@
+package org.jboss.envers.test.entities.collection;
+
+import org.jboss.envers.Versioned;
+import org.hibernate.annotations.CollectionOfElements;
+import org.hibernate.annotations.IndexColumn;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class StringListEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @CollectionOfElements
+    @IndexColumn(name = "list_index")
+    private List<String> strings;
+
+    public StringListEntity() {
+        strings = new ArrayList<String>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public List<String> getStrings() {
+        return strings;
+    }
+
+    public void setStrings(List<String> strings) {
+        this.strings = strings;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof StringListEntity)) return false;
+
+        StringListEntity that = (StringListEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "SLE(id = " + id + ", strings = " + strings + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringMapEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringMapEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringMapEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,63 @@
+package org.jboss.envers.test.entities.collection;
+
+import org.jboss.envers.Versioned;
+import org.hibernate.annotations.CollectionOfElements;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class StringMapEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @CollectionOfElements
+    private Map<String, String> strings;
+
+    public StringMapEntity() {
+        strings = new HashMap<String, String>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Map<String, String> getStrings() {
+        return strings;
+    }
+
+    public void setStrings(Map<String, String> strings) {
+        this.strings = strings;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof StringMapEntity)) return false;
+
+        StringMapEntity that = (StringMapEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "SME(id = " + id + ", strings = " + strings + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringSetEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringSetEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/collection/StringSetEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,63 @@
+package org.jboss.envers.test.entities.collection;
+
+import org.jboss.envers.Versioned;
+import org.hibernate.annotations.CollectionOfElements;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class StringSetEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @CollectionOfElements
+    private Set<String> strings;
+
+    public StringSetEntity() {
+        strings = new HashSet<String>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Set<String> getStrings() {
+        return strings;
+    }
+
+    public void setStrings(Set<String> strings) {
+        this.strings = strings;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof StringSetEntity)) return false;
+
+        StringSetEntity that = (StringSetEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "SSE(id = " + id + ", strings = " + strings + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/Component1.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/Component1.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/Component1.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,57 @@
+package org.jboss.envers.test.entities.components;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Component1 {
+    private String str1;
+
+    private String str2;
+
+    public Component1(String str1, String str2) {
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+
+    public Component1() {
+    }
+
+    public String getStr2() {
+        return str2;
+    }
+
+    public void setStr2(String str2) {
+        this.str2 = str2;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Component1)) return false;
+
+        Component1 that = (Component1) o;
+
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+        if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (str1 != null ? str1.hashCode() : 0);
+        result = 31 * result + (str2 != null ? str2.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "Comp1(str1 = " + str1 + ", " + str2 + ")";
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/Component2.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/Component2.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/Component2.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,57 @@
+package org.jboss.envers.test.entities.components;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Component2 {
+    private String str5;
+
+    private String str6;
+
+    public Component2(String str5, String str6) {
+        this.str5 = str5;
+        this.str6 = str6;
+    }
+
+    public Component2() {
+    }
+
+    public String getStr5() {
+        return str5;
+    }
+
+    public void setStr5(String str5) {
+        this.str5 = str5;
+    }
+
+    public String getStr6() {
+        return str6;
+    }
+
+    public void setStr6(String str6) {
+        this.str6 = str6;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Component2)) return false;
+
+        Component2 that = (Component2) o;
+
+        if (str5 != null ? !str5.equals(that.str5) : that.str5 != null) return false;
+        if (str6 != null ? !str6.equals(that.str6) : that.str6 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (str5 != null ? str5.hashCode() : 0);
+        result = 31 * result + (str6 != null ? str6.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "Comp2(str1 = " + str5 + ", " + str6 + ")";
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/ComponentTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/ComponentTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/components/ComponentTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,89 @@
+package org.jboss.envers.test.entities.components;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Embedded;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class ComponentTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Embedded
+    @Versioned
+    private Component1 comp1;
+
+    @Embedded
+    private Component2 comp2;
+
+    public ComponentTestEntity() {
+    }
+
+    public ComponentTestEntity(Integer id, Component1 comp1, Component2 comp2) {
+        this.id = id;
+        this.comp1 = comp1;
+        this.comp2 = comp2;
+    }
+
+    public ComponentTestEntity(Component1 comp1, Component2 comp2) {
+        this.comp1 = comp1;
+        this.comp2 = comp2;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Component1 getComp1() {
+        return comp1;
+    }
+
+    public void setComp1(Component1 comp1) {
+        this.comp1 = comp1;
+    }
+
+    public Component2 getComp2() {
+        return comp2;
+    }
+
+    public void setComp2(Component2 comp2) {
+        this.comp2 = comp2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ComponentTestEntity)) return false;
+
+        ComponentTestEntity that = (ComponentTestEntity) o;
+
+        if (comp1 != null ? !comp1.equals(that.comp1) : that.comp1 != null) return false;
+        if (comp2 != null ? !comp2.equals(that.comp2) : that.comp2 != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (comp1 != null ? comp1.hashCode() : 0);
+        result = 31 * result + (comp2 != null ? comp2.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "CTE(id = " + id + ", comp1 = " + comp1 + ", comp2 = " + comp2 + ")"; 
+    }
+}
+

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/Component.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/Component.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/Component.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,54 @@
+package org.jboss.envers.test.entities.customtype;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Component implements Serializable {
+    private String prop1;
+    private int prop2;
+
+    public Component(String prop1, int prop2) {
+        this.prop1 = prop1;
+        this.prop2 = prop2;
+    }
+
+    public Component() {
+    }
+
+    public String getProp1() {
+        return prop1;
+    }
+
+    public void setProp1(String prop1) {
+        this.prop1 = prop1;
+    }
+
+    public int getProp2() {
+        return prop2;
+    }
+
+    public void setProp2(int prop2) {
+        this.prop2 = prop2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Component)) return false;
+
+        Component that = (Component) o;
+
+        if (prop2 != that.prop2) return false;
+        if (prop1 != null ? !prop1.equals(that.prop1) : that.prop1 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (prop1 != null ? prop1.hashCode() : 0);
+        result = 31 * result + prop2;
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/CompositeCustomTypeEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/CompositeCustomTypeEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/CompositeCustomTypeEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,74 @@
+package org.jboss.envers.test.entities.customtype;
+
+import org.jboss.envers.Versioned;
+import org.hibernate.annotations.TypeDef;
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.Columns;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Column;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at TypeDef(name = "comp", typeClass = CompositeTestUserType.class)
+public class CompositeCustomTypeEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @Type(type = "comp")
+    @Columns(columns = { @Column(name = "str"), @Column(name = "num") })
+    private Component component;
+
+    public CompositeCustomTypeEntity() {
+    }
+
+    public CompositeCustomTypeEntity(Integer id, Component component) {
+        this.id = id;
+        this.component = component;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Component getComponent() {
+        return component;
+    }
+
+    public void setComponent(Component component) {
+        this.component = component;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CompositeCustomTypeEntity)) return false;
+
+        CompositeCustomTypeEntity that = (CompositeCustomTypeEntity) o;
+
+        if (component != null ? !component.equals(that.component) : that.component != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (component != null ? component.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "CCTE(id = " + id + ", component = " + component + ")";
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/CompositeTestUserType.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/CompositeTestUserType.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/CompositeTestUserType.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,117 @@
+package org.jboss.envers.test.entities.customtype;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Hibernate;
+import org.hibernate.usertype.CompositeUserType;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.io.Serializable;
+
+/**
+ * @author Andrew DePue
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class CompositeTestUserType implements CompositeUserType {
+    public String[] getPropertyNames() {
+        return new String[] { "prop1", "prop2" };
+    }
+
+    public Type[] getPropertyTypes() {
+        return new Type[] { Hibernate.STRING, Hibernate.INTEGER };
+    }
+
+    public Object getPropertyValue(final Object component, final int property) throws HibernateException {
+        Component comp = (Component) component;
+        if (property == 0) {
+            return comp.getProp1();
+        } else {
+            return comp.getProp2();
+        }
+    }
+
+    public void setPropertyValue(final Object component, final int property, final Object value) throws HibernateException {
+        Component comp = (Component) component;
+        if (property == 0) {
+            comp.setProp1((String) value);
+        } else {
+            comp.setProp2((Integer) value);
+        }
+    }
+
+    public Class returnedClass() {
+        return Component.class;
+    }
+
+    public boolean equals(final Object x, final Object y) throws HibernateException {
+        //noinspection ObjectEquality
+        if (x == y) {
+            return true;
+        }
+
+        if (x == null || y == null) {
+            return false;
+        }
+
+        return x.equals(y);
+    }
+
+    public int hashCode(final Object x) throws HibernateException {
+        return x.hashCode();
+    }
+
+    public Object nullSafeGet(final ResultSet rs, final String[] names,
+                              final SessionImplementor session,
+                              final Object owner) throws HibernateException, SQLException {
+        if (rs.wasNull()) {
+            return null;
+        }
+        final String prop1 = rs.getString(names[0]);
+        if (prop1 == null) {
+            return null;
+        }
+        final int prop2 = rs.getInt(names[1]);
+        
+        return new Component(prop1, prop2);
+    }
+
+    public void nullSafeSet(final PreparedStatement st, final Object value,
+                            final int index, final SessionImplementor session)
+            throws HibernateException, SQLException
+    {
+        if (value == null) {
+            st.setNull(index, Hibernate.STRING.sqlType());
+            st.setNull(index + 1, Hibernate.INTEGER.sqlType());
+        } else {
+            final Component comp = (Component) value;
+            st.setString(index, comp.getProp1());
+            st.setInt(index + 1, comp.getProp2());
+        }
+    }
+
+    public Object deepCopy(final Object value) throws HibernateException {
+        Component comp = (Component) value;
+        return new Component(comp.getProp1(), comp.getProp2());
+    }
+
+    public boolean isMutable() {
+        return true;
+    }
+
+    public Serializable disassemble(final Object value, final SessionImplementor session) throws HibernateException {
+        return (Serializable) value;
+    }
+
+    public Object assemble(final Serializable cached, final SessionImplementor session,
+                           final Object owner) throws HibernateException {
+        return cached;
+    }
+
+    public Object replace(Object original, Object target,
+                          SessionImplementor session, Object owner) throws HibernateException {
+        return original;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/ParametrizedCustomTypeEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/ParametrizedCustomTypeEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/ParametrizedCustomTypeEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,73 @@
+package org.jboss.envers.test.entities.customtype;
+
+import org.jboss.envers.Versioned;
+import org.hibernate.annotations.TypeDef;
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.Parameter;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at TypeDef(name = "param", typeClass = ParametrizedTestUserType.class,
+        parameters = { @Parameter(name="param1", value = "x"), @Parameter(name="param2", value = "y") })
+public class ParametrizedCustomTypeEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @Type(type = "param")
+    private String str;
+
+    public ParametrizedCustomTypeEntity() {
+    }
+
+    public ParametrizedCustomTypeEntity(Integer id, String str) {
+        this.id = id;
+        this.str = str;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr() {
+        return str;
+    }
+
+    public void setStr(String str) {
+        this.str = str;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ParametrizedCustomTypeEntity)) return false;
+
+        ParametrizedCustomTypeEntity that = (ParametrizedCustomTypeEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str != null ? !str.equals(that.str) : that.str != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str != null ? str.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "PCTE(id = " + id + ", str = " + str + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/ParametrizedTestUserType.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/ParametrizedTestUserType.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/customtype/ParametrizedTestUserType.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,92 @@
+package org.jboss.envers.test.entities.customtype;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Properties;
+
+import org.hibernate.HibernateException;
+import org.hibernate.Hibernate;
+import org.hibernate.usertype.ParameterizedType;
+import org.hibernate.usertype.UserType;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ParametrizedTestUserType implements UserType, ParameterizedType {
+    private static final int[] TYPES = new int[] { Types.VARCHAR };
+
+    private String param1;
+    private String param2;
+
+    public void setParameterValues(Properties parameters) {
+        param1 = parameters.getProperty("param1");
+        param2 = parameters.getProperty("param2");
+    }
+
+    public Class returnedClass() {
+        return String.class;
+    }
+
+    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
+        return Hibernate.STRING.nullSafeGet(rs, names[0]);
+    }
+
+    public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
+        if (value != null) {
+            String v = (String) value;
+            if (!v.startsWith(param1)) {
+                v = param1 + v;
+            }
+            if (!v.endsWith(param2)) {
+                v = v + param2;
+            }
+            Hibernate.STRING.nullSafeSet(st, v, index);
+        } else {
+            Hibernate.STRING.nullSafeSet(st, value, index);
+        }
+    }
+
+    public int[] sqlTypes() {
+        return TYPES;
+    }
+
+    public Object assemble(Serializable cached, Object owner) throws HibernateException {
+        return cached;
+    }
+
+    public Object deepCopy(Object value) throws HibernateException {
+        return value;
+    }
+
+    public Serializable disassemble(Object value) throws HibernateException {
+        return (Serializable) value;
+    }
+
+    public boolean equals(Object x, Object y) throws HibernateException {
+        //noinspection ObjectEquality
+        if (x == y) {
+            return true;
+        }
+
+        if (x == null || y == null) {
+            return false;
+        }
+
+        return x.equals(y);
+    }
+
+    public int hashCode(Object x) throws HibernateException {
+        return x.hashCode();
+    }
+
+    public boolean isMutable() {
+        return false;
+    }
+
+    public Object replace(Object original, Object target, Object owner) throws HibernateException {
+        return original;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/EmbId.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/EmbId.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/EmbId.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,60 @@
+package org.jboss.envers.test.entities.ids;
+
+import javax.persistence.Embeddable;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Embeddable
+public class EmbId implements Serializable {
+    private Integer x;
+    private Integer y;
+
+    public EmbId() {
+    }
+
+    public EmbId(Integer x, Integer y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public Integer getX() {
+        return x;
+    }
+
+    public void setX(Integer x) {
+        this.x = x;
+    }
+
+    public Integer getY() {
+        return y;
+    }
+
+    public void setY(Integer y) {
+        this.y = y;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EmbId)) return false;
+
+        EmbId embId = (EmbId) o;
+
+        if (x != null ? !x.equals(embId.x) : embId.x != null) return false;
+        if (y != null ? !y.equals(embId.y) : embId.y != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (x != null ? x.hashCode() : 0);
+        result = 31 * result + (y != null ? y.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "EmbId(" + x + ", " + y + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/EmbIdTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/EmbIdTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/EmbIdTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,66 @@
+package org.jboss.envers.test.entities.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.EmbId;
+
+import javax.persistence.Entity;
+import javax.persistence.EmbeddedId;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class EmbIdTestEntity {
+    @EmbeddedId
+    private EmbId id;
+
+    @Versioned
+    private String str1;
+
+    public EmbIdTestEntity() {
+    }
+
+    public EmbIdTestEntity(String str1) {
+        this.str1 = str1;
+    }
+
+    public EmbIdTestEntity(EmbId id, String str1) {
+        this.id = id;
+        this.str1 = str1;
+    }
+
+    public EmbId getId() {
+        return id;
+    }
+
+    public void setId(EmbId id) {
+        this.id = id;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EmbIdTestEntity)) return false;
+
+        EmbIdTestEntity that = (EmbIdTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/MulId.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/MulId.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/MulId.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,59 @@
+package org.jboss.envers.test.entities.ids;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MulId implements Serializable {
+    private Integer id1;
+
+    private Integer id2;
+
+    public MulId() {
+    }
+
+    public MulId(Integer id1, Integer id2) {
+        this.id1 = id1;
+        this.id2 = id2;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MulId)) return false;
+
+        MulId mulId = (MulId) o;
+
+        if (id1 != null ? !id1.equals(mulId.id1) : mulId.id1 != null) return false;
+        if (id2 != null ? !id2.equals(mulId.id2) : mulId.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "MulId(" + id1 + ", " + id2 + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/MulIdTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/MulIdTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/ids/MulIdTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+package org.jboss.envers.test.entities.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.MulId;
+
+import javax.persistence.Entity;
+import javax.persistence.IdClass;
+import javax.persistence.Id;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at IdClass(MulId.class)
+public class MulIdTestEntity {
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    @Versioned
+    private String str1;
+
+    public MulIdTestEntity() {
+    }
+
+    public MulIdTestEntity(String str1) {
+        this.str1 = str1;
+    }
+
+    public MulIdTestEntity(Integer id1, Integer id2, String str1) {
+        this.id1 = id1;
+        this.id2 = id2;
+        this.str1 = str1;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MulIdTestEntity)) return false;
+
+        MulIdTestEntity that = (MulIdTestEntity) o;
+
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/ListOwnedEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/ListOwnedEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/ListOwnedEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.entities.manytomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import java.util.List;
+
+/**
+ * Many-to-many not-owning entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class ListOwnedEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany(mappedBy="references")
+    private List<ListOwningEntity> referencing;
+
+    public ListOwnedEntity() {
+    }
+
+    public ListOwnedEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public ListOwnedEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public List<ListOwningEntity> getReferencing() {
+        return referencing;
+    }
+
+    public void setReferencing(List<ListOwningEntity> referencing) {
+        this.referencing = referencing;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ListOwnedEntity)) return false;
+
+        ListOwnedEntity that = (ListOwnedEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetOwnedEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/ListOwningEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/ListOwningEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/ListOwningEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.entities.manytomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * Entity owning the many-to-many relation
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class ListOwningEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany
+    private List<ListOwnedEntity> references;
+
+    public ListOwningEntity() { }
+
+    public ListOwningEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public ListOwningEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public List<ListOwnedEntity> getReferences() {
+        return references;
+    }
+
+    public void setReferences(List<ListOwnedEntity> references) {
+        this.references = references;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ListOwningEntity)) return false;
+
+        ListOwningEntity that = (ListOwningEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetOwningEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/MapOwnedEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/MapOwnedEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/MapOwnedEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.entities.manytomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import java.util.Set;
+
+/**
+ * Many-to-many not-owning entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class MapOwnedEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany(mappedBy="references")
+    private Set<MapOwningEntity> referencing;
+
+    public MapOwnedEntity() {
+    }
+
+    public MapOwnedEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public MapOwnedEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<MapOwningEntity> getReferencing() {
+        return referencing;
+    }
+
+    public void setReferencing(Set<MapOwningEntity> referencing) {
+        this.referencing = referencing;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MapOwnedEntity)) return false;
+
+        MapOwnedEntity that = (MapOwnedEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "MapOwnedEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/MapOwningEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/MapOwningEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/MapOwningEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+package org.jboss.envers.test.entities.manytomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Entity owning the many-to-many relation
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class MapOwningEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany
+    private Map<String, MapOwnedEntity> references = new HashMap<String, MapOwnedEntity>();
+
+    public MapOwningEntity() { }
+
+    public MapOwningEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public MapOwningEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Map<String, MapOwnedEntity> getReferences() {
+        return references;
+    }
+
+    public void setReferences(Map<String, MapOwnedEntity> references) {
+        this.references = references;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MapOwningEntity)) return false;
+
+        MapOwningEntity that = (MapOwningEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "MapOwningEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/SetOwnedEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/SetOwnedEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/SetOwnedEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.entities.manytomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import java.util.Set;
+
+/**
+ * Many-to-many not-owning entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetOwnedEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany(mappedBy="references")
+    private Set<SetOwningEntity> referencing;
+
+    public SetOwnedEntity() {
+    }
+
+    public SetOwnedEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public SetOwnedEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<SetOwningEntity> getReferencing() {
+        return referencing;
+    }
+
+    public void setReferencing(Set<SetOwningEntity> referencing) {
+        this.referencing = referencing;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetOwnedEntity)) return false;
+
+        SetOwnedEntity that = (SetOwnedEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetOwnedEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/SetOwningEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/SetOwningEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/SetOwningEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.entities.manytomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.Set;
+
+/**
+ * Entity owning the many-to-many relation
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetOwningEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany
+    private Set<SetOwnedEntity> references;
+
+    public SetOwningEntity() { }
+
+    public SetOwningEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public SetOwningEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<SetOwnedEntity> getReferences() {
+        return references;
+    }
+
+    public void setReferences(Set<SetOwnedEntity> references) {
+        this.references = references;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetOwningEntity)) return false;
+
+        SetOwningEntity that = (SetOwningEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetOwningEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/ListUniEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/ListUniEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/ListUniEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+package org.jboss.envers.test.entities.manytomany.unidirectional;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * Entity owning the many-to-many relation
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class ListUniEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany
+    private List<StrTestEntity> references;
+
+    public ListUniEntity() { }
+
+    public ListUniEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public ListUniEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public List<StrTestEntity> getReferences() {
+        return references;
+    }
+
+    public void setReferences(List<StrTestEntity> references) {
+        this.references = references;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ListUniEntity)) return false;
+
+        ListUniEntity that = (ListUniEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ListUniEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/MapUniEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/MapUniEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/MapUniEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.entities.manytomany.unidirectional;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import java.util.Map;
+
+/**
+ * Entity with a map from a string to an entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class MapUniEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany
+    private Map<String, StrTestEntity> map;
+
+    public MapUniEntity() {
+    }
+
+    public MapUniEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public MapUniEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Map<String, StrTestEntity> getMap() {
+        return map;
+    }
+
+    public void setMap(Map<String, StrTestEntity> map) {
+        this.map = map;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MapUniEntity)) return false;
+
+        MapUniEntity that = (MapUniEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "MapUniEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/SetUniEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/SetUniEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/manytomany/unidirectional/SetUniEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+package org.jboss.envers.test.entities.manytomany.unidirectional;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.*;
+import java.util.Set;
+
+/**
+ * Entity owning the many-to-many relation
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetUniEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToMany
+    private Set<StrTestEntity> references;
+
+    public SetUniEntity() { }
+
+    public SetUniEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public SetUniEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<StrTestEntity> getReferences() {
+        return references;
+    }
+
+    public void setReferences(Set<StrTestEntity> references) {
+        this.references = references;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetUniEntity)) return false;
+
+        SetUniEntity that = (SetUniEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetUniEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/CollectionRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/CollectionRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/CollectionRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.entities.onetomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import java.util.Collection;
+
+/**
+ * ReferencEd entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class CollectionRefEdEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    private Collection<CollectionRefIngEntity> reffering;
+
+    public CollectionRefEdEntity() {
+    }
+
+    public CollectionRefEdEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public CollectionRefEdEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Collection<CollectionRefIngEntity> getReffering() {
+        return reffering;
+    }
+
+    public void setReffering(Collection<CollectionRefIngEntity> reffering) {
+        this.reffering = reffering;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CollectionRefEdEntity)) return false;
+
+        CollectionRefEdEntity that = (CollectionRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "CollectionRefEdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/CollectionRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/CollectionRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/CollectionRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+package org.jboss.envers.test.entities.onetomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * ReferencIng entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class CollectionRefIngEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToOne
+    private CollectionRefEdEntity reference;
+
+    public CollectionRefIngEntity() { }
+
+    public CollectionRefIngEntity(Integer id, String data, CollectionRefEdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public CollectionRefIngEntity(String data, CollectionRefEdEntity reference) {
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public CollectionRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(CollectionRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CollectionRefIngEntity)) return false;
+
+        CollectionRefIngEntity that = (CollectionRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "CollectionRefIngEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ListRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ListRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ListRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.entities.onetomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import java.util.List;
+
+/**
+ * ReferencEd entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class ListRefEdEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    private List<ListRefIngEntity> reffering;
+
+    public ListRefEdEntity() {
+    }
+
+    public ListRefEdEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public ListRefEdEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public List<ListRefIngEntity> getReffering() {
+        return reffering;
+    }
+
+    public void setReffering(List<ListRefIngEntity> reffering) {
+        this.reffering = reffering;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ListRefEdEntity)) return false;
+
+        ListRefEdEntity that = (ListRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ListRefEdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ListRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ListRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ListRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+package org.jboss.envers.test.entities.onetomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * ReferencIng entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class ListRefIngEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToOne
+    private ListRefEdEntity reference;
+
+    public ListRefIngEntity() { }
+
+    public ListRefIngEntity(Integer id, String data, ListRefEdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public ListRefIngEntity(String data, ListRefEdEntity reference) {
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public ListRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(ListRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ListRefIngEntity)) return false;
+
+        ListRefIngEntity that = (ListRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ListRefIngEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/SetRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/SetRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/SetRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.entities.onetomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import java.util.Set;
+
+/**
+ * ReferencEd entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetRefEdEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    private Set<SetRefIngEntity> reffering;
+
+    public SetRefEdEntity() {
+    }
+
+    public SetRefEdEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public SetRefEdEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<SetRefIngEntity> getReffering() {
+        return reffering;
+    }
+
+    public void setReffering(Set<SetRefIngEntity> reffering) {
+        this.reffering = reffering;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefEdEntity)) return false;
+
+        SetRefEdEntity that = (SetRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefEdEntity(id = " + id + ", data = " + data + ")";
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/SetRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/SetRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/SetRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,87 @@
+package org.jboss.envers.test.entities.onetomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * ReferencIng entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetRefIngEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToOne
+    private SetRefEdEntity reference;
+
+    public SetRefIngEntity() { }
+
+    public SetRefIngEntity(Integer id, String data, SetRefEdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public SetRefIngEntity(String data, SetRefEdEntity reference) {
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public SetRefIngEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public SetRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(SetRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefIngEntity)) return false;
+
+        SetRefIngEntity that = (SetRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefIngEntity(id = " + id + ", data = " + data + ")";
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/DoubleSetRefCollEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/DoubleSetRefCollEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/DoubleSetRefCollEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,100 @@
+package org.jboss.envers.test.entities.onetomany.detached;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.JoinTable;
+import java.util.Set;
+
+/**
+ * Set collection of references entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class DoubleSetRefCollEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany
+    @JoinTable(name = "DOUBLE_STR_1")
+    private Set<StrTestEntity> collection;
+
+    @Versioned
+    @OneToMany
+    @JoinTable(name = "DOUBLE_STR_2")
+    private Set<StrTestEntity> collection2;
+
+    public DoubleSetRefCollEntity() {
+    }
+
+    public DoubleSetRefCollEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public DoubleSetRefCollEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<StrTestEntity> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Set<StrTestEntity> collection) {
+        this.collection = collection;
+    }
+
+    public Set<StrTestEntity> getCollection2() {
+        return collection2;
+    }
+
+    public void setCollection2(Set<StrTestEntity> collection2) {
+        this.collection2 = collection2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DoubleSetRefCollEntity)) return false;
+
+        DoubleSetRefCollEntity that = (DoubleSetRefCollEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "DoubleSetRefEdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ListRefCollEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ListRefCollEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ListRefCollEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.entities.onetomany.detached;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import java.util.List;
+
+/**
+ * Set collection of references entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class ListRefCollEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany
+    private List<StrTestEntity> collection;
+
+    public ListRefCollEntity() {
+    }
+
+    public ListRefCollEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public ListRefCollEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public List<StrTestEntity> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(List<StrTestEntity> collection) {
+        this.collection = collection;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ListRefCollEntity)) return false;
+
+        ListRefCollEntity that = (ListRefCollEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefEdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/SetJoinColumnRefCollEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/SetJoinColumnRefCollEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/SetJoinColumnRefCollEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,87 @@
+package org.jboss.envers.test.entities.onetomany.detached;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.JoinColumn;
+import java.util.Set;
+
+/**
+ * A detached relation to another entity, with a @OneToMany+ at JoinColumn mapping.
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetJoinColumnRefCollEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany
+    @JoinColumn(name = "SJCR_ID")
+    private Set<StrTestEntity> collection;
+
+    public SetJoinColumnRefCollEntity() {
+    }
+
+    public SetJoinColumnRefCollEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public SetJoinColumnRefCollEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<StrTestEntity> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Set<StrTestEntity> collection) {
+        this.collection = collection;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetJoinColumnRefCollEntity)) return false;
+
+        SetJoinColumnRefCollEntity that = (SetJoinColumnRefCollEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetJoinColumnRefCollEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/SetRefCollEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/SetRefCollEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/SetRefCollEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.entities.onetomany.detached;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import java.util.Set;
+
+/**
+ * Set collection of references entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetRefCollEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany
+    private Set<StrTestEntity> collection;
+
+    public SetRefCollEntity() {
+    }
+
+    public SetRefCollEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public SetRefCollEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<StrTestEntity> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Set<StrTestEntity> collection) {
+        this.collection = collection;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefCollEntity)) return false;
+
+        SetRefCollEntity that = (SetRefCollEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefEdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/SetRefCollEntityEmbId.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/SetRefCollEntityEmbId.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/SetRefCollEntityEmbId.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,86 @@
+package org.jboss.envers.test.entities.onetomany.detached.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.EmbId;
+import org.jboss.envers.test.entities.ids.EmbIdTestEntity;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.EmbeddedId;
+import java.util.Set;
+
+/**
+ * Set collection of references entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetRefCollEntityEmbId {
+    @EmbeddedId
+    private EmbId id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany
+    private Set<EmbIdTestEntity> collection;
+
+    public SetRefCollEntityEmbId() {
+    }
+
+    public SetRefCollEntityEmbId(EmbId id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public SetRefCollEntityEmbId(String data) {
+        this.data = data;
+    }
+
+    public EmbId getId() {
+        return id;
+    }
+
+    public void setId(EmbId id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<EmbIdTestEntity> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Set<EmbIdTestEntity> collection) {
+        this.collection = collection;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefCollEntityEmbId)) return false;
+
+        SetRefCollEntityEmbId that = (SetRefCollEntityEmbId) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefCollEntityEmbId(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/SetRefCollEntityMulId.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/SetRefCollEntityMulId.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/detached/ids/SetRefCollEntityMulId.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,99 @@
+package org.jboss.envers.test.entities.onetomany.detached.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.MulIdTestEntity;
+import org.jboss.envers.test.entities.ids.MulId;
+
+import javax.persistence.*;
+import java.util.Set;
+
+/**
+ * Set collection of references entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at IdClass(MulId.class)
+public class SetRefCollEntityMulId {
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany
+    private Set<MulIdTestEntity> collection;
+
+    public SetRefCollEntityMulId() {
+    }
+
+    public SetRefCollEntityMulId(Integer id1, Integer id2, String data) {
+        this.id1 = id1;
+        this.id2 = id2;
+        this.data = data;
+    }
+
+    public SetRefCollEntityMulId(String data) {
+        this.data = data;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<MulIdTestEntity> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Set<MulIdTestEntity> collection) {
+        this.collection = collection;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefCollEntityMulId)) return false;
+
+        SetRefCollEntityMulId that = (SetRefCollEntityMulId) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefCollEntityMulId(id1 = " + id1 + ", id2 = " + id2 + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefEdEmbIdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefEdEmbIdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefEdEmbIdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.entities.onetomany.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.EmbId;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.EmbeddedId;
+import java.util.Set;
+
+/**
+ * ReferencEd entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetRefEdEmbIdEntity {
+    @EmbeddedId
+    private EmbId id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    private Set<SetRefIngEmbIdEntity> reffering;
+
+    public SetRefEdEmbIdEntity() {
+    }
+
+    public SetRefEdEmbIdEntity(EmbId id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public SetRefEdEmbIdEntity(String data) {
+        this.data = data;
+    }
+
+    public EmbId getId() {
+        return id;
+    }
+
+    public void setId(EmbId id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<SetRefIngEmbIdEntity> getReffering() {
+        return reffering;
+    }
+
+    public void setReffering(Set<SetRefIngEmbIdEntity> reffering) {
+        this.reffering = reffering;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefEdEmbIdEntity)) return false;
+
+        SetRefEdEmbIdEntity that = (SetRefEdEmbIdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefEdEmbIdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefEdMulIdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefEdMulIdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefEdMulIdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,107 @@
+package org.jboss.envers.test.entities.onetomany.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.MulId;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.IdClass;
+import java.util.Set;
+
+/**
+ * ReferencEd entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at IdClass(MulId.class)
+public class SetRefEdMulIdEntity {
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    private Set<SetRefIngMulIdEntity> reffering;
+
+    public SetRefEdMulIdEntity() {
+    }
+
+    public SetRefEdMulIdEntity(MulId id, String data) {
+        this.id1 = id.getId1();
+        this.id2 = id.getId2();
+        this.data = data;
+    }
+
+    public SetRefEdMulIdEntity(Integer id1, Integer id2, String data) {
+        this.id1 = id1;
+        this.id2 = id2;
+        this.data = data;
+    }
+
+    public SetRefEdMulIdEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<SetRefIngMulIdEntity> getReffering() {
+        return reffering;
+    }
+
+    public void setReffering(Set<SetRefIngMulIdEntity> reffering) {
+        this.reffering = reffering;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefEdMulIdEntity)) return false;
+
+        SetRefEdMulIdEntity that = (SetRefEdMulIdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefEdMulIdEntity(id1 = " + id1 + ", id2 = " + id2 + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefIngEmbIdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefIngEmbIdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefIngEmbIdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,83 @@
+package org.jboss.envers.test.entities.onetomany.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.EmbId;
+
+import javax.persistence.*;
+
+/**
+ * ReferencIng entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SetRefIngEmbIdEntity {
+    @EmbeddedId
+    private EmbId id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToOne
+    private SetRefEdEmbIdEntity reference;
+
+    public SetRefIngEmbIdEntity() { }
+
+    public SetRefIngEmbIdEntity(EmbId id, String data, SetRefEdEmbIdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public SetRefIngEmbIdEntity(String data, SetRefEdEmbIdEntity reference) {
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public EmbId getId() {
+        return id;
+    }
+
+    public void setId(EmbId id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public SetRefEdEmbIdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(SetRefEdEmbIdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefIngEmbIdEntity)) return false;
+
+        SetRefIngEmbIdEntity that = (SetRefIngEmbIdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefIngEmbIdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefIngMulIdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefIngMulIdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/onetomany/ids/SetRefIngMulIdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,105 @@
+package org.jboss.envers.test.entities.onetomany.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.MulId;
+
+import javax.persistence.*;
+
+/**
+ * ReferencIng entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at IdClass(MulId.class)
+public class SetRefIngMulIdEntity {
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToOne
+    private SetRefEdMulIdEntity reference;
+
+    public SetRefIngMulIdEntity() { }
+
+    public SetRefIngMulIdEntity(MulId id, String data, SetRefEdMulIdEntity reference) {
+        this.id1 = id.getId1();
+        this.id2 = id.getId2();
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public SetRefIngMulIdEntity(Integer id1, Integer id2, String data, SetRefEdMulIdEntity reference) {
+        this.id1 = id1;
+        this.id2 = id2;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public SetRefIngMulIdEntity(String data, SetRefEdMulIdEntity reference) {
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public SetRefEdMulIdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(SetRefEdMulIdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SetRefIngMulIdEntity)) return false;
+
+        SetRefIngMulIdEntity that = (SetRefIngMulIdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "SetRefIngMulIdEntity(id1 = " + id1 + ", id2 = " + id2 + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/CustomPropertyAccessRevEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/CustomPropertyAccessRevEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/CustomPropertyAccessRevEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,59 @@
+package org.jboss.envers.test.entities.reventity;
+
+import org.jboss.envers.RevisionNumber;
+import org.jboss.envers.RevisionTimestamp;
+import org.jboss.envers.RevisionEntity;
+
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Entity;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at RevisionEntity
+public class CustomPropertyAccessRevEntity {
+    private int customId;
+
+    private long customTimestamp;
+
+    @Id
+    @GeneratedValue
+    @RevisionNumber
+    public int getCustomId() {
+        return customId;
+    }
+
+    public void setCustomId(int customId) {
+        this.customId = customId;
+    }
+
+    @RevisionTimestamp
+    public long getCustomTimestamp() {
+        return customTimestamp;
+    }
+
+    public void setCustomTimestamp(long customTimestamp) {
+        this.customTimestamp = customTimestamp;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CustomPropertyAccessRevEntity)) return false;
+
+        CustomPropertyAccessRevEntity that = (CustomPropertyAccessRevEntity) o;
+
+        if (customId != that.customId) return false;
+        if (customTimestamp != that.customTimestamp) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = customId;
+        result = 31 * result + (int) (customTimestamp ^ (customTimestamp >>> 32));
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/CustomRevEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/CustomRevEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/entities/reventity/CustomRevEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,59 @@
+package org.jboss.envers.test.entities.reventity;
+
+import org.jboss.envers.RevisionNumber;
+import org.jboss.envers.RevisionTimestamp;
+import org.jboss.envers.RevisionEntity;
+
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Entity;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at RevisionEntity
+public class CustomRevEntity {
+    @Id
+    @GeneratedValue
+    @RevisionNumber
+    private int customId;
+
+    @RevisionTimestamp
+    private long customTimestamp;
+
+    public int getCustomId() {
+        return customId;
+    }
+
+    public void setCustomId(int customId) {
+        this.customId = customId;
+    }
+
+    public long getCustomTimestamp() {
+        return customTimestamp;
+    }
+
+    public void setCustomTimestamp(long customTimestamp) {
+        this.customTimestamp = customTimestamp;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CustomRevEntity)) return false;
+
+        CustomRevEntity that = (CustomRevEntity) o;
+
+        if (customId != that.customId) return false;
+        if (customTimestamp != that.customTimestamp) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = customId;
+        result = 31 * result + (int) (customTimestamp ^ (customTimestamp >>> 32));
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity1.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity1.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity1.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class BasicTestEntity1 {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    private String str1;
+
+    @Versioned
+    private long long1;
+
+    public BasicTestEntity1() {
+    }
+
+    public BasicTestEntity1(String str1, long long1) {
+        this.str1 = str1;
+        this.long1 = long1;
+    }
+
+    public BasicTestEntity1(Integer id, String str1, long long1) {
+        this.id = id;
+        this.str1 = str1;
+        this.long1 = long1;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public long getLong1() {
+        return long1;
+    }
+
+    public void setLong1(long long1) {
+        this.long1 = long1;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BasicTestEntity1)) return false;
+
+        BasicTestEntity1 that = (BasicTestEntity1) o;
+
+        if (long1 != that.long1) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        result = 31 * result + (int) (long1 ^ (long1 >>> 32));
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity2.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity2.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity2.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class BasicTestEntity2 {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    private String str1;
+
+    private String str2;
+
+    public BasicTestEntity2() {
+    }
+
+    public BasicTestEntity2(String str1, String str2) {
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+
+    public BasicTestEntity2(Integer id, String str1, String str2) {
+        this.id = id;
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public String getStr2() {
+        return str2;
+    }
+
+    public void setStr2(String str2) {
+        this.str2 = str2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BasicTestEntity2)) return false;
+
+        BasicTestEntity2 that = (BasicTestEntity2) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+        if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        result = 31 * result + (str2 != null ? str2.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity3.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity3.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity3.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,78 @@
+package org.jboss.envers.test.integration.basic;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class BasicTestEntity3 {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    private String str1;
+
+    private String str2;
+
+    public BasicTestEntity3() {
+    }
+
+    public BasicTestEntity3(String str1, String str2) {
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+
+    public BasicTestEntity3(Integer id, String str1, String str2) {
+        this.id = id;
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public String getStr2() {
+        return str2;
+    }
+
+    public void setStr2(String str2) {
+        this.str2 = str2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BasicTestEntity3)) return false;
+
+        BasicTestEntity3 that = (BasicTestEntity3) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+        if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        result = 31 * result + (str2 != null ? str2.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity4.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity4.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/BasicTestEntity4.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Versioned
+public class BasicTestEntity4 {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    private String str1;
+
+    private String str2;
+
+    public BasicTestEntity4() {
+    }
+
+    public BasicTestEntity4(String str1, String str2) {
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+
+    public BasicTestEntity4(Integer id, String str1, String str2) {
+        this.id = id;
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public String getStr2() {
+        return str2;
+    }
+
+    public void setStr2(String str2) {
+        this.str2 = str2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BasicTestEntity4)) return false;
+
+        BasicTestEntity4 that = (BasicTestEntity4) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+        if (str2 != null ? !str2.equals(that.str2) : that.str2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        result = 31 * result + (str2 != null ? str2.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/Delete.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/Delete.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/Delete.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,113 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Delete extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+    private Integer id3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BasicTestEntity2.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        BasicTestEntity2 bte1 = new BasicTestEntity2("x", "a");
+        BasicTestEntity2 bte2 = new BasicTestEntity2("y", "b");
+        BasicTestEntity2 bte3 = new BasicTestEntity2("z", "c");
+        em.persist(bte1);
+        em.persist(bte2);
+        em.persist(bte3);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        bte1 = em.find(BasicTestEntity2.class, bte1.getId());
+        bte2 = em.find(BasicTestEntity2.class, bte2.getId());
+        bte3 = em.find(BasicTestEntity2.class, bte3.getId());
+        bte1.setStr1("x2");
+        bte2.setStr2("b2");
+        em.remove(bte3);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        bte2 = em.find(BasicTestEntity2.class, bte2.getId());
+        em.remove(bte2);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        bte1 = em.find(BasicTestEntity2.class, bte1.getId());
+        em.remove(bte1);
+
+        em.getTransaction().commit();
+
+        id1 = bte1.getId();
+        id2 = bte2.getId();
+        id3 = bte3.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 4).equals(getVersionsReader().getRevisions(BasicTestEntity2.class, id1));
+
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(BasicTestEntity2.class, id2));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BasicTestEntity2.class, id3));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        BasicTestEntity2 ver1 = new BasicTestEntity2(id1, "x", null);
+        BasicTestEntity2 ver2 = new BasicTestEntity2(id1, "x2", null);
+
+        assert getVersionsReader().find(BasicTestEntity2.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity2.class, id1, 2).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity2.class, id1, 3).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity2.class, id1, 4) == null;
+    }
+
+    @Test
+    public void testHistoryOfId2() {
+        BasicTestEntity2 ver1 = new BasicTestEntity2(id2, "y", null);
+
+        assert getVersionsReader().find(BasicTestEntity2.class, id2, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity2.class, id2, 2).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity2.class, id2, 3) == null;
+        assert getVersionsReader().find(BasicTestEntity2.class, id2, 4) == null;
+    }
+
+    @Test
+    public void testHistoryOfId3() {
+        BasicTestEntity2 ver1 = new BasicTestEntity2(id3, "z", null);
+
+        assert getVersionsReader().find(BasicTestEntity2.class, id3, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity2.class, id3, 2) == null;
+        assert getVersionsReader().find(BasicTestEntity2.class, id3, 3) == null;
+        assert getVersionsReader().find(BasicTestEntity2.class, id3, 4) == null;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/GlobalVersioned.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/GlobalVersioned.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/GlobalVersioned.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,50 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class GlobalVersioned extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BasicTestEntity4.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        BasicTestEntity4 bte1 = new BasicTestEntity4("x", "y");
+        em.persist(bte1);
+        id1 = bte1.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        bte1 = em.find(BasicTestEntity4.class, id1);
+        bte1.setStr1("a");
+        bte1.setStr2("b");
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BasicTestEntity4.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        BasicTestEntity4 ver1 = new BasicTestEntity4(id1, "x", "y");
+        BasicTestEntity4 ver2 = new BasicTestEntity4(id1, "a", "b");
+
+        assert getVersionsReader().find(BasicTestEntity4.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity4.class, id1, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/ManyOperationsInTransaction.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/ManyOperationsInTransaction.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/ManyOperationsInTransaction.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,103 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ManyOperationsInTransaction extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+    private Integer id3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BasicTestEntity1.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        BasicTestEntity1 bte1 = new BasicTestEntity1("x", 1);
+        BasicTestEntity1 bte2 = new BasicTestEntity1("y", 20);
+        em.persist(bte1);
+        em.persist(bte2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        bte1 = em.find(BasicTestEntity1.class, bte1.getId());
+        bte2 = em.find(BasicTestEntity1.class, bte2.getId());
+        BasicTestEntity1 bte3 = new BasicTestEntity1("z", 300);
+        bte1.setStr1("x2");
+        bte2.setLong1(21);
+        em.persist(bte3);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        bte2 = em.find(BasicTestEntity1.class, bte2.getId());
+        bte3 = em.find(BasicTestEntity1.class, bte3.getId());
+        bte2.setStr1("y3");
+        bte2.setLong1(22);
+        bte3.setStr1("z3");
+
+        em.getTransaction().commit();
+
+        id1 = bte1.getId();
+        id2 = bte2.getId();
+        id3 = bte3.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BasicTestEntity1.class, id1));
+
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(BasicTestEntity1.class, id2));
+
+        assert Arrays.asList(2, 3).equals(getVersionsReader().getRevisions(BasicTestEntity1.class, id3));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        BasicTestEntity1 ver1 = new BasicTestEntity1(id1, "x", 1);
+        BasicTestEntity1 ver2 = new BasicTestEntity1(id1, "x2", 1);
+
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 2).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 3).equals(ver2);
+    }
+
+    @Test
+    public void testHistoryOfId2() {
+        BasicTestEntity1 ver1 = new BasicTestEntity1(id2, "y", 20);
+        BasicTestEntity1 ver2 = new BasicTestEntity1(id2, "y", 21);
+        BasicTestEntity1 ver3 = new BasicTestEntity1(id2, "y3", 22);
+
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 2).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 3).equals(ver3);
+    }
+
+    @Test
+    public void testHistoryOfId3() {
+        BasicTestEntity1 ver1 = new BasicTestEntity1(id3, "z", 300);
+        BasicTestEntity1 ver2 = new BasicTestEntity1(id3, "z3", 300);
+
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 1) == null;
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 2).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 3).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/NotVersioned.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/NotVersioned.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/NotVersioned.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,46 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.exception.NotVersionedException;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NotVersioned extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BasicTestEntity3.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        BasicTestEntity3 bte1 = new BasicTestEntity3("x", "y");
+        em.persist(bte1);
+        id1 = bte1.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        bte1 = em.find(BasicTestEntity3.class, id1);
+        bte1.setStr1("a");
+        bte1.setStr2("b");
+        em.getTransaction().commit();
+    }
+
+    @Test(expectedExceptions = NotVersionedException.class)
+    public void testRevisionsCounts() {
+        getVersionsReader().getRevisions(BasicTestEntity3.class, id1);
+    }
+
+    @Test(expectedExceptions = NotVersionedException.class)
+    public void testHistoryOfId1() {
+        getVersionsReader().find(BasicTestEntity3.class, id1, 1);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/NullProperties.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/NullProperties.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/NullProperties.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,78 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NullProperties extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BasicTestEntity1.class);
+    }
+
+    private Integer addNewEntity(String str, long lng) {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        BasicTestEntity1 bte1 = new BasicTestEntity1(str, lng);
+        em.persist(bte1);
+        em.getTransaction().commit();
+
+        return bte1.getId();
+    }
+
+    private void modifyEntity(Integer id, String str, long lng) {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        BasicTestEntity1 bte1 = em.find(BasicTestEntity1.class, id);
+        bte1.setLong1(lng);
+        bte1.setStr1(str);
+        em.getTransaction().commit();
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        id1 = addNewEntity("x", 1); // rev 1
+        id2 = addNewEntity(null, 20); // rev 2
+
+        modifyEntity(id1, null, 1); // rev 3
+        modifyEntity(id2, "y2", 20); // rev 4
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(BasicTestEntity1.class, id1));
+
+        assert Arrays.asList(2, 4).equals(getVersionsReader().getRevisions(BasicTestEntity1.class, id2));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        BasicTestEntity1 ver1 = new BasicTestEntity1(id1, "x", 1);
+        BasicTestEntity1 ver2 = new BasicTestEntity1(id1, null, 1);
+
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 2).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 3).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 4).equals(ver2);
+    }
+
+    @Test
+    public void testHistoryOfId2() {
+        BasicTestEntity1 ver1 = new BasicTestEntity1(id2, null, 20);
+        BasicTestEntity1 ver2 = new BasicTestEntity1(id2, "y2", 20);
+
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 1) == null;
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 2).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 3).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 4).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/Simple.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/Simple.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/Simple.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,50 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.IntTestEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Simple extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(IntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        IntTestEntity ite = new IntTestEntity(10);
+        em.persist(ite);
+        id1 = ite.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        ite = em.find(IntTestEntity.class, id1);
+        ite.setNumber(20);
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(IntTestEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        IntTestEntity ver1 = new IntTestEntity(10, id1);
+        IntTestEntity ver2 = new IntTestEntity(20, id1);
+
+        assert getVersionsReader().find(IntTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(IntTestEntity.class, id1, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/SingleOperationInTransaction.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/SingleOperationInTransaction.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/SingleOperationInTransaction.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,136 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SingleOperationInTransaction extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+    private Integer id3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BasicTestEntity1.class);
+    }
+
+    private Integer addNewEntity(String str, long lng) {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        BasicTestEntity1 bte1 = new BasicTestEntity1(str, lng);
+        em.persist(bte1);
+        em.getTransaction().commit();
+
+        return bte1.getId();
+    }
+
+    private void modifyEntity(Integer id, String str, long lng) {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        BasicTestEntity1 bte1 = em.find(BasicTestEntity1.class, id);
+        bte1.setLong1(lng);
+        bte1.setStr1(str);
+        em.getTransaction().commit();
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        id1 = addNewEntity("x", 1); // rev 1
+        id2 = addNewEntity("y", 20); // rev 2
+        id3 = addNewEntity("z", 30); // rev 3
+
+        modifyEntity(id1, "x2", 2); // rev 4
+        modifyEntity(id2, "y2", 20); // rev 5
+        modifyEntity(id1, "x3", 3); // rev 6
+        modifyEntity(id1, "x3", 3); // no rev
+        modifyEntity(id2, "y3", 21); // rev 7
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 4, 6).equals(getVersionsReader().getRevisions(BasicTestEntity1.class, id1));
+
+        assert Arrays.asList(2, 5, 7).equals(getVersionsReader().getRevisions(BasicTestEntity1.class, id2));
+
+        assert Arrays.asList(3).equals(getVersionsReader().getRevisions(BasicTestEntity1.class, id3));
+    }
+
+    @Test
+    public void testRevisionsDates() {
+        for (int i=1; i<7; i++) {
+            assert getVersionsReader().getRevisionDate(i).getTime() <=
+                    getVersionsReader().getRevisionDate(i+1).getTime();
+        }
+    }
+
+    @Test(expectedExceptions = RevisionDoesNotExistException.class)
+    public void testNotExistingRevision() {
+        getVersionsReader().getRevisionDate(8);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testIllegalRevision() {
+        getVersionsReader().getRevisionDate(0);
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        BasicTestEntity1 ver1 = new BasicTestEntity1(id1, "x", 1);
+        BasicTestEntity1 ver2 = new BasicTestEntity1(id1, "x2", 2);
+        BasicTestEntity1 ver3 = new BasicTestEntity1(id1, "x3", 3);
+
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 2).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 3).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 4).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 5).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 6).equals(ver3);
+        assert getVersionsReader().find(BasicTestEntity1.class, id1, 7).equals(ver3);
+    }
+
+    @Test
+    public void testHistoryOfId2() {
+        BasicTestEntity1 ver1 = new BasicTestEntity1(id2, "y", 20);
+        BasicTestEntity1 ver2 = new BasicTestEntity1(id2, "y2", 20);
+        BasicTestEntity1 ver3 = new BasicTestEntity1(id2, "y3", 21);
+
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 1) == null;
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 2).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 3).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 4).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 5).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 6).equals(ver2);
+        assert getVersionsReader().find(BasicTestEntity1.class, id2, 7).equals(ver3);
+    }
+
+    @Test
+    public void testHistoryOfId3() {
+        BasicTestEntity1 ver1 = new BasicTestEntity1(id3, "z", 30);
+
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 1) == null;
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 2) == null;
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 3).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 4).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 5).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 6).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity1.class, id3, 7).equals(ver1);
+    }
+
+    @Test
+    public void testHistoryOfNotExistingEntity() {
+        assert getVersionsReader().find(BasicTestEntity1.class, id1+id2+id3, 1) == null;
+        assert getVersionsReader().find(BasicTestEntity1.class, id1+id2+id3, 7) == null;
+    }
+
+    @Test
+    public void testRevisionsOfNotExistingEntity() {
+        assert getVersionsReader().getRevisions(BasicTestEntity1.class, id1+id2+id3).size() == 0;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/UnversionedPropertiesChange.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/UnversionedPropertiesChange.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/UnversionedPropertiesChange.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,61 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class UnversionedPropertiesChange extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BasicTestEntity2.class);
+    }
+
+    private Integer addNewEntity(String str1, String str2) {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        BasicTestEntity2 bte2 = new BasicTestEntity2(str1, str2);
+        em.persist(bte2);
+        em.getTransaction().commit();
+
+        return bte2.getId();
+    }
+
+    private void modifyEntity(Integer id, String str1, String str2) {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        BasicTestEntity2 bte2 = em.find(BasicTestEntity2.class, id);
+        bte2.setStr1(str1);
+        bte2.setStr2(str2);
+        em.getTransaction().commit();
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        id1 = addNewEntity("x", "a"); // rev 1
+        modifyEntity(id1, "x", "a"); // no rev
+        modifyEntity(id1, "y", "b"); // rev 2
+        modifyEntity(id1, "y", "c"); // no rev
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BasicTestEntity2.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        BasicTestEntity2 ver1 = new BasicTestEntity2(id1, "x", null);
+        BasicTestEntity2 ver2 = new BasicTestEntity2(id1, "y", null);
+
+        assert getVersionsReader().find(BasicTestEntity2.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(BasicTestEntity2.class, id1, 2).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/UnversionedProperty.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/UnversionedProperty.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/basic/UnversionedProperty.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,54 @@
+package org.jboss.envers.test.integration.basic;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.jboss.envers.test.entities.UnversionedEntity;
+import org.jboss.envers.test.AbstractEntityTest;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class UnversionedProperty extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(UnversionedEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        // Rev 1
+        em.getTransaction().begin();
+        UnversionedEntity ue1 = new UnversionedEntity("a1", "b1");
+        em.persist(ue1);
+        id1 = ue1.getId();
+        em.getTransaction().commit();
+
+        // Rev 2
+        em.getTransaction().begin();
+        ue1 = em.find(UnversionedEntity.class, id1);
+        ue1.setData1("a2");
+        ue1.setData2("b2");
+        em.getTransaction().commit();
+    }
+
+     @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(UnversionedEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        UnversionedEntity rev1 = new UnversionedEntity(id1, "a1", null);
+        UnversionedEntity rev2 = new UnversionedEntity(id1, "a2", null);
+
+        assert getVersionsReader().find(UnversionedEntity.class, id1, 1).equals(rev1);
+        assert getVersionsReader().find(UnversionedEntity.class, id1, 2).equals(rev2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/OneToManyCache.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/OneToManyCache.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/OneToManyCache.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,120 @@
+package org.jboss.envers.test.integration.cache;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.SetRefEdEntity;
+import org.jboss.envers.test.entities.onetomany.SetRefIngEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"ObjectEquality"})
+public class OneToManyCache extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetRefEdEntity.class);
+        cfg.addAnnotatedClass(SetRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        SetRefEdEntity ed1 = new SetRefEdEntity(1, "data_ed_1");
+        SetRefEdEntity ed2 = new SetRefEdEntity(2, "data_ed_2");
+
+        SetRefIngEntity ing1 = new SetRefIngEntity(1, "data_ing_1");
+        SetRefIngEntity ing2 = new SetRefIngEntity(2, "data_ing_2");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        ing1.setReference(ed1);
+        ing2.setReference(ed1);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetRefIngEntity.class, ing1.getId());
+        ing2 = em.find(SetRefIngEntity.class, ing2.getId());
+        ed2 = em.find(SetRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+        ing2.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testCacheReferenceAccessAfterFind() {
+        SetRefEdEntity ed1_rev1 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 1);
+
+        SetRefIngEntity ing1_rev1 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 1);
+        SetRefIngEntity ing2_rev1 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 1);
+
+        // It should be exactly the same object
+        assert ing1_rev1.getReference() == ed1_rev1;
+        assert ing2_rev1.getReference() == ed1_rev1;
+    }
+
+    @Test
+    public void testCacheReferenceAccessAfterCollectionAccessRev1() {
+        SetRefEdEntity ed1_rev1 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 1);
+
+        // It should be exactly the same object
+        assert ed1_rev1.getReffering().size() == 2;
+        for (SetRefIngEntity setRefIngEntity : ed1_rev1.getReffering()) {
+            assert setRefIngEntity.getReference() == ed1_rev1;
+        }
+    }
+
+    @Test
+    public void testCacheReferenceAccessAfterCollectionAccessRev2() {
+        SetRefEdEntity ed2_rev2 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 2);
+
+        assert ed2_rev2.getReffering().size() == 2;
+        for (SetRefIngEntity setRefIngEntity : ed2_rev2.getReffering()) {
+            assert setRefIngEntity.getReference() == ed2_rev2;
+        }
+    }
+
+    @Test
+    public void testCacheFindAfterCollectionAccessRev1() {
+        SetRefEdEntity ed1_rev1 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 1);
+
+        // Reading the collection
+        assert ed1_rev1.getReffering().size() == 2;
+
+        SetRefIngEntity ing1_rev1 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 1);
+        SetRefIngEntity ing2_rev1 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 1);
+
+        for (SetRefIngEntity setRefIngEntity : ed1_rev1.getReffering()) {
+            assert setRefIngEntity == ing1_rev1 || setRefIngEntity == ing2_rev1;
+        }
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/OneToOneCache.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/OneToOneCache.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/OneToOneCache.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,80 @@
+package org.jboss.envers.test.integration.cache;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.integration.onetoone.bidirectional.BiRefEdEntity;
+import org.jboss.envers.test.integration.onetoone.bidirectional.BiRefIngEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"ObjectEquality"})
+public class OneToOneCache extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BiRefEdEntity.class);
+        cfg.addAnnotatedClass(BiRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        BiRefEdEntity ed1 = new BiRefEdEntity(1, "data_ed_1");
+        BiRefEdEntity ed2 = new BiRefEdEntity(2, "data_ed_2");
+
+        BiRefIngEntity ing1 = new BiRefIngEntity(3, "data_ing_1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing1.setReference(ed1);
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(BiRefIngEntity.class, ing1.getId());
+        ed2 = em.find(BiRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+    }
+
+    @Test
+    public void testCacheReferenceAccessAfterFindRev1() {
+        BiRefEdEntity ed1_rev1 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 1);
+        BiRefIngEntity ing1_rev1 = getVersionsReader().find(BiRefIngEntity.class, ing1_id, 1);
+
+        assert ing1_rev1.getReference() == ed1_rev1;
+    }
+
+    @Test
+    public void testCacheReferenceAccessAfterFindRev2() {
+        BiRefEdEntity ed2_rev2 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 2);
+        BiRefIngEntity ing1_rev2 = getVersionsReader().find(BiRefIngEntity.class, ing1_id, 2);
+
+        assert ing1_rev2.getReference() == ed2_rev2;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/QueryCache.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/QueryCache.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/cache/QueryCache.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,63 @@
+package org.jboss.envers.test.integration.cache;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.IntTestEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"ObjectEquality"})
+public class QueryCache extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(IntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        IntTestEntity ite = new IntTestEntity(10);
+        em.persist(ite);
+        id1 = ite.getId();
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+        ite = em.find(IntTestEntity.class, id1);
+        ite.setNumber(20);
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testCacheFindAfterRevisionsOfEntityQuery() {
+        List entsFromQuery = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(IntTestEntity.class, true, false)
+                .getResultList();
+
+        IntTestEntity entFromFindRev1 = getVersionsReader().find(IntTestEntity.class, id1, 1);
+        IntTestEntity entFromFindRev2 = getVersionsReader().find(IntTestEntity.class, id1, 2);
+
+        assert entFromFindRev1 == entsFromQuery.get(0);
+        assert entFromFindRev2 == entsFromQuery.get(1);
+    }
+
+    @Test
+    public void testCacheFindAfterEntitiesAtRevisionQuery() {
+        IntTestEntity entFromQuery = (IntTestEntity) getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 1)
+                .getSingleResult();
+
+        IntTestEntity entFromFind = getVersionsReader().find(IntTestEntity.class, id1, 1);
+
+        assert entFromFind == entFromQuery;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/EnumSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/EnumSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/EnumSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.integration.collection;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.test.entities.collection.EnumSetEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+import static org.jboss.envers.test.entities.collection.EnumSetEntity.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EnumSet extends AbstractEntityTest {
+    private Integer sse1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(EnumSetEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        EnumSetEntity sse1 = new EnumSetEntity();
+
+        // Revision 1 (sse1: initialy 1 element)
+        em.getTransaction().begin();
+
+        sse1.getEnums1().add(E1.X);
+        sse1.getEnums2().add(E2.A);
+
+        em.persist(sse1);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (sse1: adding 1 element/removing a non-existing element)
+        em.getTransaction().begin();
+
+        sse1 = em.find(EnumSetEntity.class, sse1.getId());
+
+        sse1.getEnums1().add(E1.Y);
+        sse1.getEnums2().remove(E2.B);
+
+        em.getTransaction().commit();
+
+        // Revision 3 (sse1: removing 1 element/adding an exisiting element)
+        em.getTransaction().begin();
+
+        sse1 = em.find(EnumSetEntity.class, sse1.getId());
+
+        sse1.getEnums1().remove(E1.X);
+        sse1.getEnums2().add(E2.A);
+
+        em.getTransaction().commit();
+
+        //
+
+        sse1_id = sse1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(EnumSetEntity.class, sse1_id));
+    }
+
+    @Test
+    public void testHistoryOfSse1() {
+        EnumSetEntity rev1 = getVersionsReader().find(EnumSetEntity.class, sse1_id, 1);
+        EnumSetEntity rev2 = getVersionsReader().find(EnumSetEntity.class, sse1_id, 2);
+        EnumSetEntity rev3 = getVersionsReader().find(EnumSetEntity.class, sse1_id, 3);
+
+        assert rev1.getEnums1().equals(TestTools.makeSet(E1.X));
+        assert rev2.getEnums1().equals(TestTools.makeSet(E1.X, E1.Y));
+        assert rev3.getEnums1().equals(TestTools.makeSet(E1.Y));
+
+        assert rev1.getEnums2().equals(TestTools.makeSet(E2.A));
+        assert rev2.getEnums2().equals(TestTools.makeSet(E2.A));
+        assert rev3.getEnums2().equals(TestTools.makeSet(E2.A));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringList.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringList.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringList.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,101 @@
+package org.jboss.envers.test.integration.collection;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.test.entities.collection.StringListEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class StringList extends AbstractEntityTest {
+    private Integer sle1_id;
+    private Integer sle2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StringListEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StringListEntity sle1 = new StringListEntity();
+        StringListEntity sle2 = new StringListEntity();
+
+        // Revision 1 (sle1: initialy empty, sle2: initialy 2 elements)
+        em.getTransaction().begin();
+
+        sle2.getStrings().add("sle2_string1");
+        sle2.getStrings().add("sle2_string2");
+
+        em.persist(sle1);
+        em.persist(sle2);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (sle1: adding 2 elements, sle2: adding an existing element)
+        em.getTransaction().begin();
+
+        sle1 = em.find(StringListEntity.class, sle1.getId());
+        sle2 = em.find(StringListEntity.class, sle2.getId());
+
+        sle1.getStrings().add("sle1_string1");
+        sle1.getStrings().add("sle1_string2");
+
+        sle2.getStrings().add("sle2_string1");
+
+        em.getTransaction().commit();
+
+        // Revision 3 (sle1: replacing an element at index 0, sle2: removing an element at index 0)
+        em.getTransaction().begin();
+
+        sle1 = em.find(StringListEntity.class, sle1.getId());
+        sle2 = em.find(StringListEntity.class, sle2.getId());
+
+        sle1.getStrings().set(0, "sle1_string3");
+
+        sle2.getStrings().remove(0);
+
+        em.getTransaction().commit();
+
+        //
+
+        sle1_id = sle1.getId();
+        sle2_id = sle2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(StringListEntity.class, sle1_id));
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(StringListEntity.class, sle2_id));
+    }
+
+    @Test
+    public void testHistoryOfSle1() {
+        StringListEntity rev1 = getVersionsReader().find(StringListEntity.class, sle1_id, 1);
+        StringListEntity rev2 = getVersionsReader().find(StringListEntity.class, sle1_id, 2);
+        StringListEntity rev3 = getVersionsReader().find(StringListEntity.class, sle1_id, 3);
+
+        assert rev1.getStrings().equals(Collections.EMPTY_LIST);
+        assert rev2.getStrings().equals(TestTools.makeList("sle1_string1", "sle1_string2"));
+        assert rev3.getStrings().equals(TestTools.makeList("sle1_string3", "sle1_string2"));
+    }
+
+    @Test
+    public void testHistoryOfSse2() {
+        StringListEntity rev1 = getVersionsReader().find(StringListEntity.class, sle2_id, 1);
+        StringListEntity rev2 = getVersionsReader().find(StringListEntity.class, sle2_id, 2);
+        StringListEntity rev3 = getVersionsReader().find(StringListEntity.class, sle2_id, 3);
+
+        assert rev1.getStrings().equals(TestTools.makeList("sle2_string1", "sle2_string2"));
+        assert rev2.getStrings().equals(TestTools.makeList("sle2_string1", "sle2_string2", "sle2_string1"));
+        assert rev3.getStrings().equals(TestTools.makeList("sle2_string2", "sle2_string1"));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringMap.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringMap.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringMap.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,112 @@
+package org.jboss.envers.test.integration.collection;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.test.entities.collection.StringMapEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class StringMap extends AbstractEntityTest {
+    private Integer sme1_id;
+    private Integer sme2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StringMapEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StringMapEntity sme1 = new StringMapEntity();
+        StringMapEntity sme2 = new StringMapEntity();
+
+        // Revision 1 (sme1: initialy empty, sme2: initialy 1 mapping)
+        em.getTransaction().begin();
+
+        sme2.getStrings().put("1", "a");
+
+        em.persist(sme1);
+        em.persist(sme2);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (sme1: adding 2 mappings, sme2: no changes)
+        em.getTransaction().begin();
+
+        sme1 = em.find(StringMapEntity.class, sme1.getId());
+        sme2 = em.find(StringMapEntity.class, sme2.getId());
+
+        sme1.getStrings().put("1", "a");
+        sme1.getStrings().put("2", "b");
+
+        em.getTransaction().commit();
+
+        // Revision 3 (sme1: removing an existing mapping, sme2: replacing a value)
+        em.getTransaction().begin();
+
+        sme1 = em.find(StringMapEntity.class, sme1.getId());
+        sme2 = em.find(StringMapEntity.class, sme2.getId());
+
+        sme1.getStrings().remove("1");
+        sme2.getStrings().put("1", "b");
+        
+        em.getTransaction().commit();
+
+        // No revision (sme1: removing a non-existing mapping, sme2: replacing with the same value)
+        em.getTransaction().begin();
+
+        sme1 = em.find(StringMapEntity.class, sme1.getId());
+        sme2 = em.find(StringMapEntity.class, sme2.getId());
+
+        sme1.getStrings().remove("3");
+        sme2.getStrings().put("1", "b");
+
+        em.getTransaction().commit();
+
+        //
+
+        sme1_id = sme1.getId();
+        sme2_id = sme2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(StringMapEntity.class, sme1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(StringMapEntity.class, sme2_id));
+    }
+
+    @Test
+    public void testHistoryOfSse1() {
+        StringMapEntity rev1 = getVersionsReader().find(StringMapEntity.class, sme1_id, 1);
+        StringMapEntity rev2 = getVersionsReader().find(StringMapEntity.class, sme1_id, 2);
+        StringMapEntity rev3 = getVersionsReader().find(StringMapEntity.class, sme1_id, 3);
+        StringMapEntity rev4 = getVersionsReader().find(StringMapEntity.class, sme1_id, 4);
+
+        assert rev1.getStrings().equals(Collections.EMPTY_MAP);
+        assert rev2.getStrings().equals(TestTools.makeMap("1", "a", "2", "b"));
+        assert rev3.getStrings().equals(TestTools.makeMap("2", "b"));
+        assert rev4.getStrings().equals(TestTools.makeMap("2", "b"));
+    }
+
+    @Test
+    public void testHistoryOfSse2() {
+        StringMapEntity rev1 = getVersionsReader().find(StringMapEntity.class, sme2_id, 1);
+        StringMapEntity rev2 = getVersionsReader().find(StringMapEntity.class, sme2_id, 2);
+        StringMapEntity rev3 = getVersionsReader().find(StringMapEntity.class, sme2_id, 3);
+        StringMapEntity rev4 = getVersionsReader().find(StringMapEntity.class, sme2_id, 4);
+
+        assert rev1.getStrings().equals(TestTools.makeMap("1", "a"));
+        assert rev2.getStrings().equals(TestTools.makeMap("1", "a"));
+        assert rev3.getStrings().equals(TestTools.makeMap("1", "b"));
+        assert rev4.getStrings().equals(TestTools.makeMap("1", "b"));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/StringSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,100 @@
+package org.jboss.envers.test.integration.collection;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.test.entities.collection.StringSetEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class StringSet extends AbstractEntityTest {
+    private Integer sse1_id;
+    private Integer sse2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StringSetEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StringSetEntity sse1 = new StringSetEntity();
+        StringSetEntity sse2 = new StringSetEntity();
+
+        // Revision 1 (sse1: initialy empty, sse2: initialy 2 elements)
+        em.getTransaction().begin();
+
+        sse2.getStrings().add("sse2_string1");
+        sse2.getStrings().add("sse2_string2");
+
+        em.persist(sse1);
+        em.persist(sse2);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (sse1: adding 2 elements, sse2: adding an existing element)
+        em.getTransaction().begin();
+
+        sse1 = em.find(StringSetEntity.class, sse1.getId());
+        sse2 = em.find(StringSetEntity.class, sse2.getId());
+
+        sse1.getStrings().add("sse1_string1");
+        sse1.getStrings().add("sse1_string2");
+
+        sse2.getStrings().add("sse2_string1");
+
+        em.getTransaction().commit();
+
+        // Revision 3 (sse1: removing a non-existing element, sse2: removing one element)
+        em.getTransaction().begin();
+
+        sse1 = em.find(StringSetEntity.class, sse1.getId());
+        sse2 = em.find(StringSetEntity.class, sse2.getId());
+
+        sse1.getStrings().remove("sse1_string3");
+        sse2.getStrings().remove("sse2_string1");
+
+        em.getTransaction().commit();
+
+        //
+
+        sse1_id = sse1.getId();
+        sse2_id = sse2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StringSetEntity.class, sse1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(StringSetEntity.class, sse2_id));
+    }
+
+    @Test
+    public void testHistoryOfSse1() {
+        StringSetEntity rev1 = getVersionsReader().find(StringSetEntity.class, sse1_id, 1);
+        StringSetEntity rev2 = getVersionsReader().find(StringSetEntity.class, sse1_id, 2);
+        StringSetEntity rev3 = getVersionsReader().find(StringSetEntity.class, sse1_id, 3);
+
+        assert rev1.getStrings().equals(Collections.EMPTY_SET);
+        assert rev2.getStrings().equals(TestTools.makeSet("sse1_string1", "sse1_string2"));
+        assert rev3.getStrings().equals(TestTools.makeSet("sse1_string1", "sse1_string2"));
+    }
+
+    @Test
+    public void testHistoryOfSse2() {
+        StringSetEntity rev1 = getVersionsReader().find(StringSetEntity.class, sse2_id, 1);
+        StringSetEntity rev2 = getVersionsReader().find(StringSetEntity.class, sse2_id, 2);
+        StringSetEntity rev3 = getVersionsReader().find(StringSetEntity.class, sse2_id, 3);
+
+        assert rev1.getStrings().equals(TestTools.makeSet("sse2_string1", "sse2_string2"));
+        assert rev2.getStrings().equals(TestTools.makeSet("sse2_string1", "sse2_string2"));
+        assert rev3.getStrings().equals(TestTools.makeSet("sse2_string2"));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/ComponentMapKey.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/ComponentMapKey.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/ComponentMapKey.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,88 @@
+package org.jboss.envers.test.integration.collection.mapkey;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.test.entities.components.ComponentTestEntity;
+import org.jboss.envers.test.entities.components.Component1;
+import org.jboss.envers.test.entities.components.Component2;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ComponentMapKey extends AbstractEntityTest {
+    private Integer cmke_id;
+
+    private Integer cte1_id;
+    private Integer cte2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ComponentMapKeyEntity.class);
+        cfg.addAnnotatedClass(ComponentTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        ComponentMapKeyEntity imke = new ComponentMapKeyEntity();
+
+        // Revision 1 (intialy 1 mapping)
+        em.getTransaction().begin();
+
+        ComponentTestEntity cte1 = new ComponentTestEntity(new Component1("x1", "y2"), new Component2("a1", "b2"));
+        ComponentTestEntity cte2 = new ComponentTestEntity(new Component1("x1", "y2"), new Component2("a1", "b2"));
+
+        em.persist(cte1);
+        em.persist(cte2);
+
+        imke.getIdmap().put(cte1.getComp1(), cte1);
+
+        em.persist(imke);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (sse1: adding 1 mapping)
+        em.getTransaction().begin();
+
+        cte2 = em.find(ComponentTestEntity.class, cte2.getId());
+        imke = em.find(ComponentMapKeyEntity.class, imke.getId());
+
+        imke.getIdmap().put(cte2.getComp1(), cte2);
+
+        em.getTransaction().commit();
+
+        //
+
+        cmke_id = imke.getId();
+
+        cte1_id = cte1.getId();
+        cte2_id = cte2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(ComponentMapKeyEntity.class, cmke_id));
+    }
+
+    @Test
+    public void testHistoryOfImke() {
+        ComponentTestEntity cte1 = getEntityManager().find(ComponentTestEntity.class, cte1_id);
+        ComponentTestEntity cte2 = getEntityManager().find(ComponentTestEntity.class, cte2_id);
+
+        // These fields are unversioned.
+        cte1.setComp2(null);
+        cte2.setComp2(null);
+
+        ComponentMapKeyEntity rev1 = getVersionsReader().find(ComponentMapKeyEntity.class, cmke_id, 1);
+        ComponentMapKeyEntity rev2 = getVersionsReader().find(ComponentMapKeyEntity.class, cmke_id, 2);
+
+        assert rev1.getIdmap().equals(TestTools.makeMap(cte1.getComp1(), cte1));
+        assert rev2.getIdmap().equals(TestTools.makeMap(cte1.getComp1(), cte1, cte2.getComp1(), cte2));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/ComponentMapKeyEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/ComponentMapKeyEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/ComponentMapKeyEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,63 @@
+package org.jboss.envers.test.integration.collection.mapkey;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.components.Component1;
+import org.jboss.envers.test.entities.components.ComponentTestEntity;
+
+import javax.persistence.*;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class ComponentMapKeyEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @ManyToMany
+    @MapKey(name = "comp1")
+    private Map<Component1, ComponentTestEntity> idmap;
+
+    public ComponentMapKeyEntity() {
+        idmap = new HashMap<Component1, ComponentTestEntity>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Map<Component1, ComponentTestEntity> getIdmap() {
+        return idmap;
+    }
+
+    public void setIdmap(Map<Component1, ComponentTestEntity> idmap) {
+        this.idmap = idmap;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ComponentMapKeyEntity)) return false;
+
+        ComponentMapKeyEntity that = (ComponentMapKeyEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "CMKE(id = " + id + ", idmap = " + idmap + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/IdMapKey.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/IdMapKey.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/IdMapKey.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,82 @@
+package org.jboss.envers.test.integration.collection.mapkey;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class IdMapKey extends AbstractEntityTest {
+    private Integer imke_id;
+
+    private Integer ste1_id;
+    private Integer ste2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(IdMapKeyEntity.class);
+        cfg.addAnnotatedClass(StrTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        IdMapKeyEntity imke = new IdMapKeyEntity();
+
+        // Revision 1 (intialy 1 mapping)
+        em.getTransaction().begin();
+
+        StrTestEntity ste1 = new StrTestEntity("x");
+        StrTestEntity ste2 = new StrTestEntity("y");
+
+        em.persist(ste1);
+        em.persist(ste2);
+
+        imke.getIdmap().put(ste1.getId(), ste1);
+
+        em.persist(imke);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (sse1: adding 1 mapping)
+        em.getTransaction().begin();
+
+        ste2 = em.find(StrTestEntity.class, ste2.getId());
+        imke = em.find(IdMapKeyEntity.class, imke.getId());
+
+        imke.getIdmap().put(ste2.getId(), ste2);
+
+        em.getTransaction().commit();
+
+        //
+
+        imke_id = imke.getId();
+
+        ste1_id = ste1.getId();
+        ste2_id = ste2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(IdMapKeyEntity.class, imke_id));
+    }
+
+    @Test
+    public void testHistoryOfImke() {
+        StrTestEntity ste1 = getEntityManager().find(StrTestEntity.class, ste1_id);
+        StrTestEntity ste2 = getEntityManager().find(StrTestEntity.class, ste2_id);
+
+        IdMapKeyEntity rev1 = getVersionsReader().find(IdMapKeyEntity.class, imke_id, 1);
+        IdMapKeyEntity rev2 = getVersionsReader().find(IdMapKeyEntity.class, imke_id, 2);
+
+        assert rev1.getIdmap().equals(TestTools.makeMap(ste1.getId(), ste1));
+        assert rev2.getIdmap().equals(TestTools.makeMap(ste1.getId(), ste1, ste2.getId(), ste2));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/IdMapKeyEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/IdMapKeyEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/collection/mapkey/IdMapKeyEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,62 @@
+package org.jboss.envers.test.integration.collection.mapkey;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.*;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class IdMapKeyEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @ManyToMany
+    @MapKey
+    private Map<Integer, StrTestEntity> idmap;
+
+    public IdMapKeyEntity() {
+        idmap = new HashMap<Integer, StrTestEntity>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Map<Integer, StrTestEntity> getIdmap() {
+        return idmap;
+    }
+
+    public void setIdmap(Map<Integer, StrTestEntity> idmap) {
+        this.idmap = idmap;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof IdMapKeyEntity)) return false;
+
+        IdMapKeyEntity that = (IdMapKeyEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "IMKE(id = " + id + ", idmap = " + idmap + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/components/Components.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/components/Components.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/components/Components.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,125 @@
+package org.jboss.envers.test.integration.components;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.components.Component1;
+import org.jboss.envers.test.entities.components.Component2;
+import org.jboss.envers.test.entities.components.ComponentTestEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Components extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+    private Integer id3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ComponentTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        ComponentTestEntity cte1 = new ComponentTestEntity(new Component1("a", "b"), new Component2("x", "y"));
+        ComponentTestEntity cte2 = new ComponentTestEntity(new Component1("a2", "b2"), new Component2("x2", "y2"));
+        ComponentTestEntity cte3 = new ComponentTestEntity(new Component1("a3", "b3"), new Component2("x3", "y3"));
+
+        em.persist(cte1);
+        em.persist(cte2);
+        em.persist(cte3);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        cte1 = em.find(ComponentTestEntity.class, cte1.getId());
+        cte2 = em.find(ComponentTestEntity.class, cte2.getId());
+        cte3 = em.find(ComponentTestEntity.class, cte3.getId());
+
+        cte1.setComp1(new Component1("a'", "b'"));
+        cte2.getComp1().setStr1("a2'");
+        cte3.getComp2().setStr6("y3'");
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        cte1 = em.find(ComponentTestEntity.class, cte1.getId());
+        cte2 = em.find(ComponentTestEntity.class, cte2.getId());
+        cte3 = em.find(ComponentTestEntity.class, cte3.getId());
+
+        cte1.setComp2(new Component2("x'", "y'"));
+        cte3.getComp1().setStr2("b3'");
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        cte2 = em.find(ComponentTestEntity.class, cte2.getId());
+
+        em.remove(cte2);
+
+        em.getTransaction().commit();
+
+        id1 = cte1.getId();
+        id2 = cte2.getId();
+        id3 = cte3.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(ComponentTestEntity.class, id1));
+
+        assert Arrays.asList(1, 2, 4).equals(getVersionsReader().getRevisions(ComponentTestEntity.class, id2));
+
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(ComponentTestEntity.class, id3));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        ComponentTestEntity ver1 = new ComponentTestEntity(id1, new Component1("a", "b"), null);
+        ComponentTestEntity ver2 = new ComponentTestEntity(id1, new Component1("a'", "b'"), null);
+
+        assert getVersionsReader().find(ComponentTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(ComponentTestEntity.class, id1, 2).equals(ver2);
+        assert getVersionsReader().find(ComponentTestEntity.class, id1, 3).equals(ver2);
+        assert getVersionsReader().find(ComponentTestEntity.class, id1, 4).equals(ver2);
+    }
+
+    @Test
+    public void testHistoryOfId2() {
+        ComponentTestEntity ver1 = new ComponentTestEntity(id2, new Component1("a2", "b2"), null);
+        ComponentTestEntity ver2 = new ComponentTestEntity(id2, new Component1("a2'", "b2"), null);
+
+        assert getVersionsReader().find(ComponentTestEntity.class, id2, 1).equals(ver1);
+        assert getVersionsReader().find(ComponentTestEntity.class, id2, 2).equals(ver2);
+        assert getVersionsReader().find(ComponentTestEntity.class, id2, 3).equals(ver2);
+        assert getVersionsReader().find(ComponentTestEntity.class, id2, 4) == null;
+    }
+
+    @Test
+    public void testHistoryOfId3() {
+        ComponentTestEntity ver1 = new ComponentTestEntity(id3, new Component1("a3", "b3"), null);
+        ComponentTestEntity ver2 = new ComponentTestEntity(id3, new Component1("a3", "b3'"), null);
+
+        assert getVersionsReader().find(ComponentTestEntity.class, id3, 1).equals(ver1);
+        assert getVersionsReader().find(ComponentTestEntity.class, id3, 2).equals(ver1);
+        assert getVersionsReader().find(ComponentTestEntity.class, id3, 3).equals(ver2);
+        assert getVersionsReader().find(ComponentTestEntity.class, id3, 4).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/CompositeCustom.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/CompositeCustom.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/CompositeCustom.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,76 @@
+package org.jboss.envers.test.integration.customtype;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.customtype.CompositeCustomTypeEntity;
+import org.jboss.envers.test.entities.customtype.Component;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class CompositeCustom extends AbstractEntityTest {
+    private Integer ccte_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(CompositeCustomTypeEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        CompositeCustomTypeEntity ccte = new CompositeCustomTypeEntity();
+
+        // Revision 1 (persisting 1 entity)
+        em.getTransaction().begin();
+
+        ccte.setComponent(new Component("a", 1));
+
+        em.persist(ccte);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (changing the component)
+        em.getTransaction().begin();
+
+        ccte = em.find(CompositeCustomTypeEntity.class, ccte.getId());
+
+        ccte.getComponent().setProp1("b");
+
+        em.getTransaction().commit();
+
+        // Revision 3 (replacing the component)
+        em.getTransaction().begin();
+
+        ccte = em.find(CompositeCustomTypeEntity.class, ccte.getId());
+
+        ccte.setComponent(new Component("c", 3));
+
+        em.getTransaction().commit();
+
+        //
+
+        ccte_id = ccte.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(CompositeCustomTypeEntity.class, ccte_id));
+    }
+
+    @Test
+    public void testHistoryOfCcte() {
+        CompositeCustomTypeEntity rev1 = getVersionsReader().find(CompositeCustomTypeEntity.class, ccte_id, 1);
+        CompositeCustomTypeEntity rev2 = getVersionsReader().find(CompositeCustomTypeEntity.class, ccte_id, 2);
+        CompositeCustomTypeEntity rev3 = getVersionsReader().find(CompositeCustomTypeEntity.class, ccte_id, 3);
+
+        assert rev1.getComponent().equals(new Component("a", 1));
+        assert rev2.getComponent().equals(new Component("b", 1));
+        assert rev3.getComponent().equals(new Component("c", 3));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/ParametrizedCustom.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/ParametrizedCustom.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/customtype/ParametrizedCustom.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,64 @@
+package org.jboss.envers.test.integration.customtype;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.customtype.ParametrizedCustomTypeEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ParametrizedCustom extends AbstractEntityTest {
+    private Integer pcte_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ParametrizedCustomTypeEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        ParametrizedCustomTypeEntity pcte = new ParametrizedCustomTypeEntity();
+
+        // Revision 1 (persisting 1 entity)
+        em.getTransaction().begin();
+
+        pcte.setStr("U");
+
+        em.persist(pcte);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (changing the value)
+        em.getTransaction().begin();
+
+        pcte = em.find(ParametrizedCustomTypeEntity.class, pcte.getId());
+
+        pcte.setStr("V");
+
+        em.getTransaction().commit();
+
+        //
+
+        pcte_id = pcte.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(ParametrizedCustomTypeEntity.class, pcte_id));
+    }
+
+    @Test
+    public void testHistoryOfCcte() {
+        ParametrizedCustomTypeEntity rev1 = getVersionsReader().find(ParametrizedCustomTypeEntity.class, pcte_id, 1);
+        ParametrizedCustomTypeEntity rev2 = getVersionsReader().find(ParametrizedCustomTypeEntity.class, pcte_id, 2);
+
+        assert "xUy".equals(rev1.getStr());
+        assert "xVy".equals(rev2.getStr());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/DateTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/DateTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/DateTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,77 @@
+package org.jboss.envers.test.integration.data;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class DateTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    private Date date;
+
+    public DateTestEntity() {
+    }
+
+    public DateTestEntity(Date date) {
+        this.date = date;
+    }
+
+    public DateTestEntity(Integer id, Date date) {
+        this.id = id;
+        this.date = date;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DateTestEntity)) return false;
+
+        DateTestEntity that = (DateTestEntity) o;
+
+        if (date != null) {
+            if (that.date == null) {
+                return false;
+            }
+
+            if (date.getTime() != that.date.getTime()) {
+                return false;
+            }
+        }
+        
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (date != null ? date.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Dates.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Dates.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Dates.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,50 @@
+package org.jboss.envers.test.integration.data;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Dates extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(DateTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        DateTestEntity dte = new DateTestEntity(new Date(12345000));
+        em.persist(dte);
+        id1 = dte.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        dte = em.find(DateTestEntity.class, id1);
+        dte.setDate(new Date(45678000));
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(DateTestEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        DateTestEntity ver1 = new DateTestEntity(id1, new Date(12345000));
+        DateTestEntity ver2 = new DateTestEntity(id1, new Date(45678000));
+
+        assert getVersionsReader().find(DateTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(DateTestEntity.class, id1, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/EnumTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/EnumTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/EnumTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.integration.data;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Versioned
+public class EnumTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    public enum E1 { X, Y }
+    public enum E2 { A, B }
+
+    @Enumerated(EnumType.STRING)
+    private E1 enum1;
+
+    @Enumerated(EnumType.ORDINAL)
+    private E2 enum2;
+
+    public EnumTestEntity() {
+    }
+
+    public EnumTestEntity(E1 enum1, E2 enum2) {
+        this.enum1 = enum1;
+        this.enum2 = enum2;
+    }
+
+    public EnumTestEntity(Integer id, E1 enum1, E2 enum2) {
+        this.id = id;
+        this.enum1 = enum1;
+        this.enum2 = enum2;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public E1 getEnum1() {
+        return enum1;
+    }
+
+    public void setEnum1(E1 enum1) {
+        this.enum1 = enum1;
+    }
+
+    public E2 getEnum2() {
+        return enum2;
+    }
+
+    public void setEnum2(E2 enum2) {
+        this.enum2 = enum2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EnumTestEntity)) return false;
+
+        EnumTestEntity that = (EnumTestEntity) o;
+
+        if (enum1 != that.enum1) return false;
+        if (enum2 != that.enum2) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (enum1 != null ? enum1.hashCode() : 0);
+        result = 31 * result + (enum2 != null ? enum2.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Enums.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Enums.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Enums.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,52 @@
+package org.jboss.envers.test.integration.data;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Enums extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(EnumTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        EnumTestEntity ete = new EnumTestEntity(EnumTestEntity.E1.X, EnumTestEntity.E2.A);
+        em.persist(ete);
+        id1 = ete.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        ete = em.find(EnumTestEntity.class, id1);
+        ete.setEnum1(EnumTestEntity.E1.Y);
+        ete.setEnum2(EnumTestEntity.E2.B);
+        em.getTransaction().commit();
+
+        newEntityManager();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(EnumTestEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        EnumTestEntity ver1 = new EnumTestEntity(id1, EnumTestEntity.E1.X, EnumTestEntity.E2.A);
+        EnumTestEntity ver2 = new EnumTestEntity(id1, EnumTestEntity.E1.Y, EnumTestEntity.E2.B);
+
+        assert getVersionsReader().find(EnumTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(EnumTestEntity.class, id1, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/LobTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/LobTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/LobTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,102 @@
+package org.jboss.envers.test.integration.data;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Lob;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class LobTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Lob
+    @Versioned
+    private String stringLob;
+
+    @Lob
+    @Versioned
+    private byte[] byteLob;
+
+    @Lob
+    @Versioned
+    private char[] charLob;
+
+    public LobTestEntity() {
+    }
+
+    public LobTestEntity(String stringLob, byte[] byteLob, char[] charLob) {
+        this.stringLob = stringLob;
+        this.byteLob = byteLob;
+        this.charLob = charLob;
+    }
+
+    public LobTestEntity(Integer id, String stringLob, byte[] byteLob, char[] charLob) {
+        this.id = id;
+        this.stringLob = stringLob;
+        this.byteLob = byteLob;
+        this.charLob = charLob;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStringLob() {
+        return stringLob;
+    }
+
+    public void setStringLob(String stringLob) {
+        this.stringLob = stringLob;
+    }
+
+    public byte[] getByteLob() {
+        return byteLob;
+    }
+
+    public void setByteLob(byte[] byteLob) {
+        this.byteLob = byteLob;
+    }
+
+    public char[] getCharLob() {
+        return charLob;
+    }
+
+    public void setCharLob(char[] charLob) {
+        this.charLob = charLob;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof LobTestEntity)) return false;
+
+        LobTestEntity that = (LobTestEntity) o;
+
+        if (!Arrays.equals(byteLob, that.byteLob)) return false;
+        if (!Arrays.equals(charLob, that.charLob)) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (stringLob != null ? !stringLob.equals(that.stringLob) : that.stringLob != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (stringLob != null ? stringLob.hashCode() : 0);
+        result = 31 * result + (byteLob != null ? Arrays.hashCode(byteLob) : 0);
+        result = 31 * result + (charLob != null ? Arrays.hashCode(charLob) : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Lobs.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Lobs.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Lobs.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,51 @@
+package org.jboss.envers.test.integration.data;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Lobs extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(LobTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        LobTestEntity lte = new LobTestEntity("abc", new byte[] { 0, 1, 2 }, new char[] { 'x', 'y', 'z' });
+        em.persist(lte);
+        id1 = lte.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        lte = em.find(LobTestEntity.class, id1);
+        lte.setStringLob("def");
+        lte.setByteLob(new byte[] { 3, 4, 5 });
+        lte.setCharLob(new char[] { 'h', 'i', 'j' });
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(LobTestEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        LobTestEntity ver1 = new LobTestEntity(id1, "abc", new byte[] { 0, 1, 2 }, new char[] { 'x', 'y', 'z' });
+        LobTestEntity ver2 = new LobTestEntity(id1, "def", new byte[] { 3, 4, 5 }, new char[] { 'h', 'i', 'j' });
+
+        assert getVersionsReader().find(LobTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(LobTestEntity.class, id1, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/SerObject.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/SerObject.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/SerObject.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,42 @@
+package org.jboss.envers.test.integration.data;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SerObject implements Serializable {
+    static final long serialVersionUID = 982352321924L;
+
+    private String data;
+
+    public SerObject() {
+    }
+
+    public SerObject(String data) {
+        this.data = data;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SerObject)) return false;
+
+        SerObject serObject = (SerObject) o;
+
+        if (data != null ? !data.equals(serObject.data) : serObject.data != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (data != null ? data.hashCode() : 0);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/SerializableTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/SerializableTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/SerializableTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,68 @@
+package org.jboss.envers.test.integration.data;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SerializableTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    private SerObject obj;
+
+    public SerializableTestEntity() {
+    }
+
+    public SerializableTestEntity(SerObject obj) {
+        this.obj = obj;
+    }
+
+    public SerializableTestEntity(Integer id, SerObject obj) {
+        this.obj = obj;
+        this.id = id;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public SerObject getObj() {
+        return obj;
+    }
+
+    public void setObj(SerObject obj) {
+        this.obj = obj;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SerializableTestEntity)) return false;
+
+        SerializableTestEntity that = (SerializableTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (obj != null ? !obj.equals(that.obj) : that.obj != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (obj != null ? obj.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Serializables.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Serializables.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/data/Serializables.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,49 @@
+package org.jboss.envers.test.integration.data;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Serializables extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SerializableTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        SerializableTestEntity ste = new SerializableTestEntity(new SerObject("d1"));
+        em.persist(ste);
+        id1 = ste.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        ste = em.find(SerializableTestEntity.class, id1);
+        ste.setObj(new SerObject("d2"));
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SerializableTestEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        SerializableTestEntity ver1 = new SerializableTestEntity(id1, new SerObject("d1"));
+        SerializableTestEntity ver2 = new SerializableTestEntity(id1, new SerObject("d2"));
+
+        assert getVersionsReader().find(SerializableTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(SerializableTestEntity.class, id1, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/AbstractFlushTest.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/AbstractFlushTest.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/AbstractFlushTest.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,43 @@
+package org.jboss.envers.test.integration.flush;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.hibernate.FlushMode;
+import org.hibernate.Session;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractFlushTest extends AbstractEntityTest {
+    public abstract FlushMode getFlushMode();
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+    }
+
+    private static Session getSession(EntityManager em) {
+        Object delegate = em.getDelegate();
+        if (delegate instanceof Session) {
+            return (Session) delegate;
+        } else if (delegate instanceof EntityManager) {
+            Object delegate2 = ((EntityManager) delegate).getDelegate();
+
+            if (delegate2 instanceof Session) {
+                return (Session) delegate2;
+            }
+        }
+
+        throw new RuntimeException("Invalid entity manager");
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initFlush() throws IOException {
+        Session session = getSession(getEntityManager());
+        session.setFlushMode(getFlushMode());
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushAddDel.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushAddDel.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushAddDel.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,52 @@
+package org.jboss.envers.test.integration.flush;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.FlushMode;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DoubleFlushAddDel extends AbstractFlushTest {
+    private Integer id;
+
+    public FlushMode getFlushMode() {
+        return FlushMode.MANUAL;
+    }
+
+    @BeforeClass(dependsOnMethods = "initFlush")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrTestEntity fe = new StrTestEntity("x");
+        em.persist(fe);
+
+        em.flush();
+
+        em.remove(em.find(StrTestEntity.class, fe.getId()));
+
+        em.flush();
+
+        em.getTransaction().commit();
+
+        //
+
+        id = fe.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList().equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        assert getVersionsReader().find(StrTestEntity.class, id, 1) == null;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushAddMod.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushAddMod.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushAddMod.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,66 @@
+package org.jboss.envers.test.integration.flush;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.FlushMode;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DoubleFlushAddMod extends AbstractFlushTest {
+    private Integer id;
+
+    public FlushMode getFlushMode() {
+        return FlushMode.MANUAL;
+    }
+
+    @BeforeClass(dependsOnMethods = "initFlush")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrTestEntity fe = new StrTestEntity("x");
+        em.persist(fe);
+
+        em.flush();
+
+        fe.setStr("y");
+
+        em.flush();
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        fe = em.find(StrTestEntity.class, fe.getId());
+
+        fe.setStr("z");
+        em.flush();
+
+        em.getTransaction().commit();
+
+        //
+
+        id = fe.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        StrTestEntity ver1 = new StrTestEntity("y", id);
+        StrTestEntity ver2 = new StrTestEntity("z", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushModDel.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushModDel.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushModDel.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,62 @@
+package org.jboss.envers.test.integration.flush;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.FlushMode;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DoubleFlushModDel extends AbstractFlushTest {
+    private Integer id;
+
+    public FlushMode getFlushMode() {
+        return FlushMode.MANUAL;
+    }
+
+    @BeforeClass(dependsOnMethods = "initFlush")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrTestEntity fe = new StrTestEntity("x");
+        em.persist(fe);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        fe = em.find(StrTestEntity.class, fe.getId());
+
+        fe.setStr("y");
+        em.flush();
+
+        em.remove(em.find(StrTestEntity.class, fe.getId()));
+        em.flush();
+
+        em.getTransaction().commit();
+
+        //
+
+        id = fe.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2) == null;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushModMod.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushModMod.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/DoubleFlushModMod.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,63 @@
+package org.jboss.envers.test.integration.flush;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.FlushMode;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DoubleFlushModMod extends AbstractFlushTest {
+    private Integer id;
+
+    public FlushMode getFlushMode() {
+        return FlushMode.MANUAL;
+    }
+
+    @BeforeClass(dependsOnMethods = "initFlush")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrTestEntity fe = new StrTestEntity("x");
+        em.persist(fe);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        fe = em.find(StrTestEntity.class, fe.getId());
+
+        fe.setStr("y");
+        em.flush();
+
+        fe.setStr("z");
+        em.flush();
+
+        em.getTransaction().commit();
+
+        //
+
+        id = fe.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+        StrTestEntity ver2 = new StrTestEntity("z", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/ManualFlush.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/ManualFlush.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/flush/ManualFlush.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,75 @@
+package org.jboss.envers.test.integration.flush;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.FlushMode;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ManualFlush extends AbstractFlushTest {
+    private Integer id;
+
+    public FlushMode getFlushMode() {
+        return FlushMode.MANUAL;
+    }
+
+    @BeforeClass(dependsOnMethods = "initFlush")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrTestEntity fe = new StrTestEntity("x");
+        em.persist(fe);
+
+        em.getTransaction().commit();
+
+        // No revision - we change the data, but do not flush the session
+        em.getTransaction().begin();
+
+        fe = em.find(StrTestEntity.class, fe.getId());
+        fe.setStr("y");
+
+        em.getTransaction().commit();
+
+        // Revision 2 - only the first change should be saved
+        em.getTransaction().begin();
+
+        fe = em.find(StrTestEntity.class, fe.getId());
+        fe.setStr("z");
+        em.flush();
+
+        fe = em.find(StrTestEntity.class, fe.getId());
+        fe.setStr("z2");
+
+        em.getTransaction().commit();
+
+        //
+
+        id = fe.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+        StrTestEntity ver2 = new StrTestEntity("z", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2).equals(ver2);
+    }
+
+    @Test
+    public void testCurrent() {
+        assert getEntityManager().find(StrTestEntity.class, id).equals(new StrTestEntity("z", id));
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/ids/CompositeIds.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/ids/CompositeIds.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/ids/CompositeIds.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,155 @@
+package org.jboss.envers.test.integration.ids;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.ids.EmbId;
+import org.jboss.envers.test.entities.ids.MulId;
+import org.jboss.envers.test.entities.ids.EmbIdTestEntity;
+import org.jboss.envers.test.entities.ids.MulIdTestEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class CompositeIds extends AbstractEntityTest {
+    private EmbId id1;
+    private EmbId id2;
+    private MulId id3;
+    private MulId id4;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(EmbIdTestEntity.class);
+        cfg.addAnnotatedClass(MulIdTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        id1 = new EmbId(1, 2);
+        id2 = new EmbId(10, 20);
+        id3 = new MulId(100, 101);
+        id4 = new MulId(102, 103);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(new EmbIdTestEntity(id1, "x"));
+        em.persist(new MulIdTestEntity(id3.getId1(), id3.getId2(), "a"));
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(new EmbIdTestEntity(id2, "y"));
+        em.persist(new MulIdTestEntity(id4.getId1(), id4.getId2(), "b"));
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        EmbIdTestEntity ete1 = em.find(EmbIdTestEntity.class, id1);
+        EmbIdTestEntity ete2 = em.find(EmbIdTestEntity.class, id2);
+        MulIdTestEntity mte3 = em.find(MulIdTestEntity.class, id3);
+        MulIdTestEntity mte4 = em.find(MulIdTestEntity.class, id4);
+
+        ete1.setStr1("x2");
+        ete2.setStr1("y2");
+        mte3.setStr1("a2");
+        mte4.setStr1("b2");
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        ete1 = em.find(EmbIdTestEntity.class, id1);
+        ete2 = em.find(EmbIdTestEntity.class, id2);
+        mte3 = em.find(MulIdTestEntity.class, id3);
+
+        em.remove(ete1);
+        em.remove(mte3);
+
+        ete2.setStr1("y3");
+
+        em.getTransaction().commit();
+
+        // Revision 5
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        ete2 = em.find(EmbIdTestEntity.class, id2);
+
+        em.remove(ete2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 3, 4).equals(getVersionsReader().getRevisions(EmbIdTestEntity.class, id1));
+
+        assert Arrays.asList(2, 3, 4, 5).equals(getVersionsReader().getRevisions(EmbIdTestEntity.class, id2));
+
+        assert Arrays.asList(1, 3, 4).equals(getVersionsReader().getRevisions(MulIdTestEntity.class, id3));
+
+        assert Arrays.asList(2, 3).equals(getVersionsReader().getRevisions(MulIdTestEntity.class, id4));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        EmbIdTestEntity ver1 = new EmbIdTestEntity(id1, "x");
+        EmbIdTestEntity ver2 = new EmbIdTestEntity(id1, "x2");
+
+        assert getVersionsReader().find(EmbIdTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(EmbIdTestEntity.class, id1, 2).equals(ver1);
+        assert getVersionsReader().find(EmbIdTestEntity.class, id1, 3).equals(ver2);
+        assert getVersionsReader().find(EmbIdTestEntity.class, id1, 4) == null;
+        assert getVersionsReader().find(EmbIdTestEntity.class, id1, 5) == null;
+    }
+
+    @Test
+    public void testHistoryOfId2() {
+        EmbIdTestEntity ver1 = new EmbIdTestEntity(id2, "y");
+        EmbIdTestEntity ver2 = new EmbIdTestEntity(id2, "y2");
+        EmbIdTestEntity ver3 = new EmbIdTestEntity(id2, "y3");
+
+        assert getVersionsReader().find(EmbIdTestEntity.class, id2, 1) == null;
+        assert getVersionsReader().find(EmbIdTestEntity.class, id2, 2).equals(ver1);
+        assert getVersionsReader().find(EmbIdTestEntity.class, id2, 3).equals(ver2);
+        assert getVersionsReader().find(EmbIdTestEntity.class, id2, 4).equals(ver3);
+        assert getVersionsReader().find(EmbIdTestEntity.class, id2, 5) == null;
+    }
+
+    @Test
+    public void testHistoryOfId3() {
+        MulIdTestEntity ver1 = new MulIdTestEntity(id3.getId1(), id3.getId2(), "a");
+        MulIdTestEntity ver2 = new MulIdTestEntity(id3.getId1(), id3.getId2(), "a2");
+
+        assert getVersionsReader().find(MulIdTestEntity.class, id3, 1).equals(ver1);
+        assert getVersionsReader().find(MulIdTestEntity.class, id3, 2).equals(ver1);
+        assert getVersionsReader().find(MulIdTestEntity.class, id3, 3).equals(ver2);
+        assert getVersionsReader().find(MulIdTestEntity.class, id3, 4) == null;
+        assert getVersionsReader().find(MulIdTestEntity.class, id3, 5) == null;
+    }
+
+    @Test
+    public void testHistoryOfId4() {
+        MulIdTestEntity ver1 = new MulIdTestEntity(id4.getId1(), id4.getId2(), "b");
+        MulIdTestEntity ver2 = new MulIdTestEntity(id4.getId1(), id4.getId2(), "b2");
+
+        assert getVersionsReader().find(MulIdTestEntity.class, id4, 1) == null;
+        assert getVersionsReader().find(MulIdTestEntity.class, id4, 2).equals(ver1);
+        assert getVersionsReader().find(MulIdTestEntity.class, id4, 3).equals(ver2);
+        assert getVersionsReader().find(MulIdTestEntity.class, id4, 4).equals(ver2);
+        assert getVersionsReader().find(MulIdTestEntity.class, id4, 5).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ChildEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ChildEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ChildEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,59 @@
+package org.jboss.envers.test.integration.inheritance.single;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at DiscriminatorValue("2")
+ at Versioned
+public class ChildEntity extends ParentEntity {
+    @Basic
+    private Long number;
+
+    public ChildEntity() {
+    }
+
+    public ChildEntity(String data, Long number) {
+        super(data);
+        this.number = number;
+    }
+
+    public ChildEntity(Integer id, String data, Long number) {
+        super(id, data);
+        this.number = number;
+    }
+
+    public Long getNumber() {
+        return number;
+    }
+
+    public void setNumber(Long number) {
+        this.number = number;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ChildEntity)) return false;
+        if (!super.equals(o)) return false;
+
+        ChildEntity childEntity = (ChildEntity) o;
+
+        if (number != null ? !number.equals(childEntity.number) : childEntity.number != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (number != null ? number.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ChildEntity(id = " + getId() + ", data = " + getData() + ", number = " + number + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ChildVersioning.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ChildVersioning.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ChildVersioning.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,68 @@
+package org.jboss.envers.test.integration.inheritance.single;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.jboss.envers.test.AbstractEntityTest;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ChildVersioning extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ChildEntity.class);
+        cfg.addAnnotatedClass(ParentEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        // Rev 1
+        em.getTransaction().begin();
+        ChildEntity ce = new ChildEntity("x", 1l);
+        em.persist(ce);
+        id1 = ce.getId();
+        em.getTransaction().commit();
+
+        // Rev 2
+        em.getTransaction().begin();
+        ce = em.find(ChildEntity.class, id1);
+        ce.setData("y");
+        ce.setNumber(2l);
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(ChildEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfChildId1() {
+        ChildEntity ver1 = new ChildEntity(id1, "x", 1l);
+        ChildEntity ver2 = new ChildEntity(id1, "y", 2l);
+
+        assert getVersionsReader().find(ChildEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(ChildEntity.class, id1, 2).equals(ver2);
+
+        assert getVersionsReader().find(ParentEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(ParentEntity.class, id1, 2).equals(ver2);
+    }
+
+    @Test
+    public void testPolymorphicQuery() {
+        ChildEntity childVer1 = new ChildEntity(id1, "x", 1l);
+
+        assert getVersionsReader().createQuery().forEntitiesAtRevision(ChildEntity.class, 1).getSingleResult()
+                .equals(childVer1);
+
+        assert getVersionsReader().createQuery().forEntitiesAtRevision(ParentEntity.class, 1).getSingleResult()
+                .equals(childVer1);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ParentEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ParentEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ParentEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,73 @@
+package org.jboss.envers.test.integration.inheritance.single;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+ at DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.INTEGER)
+ at DiscriminatorValue("1")
+ at Versioned
+public class ParentEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Basic
+    private String data;
+
+    public ParentEntity() {
+    }
+
+    public ParentEntity(String data) {
+        this.data = data;
+    }
+
+    public ParentEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ParentEntity)) return false;
+
+        ParentEntity that = (ParentEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ParentEntity(id = " + getId() + ", data = " + getData() + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ParentVersioning.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ParentVersioning.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/ParentVersioning.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,69 @@
+package org.jboss.envers.test.integration.inheritance.single;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.jboss.envers.test.AbstractEntityTest;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ParentVersioning extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ChildEntity.class);
+        cfg.addAnnotatedClass(ParentEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        // Rev 1
+        em.getTransaction().begin();
+        ParentEntity pe = new ParentEntity("x");
+        em.persist(pe);
+        id1 = pe.getId();
+        em.getTransaction().commit();
+
+        // Rev 2
+        em.getTransaction().begin();
+        pe = em.find(ParentEntity.class, id1);
+        pe.setData("y");
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(ParentEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfChildId1() {
+        assert getVersionsReader().find(ChildEntity.class, id1, 1) == null;
+        assert getVersionsReader().find(ChildEntity.class, id1, 2) == null;
+    }
+
+    @Test
+    public void testHistoryOfParentId1() {
+        ParentEntity ver1 = new ParentEntity(id1, "x");
+        ParentEntity ver2 = new ParentEntity(id1, "y");
+
+        assert getVersionsReader().find(ParentEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(ParentEntity.class, id1, 2).equals(ver2);
+    }
+
+    @Test
+    public void testPolymorphicQuery() {
+        ParentEntity parentVer1 = new ParentEntity(id1, "x");
+
+        assert getVersionsReader().createQuery().forEntitiesAtRevision(ParentEntity.class, 1).getSingleResult()
+                .equals(parentVer1);
+        assert getVersionsReader().createQuery().forEntitiesAtRevision(ChildEntity.class, 1)
+                .getResultList().size() == 0;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ChildIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ChildIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ChildIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,70 @@
+package org.jboss.envers.test.integration.inheritance.single.childrelation;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at DiscriminatorValue("2")
+ at Versioned
+public class ChildIngEntity extends ParentNotIngEntity {
+    @Basic
+    private Long number;
+
+    @ManyToOne
+    private ReferencedEntity referenced;
+
+    public ChildIngEntity() {
+    }
+
+    public ChildIngEntity(String data, Long number) {
+        super(data);
+        this.number = number;
+    }
+
+    public ChildIngEntity(Integer id, String data, Long number) {
+        super(id, data);
+        this.number = number;
+    }
+
+    public Long getNumber() {
+        return number;
+    }
+
+    public void setNumber(Long number) {
+        this.number = number;
+    }
+
+    public ReferencedEntity getReferenced() {
+        return referenced;
+    }
+
+    public void setReferenced(ReferencedEntity referenced) {
+        this.referenced = referenced;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ChildIngEntity)) return false;
+        if (!super.equals(o)) return false;
+
+        ChildIngEntity childEntity = (ChildIngEntity) o;
+
+        if (number != null ? !number.equals(childEntity.number) : childEntity.number != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (number != null ? number.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ChildIngEntity(id = " + getId() + ", data = " + getData() + ", number = " + number + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ChildReferencing.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ChildReferencing.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ChildReferencing.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,97 @@
+package org.jboss.envers.test.integration.inheritance.single.childrelation;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ChildReferencing extends AbstractEntityTest {
+    private Integer re_id1;
+    private Integer re_id2;
+    private Integer c_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ChildIngEntity.class);
+        cfg.addAnnotatedClass(ParentNotIngEntity.class);
+        cfg.addAnnotatedClass(ReferencedEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        // Rev 1
+        em.getTransaction().begin();
+
+        ReferencedEntity re1 = new ReferencedEntity();
+        em.persist(re1);
+        re_id1 = re1.getId();
+
+        ReferencedEntity re2 = new ReferencedEntity();
+        em.persist(re2);
+        re_id2 = re2.getId();
+
+        em.getTransaction().commit();
+
+        // Rev 2
+        em.getTransaction().begin();
+
+        re1 = em.find(ReferencedEntity.class, re_id1);
+
+        ChildIngEntity cie = new ChildIngEntity("y", 1l);
+        cie.setReferenced(re1);
+        em.persist(cie);
+        c_id = cie.getId();
+
+        em.getTransaction().commit();
+
+        // Rev 3
+        em.getTransaction().begin();
+
+        re2 = em.find(ReferencedEntity.class, re_id2);
+        cie = em.find(ChildIngEntity.class, c_id);
+
+        cie.setReferenced(re2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(ReferencedEntity.class, re_id1));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(ReferencedEntity.class, re_id2));
+        assert Arrays.asList(2, 3).equals(getVersionsReader().getRevisions(ChildIngEntity.class, c_id));
+    }
+
+    @Test
+    public void testHistoryOfReferencedCollection1() {
+        assert getVersionsReader().find(ReferencedEntity.class, re_id1, 1).getReferencing().size() == 0;
+        assert getVersionsReader().find(ReferencedEntity.class, re_id1, 2).getReferencing().equals(
+                TestTools.makeSet(new ChildIngEntity(c_id, "y", 1l)));
+        assert getVersionsReader().find(ReferencedEntity.class, re_id1, 3).getReferencing().size() == 0;
+    }
+
+    @Test
+    public void testHistoryOfReferencedCollection2() {
+        assert getVersionsReader().find(ReferencedEntity.class, re_id2, 1).getReferencing().size() == 0;
+        assert getVersionsReader().find(ReferencedEntity.class, re_id2, 2).getReferencing().size() == 0;
+        assert getVersionsReader().find(ReferencedEntity.class, re_id2, 3).getReferencing().equals(
+                TestTools.makeSet(new ChildIngEntity(c_id, "y", 1l)));
+    }
+
+    @Test
+    public void testChildHistory() {
+        assert getVersionsReader().find(ChildIngEntity.class, c_id, 1) == null;
+        assert getVersionsReader().find(ChildIngEntity.class, c_id, 2).getReferenced().equals(
+                new ReferencedEntity(re_id1));
+        assert getVersionsReader().find(ChildIngEntity.class, c_id, 3).getReferenced().equals(
+                new ReferencedEntity(re_id2));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ParentNotIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ParentNotIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ParentNotIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,73 @@
+package org.jboss.envers.test.integration.inheritance.single.childrelation;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+ at DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.INTEGER)
+ at DiscriminatorValue("1")
+ at Versioned
+public class ParentNotIngEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Basic
+    private String data;
+
+    public ParentNotIngEntity() {
+    }
+
+    public ParentNotIngEntity(String data) {
+        this.data = data;
+    }
+
+    public ParentNotIngEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ParentNotIngEntity)) return false;
+
+        ParentNotIngEntity that = (ParentNotIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ParentNotIngEntity(id = " + getId() + ", data = " + getData() + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ReferencedEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ReferencedEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/childrelation/ReferencedEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,61 @@
+package org.jboss.envers.test.integration.inheritance.single.childrelation;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import java.util.Set;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Versioned
+public class ReferencedEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @OneToMany(mappedBy = "referenced")
+    private Set<ChildIngEntity> referencing;
+
+    public ReferencedEntity(Integer id) {
+        this.id = id;
+    }
+
+    public ReferencedEntity() {
+    }
+
+    public Set<ChildIngEntity> getReferencing() {
+        return referencing;
+    }
+
+    public void setReferencing(Set<ChildIngEntity> referencing) {
+        this.referencing = referencing;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ReferencedEntity)) return false;
+
+        ReferencedEntity that = (ReferencedEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return id;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ChildIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ChildIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ChildIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,59 @@
+package org.jboss.envers.test.integration.inheritance.single.relation;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at DiscriminatorValue("2")
+ at Versioned
+public class ChildIngEntity extends ParentIngEntity {
+    @Basic
+    private Long number;
+
+    public ChildIngEntity() {
+    }
+
+    public ChildIngEntity(String data, Long number) {
+        super(data);
+        this.number = number;
+    }
+
+    public ChildIngEntity(Integer id, String data, Long number) {
+        super(id, data);
+        this.number = number;
+    }
+
+    public Long getNumber() {
+        return number;
+    }
+
+    public void setNumber(Long number) {
+        this.number = number;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ChildIngEntity)) return false;
+        if (!super.equals(o)) return false;
+
+        ChildIngEntity childEntity = (ChildIngEntity) o;
+
+        if (number != null ? !number.equals(childEntity.number) : childEntity.number != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (number != null ? number.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ChildIngEntity(id = " + getId() + ", data = " + getData() + ", number = " + number + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ParentIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ParentIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ParentIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.integration.inheritance.single.relation;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+ at DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.INTEGER)
+ at DiscriminatorValue("1")
+ at Versioned
+public class ParentIngEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Basic
+    private String data;
+
+    @ManyToOne
+    private ReferencedEntity referenced;
+
+    public ParentIngEntity() {
+    }
+
+    public ParentIngEntity(String data) {
+        this.data = data;
+    }
+
+    public ParentIngEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public ReferencedEntity getReferenced() {
+        return referenced;
+    }
+
+    public void setReferenced(ReferencedEntity referenced) {
+        this.referenced = referenced;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ParentIngEntity)) return false;
+
+        ParentIngEntity that = (ParentIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "ParentIngEntity(id = " + getId() + ", data = " + getData() + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/PolymorphicCollection.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/PolymorphicCollection.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/PolymorphicCollection.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,79 @@
+package org.jboss.envers.test.integration.inheritance.single.relation;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PolymorphicCollection extends AbstractEntityTest {
+    private Integer ed_id1;
+    private Integer c_id;
+    private Integer p_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ChildIngEntity.class);
+        cfg.addAnnotatedClass(ParentIngEntity.class);
+        cfg.addAnnotatedClass(ReferencedEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        // Rev 1
+        em.getTransaction().begin();
+
+        ReferencedEntity re = new ReferencedEntity();
+        em.persist(re);
+        ed_id1 = re.getId();
+
+        em.getTransaction().commit();
+
+        // Rev 2
+        em.getTransaction().begin();
+
+        re = em.find(ReferencedEntity.class, ed_id1);
+
+        ParentIngEntity pie = new ParentIngEntity("x");
+        pie.setReferenced(re);
+        em.persist(pie);
+        p_id = pie.getId();
+
+        em.getTransaction().commit();
+
+        // Rev 3
+        em.getTransaction().begin();
+        
+        re = em.find(ReferencedEntity.class, ed_id1);
+
+        ChildIngEntity cie = new ChildIngEntity("y", 1l);
+        cie.setReferenced(re);
+        em.persist(cie);
+        c_id = cie.getId();
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(ReferencedEntity.class, ed_id1));
+        assert Arrays.asList(2).equals(getVersionsReader().getRevisions(ParentIngEntity.class, p_id));
+        assert Arrays.asList(3).equals(getVersionsReader().getRevisions(ChildIngEntity.class, c_id));
+    }
+
+    @Test
+    public void testHistoryOfReferencedCollection() {      
+        assert getVersionsReader().find(ReferencedEntity.class, ed_id1, 1).getReferencing().size() == 0;
+        assert getVersionsReader().find(ReferencedEntity.class, ed_id1, 2).getReferencing().equals(
+                TestTools.makeSet(new ParentIngEntity(p_id, "x")));
+        assert getVersionsReader().find(ReferencedEntity.class, ed_id1, 3).getReferencing().equals(
+                TestTools.makeSet(new ParentIngEntity(p_id, "x"), new ChildIngEntity(c_id, "y", 1l)));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ReferencedEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ReferencedEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/inheritance/single/relation/ReferencedEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,54 @@
+package org.jboss.envers.test.integration.inheritance.single.relation;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import java.util.Set;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Versioned
+public class ReferencedEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @OneToMany(mappedBy = "referenced")
+    private Set<ParentIngEntity> referencing;
+
+    public Set<ParentIngEntity> getReferencing() {
+        return referencing;
+    }
+
+    public void setReferencing(Set<ParentIngEntity> referencing) {
+        this.referencing = referencing;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ReferencedEntity)) return false;
+
+        ReferencedEntity that = (ReferencedEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return id;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicList.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicList.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicList.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,189 @@
+package org.jboss.envers.test.integration.manytomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.manytomany.ListOwningEntity;
+import org.jboss.envers.test.entities.manytomany.ListOwnedEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicList extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ListOwningEntity.class);
+        cfg.addAnnotatedClass(ListOwnedEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        ListOwnedEntity ed1 = new ListOwnedEntity(1, "data_ed_1");
+        ListOwnedEntity ed2 = new ListOwnedEntity(2, "data_ed_2");
+
+        ListOwningEntity ing1 = new ListOwningEntity(3, "data_ing_1");
+        ListOwningEntity ing2 = new ListOwningEntity(4, "data_ing_2");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListOwningEntity.class, ing1.getId());
+        ing2 = em.find(ListOwningEntity.class, ing2.getId());
+        ed1 = em.find(ListOwnedEntity.class, ed1.getId());
+        ed2 = em.find(ListOwnedEntity.class, ed2.getId());
+
+        ing1.setReferences(new ArrayList<ListOwnedEntity>());
+        ing1.getReferences().add(ed1);
+
+        ing2.setReferences(new ArrayList<ListOwnedEntity>());
+        ing2.getReferences().add(ed1);
+        ing2.getReferences().add(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListOwningEntity.class, ing1.getId());
+        ed2 = em.find(ListOwnedEntity.class, ed2.getId());
+        ed1 = em.find(ListOwnedEntity.class, ed1.getId());
+
+        ing1.getReferences().add(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListOwningEntity.class, ing1.getId());
+        ed2 = em.find(ListOwnedEntity.class, ed2.getId());
+        ed1 = em.find(ListOwnedEntity.class, ed1.getId());
+
+        ing1.getReferences().remove(ed1);
+
+        em.getTransaction().commit();
+
+        // Revision 5
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListOwningEntity.class, ing1.getId());
+
+        ing1.setReferences(null);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 4).equals(getVersionsReader().getRevisions(ListOwnedEntity.class, ed1_id));
+        assert Arrays.asList(1, 2, 3, 5).equals(getVersionsReader().getRevisions(ListOwnedEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2, 3, 4, 5).equals(getVersionsReader().getRevisions(ListOwningEntity.class, ing1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(ListOwningEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        ListOwningEntity ing1 = getEntityManager().find(ListOwningEntity.class, ing1_id);
+        ListOwningEntity ing2 = getEntityManager().find(ListOwningEntity.class, ing2_id);
+
+        ListOwnedEntity rev1 = getVersionsReader().find(ListOwnedEntity.class, ed1_id, 1);
+        ListOwnedEntity rev2 = getVersionsReader().find(ListOwnedEntity.class, ed1_id, 2);
+        ListOwnedEntity rev3 = getVersionsReader().find(ListOwnedEntity.class, ed1_id, 3);
+        ListOwnedEntity rev4 = getVersionsReader().find(ListOwnedEntity.class, ed1_id, 4);
+        ListOwnedEntity rev5 = getVersionsReader().find(ListOwnedEntity.class, ed1_id, 5);
+
+        assert rev1.getReferencing().equals(Collections.EMPTY_LIST);
+        assert TestTools.checkList(rev2.getReferencing(), ing1, ing2);
+        assert TestTools.checkList(rev3.getReferencing(), ing1, ing2);
+        assert TestTools.checkList(rev4.getReferencing(), ing2);
+        assert TestTools.checkList(rev5.getReferencing(), ing2);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        ListOwningEntity ing1 = getEntityManager().find(ListOwningEntity.class, ing1_id);
+        ListOwningEntity ing2 = getEntityManager().find(ListOwningEntity.class, ing2_id);
+
+        ListOwnedEntity rev1 = getVersionsReader().find(ListOwnedEntity.class, ed2_id, 1);
+        ListOwnedEntity rev2 = getVersionsReader().find(ListOwnedEntity.class, ed2_id, 2);
+        ListOwnedEntity rev3 = getVersionsReader().find(ListOwnedEntity.class, ed2_id, 3);
+        ListOwnedEntity rev4 = getVersionsReader().find(ListOwnedEntity.class, ed2_id, 4);
+        ListOwnedEntity rev5 = getVersionsReader().find(ListOwnedEntity.class, ed2_id, 5);
+
+        assert rev1.getReferencing().equals(Collections.EMPTY_LIST);
+        assert TestTools.checkList(rev2.getReferencing(), ing2);
+        assert TestTools.checkList(rev3.getReferencing(), ing1, ing2);
+        assert TestTools.checkList(rev4.getReferencing(), ing1, ing2);
+        assert TestTools.checkList(rev5.getReferencing(), ing2);
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        ListOwnedEntity ed1 = getEntityManager().find(ListOwnedEntity.class, ed1_id);
+        ListOwnedEntity ed2 = getEntityManager().find(ListOwnedEntity.class, ed2_id);
+
+        ListOwningEntity rev1 = getVersionsReader().find(ListOwningEntity.class, ing1_id, 1);
+        ListOwningEntity rev2 = getVersionsReader().find(ListOwningEntity.class, ing1_id, 2);
+        ListOwningEntity rev3 = getVersionsReader().find(ListOwningEntity.class, ing1_id, 3);
+        ListOwningEntity rev4 = getVersionsReader().find(ListOwningEntity.class, ing1_id, 4);
+        ListOwningEntity rev5 = getVersionsReader().find(ListOwningEntity.class, ing1_id, 5);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_LIST);
+        assert TestTools.checkList(rev2.getReferences(), ed1);
+        assert TestTools.checkList(rev3.getReferences(), ed1, ed2);
+        assert TestTools.checkList(rev4.getReferences(), ed2);
+        assert rev5.getReferences().equals(Collections.EMPTY_LIST);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        ListOwnedEntity ed1 = getEntityManager().find(ListOwnedEntity.class, ed1_id);
+        ListOwnedEntity ed2 = getEntityManager().find(ListOwnedEntity.class, ed2_id);
+
+        ListOwningEntity rev1 = getVersionsReader().find(ListOwningEntity.class, ing2_id, 1);
+        ListOwningEntity rev2 = getVersionsReader().find(ListOwningEntity.class, ing2_id, 2);
+        ListOwningEntity rev3 = getVersionsReader().find(ListOwningEntity.class, ing2_id, 3);
+        ListOwningEntity rev4 = getVersionsReader().find(ListOwningEntity.class, ing2_id, 4);
+        ListOwningEntity rev5 = getVersionsReader().find(ListOwningEntity.class, ing2_id, 5);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_LIST);
+        assert TestTools.checkList(rev2.getReferences(), ed1, ed2);
+        assert TestTools.checkList(rev3.getReferences(), ed1, ed2);
+        assert TestTools.checkList(rev4.getReferences(), ed1, ed2);
+        assert TestTools.checkList(rev5.getReferences(), ed1, ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicMap.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicMap.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicMap.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,163 @@
+package org.jboss.envers.test.integration.manytomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.manytomany.MapOwningEntity;
+import org.jboss.envers.test.entities.manytomany.MapOwnedEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicMap extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(MapOwningEntity.class);
+        cfg.addAnnotatedClass(MapOwnedEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        MapOwnedEntity ed1 = new MapOwnedEntity(1, "data_ed_1");
+        MapOwnedEntity ed2 = new MapOwnedEntity(2, "data_ed_2");
+
+        MapOwningEntity ing1 = new MapOwningEntity(3, "data_ing_1");
+        MapOwningEntity ing2 = new MapOwningEntity(4, "data_ing_2");
+
+        // Revision 1 (ing1: initialy empty, ing2: one mapping)
+        em.getTransaction().begin();
+
+        ing2.getReferences().put("2", ed2);
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (ing1: adding two mappings, ing2: replacing an existing mapping)
+
+        em.getTransaction().begin();
+
+        ing1 = em.find(MapOwningEntity.class, ing1.getId());
+        ing2 = em.find(MapOwningEntity.class, ing2.getId());
+        ed1 = em.find(MapOwnedEntity.class, ed1.getId());
+        ed2 = em.find(MapOwnedEntity.class, ed2.getId());
+
+        ing1.getReferences().put("1", ed1);
+        ing1.getReferences().put("2", ed1);
+
+        ing2.getReferences().put("2", ed1);
+
+        em.getTransaction().commit();
+
+        // No revision (ing1: adding an existing mapping, ing2: removing a non existing mapping)
+        em.getTransaction().begin();
+
+        ing1 = em.find(MapOwningEntity.class, ing1.getId());
+        ing2 = em.find(MapOwningEntity.class, ing2.getId());
+
+        ing1.getReferences().put("1", ed1);
+
+        ing2.getReferences().remove("3");
+
+        em.getTransaction().commit();
+
+        // Revision 3 (ing1: clearing, ing2: replacing with a new map)
+        em.getTransaction().begin();
+
+        ing1 = em.find(MapOwningEntity.class, ing1.getId());
+        ed1 = em.find(MapOwnedEntity.class, ed1.getId());
+
+        ing1.getReferences().clear();
+        ing2.setReferences(new HashMap<String, MapOwnedEntity>());
+        ing2.getReferences().put("1", ed2);
+
+        em.getTransaction().commit();
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(MapOwnedEntity.class, ed1_id));
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(MapOwnedEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(MapOwningEntity.class, ing1_id));
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(MapOwningEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        MapOwningEntity ing1 = getEntityManager().find(MapOwningEntity.class, ing1_id);
+        MapOwningEntity ing2 = getEntityManager().find(MapOwningEntity.class, ing2_id);
+
+        MapOwnedEntity rev1 = getVersionsReader().find(MapOwnedEntity.class, ed1_id, 1);
+        MapOwnedEntity rev2 = getVersionsReader().find(MapOwnedEntity.class, ed1_id, 2);
+        MapOwnedEntity rev3 = getVersionsReader().find(MapOwnedEntity.class, ed1_id, 3);
+
+        assert rev1.getReferencing().equals(Collections.EMPTY_SET);
+        assert rev2.getReferencing().equals(TestTools.makeSet(ing1, ing2));
+        assert rev3.getReferencing().equals(Collections.EMPTY_SET);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        MapOwningEntity ing2 = getEntityManager().find(MapOwningEntity.class, ing2_id);
+
+        MapOwnedEntity rev1 = getVersionsReader().find(MapOwnedEntity.class, ed2_id, 1);
+        MapOwnedEntity rev2 = getVersionsReader().find(MapOwnedEntity.class, ed2_id, 2);
+        MapOwnedEntity rev3 = getVersionsReader().find(MapOwnedEntity.class, ed2_id, 3);
+
+        assert rev1.getReferencing().equals(TestTools.makeSet(ing2));
+        assert rev2.getReferencing().equals(Collections.EMPTY_SET);
+        assert rev3.getReferencing().equals(TestTools.makeSet(ing2));
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        MapOwnedEntity ed1 = getEntityManager().find(MapOwnedEntity.class, ed1_id);
+
+        MapOwningEntity rev1 = getVersionsReader().find(MapOwningEntity.class, ing1_id, 1);
+        MapOwningEntity rev2 = getVersionsReader().find(MapOwningEntity.class, ing1_id, 2);
+        MapOwningEntity rev3 = getVersionsReader().find(MapOwningEntity.class, ing1_id, 3);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_MAP);
+        assert rev2.getReferences().equals(TestTools.makeMap("1", ed1, "2", ed1));
+        assert rev3.getReferences().equals(Collections.EMPTY_MAP);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        MapOwnedEntity ed1 = getEntityManager().find(MapOwnedEntity.class, ed1_id);
+        MapOwnedEntity ed2 = getEntityManager().find(MapOwnedEntity.class, ed2_id);
+
+        MapOwningEntity rev1 = getVersionsReader().find(MapOwningEntity.class, ing2_id, 1);
+        MapOwningEntity rev2 = getVersionsReader().find(MapOwningEntity.class, ing2_id, 2);
+        MapOwningEntity rev3 = getVersionsReader().find(MapOwningEntity.class, ing2_id, 3);
+
+        assert rev1.getReferences().equals(TestTools.makeMap("2", ed2));
+        assert rev2.getReferences().equals(TestTools.makeMap("2", ed1));
+        assert rev3.getReferences().equals(TestTools.makeMap("1", ed2));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/BasicSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,189 @@
+package org.jboss.envers.test.integration.manytomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.manytomany.SetOwningEntity;
+import org.jboss.envers.test.entities.manytomany.SetOwnedEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicSet extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetOwningEntity.class);
+        cfg.addAnnotatedClass(SetOwnedEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        SetOwnedEntity ed1 = new SetOwnedEntity(1, "data_ed_1");
+        SetOwnedEntity ed2 = new SetOwnedEntity(2, "data_ed_2");
+
+        SetOwningEntity ing1 = new SetOwningEntity(3, "data_ing_1");
+        SetOwningEntity ing2 = new SetOwningEntity(4, "data_ing_2");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetOwningEntity.class, ing1.getId());
+        ing2 = em.find(SetOwningEntity.class, ing2.getId());
+        ed1 = em.find(SetOwnedEntity.class, ed1.getId());
+        ed2 = em.find(SetOwnedEntity.class, ed2.getId());
+
+        ing1.setReferences(new HashSet<SetOwnedEntity>());
+        ing1.getReferences().add(ed1);
+
+        ing2.setReferences(new HashSet<SetOwnedEntity>());
+        ing2.getReferences().add(ed1);
+        ing2.getReferences().add(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetOwningEntity.class, ing1.getId());
+        ed2 = em.find(SetOwnedEntity.class, ed2.getId());
+        ed1 = em.find(SetOwnedEntity.class, ed1.getId());
+
+        ing1.getReferences().add(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetOwningEntity.class, ing1.getId());
+        ed2 = em.find(SetOwnedEntity.class, ed2.getId());
+        ed1 = em.find(SetOwnedEntity.class, ed1.getId());
+
+        ing1.getReferences().remove(ed1);
+
+        em.getTransaction().commit();
+
+        // Revision 5
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetOwningEntity.class, ing1.getId());
+
+        ing1.setReferences(null);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 4).equals(getVersionsReader().getRevisions(SetOwnedEntity.class, ed1_id));
+        assert Arrays.asList(1, 2, 3, 5).equals(getVersionsReader().getRevisions(SetOwnedEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2, 3, 4, 5).equals(getVersionsReader().getRevisions(SetOwningEntity.class, ing1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SetOwningEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        SetOwningEntity ing1 = getEntityManager().find(SetOwningEntity.class, ing1_id);
+        SetOwningEntity ing2 = getEntityManager().find(SetOwningEntity.class, ing2_id);
+
+        SetOwnedEntity rev1 = getVersionsReader().find(SetOwnedEntity.class, ed1_id, 1);
+        SetOwnedEntity rev2 = getVersionsReader().find(SetOwnedEntity.class, ed1_id, 2);
+        SetOwnedEntity rev3 = getVersionsReader().find(SetOwnedEntity.class, ed1_id, 3);
+        SetOwnedEntity rev4 = getVersionsReader().find(SetOwnedEntity.class, ed1_id, 4);
+        SetOwnedEntity rev5 = getVersionsReader().find(SetOwnedEntity.class, ed1_id, 5);
+
+        assert rev1.getReferencing().equals(Collections.EMPTY_SET);
+        assert rev2.getReferencing().equals(TestTools.makeSet(ing1, ing2));
+        assert rev3.getReferencing().equals(TestTools.makeSet(ing1, ing2));
+        assert rev4.getReferencing().equals(TestTools.makeSet(ing2));
+        assert rev5.getReferencing().equals(TestTools.makeSet(ing2));
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        SetOwningEntity ing1 = getEntityManager().find(SetOwningEntity.class, ing1_id);
+        SetOwningEntity ing2 = getEntityManager().find(SetOwningEntity.class, ing2_id);
+
+        SetOwnedEntity rev1 = getVersionsReader().find(SetOwnedEntity.class, ed2_id, 1);
+        SetOwnedEntity rev2 = getVersionsReader().find(SetOwnedEntity.class, ed2_id, 2);
+        SetOwnedEntity rev3 = getVersionsReader().find(SetOwnedEntity.class, ed2_id, 3);
+        SetOwnedEntity rev4 = getVersionsReader().find(SetOwnedEntity.class, ed2_id, 4);
+        SetOwnedEntity rev5 = getVersionsReader().find(SetOwnedEntity.class, ed2_id, 5);
+
+        assert rev1.getReferencing().equals(Collections.EMPTY_SET);
+        assert rev2.getReferencing().equals(TestTools.makeSet(ing2));
+        assert rev3.getReferencing().equals(TestTools.makeSet(ing1, ing2));
+        assert rev4.getReferencing().equals(TestTools.makeSet(ing1, ing2));
+        assert rev5.getReferencing().equals(TestTools.makeSet(ing2));
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        SetOwnedEntity ed1 = getEntityManager().find(SetOwnedEntity.class, ed1_id);
+        SetOwnedEntity ed2 = getEntityManager().find(SetOwnedEntity.class, ed2_id);
+
+        SetOwningEntity rev1 = getVersionsReader().find(SetOwningEntity.class, ing1_id, 1);
+        SetOwningEntity rev2 = getVersionsReader().find(SetOwningEntity.class, ing1_id, 2);
+        SetOwningEntity rev3 = getVersionsReader().find(SetOwningEntity.class, ing1_id, 3);
+        SetOwningEntity rev4 = getVersionsReader().find(SetOwningEntity.class, ing1_id, 4);
+        SetOwningEntity rev5 = getVersionsReader().find(SetOwningEntity.class, ing1_id, 5);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_SET);
+        assert rev2.getReferences().equals(TestTools.makeSet(ed1));
+        assert rev3.getReferences().equals(TestTools.makeSet(ed1, ed2));
+        assert rev4.getReferences().equals(TestTools.makeSet(ed2));
+        assert rev5.getReferences().equals(Collections.EMPTY_SET);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        SetOwnedEntity ed1 = getEntityManager().find(SetOwnedEntity.class, ed1_id);
+        SetOwnedEntity ed2 = getEntityManager().find(SetOwnedEntity.class, ed2_id);
+
+        SetOwningEntity rev1 = getVersionsReader().find(SetOwningEntity.class, ing2_id, 1);
+        SetOwningEntity rev2 = getVersionsReader().find(SetOwningEntity.class, ing2_id, 2);
+        SetOwningEntity rev3 = getVersionsReader().find(SetOwningEntity.class, ing2_id, 3);
+        SetOwningEntity rev4 = getVersionsReader().find(SetOwningEntity.class, ing2_id, 4);
+        SetOwningEntity rev5 = getVersionsReader().find(SetOwningEntity.class, ing2_id, 5);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_SET);
+        assert rev2.getReferences().equals(TestTools.makeSet(ed1, ed2));
+        assert rev3.getReferences().equals(TestTools.makeSet(ed1, ed2));
+        assert rev4.getReferences().equals(TestTools.makeSet(ed1, ed2));
+        assert rev5.getReferences().equals(TestTools.makeSet(ed1, ed2));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/TernaryMap.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/TernaryMap.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/TernaryMap.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,176 @@
+package org.jboss.envers.test.integration.manytomany.ternary;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.entities.IntTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class TernaryMap extends AbstractEntityTest {
+    private Integer str1_id;
+    private Integer str2_id;
+
+    private Integer int1_id;
+    private Integer int2_id;
+
+    private Integer map1_id;
+    private Integer map2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(TernaryMapEntity.class);
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(IntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity str1 = new StrTestEntity("a");
+        StrTestEntity str2 = new StrTestEntity("b");
+
+        IntTestEntity int1 = new IntTestEntity(1);
+        IntTestEntity int2 = new IntTestEntity(2);
+
+        TernaryMapEntity map1 = new TernaryMapEntity();
+        TernaryMapEntity map2 = new TernaryMapEntity();
+
+        // Revision 1 (map1: initialy one mapping int1 -> str1, map2: empty)
+        em.getTransaction().begin();
+
+        em.persist(str1);
+        em.persist(str2);
+        em.persist(int1);
+        em.persist(int2);
+
+        map1.getMap().put(int1, str1);
+
+        em.persist(map1);
+        em.persist(map2);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (map1: replacing the mapping, map2: adding two mappings)
+
+        em.getTransaction().begin();
+
+        map1 = em.find(TernaryMapEntity.class, map1.getId());
+        map2 = em.find(TernaryMapEntity.class, map2.getId());
+
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        str2 = em.find(StrTestEntity.class, str2.getId());
+
+        int1 = em.find(IntTestEntity.class, int1.getId());
+        int2 = em.find(IntTestEntity.class, int2.getId());
+
+        map1.getMap().put(int1, str2);
+
+        map2.getMap().put(int1, str1);
+        map2.getMap().put(int2, str1);
+
+        em.getTransaction().commit();
+
+        // Revision 3 (map1: removing a non-existing mapping, adding an existing mapping - no changes, map2: removing a mapping)
+        em.getTransaction().begin();
+
+        map1 = em.find(TernaryMapEntity.class, map1.getId());
+        map2 = em.find(TernaryMapEntity.class, map2.getId());
+
+        str2 = em.find(StrTestEntity.class, str2.getId());
+
+        int1 = em.find(IntTestEntity.class, int1.getId());
+        int2 = em.find(IntTestEntity.class, int2.getId());
+
+        map1.getMap().remove(int2);
+        map1.getMap().put(int1, str2);
+
+        map2.getMap().remove(int1);
+
+        em.getTransaction().commit();
+
+        // Revision 4 (map1: adding a mapping, map2: adding a mapping)
+        em.getTransaction().begin();
+
+        map1 = em.find(TernaryMapEntity.class, map1.getId());
+        map2 = em.find(TernaryMapEntity.class, map2.getId());
+
+        str2 = em.find(StrTestEntity.class, str2.getId());
+
+        int1 = em.find(IntTestEntity.class, int1.getId());
+        int2 = em.find(IntTestEntity.class, int2.getId());
+
+        map1.getMap().put(int2, str2);
+
+        map2.getMap().put(int1, str2);
+
+        em.getTransaction().commit();
+        //
+
+        map1_id = map1.getId();
+        map2_id = map2.getId();
+
+        str1_id = str1.getId();
+        str2_id = str2.getId();
+
+        int1_id = int1.getId();
+        int2_id = int2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 4).equals(getVersionsReader().getRevisions(TernaryMapEntity.class, map1_id));
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(TernaryMapEntity.class, map2_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str2_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(IntTestEntity.class, int1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(IntTestEntity.class, int2_id));
+    }
+
+    @Test
+    public void testHistoryOfMap1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+        StrTestEntity str2 = getEntityManager().find(StrTestEntity.class, str2_id);
+
+        IntTestEntity int1 = getEntityManager().find(IntTestEntity.class, int1_id);
+        IntTestEntity int2 = getEntityManager().find(IntTestEntity.class, int2_id);
+
+        TernaryMapEntity rev1 = getVersionsReader().find(TernaryMapEntity.class, map1_id, 1);
+        TernaryMapEntity rev2 = getVersionsReader().find(TernaryMapEntity.class, map1_id, 2);
+        TernaryMapEntity rev3 = getVersionsReader().find(TernaryMapEntity.class, map1_id, 3);
+        TernaryMapEntity rev4 = getVersionsReader().find(TernaryMapEntity.class, map1_id, 4);
+
+        assert rev1.getMap().equals(TestTools.makeMap(int1, str1));
+        assert rev2.getMap().equals(TestTools.makeMap(int1, str2));
+        assert rev3.getMap().equals(TestTools.makeMap(int1, str2));
+        assert rev4.getMap().equals(TestTools.makeMap(int1, str2, int2, str2));
+    }
+
+    @Test
+    public void testHistoryOfMap2() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+        StrTestEntity str2 = getEntityManager().find(StrTestEntity.class, str2_id);
+
+        IntTestEntity int1 = getEntityManager().find(IntTestEntity.class, int1_id);
+        IntTestEntity int2 = getEntityManager().find(IntTestEntity.class, int2_id);
+
+        TernaryMapEntity rev1 = getVersionsReader().find(TernaryMapEntity.class, map2_id, 1);
+        TernaryMapEntity rev2 = getVersionsReader().find(TernaryMapEntity.class, map2_id, 2);
+        TernaryMapEntity rev3 = getVersionsReader().find(TernaryMapEntity.class, map2_id, 3);
+        TernaryMapEntity rev4 = getVersionsReader().find(TernaryMapEntity.class, map2_id, 4);
+
+        assert rev1.getMap().equals(TestTools.makeMap());
+        assert rev2.getMap().equals(TestTools.makeMap(int1, str1, int2, str1));
+        assert rev3.getMap().equals(TestTools.makeMap(int2, str1));
+        assert rev4.getMap().equals(TestTools.makeMap(int1, str2, int2, str1));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/TernaryMapEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/TernaryMapEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/ternary/TernaryMapEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,67 @@
+package org.jboss.envers.test.integration.manytomany.ternary;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.IntTestEntity;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.hibernate.annotations.MapKeyManyToMany;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.ManyToMany;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class TernaryMapEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @ManyToMany
+    @MapKeyManyToMany
+    private Map<IntTestEntity, StrTestEntity> map;
+
+    public TernaryMapEntity() {
+        map = new HashMap<IntTestEntity, StrTestEntity>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Map<IntTestEntity, StrTestEntity> getMap() {
+        return map;
+    }
+
+    public void setMap(Map<IntTestEntity, StrTestEntity> map) {
+        this.map = map;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TernaryMapEntity)) return false;
+
+        TernaryMapEntity that = (TernaryMapEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "TME(id = " + id + ", map = " + map + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniList.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniList.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniList.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,153 @@
+package org.jboss.envers.test.integration.manytomany.unidirectional;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.manytomany.unidirectional.ListUniEntity;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicUniList extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ListUniEntity.class);
+        cfg.addAnnotatedClass(StrTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity ed1 = new StrTestEntity("data_ed_1");
+        StrTestEntity ed2 = new StrTestEntity("data_ed_2");
+
+        ListUniEntity ing1 = new ListUniEntity(3, "data_ing_1");
+        ListUniEntity ing2 = new ListUniEntity(4, "data_ing_2");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListUniEntity.class, ing1.getId());
+        ing2 = em.find(ListUniEntity.class, ing2.getId());
+        ed1 = em.find(StrTestEntity.class, ed1.getId());
+        ed2 = em.find(StrTestEntity.class, ed2.getId());
+
+        ing1.setReferences(new ArrayList<StrTestEntity>());
+        ing1.getReferences().add(ed1);
+
+        ing2.setReferences(new ArrayList<StrTestEntity>());
+        ing2.getReferences().add(ed1);
+        ing2.getReferences().add(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListUniEntity.class, ing1.getId());
+        ed2 = em.find(StrTestEntity.class, ed2.getId());
+        ed1 = em.find(StrTestEntity.class, ed1.getId());
+
+        ing1.getReferences().add(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListUniEntity.class, ing1.getId());
+        ed2 = em.find(StrTestEntity.class, ed2.getId());
+        ed1 = em.find(StrTestEntity.class, ed1.getId());
+
+        ing1.getReferences().remove(ed1);
+
+        em.getTransaction().commit();
+
+        // Revision 5
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListUniEntity.class, ing1.getId());
+
+        ing1.setReferences(null);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, ed1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2, 3, 4, 5).equals(getVersionsReader().getRevisions(ListUniEntity.class, ing1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(ListUniEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        StrTestEntity ed1 = getEntityManager().find(StrTestEntity.class, ed1_id);
+        StrTestEntity ed2 = getEntityManager().find(StrTestEntity.class, ed2_id);
+
+        ListUniEntity rev1 = getVersionsReader().find(ListUniEntity.class, ing1_id, 1);
+        ListUniEntity rev2 = getVersionsReader().find(ListUniEntity.class, ing1_id, 2);
+        ListUniEntity rev3 = getVersionsReader().find(ListUniEntity.class, ing1_id, 3);
+        ListUniEntity rev4 = getVersionsReader().find(ListUniEntity.class, ing1_id, 4);
+        ListUniEntity rev5 = getVersionsReader().find(ListUniEntity.class, ing1_id, 5);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_LIST);
+        assert TestTools.checkList(rev2.getReferences(), ed1);
+        assert TestTools.checkList(rev3.getReferences(), ed1, ed2);
+        assert TestTools.checkList(rev4.getReferences(), ed2);
+        assert rev5.getReferences().equals(Collections.EMPTY_LIST);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        StrTestEntity ed1 = getEntityManager().find(StrTestEntity.class, ed1_id);
+        StrTestEntity ed2 = getEntityManager().find(StrTestEntity.class, ed2_id);
+
+        ListUniEntity rev1 = getVersionsReader().find(ListUniEntity.class, ing2_id, 1);
+        ListUniEntity rev2 = getVersionsReader().find(ListUniEntity.class, ing2_id, 2);
+        ListUniEntity rev3 = getVersionsReader().find(ListUniEntity.class, ing2_id, 3);
+        ListUniEntity rev4 = getVersionsReader().find(ListUniEntity.class, ing2_id, 4);
+        ListUniEntity rev5 = getVersionsReader().find(ListUniEntity.class, ing2_id, 5);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_LIST);
+        assert TestTools.checkList(rev2.getReferences(), ed1, ed2);
+        assert TestTools.checkList(rev3.getReferences(), ed1, ed2);
+        assert TestTools.checkList(rev4.getReferences(), ed1, ed2);
+        assert TestTools.checkList(rev5.getReferences(), ed1, ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniMap.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniMap.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniMap.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,115 @@
+package org.jboss.envers.test.integration.manytomany.unidirectional;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.manytomany.unidirectional.MapUniEntity;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicUniMap extends AbstractEntityTest {
+    private Integer str1_id;
+    private Integer str2_id;
+
+    private Integer coll1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(MapUniEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity str1 = new StrTestEntity("str1");
+        StrTestEntity str2 = new StrTestEntity("str2");
+
+        MapUniEntity coll1 = new MapUniEntity(3, "coll1");
+
+        // Revision 1 (coll1: initialy one mapping)
+        em.getTransaction().begin();
+
+        em.persist(str1);
+        em.persist(str2);
+
+        coll1.setMap(new HashMap<String, StrTestEntity>());
+        coll1.getMap().put("1", str1);
+        em.persist(coll1);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (coll1: adding one mapping)
+        em.getTransaction().begin();
+
+        str2 = em.find(StrTestEntity.class, str2.getId());
+        coll1 = em.find(MapUniEntity.class, coll1.getId());
+
+        coll1.getMap().put("2", str2);
+
+        em.getTransaction().commit();
+
+        // Revision 3 (coll1: replacing one mapping)
+        em.getTransaction().begin();
+
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        coll1 = em.find(MapUniEntity.class, coll1.getId());
+
+        coll1.getMap().put("2", str1);
+
+        em.getTransaction().commit();
+
+        // Revision 4 (coll1: removing one mapping)
+        em.getTransaction().begin();
+
+        coll1 = em.find(MapUniEntity.class, coll1.getId());
+
+        coll1.getMap().remove("1");
+
+        em.getTransaction().commit();
+
+        //
+
+        str1_id = str1.getId();
+        str2_id = str2.getId();
+
+        coll1_id = coll1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(MapUniEntity.class, coll1_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str2_id));
+    }
+
+    @Test
+    public void testHistoryOfColl1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+        StrTestEntity str2 = getEntityManager().find(StrTestEntity.class, str2_id);
+
+        MapUniEntity rev1 = getVersionsReader().find(MapUniEntity.class, coll1_id, 1);
+        MapUniEntity rev2 = getVersionsReader().find(MapUniEntity.class, coll1_id, 2);
+        MapUniEntity rev3 = getVersionsReader().find(MapUniEntity.class, coll1_id, 3);
+        MapUniEntity rev4 = getVersionsReader().find(MapUniEntity.class, coll1_id, 4);
+
+        assert rev1.getMap().equals(TestTools.makeMap("1", str1));
+        assert rev2.getMap().equals(TestTools.makeMap("1", str1, "2", str2));
+        assert rev3.getMap().equals(TestTools.makeMap("1", str1, "2", str1));
+        assert rev4.getMap().equals(TestTools.makeMap("2", str1));
+
+        assert "coll1".equals(rev1.getData());
+        assert "coll1".equals(rev2.getData());
+        assert "coll1".equals(rev3.getData());
+        assert "coll1".equals(rev4.getData());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/manytomany/unidirectional/BasicUniSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,153 @@
+package org.jboss.envers.test.integration.manytomany.unidirectional;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.manytomany.unidirectional.SetUniEntity;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicUniSet extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetUniEntity.class);
+        cfg.addAnnotatedClass(StrTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity ed1 = new StrTestEntity("data_ed_1");
+        StrTestEntity ed2 = new StrTestEntity("data_ed_2");
+
+        SetUniEntity ing1 = new SetUniEntity(3, "data_ing_1");
+        SetUniEntity ing2 = new SetUniEntity(4, "data_ing_2");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetUniEntity.class, ing1.getId());
+        ing2 = em.find(SetUniEntity.class, ing2.getId());
+        ed1 = em.find(StrTestEntity.class, ed1.getId());
+        ed2 = em.find(StrTestEntity.class, ed2.getId());
+
+        ing1.setReferences(new HashSet<StrTestEntity>());
+        ing1.getReferences().add(ed1);
+
+        ing2.setReferences(new HashSet<StrTestEntity>());
+        ing2.getReferences().add(ed1);
+        ing2.getReferences().add(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetUniEntity.class, ing1.getId());
+        ed2 = em.find(StrTestEntity.class, ed2.getId());
+        ed1 = em.find(StrTestEntity.class, ed1.getId());
+
+        ing1.getReferences().add(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetUniEntity.class, ing1.getId());
+        ed2 = em.find(StrTestEntity.class, ed2.getId());
+        ed1 = em.find(StrTestEntity.class, ed1.getId());
+
+        ing1.getReferences().remove(ed1);
+
+        em.getTransaction().commit();
+
+        // Revision 5
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetUniEntity.class, ing1.getId());
+
+        ing1.setReferences(null);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, ed1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2, 3, 4, 5).equals(getVersionsReader().getRevisions(SetUniEntity.class, ing1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SetUniEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        StrTestEntity ed1 = getEntityManager().find(StrTestEntity.class, ed1_id);
+        StrTestEntity ed2 = getEntityManager().find(StrTestEntity.class, ed2_id);
+
+        SetUniEntity rev1 = getVersionsReader().find(SetUniEntity.class, ing1_id, 1);
+        SetUniEntity rev2 = getVersionsReader().find(SetUniEntity.class, ing1_id, 2);
+        SetUniEntity rev3 = getVersionsReader().find(SetUniEntity.class, ing1_id, 3);
+        SetUniEntity rev4 = getVersionsReader().find(SetUniEntity.class, ing1_id, 4);
+        SetUniEntity rev5 = getVersionsReader().find(SetUniEntity.class, ing1_id, 5);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_SET);
+        assert rev2.getReferences().equals(TestTools.makeSet(ed1));
+        assert rev3.getReferences().equals(TestTools.makeSet(ed1, ed2));
+        assert rev4.getReferences().equals(TestTools.makeSet(ed2));
+        assert rev5.getReferences().equals(Collections.EMPTY_SET);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        StrTestEntity ed1 = getEntityManager().find(StrTestEntity.class, ed1_id);
+        StrTestEntity ed2 = getEntityManager().find(StrTestEntity.class, ed2_id);
+
+        SetUniEntity rev1 = getVersionsReader().find(SetUniEntity.class, ing2_id, 1);
+        SetUniEntity rev2 = getVersionsReader().find(SetUniEntity.class, ing2_id, 2);
+        SetUniEntity rev3 = getVersionsReader().find(SetUniEntity.class, ing2_id, 3);
+        SetUniEntity rev4 = getVersionsReader().find(SetUniEntity.class, ing2_id, 4);
+        SetUniEntity rev5 = getVersionsReader().find(SetUniEntity.class, ing2_id, 5);
+
+        assert rev1.getReferences().equals(Collections.EMPTY_SET);
+        assert rev2.getReferences().equals(TestTools.makeSet(ed1, ed2));
+        assert rev3.getReferences().equals(TestTools.makeSet(ed1, ed2));
+        assert rev4.getReferences().equals(TestTools.makeSet(ed1, ed2));
+        assert rev5.getReferences().equals(TestTools.makeSet(ed1, ed2));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/BasicNaming.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/BasicNaming.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/BasicNaming.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,91 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicNaming extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(NamingTestEntity1.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        NamingTestEntity1 nte1 = new NamingTestEntity1("data1");
+        NamingTestEntity1 nte2 = new NamingTestEntity1("data2");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(nte1);
+        em.persist(nte2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        nte1 = em.find(NamingTestEntity1.class, nte1.getId());
+        nte1.setData("data1'");
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        nte2 = em.find(NamingTestEntity1.class, nte2.getId());
+        nte2.setData("data2'");
+
+        em.getTransaction().commit();
+
+        //
+
+        id1 = nte1.getId();
+        id2 = nte2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(NamingTestEntity1.class, id1));
+
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(NamingTestEntity1.class, id2));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        NamingTestEntity1 ver1 = new NamingTestEntity1(id1, "data1");
+        NamingTestEntity1 ver2 = new NamingTestEntity1(id1, "data1'");
+
+        assert getVersionsReader().find(NamingTestEntity1.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(NamingTestEntity1.class, id1, 2).equals(ver2);
+        assert getVersionsReader().find(NamingTestEntity1.class, id1, 3).equals(ver2);
+    }
+
+    @Test
+    public void testHistoryOfId2() {
+        NamingTestEntity1 ver1 = new NamingTestEntity1(id2, "data2");
+        NamingTestEntity1 ver2 = new NamingTestEntity1(id2, "data2'");
+
+        assert getVersionsReader().find(NamingTestEntity1.class, id2, 1).equals(ver1);
+        assert getVersionsReader().find(NamingTestEntity1.class, id2, 2).equals(ver1);
+        assert getVersionsReader().find(NamingTestEntity1.class, id2, 3).equals(ver2);
+    }
+
+    @Test
+    public void testTableName() {
+        assert "naming_test_entity_1_versions".equals(
+                getCfg().getClassMapping("org.jboss.envers.test.integration.naming.NamingTestEntity1_versions")
+                        .getTable().getName());
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/DetachedNamingTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/DetachedNamingTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/DetachedNamingTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.*;
+import java.util.Set;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class DetachedNamingTestEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany
+    @JoinTable(name = "UNI_NAMING_TEST",
+        joinColumns = @JoinColumn(name = "ID_1"),
+        inverseJoinColumns = @JoinColumn(name = "ID_2"))
+    private Set<StrTestEntity> collection;
+
+    public DetachedNamingTestEntity() {
+    }
+
+    public DetachedNamingTestEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public DetachedNamingTestEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<StrTestEntity> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Set<StrTestEntity> collection) {
+        this.collection = collection;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DetachedNamingTestEntity)) return false;
+
+        DetachedNamingTestEntity that = (DetachedNamingTestEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "DetachedNamingTestEntity(id = " + id + ", data = " + data + ")";
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNaming.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNaming.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNaming.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,113 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Column;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class JoinNaming extends AbstractEntityTest {
+    private Integer ed_id1;
+    private Integer ed_id2;
+    private Integer ing_id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(JoinNamingRefEdEntity.class);
+        cfg.addAnnotatedClass(JoinNamingRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        JoinNamingRefEdEntity ed1 = new JoinNamingRefEdEntity("data1");
+        JoinNamingRefEdEntity ed2 = new JoinNamingRefEdEntity("data2");
+
+        JoinNamingRefIngEntity ing1 = new JoinNamingRefIngEntity("x", ed1);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ing1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ed2 = em.find(JoinNamingRefEdEntity.class, ed2.getId());
+
+        ing1 = em.find(JoinNamingRefIngEntity.class, ing1.getId());
+        ing1.setData("y");
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed_id1 = ed1.getId();
+        ed_id2 = ed2.getId();
+        ing_id1 = ing1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinNamingRefEdEntity.class, ed_id1));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinNamingRefEdEntity.class, ed_id2));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinNamingRefIngEntity.class, ing_id1));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        JoinNamingRefEdEntity ver1 = new JoinNamingRefEdEntity(ed_id1, "data1");
+
+        assert getVersionsReader().find(JoinNamingRefEdEntity.class, ed_id1, 1).equals(ver1);
+        assert getVersionsReader().find(JoinNamingRefEdEntity.class, ed_id1, 2).equals(ver1);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        JoinNamingRefEdEntity ver1 = new JoinNamingRefEdEntity(ed_id2, "data2");
+
+        assert getVersionsReader().find(JoinNamingRefEdEntity.class, ed_id2, 1).equals(ver1);
+        assert getVersionsReader().find(JoinNamingRefEdEntity.class, ed_id2, 2).equals(ver1);
+    }
+
+    @Test
+    public void testHistoryOfIngId1() {
+        JoinNamingRefIngEntity ver1 = new JoinNamingRefIngEntity(ing_id1, "x", null);
+        JoinNamingRefIngEntity ver2 = new JoinNamingRefIngEntity(ing_id1, "y", null);
+
+        assert getVersionsReader().find(JoinNamingRefIngEntity.class, ing_id1, 1).equals(ver1);
+        assert getVersionsReader().find(JoinNamingRefIngEntity.class, ing_id1, 2).equals(ver2);
+
+        assert getVersionsReader().find(JoinNamingRefIngEntity.class, ing_id1, 1).getReference().equals(
+                new JoinNamingRefEdEntity(ed_id1, "data1"));
+        assert getVersionsReader().find(JoinNamingRefIngEntity.class, ing_id1, 2).getReference().equals(
+                new JoinNamingRefEdEntity(ed_id2, "data2"));
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testJoinColumnName() {
+        Iterator<Column> columns =
+                getCfg().getClassMapping("org.jboss.envers.test.integration.naming.JoinNamingRefIngEntity_versions")
+                .getProperty("reference").getColumnIterator();
+
+        while (columns.hasNext()) {
+            if ("jnree_column_reference".equals(columns.next().getName())) {
+                return;
+            }
+        }
+
+        assert false;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNamingRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNamingRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNamingRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * ReferencEd entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class JoinNamingRefEdEntity {
+    @Id
+    @GeneratedValue
+    @Column(name = "jnree_id")
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    private List<JoinNamingRefIngEntity> reffering;
+
+    public JoinNamingRefEdEntity() {
+    }
+
+    public JoinNamingRefEdEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public JoinNamingRefEdEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public List<JoinNamingRefIngEntity> getReffering() {
+        return reffering;
+    }
+
+    public void setReffering(List<JoinNamingRefIngEntity> reffering) {
+        this.reffering = reffering;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof JoinNamingRefEdEntity)) return false;
+
+        JoinNamingRefEdEntity that = (JoinNamingRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "JoinNamingRefEdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNamingRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNamingRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/JoinNamingRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * ReferencIng entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class JoinNamingRefIngEntity {
+    @Id
+    @GeneratedValue
+    @Column(name = "jnrie_id")
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToOne
+    @JoinColumn(name = "jnree_column_reference")
+    private JoinNamingRefEdEntity reference;
+
+    public JoinNamingRefIngEntity() { }
+
+    public JoinNamingRefIngEntity(Integer id, String data, JoinNamingRefEdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public JoinNamingRefIngEntity(String data, JoinNamingRefEdEntity reference) {
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public JoinNamingRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(JoinNamingRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof JoinNamingRefIngEntity)) return false;
+
+        JoinNamingRefIngEntity that = (JoinNamingRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "JoinNamingRefIngEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/NamingTestEntity1.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/NamingTestEntity1.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/NamingTestEntity1.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,70 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.VersionsTable;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Table(name="naming_test_entity_1")
+ at VersionsTable("naming_test_entity_1_versions")
+public class NamingTestEntity1 {
+    @Id
+    @GeneratedValue
+    @Column(name = "nte_id")
+    private Integer id;
+
+    @Column(name = "nte_data")
+    @Versioned
+    private String data;
+
+    public NamingTestEntity1() {
+    }
+
+    public NamingTestEntity1(String data) {
+        this.data = data;
+    }
+
+    public NamingTestEntity1(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof NamingTestEntity1)) return false;
+
+        NamingTestEntity1 that = (NamingTestEntity1) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/OneToManyUnidirectionalNaming.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/OneToManyUnidirectionalNaming.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/OneToManyUnidirectionalNaming.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,107 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Column;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class OneToManyUnidirectionalNaming extends AbstractEntityTest {
+    private Integer uni1_id;
+    private Integer str1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(DetachedNamingTestEntity.class);
+        cfg.addAnnotatedClass(StrTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        DetachedNamingTestEntity uni1 = new DetachedNamingTestEntity(1, "data1");
+        StrTestEntity str1 = new StrTestEntity("str1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        uni1.setCollection(new HashSet<StrTestEntity>());
+        em.persist(uni1);
+        em.persist(str1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        uni1 = em.find(DetachedNamingTestEntity.class, uni1.getId());
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        uni1.getCollection().add(str1);
+
+        em.getTransaction().commit();
+
+        //
+
+        uni1_id = uni1.getId();
+        str1_id = str1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(DetachedNamingTestEntity.class, uni1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+    }
+
+    @Test
+    public void testHistoryOfUniId1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+
+        DetachedNamingTestEntity rev1 = getVersionsReader().find(DetachedNamingTestEntity.class, uni1_id, 1);
+        DetachedNamingTestEntity rev2 = getVersionsReader().find(DetachedNamingTestEntity.class, uni1_id, 2);
+
+        assert rev1.getCollection().equals(TestTools.makeSet());
+        assert rev2.getCollection().equals(TestTools.makeSet(str1));
+
+        assert "data1".equals(rev1.getData());
+        assert "data1".equals(rev2.getData());
+    }
+
+    private final static String MIDDLE_VERSIONS_ENTITY_NAME = "UNI_NAMING_TEST_versions";
+    @Test
+    public void testTableName() {
+        assert MIDDLE_VERSIONS_ENTITY_NAME.equals(
+                getCfg().getClassMapping(MIDDLE_VERSIONS_ENTITY_NAME).getTable().getName());
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testJoinColumnName() {
+        Iterator<Column> columns =
+                getCfg().getClassMapping(MIDDLE_VERSIONS_ENTITY_NAME).getTable().getColumnIterator();
+
+        boolean id1Found = false;
+        boolean id2Found = false;
+
+        while (columns.hasNext()) {
+            Column column = columns.next();
+            if ("ID_1".equals(column.getName())) {
+                id1Found = true;
+            }
+
+            if ("ID_2".equals(column.getName())) {
+                id2Found = true;
+            }
+        }
+
+        assert id1Found && id2Found;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/VersionsJoinTableNaming.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/VersionsJoinTableNaming.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/VersionsJoinTableNaming.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,108 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Column;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsJoinTableNaming extends AbstractEntityTest {
+    private Integer uni1_id;
+    private Integer str1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(VersionsJoinTableTestEntity.class);
+        cfg.addAnnotatedClass(StrTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        VersionsJoinTableTestEntity uni1 = new VersionsJoinTableTestEntity(1, "data1");
+        StrTestEntity str1 = new StrTestEntity("str1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        uni1.setCollection(new HashSet<StrTestEntity>());
+        em.persist(uni1);
+        em.persist(str1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        uni1 = em.find(VersionsJoinTableTestEntity.class, uni1.getId());
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        uni1.getCollection().add(str1);
+
+        em.getTransaction().commit();
+
+        //
+
+        uni1_id = uni1.getId();
+        str1_id = str1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(VersionsJoinTableTestEntity.class, uni1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+    }
+
+    @Test
+    public void testHistoryOfUniId1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+
+        VersionsJoinTableTestEntity rev1 = getVersionsReader().find(VersionsJoinTableTestEntity.class, uni1_id, 1);
+        VersionsJoinTableTestEntity rev2 = getVersionsReader().find(VersionsJoinTableTestEntity.class, uni1_id, 2);
+
+        assert rev1.getCollection().equals(TestTools.makeSet());
+        assert rev2.getCollection().equals(TestTools.makeSet(str1));
+
+        assert "data1".equals(rev1.getData());
+        assert "data1".equals(rev2.getData());
+    }
+
+    private final static String MIDDLE_VERSIONS_ENTITY_NAME = "VERSIONS_JOIN_TABLE_TEST";
+    
+    @Test
+    public void testTableName() {
+        assert MIDDLE_VERSIONS_ENTITY_NAME.equals(
+                getCfg().getClassMapping(MIDDLE_VERSIONS_ENTITY_NAME).getTable().getName());
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testJoinColumnName() {
+        Iterator<Column> columns =
+                getCfg().getClassMapping(MIDDLE_VERSIONS_ENTITY_NAME).getTable().getColumnIterator();
+
+        boolean id1Found = false;
+        boolean id2Found = false;
+
+        while (columns.hasNext()) {
+            Column column = columns.next();
+            if ("VJT_ID".equals(column.getName())) {
+                id1Found = true;
+            }
+
+            if ("STR_ID".equals(column.getName())) {
+                id2Found = true;
+            }
+        }
+
+        assert id1Found && id2Found;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/VersionsJoinTableTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/VersionsJoinTableTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/VersionsJoinTableTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.integration.naming;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.VersionsJoinTable;
+import org.jboss.envers.test.entities.StrTestEntity;
+
+import javax.persistence.*;
+import java.util.Set;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class VersionsJoinTableTestEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany
+    @JoinColumn(name = "VJT_ID")
+    @VersionsJoinTable(name = "VERSIONS_JOIN_TABLE_TEST", inverseJoinColumns = @JoinColumn(name = "STR_ID"))
+    private Set<StrTestEntity> collection;
+
+    public VersionsJoinTableTestEntity() {
+    }
+
+    public VersionsJoinTableTestEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public VersionsJoinTableTestEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Set<StrTestEntity> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Set<StrTestEntity> collection) {
+        this.collection = collection;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof VersionsJoinTableTestEntity)) return false;
+
+        VersionsJoinTableTestEntity that = (VersionsJoinTableTestEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "VersionsJoinTableTestEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/EmbIdNaming.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/EmbIdNaming.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/EmbIdNaming.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,66 @@
+package org.jboss.envers.test.integration.naming.ids;
+
+import org.jboss.envers.test.entities.ids.EmbId;
+
+import javax.persistence.Embeddable;
+import javax.persistence.Column;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Embeddable
+public class EmbIdNaming implements Serializable {
+    @Column(name = "XX")
+    private Integer x;
+
+    @Column(name = "YY")
+    private Integer y;
+
+    public EmbIdNaming() {
+    }
+
+    public EmbIdNaming(Integer x, Integer y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public Integer getX() {
+        return x;
+    }
+
+    public void setX(Integer x) {
+        this.x = x;
+    }
+
+    public Integer getY() {
+        return y;
+    }
+
+    public void setY(Integer y) {
+        this.y = y;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EmbIdNaming)) return false;
+
+        EmbIdNaming embId = (EmbIdNaming) o;
+
+        if (x != null ? !x.equals(embId.x) : embId.x != null) return false;
+        if (y != null ? !y.equals(embId.y) : embId.y != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (x != null ? x.hashCode() : 0);
+        result = 31 * result + (y != null ? y.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "EmbIdNaming(" + x + ", " + y + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNaming.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNaming.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNaming.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,117 @@
+package org.jboss.envers.test.integration.naming.ids;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Column;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class JoinEmbIdNaming extends AbstractEntityTest {
+    private EmbIdNaming ed_id1;
+    private EmbIdNaming ed_id2;
+    private EmbIdNaming ing_id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(JoinEmbIdNamingRefEdEntity.class);
+        cfg.addAnnotatedClass(JoinEmbIdNamingRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        ed_id1 = new EmbIdNaming(10, 20);
+        ed_id2 = new EmbIdNaming(11, 21);
+        ing_id1 = new EmbIdNaming(12, 22);
+
+        JoinEmbIdNamingRefEdEntity ed1 = new JoinEmbIdNamingRefEdEntity(ed_id1, "data1");
+        JoinEmbIdNamingRefEdEntity ed2 = new JoinEmbIdNamingRefEdEntity(ed_id2, "data2");
+
+        JoinEmbIdNamingRefIngEntity ing1 = new JoinEmbIdNamingRefIngEntity(ing_id1, "x", ed1);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ing1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ed2 = em.find(JoinEmbIdNamingRefEdEntity.class, ed2.getId());
+
+        ing1 = em.find(JoinEmbIdNamingRefIngEntity.class, ing1.getId());
+        ing1.setData("y");
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinEmbIdNamingRefEdEntity.class, ed_id1));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinEmbIdNamingRefEdEntity.class, ed_id2));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinEmbIdNamingRefIngEntity.class, ing_id1));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        JoinEmbIdNamingRefEdEntity ver1 = new JoinEmbIdNamingRefEdEntity(ed_id1, "data1");
+
+        assert getVersionsReader().find(JoinEmbIdNamingRefEdEntity.class, ed_id1, 1).equals(ver1);
+        assert getVersionsReader().find(JoinEmbIdNamingRefEdEntity.class, ed_id1, 2).equals(ver1);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        JoinEmbIdNamingRefEdEntity ver1 = new JoinEmbIdNamingRefEdEntity(ed_id2, "data2");
+
+        assert getVersionsReader().find(JoinEmbIdNamingRefEdEntity.class, ed_id2, 1).equals(ver1);
+        assert getVersionsReader().find(JoinEmbIdNamingRefEdEntity.class, ed_id2, 2).equals(ver1);
+    }
+
+    @Test
+    public void testHistoryOfIngId1() {
+        JoinEmbIdNamingRefIngEntity ver1 = new JoinEmbIdNamingRefIngEntity(ing_id1, "x", null);
+        JoinEmbIdNamingRefIngEntity ver2 = new JoinEmbIdNamingRefIngEntity(ing_id1, "y", null);
+
+        assert getVersionsReader().find(JoinEmbIdNamingRefIngEntity.class, ing_id1, 1).equals(ver1);
+        assert getVersionsReader().find(JoinEmbIdNamingRefIngEntity.class, ing_id1, 2).equals(ver2);
+
+        assert getVersionsReader().find(JoinEmbIdNamingRefIngEntity.class, ing_id1, 1).getReference().equals(
+                new JoinEmbIdNamingRefEdEntity(ed_id1, "data1"));
+        assert getVersionsReader().find(JoinEmbIdNamingRefIngEntity.class, ing_id1, 2).getReference().equals(
+                new JoinEmbIdNamingRefEdEntity(ed_id2, "data2"));
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testJoinColumnNames() {
+        Iterator<Column> columns =
+                getCfg().getClassMapping("org.jboss.envers.test.integration.naming.ids.JoinEmbIdNamingRefIngEntity_versions")
+                .getProperty("reference").getColumnIterator();
+
+        boolean xxFound = false;
+        boolean yyFound = false;
+        while (columns.hasNext()) {
+            if ("XX_reference".equals(columns.next().getName())) {
+                xxFound = true;
+            }
+
+            if ("YY_reference".equals(columns.next().getName())) {
+                yyFound = true;
+            }
+        }
+
+        assert xxFound && yyFound;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNamingRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNamingRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNamingRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,83 @@
+package org.jboss.envers.test.integration.naming.ids;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * ReferencEd entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class JoinEmbIdNamingRefEdEntity {
+    @Id
+    @GeneratedValue
+    private EmbIdNaming id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    private List<JoinEmbIdNamingRefIngEntity> reffering;
+
+    public JoinEmbIdNamingRefEdEntity() {
+    }
+
+    public JoinEmbIdNamingRefEdEntity(EmbIdNaming id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public JoinEmbIdNamingRefEdEntity(String data) {
+        this.data = data;
+    }
+
+    public EmbIdNaming getId() {
+        return id;
+    }
+
+    public void setId(EmbIdNaming id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public List<JoinEmbIdNamingRefIngEntity> getReffering() {
+        return reffering;
+    }
+
+    public void setReffering(List<JoinEmbIdNamingRefIngEntity> reffering) {
+        this.reffering = reffering;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof JoinEmbIdNamingRefEdEntity)) return false;
+
+        JoinEmbIdNamingRefEdEntity that = (JoinEmbIdNamingRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "JoinEmbIdNamingRefEdEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNamingRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNamingRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinEmbIdNamingRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,85 @@
+package org.jboss.envers.test.integration.naming.ids;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * ReferencIng entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class JoinEmbIdNamingRefIngEntity {
+    @Id
+    @GeneratedValue
+    private EmbIdNaming id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToOne
+    @JoinColumns({@JoinColumn(name = "XX_reference", referencedColumnName = "XX"),
+        @JoinColumn(name = "YY_reference", referencedColumnName = "YY")})
+    private JoinEmbIdNamingRefEdEntity reference;
+
+    public JoinEmbIdNamingRefIngEntity() { }
+
+    public JoinEmbIdNamingRefIngEntity(EmbIdNaming id, String data, JoinEmbIdNamingRefEdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public JoinEmbIdNamingRefIngEntity(String data, JoinEmbIdNamingRefEdEntity reference) {
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public EmbIdNaming getId() {
+        return id;
+    }
+
+    public void setId(EmbIdNaming id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public JoinEmbIdNamingRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(JoinEmbIdNamingRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof JoinEmbIdNamingRefIngEntity)) return false;
+
+        JoinEmbIdNamingRefIngEntity that = (JoinEmbIdNamingRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "JoinEmbIdNamingRefIngEntity(id = " + id + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNaming.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNaming.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNaming.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,117 @@
+package org.jboss.envers.test.integration.naming.ids;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Column;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class JoinMulIdNaming extends AbstractEntityTest {
+    private MulIdNaming ed_id1;
+    private MulIdNaming ed_id2;
+    private MulIdNaming ing_id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(JoinMulIdNamingRefEdEntity.class);
+        cfg.addAnnotatedClass(JoinMulIdNamingRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        ed_id1 = new MulIdNaming(10, 20);
+        ed_id2 = new MulIdNaming(11, 21);
+        ing_id1 = new MulIdNaming(12, 22);
+
+        JoinMulIdNamingRefEdEntity ed1 = new JoinMulIdNamingRefEdEntity(ed_id1, "data1");
+        JoinMulIdNamingRefEdEntity ed2 = new JoinMulIdNamingRefEdEntity(ed_id2, "data2");
+
+        JoinMulIdNamingRefIngEntity ing1 = new JoinMulIdNamingRefIngEntity(ing_id1, "x", ed1);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ing1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ed2 = em.find(JoinMulIdNamingRefEdEntity.class, ed_id2);
+
+        ing1 = em.find(JoinMulIdNamingRefIngEntity.class, ing_id1);
+        ing1.setData("y");
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinMulIdNamingRefEdEntity.class, ed_id1));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinMulIdNamingRefEdEntity.class, ed_id2));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(JoinMulIdNamingRefIngEntity.class, ing_id1));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        JoinMulIdNamingRefEdEntity ver1 = new JoinMulIdNamingRefEdEntity(ed_id1, "data1");
+
+        assert getVersionsReader().find(JoinMulIdNamingRefEdEntity.class, ed_id1, 1).equals(ver1);
+        assert getVersionsReader().find(JoinMulIdNamingRefEdEntity.class, ed_id1, 2).equals(ver1);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        JoinMulIdNamingRefEdEntity ver1 = new JoinMulIdNamingRefEdEntity(ed_id2, "data2");
+
+        assert getVersionsReader().find(JoinMulIdNamingRefEdEntity.class, ed_id2, 1).equals(ver1);
+        assert getVersionsReader().find(JoinMulIdNamingRefEdEntity.class, ed_id2, 2).equals(ver1);
+    }
+
+    @Test
+    public void testHistoryOfIngId1() {
+        JoinMulIdNamingRefIngEntity ver1 = new JoinMulIdNamingRefIngEntity(ing_id1, "x", null);
+        JoinMulIdNamingRefIngEntity ver2 = new JoinMulIdNamingRefIngEntity(ing_id1, "y", null);
+
+        assert getVersionsReader().find(JoinMulIdNamingRefIngEntity.class, ing_id1, 1).equals(ver1);
+        assert getVersionsReader().find(JoinMulIdNamingRefIngEntity.class, ing_id1, 2).equals(ver2);
+
+        assert getVersionsReader().find(JoinMulIdNamingRefIngEntity.class, ing_id1, 1).getReference().equals(
+                new JoinMulIdNamingRefEdEntity(ed_id1, "data1"));
+        assert getVersionsReader().find(JoinMulIdNamingRefIngEntity.class, ing_id1, 2).getReference().equals(
+                new JoinMulIdNamingRefEdEntity(ed_id2, "data2"));
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testJoinColumnNames() {
+        Iterator<Column> columns =
+                getCfg().getClassMapping("org.jboss.envers.test.integration.naming.ids.JoinMulIdNamingRefIngEntity_versions")
+                .getProperty("reference").getColumnIterator();
+
+        boolean id1Found = false;
+        boolean id2Found = false;
+        while (columns.hasNext()) {
+            if ("ID1_reference".equals(columns.next().getName())) {
+                id1Found = true;
+            }
+
+            if ("ID2_reference".equals(columns.next().getName())) {
+                id2Found = true;
+            }
+        }
+
+        assert id1Found && id2Found;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNamingRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNamingRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNamingRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,97 @@
+package org.jboss.envers.test.integration.naming.ids;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * ReferencEd entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at IdClass(MulIdNaming.class)
+public class JoinMulIdNamingRefEdEntity {
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    private List<JoinMulIdNamingRefIngEntity> reffering;
+
+    public JoinMulIdNamingRefEdEntity() {
+    }
+
+    public JoinMulIdNamingRefEdEntity(MulIdNaming id, String data) {
+        this.id1 = id.getId1();
+        this.id2 = id.getId2();
+        this.data = data;
+    }
+
+    public JoinMulIdNamingRefEdEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public List<JoinMulIdNamingRefIngEntity> getReffering() {
+        return reffering;
+    }
+
+    public void setReffering(List<JoinMulIdNamingRefIngEntity> reffering) {
+        this.reffering = reffering;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof JoinMulIdNamingRefEdEntity)) return false;
+
+        JoinMulIdNamingRefEdEntity that = (JoinMulIdNamingRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "JoinMulIdNamingRefEdEntity(id1 = " + id1 + ", id2 = " + id2 + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNamingRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNamingRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/JoinMulIdNamingRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,91 @@
+package org.jboss.envers.test.integration.naming.ids;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * ReferencIng entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at IdClass(MulIdNaming.class)
+public class JoinMulIdNamingRefIngEntity {    
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @ManyToOne
+    @JoinColumns({@JoinColumn(name = "ID2_reference", referencedColumnName = "ID_2"),
+        @JoinColumn(name = "ID1_reference", referencedColumnName = "ID_1")})
+    private JoinMulIdNamingRefEdEntity reference;
+
+    public JoinMulIdNamingRefIngEntity() { }
+
+    public JoinMulIdNamingRefIngEntity(MulIdNaming id, String data, JoinMulIdNamingRefEdEntity reference) {
+        this.id1 = id.getId1();
+        this.id2 = id.getId2();
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public JoinMulIdNamingRefIngEntity(String data, JoinMulIdNamingRefEdEntity reference) {
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public JoinMulIdNamingRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(JoinMulIdNamingRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof JoinMulIdNamingRefIngEntity)) return false;
+
+        JoinMulIdNamingRefIngEntity that = (JoinMulIdNamingRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "JoinMulIdNamingRefIngEntity(id1 = " + id1 + ", id2 = " + id2 + ", data = " + data + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/MulIdNaming.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/MulIdNaming.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/naming/ids/MulIdNaming.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,62 @@
+package org.jboss.envers.test.integration.naming.ids;
+
+import javax.persistence.Column;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MulIdNaming implements Serializable {
+    @Column(name = "ID_1")
+    private Integer id1;
+
+    @Column(name = "ID_2")  
+    private Integer id2;
+
+    public MulIdNaming() {
+    }
+
+    public MulIdNaming(Integer id1, Integer id2) {
+        this.id1 = id1;
+        this.id2 = id2;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MulIdNaming)) return false;
+
+        MulIdNaming mulId = (MulIdNaming) o;
+
+        if (id1 != null ? !id1.equals(mulId.id1) : mulId.id1 != null) return false;
+        if (id2 != null ? !id2.equals(mulId.id2) : mulId.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        return "MulIdNaming(" + id1 + ", " + id2 + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/NotInsertable.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/NotInsertable.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/NotInsertable.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,49 @@
+package org.jboss.envers.test.integration.notinsertable;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NotInsertable extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(NotInsertableTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        NotInsertableTestEntity dte = new NotInsertableTestEntity("data1");
+        em.persist(dte);
+        id1 = dte.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        dte = em.find(NotInsertableTestEntity.class, id1);
+        dte.setData("data2");
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(NotInsertableTestEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        NotInsertableTestEntity ver1 = new NotInsertableTestEntity(id1, "data1", "data1");
+        NotInsertableTestEntity ver2 = new NotInsertableTestEntity(id1, "data2", "data2");
+
+        assert getVersionsReader().find(NotInsertableTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(NotInsertableTestEntity.class, id1, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/NotInsertableTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/NotInsertableTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/notinsertable/NotInsertableTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,83 @@
+package org.jboss.envers.test.integration.notinsertable;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Column;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Versioned
+public class NotInsertableTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Column(name = "data")
+    private String data;
+
+    @Column(name = "data", insertable = false, updatable = false)
+    private String dataCopy;
+
+    public NotInsertableTestEntity() {
+    }
+
+    public NotInsertableTestEntity(Integer id, String data, String dataCopy) {
+        this.id = id;
+        this.data = data;
+        this.dataCopy = dataCopy;
+    }
+
+    public NotInsertableTestEntity(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public String getDataCopy() {
+        return dataCopy;
+    }
+
+    public void setDataCopy(String dataCopy) {
+        this.dataCopy = dataCopy;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof NotInsertableTestEntity)) return false;
+
+        NotInsertableTestEntity that = (NotInsertableTestEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (dataCopy != null ? !dataCopy.equals(that.dataCopy) : that.dataCopy != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        result = 31 * result + (dataCopy != null ? dataCopy.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicCollection.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicCollection.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicCollection.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,163 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.CollectionRefEdEntity;
+import org.jboss.envers.test.entities.onetomany.CollectionRefIngEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicCollection extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(CollectionRefEdEntity.class);
+        cfg.addAnnotatedClass(CollectionRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        CollectionRefEdEntity ed1 = new CollectionRefEdEntity(1, "data_ed_1");
+        CollectionRefEdEntity ed2 = new CollectionRefEdEntity(2, "data_ed_2");
+
+        CollectionRefIngEntity ing1 = new CollectionRefIngEntity(3, "data_ing_1", ed1);
+        CollectionRefIngEntity ing2 = new CollectionRefIngEntity(4, "data_ing_2", ed1);
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(CollectionRefIngEntity.class, ing1.getId());
+        ed2 = em.find(CollectionRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing2 = em.find(CollectionRefIngEntity.class, ing2.getId());
+        ed2 = em.find(CollectionRefEdEntity.class, ed2.getId());
+
+        ing2.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(CollectionRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(CollectionRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(CollectionRefIngEntity.class, ing1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(CollectionRefIngEntity.class, ing2_id));
+    }
+
+    private <T> Set<T> makeSet(T... objects) {
+        Set<T> ret = new HashSet<T>();
+        //noinspection ManualArrayToCollectionCopy
+        for (T obj : objects) { ret.add(obj); }
+        return ret;
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        CollectionRefIngEntity ing1 = getEntityManager().find(CollectionRefIngEntity.class, ing1_id);
+        CollectionRefIngEntity ing2 = getEntityManager().find(CollectionRefIngEntity.class, ing2_id);
+
+        CollectionRefEdEntity rev1 = getVersionsReader().find(CollectionRefEdEntity.class, ed1_id, 1);
+        CollectionRefEdEntity rev2 = getVersionsReader().find(CollectionRefEdEntity.class, ed1_id, 2);
+        CollectionRefEdEntity rev3 = getVersionsReader().find(CollectionRefEdEntity.class, ed1_id, 3);
+
+        assert rev1.getReffering().containsAll(makeSet(ing1, ing2));
+        assert rev1.getReffering().size() == 2;
+
+        assert rev2.getReffering().containsAll(makeSet(ing2));
+        assert rev2.getReffering().size() == 1;
+
+        assert rev3.getReffering().containsAll(Collections.EMPTY_SET);
+        assert rev3.getReffering().size() == 0;
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        CollectionRefIngEntity ing1 = getEntityManager().find(CollectionRefIngEntity.class, ing1_id);
+        CollectionRefIngEntity ing2 = getEntityManager().find(CollectionRefIngEntity.class, ing2_id);
+
+        CollectionRefEdEntity rev1 = getVersionsReader().find(CollectionRefEdEntity.class, ed2_id, 1);
+        CollectionRefEdEntity rev2 = getVersionsReader().find(CollectionRefEdEntity.class, ed2_id, 2);
+        CollectionRefEdEntity rev3 = getVersionsReader().find(CollectionRefEdEntity.class, ed2_id, 3);
+
+        assert rev1.getReffering().containsAll(Collections.EMPTY_SET);
+        assert rev1.getReffering().size() == 0;
+
+        assert rev2.getReffering().containsAll(makeSet(ing1));
+        assert rev2.getReffering().size() == 1;
+
+        assert rev3.getReffering().containsAll(makeSet(ing1, ing2));
+        assert rev3.getReffering().size() == 2;
+
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        CollectionRefEdEntity ed1 = getEntityManager().find(CollectionRefEdEntity.class, ed1_id);
+        CollectionRefEdEntity ed2 = getEntityManager().find(CollectionRefEdEntity.class, ed2_id);
+
+        CollectionRefIngEntity rev1 = getVersionsReader().find(CollectionRefIngEntity.class, ing1_id, 1);
+        CollectionRefIngEntity rev2 = getVersionsReader().find(CollectionRefIngEntity.class, ing1_id, 2);
+        CollectionRefIngEntity rev3 = getVersionsReader().find(CollectionRefIngEntity.class, ing1_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed2);
+        assert rev3.getReference().equals(ed2);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        CollectionRefEdEntity ed1 = getEntityManager().find(CollectionRefEdEntity.class, ed1_id);
+        CollectionRefEdEntity ed2 = getEntityManager().find(CollectionRefEdEntity.class, ed2_id);
+
+        CollectionRefIngEntity rev1 = getVersionsReader().find(CollectionRefIngEntity.class, ing2_id, 1);
+        CollectionRefIngEntity rev2 = getVersionsReader().find(CollectionRefIngEntity.class, ing2_id, 2);
+        CollectionRefIngEntity rev3 = getVersionsReader().find(CollectionRefIngEntity.class, ing2_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed1);
+        assert rev3.getReference().equals(ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicList.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicList.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicList.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,142 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.ListRefEdEntity;
+import org.jboss.envers.test.entities.onetomany.ListRefIngEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicList extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(ListRefEdEntity.class);
+        cfg.addAnnotatedClass(ListRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        ListRefEdEntity ed1 = new ListRefEdEntity(1, "data_ed_1");
+        ListRefEdEntity ed2 = new ListRefEdEntity(2, "data_ed_2");
+
+        ListRefIngEntity ing1 = new ListRefIngEntity(3, "data_ing_1", ed1);
+        ListRefIngEntity ing2 = new ListRefIngEntity(4, "data_ing_2", ed1);
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(ListRefIngEntity.class, ing1.getId());
+        ed2 = em.find(ListRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing2 = em.find(ListRefIngEntity.class, ing2.getId());
+        ed2 = em.find(ListRefEdEntity.class, ed2.getId());
+
+        ing2.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(ListRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(ListRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(ListRefIngEntity.class, ing1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(ListRefIngEntity.class, ing2_id));
+    }
+    @Test
+    public void testHistoryOfEdId1() {
+        ListRefIngEntity ing1 = getEntityManager().find(ListRefIngEntity.class, ing1_id);
+        ListRefIngEntity ing2 = getEntityManager().find(ListRefIngEntity.class, ing2_id);
+
+        ListRefEdEntity rev1 = getVersionsReader().find(ListRefEdEntity.class, ed1_id, 1);
+        ListRefEdEntity rev2 = getVersionsReader().find(ListRefEdEntity.class, ed1_id, 2);
+        ListRefEdEntity rev3 = getVersionsReader().find(ListRefEdEntity.class, ed1_id, 3);
+
+        assert TestTools.checkList(rev1.getReffering(), ing1, ing2);
+        assert TestTools.checkList(rev2.getReffering(), ing2);
+        assert TestTools.checkList(rev3.getReffering());
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        ListRefIngEntity ing1 = getEntityManager().find(ListRefIngEntity.class, ing1_id);
+        ListRefIngEntity ing2 = getEntityManager().find(ListRefIngEntity.class, ing2_id);
+
+        ListRefEdEntity rev1 = getVersionsReader().find(ListRefEdEntity.class, ed2_id, 1);
+        ListRefEdEntity rev2 = getVersionsReader().find(ListRefEdEntity.class, ed2_id, 2);
+        ListRefEdEntity rev3 = getVersionsReader().find(ListRefEdEntity.class, ed2_id, 3);
+
+        assert TestTools.checkList(rev1.getReffering());
+        assert TestTools.checkList(rev2.getReffering(), ing1);
+        assert TestTools.checkList(rev3.getReffering(), ing1, ing2);
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        ListRefEdEntity ed1 = getEntityManager().find(ListRefEdEntity.class, ed1_id);
+        ListRefEdEntity ed2 = getEntityManager().find(ListRefEdEntity.class, ed2_id);
+
+        ListRefIngEntity rev1 = getVersionsReader().find(ListRefIngEntity.class, ing1_id, 1);
+        ListRefIngEntity rev2 = getVersionsReader().find(ListRefIngEntity.class, ing1_id, 2);
+        ListRefIngEntity rev3 = getVersionsReader().find(ListRefIngEntity.class, ing1_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed2);
+        assert rev3.getReference().equals(ed2);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        ListRefEdEntity ed1 = getEntityManager().find(ListRefEdEntity.class, ed1_id);
+        ListRefEdEntity ed2 = getEntityManager().find(ListRefEdEntity.class, ed2_id);
+
+        ListRefIngEntity rev1 = getVersionsReader().find(ListRefIngEntity.class, ing2_id, 1);
+        ListRefIngEntity rev2 = getVersionsReader().find(ListRefIngEntity.class, ing2_id, 2);
+        ListRefIngEntity rev3 = getVersionsReader().find(ListRefIngEntity.class, ing2_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed1);
+        assert rev3.getReference().equals(ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,163 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.SetRefEdEntity;
+import org.jboss.envers.test.entities.onetomany.SetRefIngEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicSet extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetRefEdEntity.class);
+        cfg.addAnnotatedClass(SetRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        SetRefEdEntity ed1 = new SetRefEdEntity(1, "data_ed_1");
+        SetRefEdEntity ed2 = new SetRefEdEntity(2, "data_ed_2");
+
+        SetRefIngEntity ing1 = new SetRefIngEntity(3, "data_ing_1");
+        SetRefIngEntity ing2 = new SetRefIngEntity(4, "data_ing_2");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+
+        em.getTransaction().begin();
+
+        ed1 = em.find(SetRefEdEntity.class, ed1.getId());
+
+        ing1.setReference(ed1);
+        ing2.setReference(ed1);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetRefIngEntity.class, ing1.getId());
+        ed2 = em.find(SetRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        ing2 = em.find(SetRefIngEntity.class, ing2.getId());
+        ed2 = em.find(SetRefEdEntity.class, ed2.getId());
+
+        ing2.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(SetRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 3, 4).equals(getVersionsReader().getRevisions(SetRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(2, 3).equals(getVersionsReader().getRevisions(SetRefIngEntity.class, ing1_id));
+        assert Arrays.asList(2, 4).equals(getVersionsReader().getRevisions(SetRefIngEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        SetRefIngEntity ing1 = getEntityManager().find(SetRefIngEntity.class, ing1_id);
+        SetRefIngEntity ing2 = getEntityManager().find(SetRefIngEntity.class, ing2_id);
+
+        SetRefEdEntity rev1 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 1);
+        SetRefEdEntity rev2 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 2);
+        SetRefEdEntity rev3 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 3);
+        SetRefEdEntity rev4 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 4);
+
+        assert rev1.getReffering().equals(Collections.EMPTY_SET);
+        assert rev2.getReffering().equals(TestTools.makeSet(ing1, ing2));
+        assert rev3.getReffering().equals(TestTools.makeSet(ing2));
+        assert rev4.getReffering().equals(Collections.EMPTY_SET);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        SetRefIngEntity ing1 = getEntityManager().find(SetRefIngEntity.class, ing1_id);
+        SetRefIngEntity ing2 = getEntityManager().find(SetRefIngEntity.class, ing2_id);
+
+        SetRefEdEntity rev1 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 1);
+        SetRefEdEntity rev2 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 2);
+        SetRefEdEntity rev3 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 3);
+        SetRefEdEntity rev4 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 4);
+
+        assert rev1.getReffering().equals(Collections.EMPTY_SET);
+        assert rev2.getReffering().equals(Collections.EMPTY_SET);
+        assert rev3.getReffering().equals(TestTools.makeSet(ing1));
+        assert rev4.getReffering().equals(TestTools.makeSet(ing1, ing2));
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        SetRefEdEntity ed1 = getEntityManager().find(SetRefEdEntity.class, ed1_id);
+        SetRefEdEntity ed2 = getEntityManager().find(SetRefEdEntity.class, ed2_id);
+
+        SetRefIngEntity rev1 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 1);
+        SetRefIngEntity rev2 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 2);
+        SetRefIngEntity rev3 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 3);
+        SetRefIngEntity rev4 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 4);
+
+        assert rev1 == null;
+        assert rev2.getReference().equals(ed1);
+        assert rev3.getReference().equals(ed2);
+        assert rev4.getReference().equals(ed2);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        SetRefEdEntity ed1 = getEntityManager().find(SetRefEdEntity.class, ed1_id);
+        SetRefEdEntity ed2 = getEntityManager().find(SetRefEdEntity.class, ed2_id);
+
+        SetRefIngEntity rev1 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 1);
+        SetRefIngEntity rev2 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 2);
+        SetRefIngEntity rev3 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 3);
+        SetRefIngEntity rev4 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 4);
+
+        assert rev1 == null;
+        assert rev2.getReference().equals(ed1);
+        assert rev3.getReference().equals(ed1);
+        assert rev4.getReference().equals(ed2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithEmbId.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithEmbId.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithEmbId.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,151 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.ids.EmbId;
+import org.jboss.envers.test.entities.onetomany.ids.SetRefEdEmbIdEntity;
+import org.jboss.envers.test.entities.onetomany.ids.SetRefIngEmbIdEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicSetWithEmbId extends AbstractEntityTest {
+    private EmbId ed1_id;
+    private EmbId ed2_id;
+
+    private EmbId ing1_id;
+    private EmbId ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetRefEdEmbIdEntity.class);
+        cfg.addAnnotatedClass(SetRefIngEmbIdEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        ed1_id = new EmbId(0, 1);
+        ed2_id = new EmbId(2, 3);
+
+        ing2_id = new EmbId(4, 5);
+        ing1_id = new EmbId(6, 7);
+
+        EntityManager em = getEntityManager();
+
+        SetRefEdEmbIdEntity ed1 = new SetRefEdEmbIdEntity(ed1_id, "data_ed_1");
+        SetRefEdEmbIdEntity ed2 = new SetRefEdEmbIdEntity(ed2_id, "data_ed_2");
+
+        SetRefIngEmbIdEntity ing1 = new SetRefIngEmbIdEntity(ing1_id, "data_ing_1", ed1);
+        SetRefIngEmbIdEntity ing2 = new SetRefIngEmbIdEntity(ing2_id, "data_ing_2", ed1);
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetRefIngEmbIdEntity.class, ing1.getId());
+        ed2 = em.find(SetRefEdEmbIdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing2 = em.find(SetRefIngEmbIdEntity.class, ing2.getId());
+        ed2 = em.find(SetRefEdEmbIdEntity.class, ed2.getId());
+
+        ing2.setReference(ed2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(SetRefEdEmbIdEntity.class, ed1_id));
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(SetRefEdEmbIdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SetRefIngEmbIdEntity.class, ing1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(SetRefIngEmbIdEntity.class, ing2_id));
+    }
+
+    private <T> Set<T> makeSet(T... objects) {
+        Set<T> ret = new HashSet<T>();
+        //noinspection ManualArrayToCollectionCopy
+        for (T obj : objects) { ret.add(obj); }
+        return ret;
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        SetRefIngEmbIdEntity ing1 = getEntityManager().find(SetRefIngEmbIdEntity.class, ing1_id);
+        SetRefIngEmbIdEntity ing2 = getEntityManager().find(SetRefIngEmbIdEntity.class, ing2_id);
+
+        SetRefEdEmbIdEntity rev1 = getVersionsReader().find(SetRefEdEmbIdEntity.class, ed1_id, 1);
+        SetRefEdEmbIdEntity rev2 = getVersionsReader().find(SetRefEdEmbIdEntity.class, ed1_id, 2);
+        SetRefEdEmbIdEntity rev3 = getVersionsReader().find(SetRefEdEmbIdEntity.class, ed1_id, 3);
+
+        assert rev1.getReffering().equals(makeSet(ing1, ing2));
+        assert rev2.getReffering().equals(makeSet(ing2));
+        assert rev3.getReffering().equals(Collections.EMPTY_SET);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        SetRefIngEmbIdEntity ing1 = getEntityManager().find(SetRefIngEmbIdEntity.class, ing1_id);
+        SetRefIngEmbIdEntity ing2 = getEntityManager().find(SetRefIngEmbIdEntity.class, ing2_id);
+
+        SetRefEdEmbIdEntity rev1 = getVersionsReader().find(SetRefEdEmbIdEntity.class, ed2_id, 1);
+        SetRefEdEmbIdEntity rev2 = getVersionsReader().find(SetRefEdEmbIdEntity.class, ed2_id, 2);
+        SetRefEdEmbIdEntity rev3 = getVersionsReader().find(SetRefEdEmbIdEntity.class, ed2_id, 3);
+
+        assert rev1.getReffering().equals(Collections.EMPTY_SET);
+        assert rev2.getReffering().equals(makeSet(ing1));
+        assert rev3.getReffering().equals(makeSet(ing1, ing2));
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        SetRefEdEmbIdEntity ed1 = getEntityManager().find(SetRefEdEmbIdEntity.class, ed1_id);
+        SetRefEdEmbIdEntity ed2 = getEntityManager().find(SetRefEdEmbIdEntity.class, ed2_id);
+
+        SetRefIngEmbIdEntity rev1 = getVersionsReader().find(SetRefIngEmbIdEntity.class, ing1_id, 1);
+        SetRefIngEmbIdEntity rev2 = getVersionsReader().find(SetRefIngEmbIdEntity.class, ing1_id, 2);
+        SetRefIngEmbIdEntity rev3 = getVersionsReader().find(SetRefIngEmbIdEntity.class, ing1_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed2);
+        assert rev3.getReference().equals(ed2);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        SetRefEdEmbIdEntity ed1 = getEntityManager().find(SetRefEdEmbIdEntity.class, ed1_id);
+        SetRefEdEmbIdEntity ed2 = getEntityManager().find(SetRefEdEmbIdEntity.class, ed2_id);
+
+        SetRefIngEmbIdEntity rev1 = getVersionsReader().find(SetRefIngEmbIdEntity.class, ing2_id, 1);
+        SetRefIngEmbIdEntity rev2 = getVersionsReader().find(SetRefIngEmbIdEntity.class, ing2_id, 2);
+        SetRefIngEmbIdEntity rev3 = getVersionsReader().find(SetRefIngEmbIdEntity.class, ing2_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed1);
+        assert rev3.getReference().equals(ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithMulId.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithMulId.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithMulId.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,151 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.ids.MulId;
+import org.jboss.envers.test.entities.onetomany.ids.SetRefEdMulIdEntity;
+import org.jboss.envers.test.entities.onetomany.ids.SetRefIngMulIdEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicSetWithMulId extends AbstractEntityTest {
+    private MulId ed1_id;
+    private MulId ed2_id;
+
+    private MulId ing1_id;
+    private MulId ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetRefEdMulIdEntity.class);
+        cfg.addAnnotatedClass(SetRefIngMulIdEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        ed1_id = new MulId(0, 1);
+        ed2_id = new MulId(2, 3);
+
+        ing2_id = new MulId(4, 5);
+        ing1_id = new MulId(6, 7);
+
+        EntityManager em = getEntityManager();
+
+        SetRefEdMulIdEntity ed1 = new SetRefEdMulIdEntity(ed1_id.getId1(), ed1_id.getId2(), "data_ed_1");
+        SetRefEdMulIdEntity ed2 = new SetRefEdMulIdEntity(ed2_id.getId1(), ed2_id.getId2(), "data_ed_2");
+
+        SetRefIngMulIdEntity ing1 = new SetRefIngMulIdEntity(ing1_id.getId1(), ing1_id.getId2(), "data_ing_1", ed1);
+        SetRefIngMulIdEntity ing2 = new SetRefIngMulIdEntity(ing2_id.getId1(), ing2_id.getId2(), "data_ing_2", ed1);
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetRefIngMulIdEntity.class, ing1_id);
+        ed2 = em.find(SetRefEdMulIdEntity.class, ed2_id);
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing2 = em.find(SetRefIngMulIdEntity.class, ing2_id);
+        ed2 = em.find(SetRefEdMulIdEntity.class, ed2_id);
+
+        ing2.setReference(ed2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(SetRefEdMulIdEntity.class, ed1_id));
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(SetRefEdMulIdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SetRefIngMulIdEntity.class, ing1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(SetRefIngMulIdEntity.class, ing2_id));
+    }
+
+    private <T> Set<T> makeSet(T... objects) {
+        Set<T> ret = new HashSet<T>();
+        //noinspection ManualArrayToCollectionCopy
+        for (T obj : objects) { ret.add(obj); }
+        return ret;
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        SetRefIngMulIdEntity ing1 = getEntityManager().find(SetRefIngMulIdEntity.class, ing1_id);
+        SetRefIngMulIdEntity ing2 = getEntityManager().find(SetRefIngMulIdEntity.class, ing2_id);
+
+        SetRefEdMulIdEntity rev1 = getVersionsReader().find(SetRefEdMulIdEntity.class, ed1_id, 1);
+        SetRefEdMulIdEntity rev2 = getVersionsReader().find(SetRefEdMulIdEntity.class, ed1_id, 2);
+        SetRefEdMulIdEntity rev3 = getVersionsReader().find(SetRefEdMulIdEntity.class, ed1_id, 3);
+
+        assert rev1.getReffering().equals(makeSet(ing1, ing2));
+        assert rev2.getReffering().equals(makeSet(ing2));
+        assert rev3.getReffering().equals(Collections.EMPTY_SET);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        SetRefIngMulIdEntity ing1 = getEntityManager().find(SetRefIngMulIdEntity.class, ing1_id);
+        SetRefIngMulIdEntity ing2 = getEntityManager().find(SetRefIngMulIdEntity.class, ing2_id);
+
+        SetRefEdMulIdEntity rev1 = getVersionsReader().find(SetRefEdMulIdEntity.class, ed2_id, 1);
+        SetRefEdMulIdEntity rev2 = getVersionsReader().find(SetRefEdMulIdEntity.class, ed2_id, 2);
+        SetRefEdMulIdEntity rev3 = getVersionsReader().find(SetRefEdMulIdEntity.class, ed2_id, 3);
+
+        assert rev1.getReffering().equals(Collections.EMPTY_SET);
+        assert rev2.getReffering().equals(makeSet(ing1));
+        assert rev3.getReffering().equals(makeSet(ing1, ing2));
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        SetRefEdMulIdEntity ed1 = getEntityManager().find(SetRefEdMulIdEntity.class, ed1_id);
+        SetRefEdMulIdEntity ed2 = getEntityManager().find(SetRefEdMulIdEntity.class, ed2_id);
+
+        SetRefIngMulIdEntity rev1 = getVersionsReader().find(SetRefIngMulIdEntity.class, ing1_id, 1);
+        SetRefIngMulIdEntity rev2 = getVersionsReader().find(SetRefIngMulIdEntity.class, ing1_id, 2);
+        SetRefIngMulIdEntity rev3 = getVersionsReader().find(SetRefIngMulIdEntity.class, ing1_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed2);
+        assert rev3.getReference().equals(ed2);
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        SetRefEdMulIdEntity ed1 = getEntityManager().find(SetRefEdMulIdEntity.class, ed1_id);
+        SetRefEdMulIdEntity ed2 = getEntityManager().find(SetRefEdMulIdEntity.class, ed2_id);
+
+        SetRefIngMulIdEntity rev1 = getVersionsReader().find(SetRefIngMulIdEntity.class, ing2_id, 1);
+        SetRefIngMulIdEntity rev2 = getVersionsReader().find(SetRefIngMulIdEntity.class, ing2_id, 2);
+        SetRefIngMulIdEntity rev3 = getVersionsReader().find(SetRefIngMulIdEntity.class, ing2_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed1);
+        assert rev3.getReference().equals(ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithNullsDelete.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithNullsDelete.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BasicSetWithNullsDelete.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,227 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.SetRefEdEntity;
+import org.jboss.envers.test.entities.onetomany.SetRefIngEntity;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicSetWithNullsDelete extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+    private Integer ing3_id;
+    private Integer ing4_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetRefEdEntity.class);
+        cfg.addAnnotatedClass(SetRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        SetRefEdEntity ed1 = new SetRefEdEntity(1, "data_ed_1");
+        SetRefEdEntity ed2 = new SetRefEdEntity(2, "data_ed_2");
+
+        SetRefIngEntity ing1 = new SetRefIngEntity(3, "data_ing_1", ed1);
+        SetRefIngEntity ing2 = new SetRefIngEntity(4, "data_ing_2", ed1);
+        SetRefIngEntity ing3 = new SetRefIngEntity(5, "data_ing_3", ed1);
+        SetRefIngEntity ing4 = new SetRefIngEntity(6, "data_ing_4", ed1);
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+        em.persist(ing2);
+        em.persist(ing3);
+        em.persist(ing4);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(SetRefIngEntity.class, ing1.getId());
+
+        ing1.setReference(null);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ing2 = em.find(SetRefIngEntity.class, ing2.getId());
+        em.remove(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        ing3 = em.find(SetRefIngEntity.class, ing3.getId());
+        ed2 = em.find(SetRefEdEntity.class, ed2.getId());
+        ing3.setReference(ed2);
+
+        em.getTransaction().commit();
+        // Revision 5
+        em.getTransaction().begin();
+
+        ing4 = em.find(SetRefIngEntity.class, ing4.getId());
+        ed1 = em.find(SetRefEdEntity.class, ed1.getId());
+        em.remove(ed1);
+        ing4.setReference(null);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+        ing3_id = ing3.getId();
+        ing4_id = ing4.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4, 5).equals(getVersionsReader().getRevisions(SetRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 4).equals(getVersionsReader().getRevisions(SetRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SetRefIngEntity.class, ing1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(SetRefIngEntity.class, ing2_id));
+        assert Arrays.asList(1, 4).equals(getVersionsReader().getRevisions(SetRefIngEntity.class, ing3_id));
+        assert Arrays.asList(1, 5).equals(getVersionsReader().getRevisions(SetRefIngEntity.class, ing4_id));
+    }
+
+    private <T> Set<T> makeSet(T... objects) {
+        Set<T> ret = new HashSet<T>();
+        //noinspection ManualArrayToCollectionCopy
+        for (T obj : objects) { ret.add(obj); }
+        return ret;
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        SetRefIngEntity ing1 = getEntityManager().find(SetRefIngEntity.class, ing1_id);
+        SetRefIngEntity ing2 = new SetRefIngEntity(4, "data_ing_2", new SetRefEdEntity(1, "data_ed_1"));
+        SetRefIngEntity ing3 = getEntityManager().find(SetRefIngEntity.class, ing3_id);
+        SetRefIngEntity ing4 = getEntityManager().find(SetRefIngEntity.class, ing4_id);
+
+        SetRefEdEntity rev1 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 1);
+        SetRefEdEntity rev2 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 2);
+        SetRefEdEntity rev3 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 3);
+        SetRefEdEntity rev4 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 4);
+        SetRefEdEntity rev5 = getVersionsReader().find(SetRefEdEntity.class, ed1_id, 5);
+
+        assert rev1.getReffering().equals(makeSet(ing1, ing2, ing3, ing4));
+        assert rev2.getReffering().equals(makeSet(ing2, ing3, ing4));
+        assert rev3.getReffering().equals(makeSet(ing3, ing4));
+        assert rev4.getReffering().equals(makeSet(ing4));
+        assert rev5 == null;
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        SetRefIngEntity ing3 = getEntityManager().find(SetRefIngEntity.class, ing3_id);
+
+        SetRefEdEntity rev1 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 1);
+        SetRefEdEntity rev2 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 2);
+        SetRefEdEntity rev3 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 3);
+        SetRefEdEntity rev4 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 4);
+        SetRefEdEntity rev5 = getVersionsReader().find(SetRefEdEntity.class, ed2_id, 5);
+
+        assert rev1.getReffering().equals(Collections.EMPTY_SET);
+        assert rev2.getReffering().equals(Collections.EMPTY_SET);
+        assert rev3.getReffering().equals(Collections.EMPTY_SET);
+        assert rev4.getReffering().equals(makeSet(ing3));
+        assert rev5.getReffering().equals(makeSet(ing3));
+    }
+
+    @Test
+    public void testHistoryOfEdIng1() {
+        SetRefEdEntity ed1 = new SetRefEdEntity(1, "data_ed_1");
+
+        SetRefIngEntity rev1 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 1);
+        SetRefIngEntity rev2 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 2);
+        SetRefIngEntity rev3 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 3);
+        SetRefIngEntity rev4 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 4);
+        SetRefIngEntity rev5 = getVersionsReader().find(SetRefIngEntity.class, ing1_id, 5);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference() == null;
+        assert rev3.getReference() == null;
+        assert rev4.getReference() == null;
+        assert rev5.getReference() == null;
+    }
+
+    @Test
+    public void testHistoryOfEdIng2() {
+        SetRefEdEntity ed1 = new SetRefEdEntity(1, "data_ed_1");
+
+        SetRefIngEntity rev1 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 1);
+        SetRefIngEntity rev2 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 2);
+        SetRefIngEntity rev3 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 3);
+        SetRefIngEntity rev4 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 4);
+        SetRefIngEntity rev5 = getVersionsReader().find(SetRefIngEntity.class, ing2_id, 5);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed1);
+        assert rev3 == null;
+        assert rev4 == null;
+        assert rev5 == null;
+    }
+
+    @Test
+    public void testHistoryOfEdIng3() {
+        SetRefEdEntity ed1 = new SetRefEdEntity(1, "data_ed_1");
+        SetRefEdEntity ed2 = new SetRefEdEntity(2, "data_ed_2");
+        
+        SetRefIngEntity rev1 = getVersionsReader().find(SetRefIngEntity.class, ing3_id, 1);
+        SetRefIngEntity rev2 = getVersionsReader().find(SetRefIngEntity.class, ing3_id, 2);
+        SetRefIngEntity rev3 = getVersionsReader().find(SetRefIngEntity.class, ing3_id, 3);
+        SetRefIngEntity rev4 = getVersionsReader().find(SetRefIngEntity.class, ing3_id, 4);
+        SetRefIngEntity rev5 = getVersionsReader().find(SetRefIngEntity.class, ing3_id, 5);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed1);
+        assert rev3.getReference().equals(ed1);
+        assert rev4.getReference().equals(ed2);
+        assert rev5.getReference().equals(ed2);
+    }
+
+    @Test
+    public void testHistoryOfEdIng4() {
+        SetRefEdEntity ed1 = new SetRefEdEntity(1, "data_ed_1");
+
+        SetRefIngEntity rev1 = getVersionsReader().find(SetRefIngEntity.class, ing4_id, 1);
+        SetRefIngEntity rev2 = getVersionsReader().find(SetRefIngEntity.class, ing4_id, 2);
+        SetRefIngEntity rev3 = getVersionsReader().find(SetRefIngEntity.class, ing4_id, 3);
+        SetRefIngEntity rev4 = getVersionsReader().find(SetRefIngEntity.class, ing4_id, 4);
+        SetRefIngEntity rev5 = getVersionsReader().find(SetRefIngEntity.class, ing4_id, 5);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed1);
+        assert rev3.getReference().equals(ed1);
+        assert rev4.getReference().equals(ed1);
+        assert rev5.getReference() == null;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BidirectionalMapKey.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BidirectionalMapKey.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/BidirectionalMapKey.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,86 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BidirectionalMapKey extends AbstractEntityTest {
+    private Integer ed_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(RefIngMapKeyEntity.class);
+        cfg.addAnnotatedClass(RefEdMapKeyEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        // Revision 1 (intialy 1 relation: ing1 -> ed)
+        em.getTransaction().begin();
+
+        RefEdMapKeyEntity ed = new RefEdMapKeyEntity();
+
+        em.persist(ed);
+
+        RefIngMapKeyEntity ing1 = new RefIngMapKeyEntity();
+        ing1.setData("a");
+        ing1.setReference(ed);
+
+        RefIngMapKeyEntity ing2 = new RefIngMapKeyEntity();
+        ing2.setData("b");
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2 (adding second relation: ing2 -> ed)
+        em.getTransaction().begin();
+
+        ed = em.find(RefEdMapKeyEntity.class, ed.getId());
+        ing2 = em.find(RefIngMapKeyEntity.class, ing2.getId());
+
+        ing2.setReference(ed);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed_id = ed.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(RefEdMapKeyEntity.class, ed_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(RefIngMapKeyEntity.class, ing1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(RefIngMapKeyEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEd() {
+        RefIngMapKeyEntity ing1 = getEntityManager().find(RefIngMapKeyEntity.class, ing1_id);
+        RefIngMapKeyEntity ing2 = getEntityManager().find(RefIngMapKeyEntity.class, ing2_id);
+
+        RefEdMapKeyEntity rev1 = getVersionsReader().find(RefEdMapKeyEntity.class, ed_id, 1);
+        RefEdMapKeyEntity rev2 = getVersionsReader().find(RefEdMapKeyEntity.class, ed_id, 2);
+
+        assert rev1.getIdmap().equals(TestTools.makeMap("a", ing1));
+        assert rev2.getIdmap().equals(TestTools.makeMap("a", ing1, "b", ing2));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/RefEdMapKeyEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/RefEdMapKeyEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/RefEdMapKeyEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,61 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class RefEdMapKeyEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @OneToMany(mappedBy="reference")
+    @MapKey(name = "data")
+    private Map<String, RefIngMapKeyEntity> idmap;
+
+    public RefEdMapKeyEntity() {
+        idmap = new HashMap<String, RefIngMapKeyEntity>();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Map<String, RefIngMapKeyEntity> getIdmap() {
+        return idmap;
+    }
+
+    public void setIdmap(Map<String, RefIngMapKeyEntity> idmap) {
+        this.idmap = idmap;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof RefEdMapKeyEntity)) return false;
+
+        RefEdMapKeyEntity that = (RefEdMapKeyEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "RedMKE(id = " + id + ", idmap = " + idmap + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/RefIngMapKeyEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/RefIngMapKeyEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/RefIngMapKeyEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,65 @@
+package org.jboss.envers.test.integration.onetomany;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class RefIngMapKeyEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    @ManyToOne
+    private RefEdMapKeyEntity reference;
+
+    @Versioned
+    private String data;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public RefEdMapKeyEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(RefEdMapKeyEntity reference) {
+        this.reference = reference;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof RefIngMapKeyEntity)) return false;
+
+        RefIngMapKeyEntity that = (RefIngMapKeyEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (id != null ? id.hashCode() : 0);
+    }
+
+    public String toString() {
+        return "RingMKE(id = " + id + ", data = " + data + ", reference = " + reference + ")";
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedList.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedList.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedList.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,115 @@
+package org.jboss.envers.test.integration.onetomany.detached;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.detached.ListRefCollEntity;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicDetachedList extends AbstractEntityTest {
+    private Integer str1_id;
+    private Integer str2_id;
+
+    private Integer coll1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(ListRefCollEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity str1 = new StrTestEntity("str1");
+        StrTestEntity str2 = new StrTestEntity("str2");
+
+        ListRefCollEntity coll1 = new ListRefCollEntity(3, "coll1");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(str1);
+        em.persist(str2);
+
+        coll1.setCollection(new ArrayList<StrTestEntity>());
+        coll1.getCollection().add(str1);
+        em.persist(coll1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        str2 = em.find(StrTestEntity.class, str2.getId());
+        coll1 = em.find(ListRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().add(str2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        coll1 = em.find(ListRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().remove(str1);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        coll1 = em.find(ListRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().clear();
+
+        em.getTransaction().commit();
+
+        //
+
+        str1_id = str1.getId();
+        str2_id = str2.getId();
+
+        coll1_id = coll1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(ListRefCollEntity.class, coll1_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str2_id));
+    }
+
+    @Test
+    public void testHistoryOfColl1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+        StrTestEntity str2 = getEntityManager().find(StrTestEntity.class, str2_id);
+
+        ListRefCollEntity rev1 = getVersionsReader().find(ListRefCollEntity.class, coll1_id, 1);
+        ListRefCollEntity rev2 = getVersionsReader().find(ListRefCollEntity.class, coll1_id, 2);
+        ListRefCollEntity rev3 = getVersionsReader().find(ListRefCollEntity.class, coll1_id, 3);
+        ListRefCollEntity rev4 = getVersionsReader().find(ListRefCollEntity.class, coll1_id, 4);
+
+        assert TestTools.checkList(rev1.getCollection(), str1);
+        assert TestTools.checkList(rev2.getCollection(), str1, str2);
+        assert TestTools.checkList(rev3.getCollection(), str2);
+        assert TestTools.checkList(rev4.getCollection());
+        
+        assert "coll1".equals(rev1.getData());
+        assert "coll1".equals(rev2.getData());
+        assert "coll1".equals(rev3.getData());
+        assert "coll1".equals(rev4.getData());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,115 @@
+package org.jboss.envers.test.integration.onetomany.detached;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.detached.SetRefCollEntity;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicDetachedSet extends AbstractEntityTest {
+    private Integer str1_id;
+    private Integer str2_id;
+
+    private Integer coll1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(SetRefCollEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity str1 = new StrTestEntity("str1");
+        StrTestEntity str2 = new StrTestEntity("str2");
+
+        SetRefCollEntity coll1 = new SetRefCollEntity(3, "coll1");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(str1);
+        em.persist(str2);
+
+        coll1.setCollection(new HashSet<StrTestEntity>());
+        coll1.getCollection().add(str1);
+        em.persist(coll1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        str2 = em.find(StrTestEntity.class, str2.getId());
+        coll1 = em.find(SetRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().add(str2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        coll1 = em.find(SetRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().remove(str1);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        coll1 = em.find(SetRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().clear();
+
+        em.getTransaction().commit();
+
+        //
+
+        str1_id = str1.getId();
+        str2_id = str2.getId();
+
+        coll1_id = coll1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(SetRefCollEntity.class, coll1_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str2_id));
+    }
+
+    @Test
+    public void testHistoryOfColl1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+        StrTestEntity str2 = getEntityManager().find(StrTestEntity.class, str2_id);
+
+        SetRefCollEntity rev1 = getVersionsReader().find(SetRefCollEntity.class, coll1_id, 1);
+        SetRefCollEntity rev2 = getVersionsReader().find(SetRefCollEntity.class, coll1_id, 2);
+        SetRefCollEntity rev3 = getVersionsReader().find(SetRefCollEntity.class, coll1_id, 3);
+        SetRefCollEntity rev4 = getVersionsReader().find(SetRefCollEntity.class, coll1_id, 4);
+
+        assert rev1.getCollection().equals(TestTools.makeSet(str1));
+        assert rev2.getCollection().equals(TestTools.makeSet(str1, str2));
+        assert rev3.getCollection().equals(TestTools.makeSet(str2));
+        assert rev4.getCollection().equals(TestTools.makeSet());
+
+        assert "coll1".equals(rev1.getData());
+        assert "coll1".equals(rev2.getData());
+        assert "coll1".equals(rev3.getData());
+        assert "coll1".equals(rev4.getData());
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSetWithEmbId.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSetWithEmbId.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSetWithEmbId.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,114 @@
+package org.jboss.envers.test.integration.onetomany.detached;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.detached.ids.SetRefCollEntityEmbId;
+import org.jboss.envers.test.entities.ids.EmbIdTestEntity;
+import org.jboss.envers.test.entities.ids.EmbId;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicDetachedSetWithEmbId extends AbstractEntityTest {
+    private EmbId str1_id;
+    private EmbId str2_id;
+
+    private EmbId coll1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(EmbIdTestEntity.class);
+        cfg.addAnnotatedClass(SetRefCollEntityEmbId.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        str1_id = new EmbId(1, 2);
+        str2_id = new EmbId(3, 4);
+
+        coll1_id = new EmbId(5, 6);
+
+        EmbIdTestEntity str1 = new EmbIdTestEntity(str1_id, "str1");
+        EmbIdTestEntity str2 = new EmbIdTestEntity(str2_id, "str2");
+
+        SetRefCollEntityEmbId coll1 = new SetRefCollEntityEmbId(coll1_id, "coll1");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(str1);
+        em.persist(str2);
+
+        coll1.setCollection(new HashSet<EmbIdTestEntity>());
+        coll1.getCollection().add(str1);
+        em.persist(coll1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        str2 = em.find(EmbIdTestEntity.class, str2.getId());
+        coll1 = em.find(SetRefCollEntityEmbId.class, coll1.getId());
+
+        coll1.getCollection().add(str2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        str1 = em.find(EmbIdTestEntity.class, str1.getId());
+        coll1 = em.find(SetRefCollEntityEmbId.class, coll1.getId());
+
+        coll1.getCollection().remove(str1);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        coll1 = em.find(SetRefCollEntityEmbId.class, coll1.getId());
+
+        coll1.getCollection().clear();
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(SetRefCollEntityEmbId.class, coll1_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(EmbIdTestEntity.class, str1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(EmbIdTestEntity.class, str2_id));
+    }
+
+    @Test
+    public void testHistoryOfColl1() {
+        EmbIdTestEntity str1 = getEntityManager().find(EmbIdTestEntity.class, str1_id);
+        EmbIdTestEntity str2 = getEntityManager().find(EmbIdTestEntity.class, str2_id);
+
+        SetRefCollEntityEmbId rev1 = getVersionsReader().find(SetRefCollEntityEmbId.class, coll1_id, 1);
+        SetRefCollEntityEmbId rev2 = getVersionsReader().find(SetRefCollEntityEmbId.class, coll1_id, 2);
+        SetRefCollEntityEmbId rev3 = getVersionsReader().find(SetRefCollEntityEmbId.class, coll1_id, 3);
+        SetRefCollEntityEmbId rev4 = getVersionsReader().find(SetRefCollEntityEmbId.class, coll1_id, 4);
+
+        assert rev1.getCollection().equals(TestTools.makeSet(str1));
+        assert rev2.getCollection().equals(TestTools.makeSet(str1, str2));
+        assert rev3.getCollection().equals(TestTools.makeSet(str2));
+        assert rev4.getCollection().equals(TestTools.makeSet());
+
+        assert "coll1".equals(rev1.getData());
+        assert "coll1".equals(rev2.getData());
+        assert "coll1".equals(rev3.getData());
+        assert "coll1".equals(rev4.getData());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSetWithMulId.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSetWithMulId.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicDetachedSetWithMulId.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,114 @@
+package org.jboss.envers.test.integration.onetomany.detached;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.detached.ids.SetRefCollEntityMulId;
+import org.jboss.envers.test.entities.ids.MulIdTestEntity;
+import org.jboss.envers.test.entities.ids.MulId;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicDetachedSetWithMulId extends AbstractEntityTest {
+    private MulId str1_id;
+    private MulId str2_id;
+
+    private MulId coll1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(MulIdTestEntity.class);
+        cfg.addAnnotatedClass(SetRefCollEntityMulId.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        str1_id = new MulId(1, 2);
+        str2_id = new MulId(3, 4);
+
+        coll1_id = new MulId(5, 6);
+
+        MulIdTestEntity str1 = new MulIdTestEntity(str1_id.getId1(), str1_id.getId2(), "str1");
+        MulIdTestEntity str2 = new MulIdTestEntity(str2_id.getId1(), str2_id.getId2(), "str2");
+
+        SetRefCollEntityMulId coll1 = new SetRefCollEntityMulId(coll1_id.getId1(), coll1_id.getId2(), "coll1");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(str1);
+        em.persist(str2);
+
+        coll1.setCollection(new HashSet<MulIdTestEntity>());
+        coll1.getCollection().add(str1);
+        em.persist(coll1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        str2 = em.find(MulIdTestEntity.class, str2_id);
+        coll1 = em.find(SetRefCollEntityMulId.class, coll1_id);
+
+        coll1.getCollection().add(str2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        str1 = em.find(MulIdTestEntity.class, str1_id);
+        coll1 = em.find(SetRefCollEntityMulId.class, coll1_id);
+
+        coll1.getCollection().remove(str1);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        coll1 = em.find(SetRefCollEntityMulId.class, coll1_id);
+
+        coll1.getCollection().clear();
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(SetRefCollEntityMulId.class, coll1_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(MulIdTestEntity.class, str1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(MulIdTestEntity.class, str2_id));
+    }
+
+    @Test
+    public void testHistoryOfColl1() {
+        MulIdTestEntity str1 = getEntityManager().find(MulIdTestEntity.class, str1_id);
+        MulIdTestEntity str2 = getEntityManager().find(MulIdTestEntity.class, str2_id);
+
+        SetRefCollEntityMulId rev1 = getVersionsReader().find(SetRefCollEntityMulId.class, coll1_id, 1);
+        SetRefCollEntityMulId rev2 = getVersionsReader().find(SetRefCollEntityMulId.class, coll1_id, 2);
+        SetRefCollEntityMulId rev3 = getVersionsReader().find(SetRefCollEntityMulId.class, coll1_id, 3);
+        SetRefCollEntityMulId rev4 = getVersionsReader().find(SetRefCollEntityMulId.class, coll1_id, 4);
+
+        assert rev1.getCollection().equals(TestTools.makeSet(str1));
+        assert rev2.getCollection().equals(TestTools.makeSet(str1, str2));
+        assert rev3.getCollection().equals(TestTools.makeSet(str2));
+        assert rev4.getCollection().equals(TestTools.makeSet());
+
+        assert "coll1".equals(rev1.getData());
+        assert "coll1".equals(rev2.getData());
+        assert "coll1".equals(rev3.getData());
+        assert "coll1".equals(rev4.getData());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicJoinColumnSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicJoinColumnSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/BasicJoinColumnSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,115 @@
+package org.jboss.envers.test.integration.onetomany.detached;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.entities.onetomany.detached.SetJoinColumnRefCollEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicJoinColumnSet extends AbstractEntityTest {
+    private Integer str1_id;
+    private Integer str2_id;
+
+    private Integer coll1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(SetJoinColumnRefCollEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity str1 = new StrTestEntity("str1");
+        StrTestEntity str2 = new StrTestEntity("str2");
+
+        SetJoinColumnRefCollEntity coll1 = new SetJoinColumnRefCollEntity(3, "coll1");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(str1);
+        em.persist(str2);
+
+        coll1.setCollection(new HashSet<StrTestEntity>());
+        coll1.getCollection().add(str1);
+        em.persist(coll1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        str2 = em.find(StrTestEntity.class, str2.getId());
+        coll1 = em.find(SetJoinColumnRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().add(str2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        coll1 = em.find(SetJoinColumnRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().remove(str1);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        coll1 = em.find(SetJoinColumnRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().clear();
+
+        em.getTransaction().commit();
+
+        //
+
+        str1_id = str1.getId();
+        str2_id = str2.getId();
+
+        coll1_id = coll1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(SetJoinColumnRefCollEntity.class, coll1_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str2_id));
+    }
+
+    @Test
+    public void testHistoryOfColl1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+        StrTestEntity str2 = getEntityManager().find(StrTestEntity.class, str2_id);
+
+        SetJoinColumnRefCollEntity rev1 = getVersionsReader().find(SetJoinColumnRefCollEntity.class, coll1_id, 1);
+        SetJoinColumnRefCollEntity rev2 = getVersionsReader().find(SetJoinColumnRefCollEntity.class, coll1_id, 2);
+        SetJoinColumnRefCollEntity rev3 = getVersionsReader().find(SetJoinColumnRefCollEntity.class, coll1_id, 3);
+        SetJoinColumnRefCollEntity rev4 = getVersionsReader().find(SetJoinColumnRefCollEntity.class, coll1_id, 4);
+
+        assert rev1.getCollection().equals(TestTools.makeSet(str1));
+        assert rev2.getCollection().equals(TestTools.makeSet(str1, str2));
+        assert rev3.getCollection().equals(TestTools.makeSet(str2));
+        assert rev4.getCollection().equals(TestTools.makeSet());
+
+        assert "coll1".equals(rev1.getData());
+        assert "coll1".equals(rev2.getData());
+        assert "coll1".equals(rev3.getData());
+        assert "coll1".equals(rev4.getData());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/DataChangesDetachedSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/DataChangesDetachedSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/DataChangesDetachedSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,84 @@
+package org.jboss.envers.test.integration.onetomany.detached;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.detached.SetRefCollEntity;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DataChangesDetachedSet extends AbstractEntityTest {
+    private Integer str1_id;
+
+    private Integer coll1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(SetRefCollEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity str1 = new StrTestEntity("str1");
+
+        SetRefCollEntity coll1 = new SetRefCollEntity(3, "coll1");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(str1);
+
+        coll1.setCollection(new HashSet<StrTestEntity>());
+        em.persist(coll1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        coll1 = em.find(SetRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().add(str1);
+        coll1.setData("coll2");
+
+        em.getTransaction().commit();
+
+        //
+
+        str1_id = str1.getId();
+
+        coll1_id = coll1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SetRefCollEntity.class, coll1_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+    }
+
+    @Test
+    public void testHistoryOfColl1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+
+        SetRefCollEntity rev1 = getVersionsReader().find(SetRefCollEntity.class, coll1_id, 1);
+        SetRefCollEntity rev2 = getVersionsReader().find(SetRefCollEntity.class, coll1_id, 2);
+
+        assert rev1.getCollection().equals(TestTools.makeSet());
+        assert rev2.getCollection().equals(TestTools.makeSet(str1));
+
+        assert "coll1".equals(rev1.getData());
+        assert "coll2".equals(rev2.getData());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/DoubleDetachedSet.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/DoubleDetachedSet.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetomany/detached/DoubleDetachedSet.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,108 @@
+package org.jboss.envers.test.integration.onetomany.detached;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.detached.DoubleSetRefCollEntity;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DoubleDetachedSet extends AbstractEntityTest {
+    private Integer str1_id;
+    private Integer str2_id;
+
+    private Integer coll1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(DoubleSetRefCollEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+
+        StrTestEntity str1 = new StrTestEntity("str1");
+        StrTestEntity str2 = new StrTestEntity("str2");
+
+        DoubleSetRefCollEntity coll1 = new DoubleSetRefCollEntity(3, "coll1");
+
+        // Revision 1
+        em.getTransaction().begin();
+
+        em.persist(str1);
+        em.persist(str2);
+
+        coll1.setCollection(new HashSet<StrTestEntity>());
+        coll1.getCollection().add(str1);
+        em.persist(coll1);
+
+        coll1.setCollection2(new HashSet<StrTestEntity>());
+        coll1.getCollection2().add(str2);
+        em.persist(coll1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        str2 = em.find(StrTestEntity.class, str2.getId());
+        coll1 = em.find(DoubleSetRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().add(str2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        str1 = em.find(StrTestEntity.class, str1.getId());
+        coll1 = em.find(DoubleSetRefCollEntity.class, coll1.getId());
+
+        coll1.getCollection().remove(str1);
+        coll1.getCollection2().add(str1);
+
+        em.getTransaction().commit();
+
+        //
+
+        str1_id = str1.getId();
+        str2_id = str2.getId();
+
+        coll1_id = coll1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3).equals(getVersionsReader().getRevisions(DoubleSetRefCollEntity.class, coll1_id));
+
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(StrTestEntity.class, str2_id));
+    }
+
+    @Test
+    public void testHistoryOfColl1() {
+        StrTestEntity str1 = getEntityManager().find(StrTestEntity.class, str1_id);
+        StrTestEntity str2 = getEntityManager().find(StrTestEntity.class, str2_id);
+
+        DoubleSetRefCollEntity rev1 = getVersionsReader().find(DoubleSetRefCollEntity.class, coll1_id, 1);
+        DoubleSetRefCollEntity rev2 = getVersionsReader().find(DoubleSetRefCollEntity.class, coll1_id, 2);
+        DoubleSetRefCollEntity rev3 = getVersionsReader().find(DoubleSetRefCollEntity.class, coll1_id, 3);
+
+        assert rev1.getCollection().equals(TestTools.makeSet(str1));
+        assert rev2.getCollection().equals(TestTools.makeSet(str1, str2));
+        assert rev3.getCollection().equals(TestTools.makeSet(str2));
+
+        assert rev1.getCollection2().equals(TestTools.makeSet(str2));
+        assert rev2.getCollection2().equals(TestTools.makeSet(str2));
+        assert rev3.getCollection2().equals(TestTools.makeSet(str1, str2));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BiRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BiRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BiRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,80 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Id;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class BiRefEdEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToOne(mappedBy="reference")
+    private BiRefIngEntity referencing;
+
+    public BiRefEdEntity() {
+    }
+
+    public BiRefEdEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public BiRefEdEntity(Integer id, String data, BiRefIngEntity referencing) {
+        this.id = id;
+        this.data = data;
+        this.referencing = referencing;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public BiRefIngEntity getReferencing() {
+        return referencing;
+    }
+
+    public void setReferencing(BiRefIngEntity referencing) {
+        this.referencing = referencing;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BiRefEdEntity)) return false;
+
+        BiRefEdEntity that = (BiRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BiRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BiRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BiRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,80 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class BiRefIngEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToOne
+    private BiRefEdEntity reference;
+
+    public BiRefIngEntity() {
+    }
+
+    public BiRefIngEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public BiRefIngEntity(Integer id, String data, BiRefEdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public BiRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(BiRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BiRefIngEntity)) return false;
+
+        BiRefIngEntity that = (BiRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/Bidirectional.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/Bidirectional.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/Bidirectional.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,92 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Bidirectional extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BiRefEdEntity.class);
+        cfg.addAnnotatedClass(BiRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        BiRefEdEntity ed1 = new BiRefEdEntity(1, "data_ed_1");
+        BiRefEdEntity ed2 = new BiRefEdEntity(2, "data_ed_2");
+
+        BiRefIngEntity ing1 = new BiRefIngEntity(3, "data_ing_1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing1.setReference(ed1);
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(BiRefIngEntity.class, ing1.getId());
+        ed2 = em.find(BiRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiRefIngEntity.class, ing1_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        BiRefIngEntity ing1 = getEntityManager().find(BiRefIngEntity.class, ing1_id);
+
+        BiRefEdEntity rev1 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 1);
+        BiRefEdEntity rev2 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 2);
+
+        assert rev1.getReferencing().equals(ing1);
+        assert rev2.getReferencing() == null;
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        BiRefIngEntity ing1 = getEntityManager().find(BiRefIngEntity.class, ing1_id);
+
+        BiRefEdEntity rev1 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 1);
+        BiRefEdEntity rev2 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 2);
+        
+        assert rev1.getReferencing() == null;
+        assert rev2.getReferencing().equals(ing1);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/Bidirectional2.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/Bidirectional2.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/Bidirectional2.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,127 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Bidirectional2 extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BiRefEdEntity.class);
+        cfg.addAnnotatedClass(BiRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        BiRefEdEntity ed1 = new BiRefEdEntity(1, "data_ed_1");
+        BiRefEdEntity ed2 = new BiRefEdEntity(2, "data_ed_2");
+
+        BiRefIngEntity ing1 = new BiRefIngEntity(3, "data_ing_1");
+        BiRefIngEntity ing2 = new BiRefIngEntity(4, "data_ing_2");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ed1 = em.find(BiRefEdEntity.class, ed1.getId());
+
+        ing1.setReference(ed1);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ed1 = em.find(BiRefEdEntity.class, ed1.getId());
+        ing1 = em.find(BiRefIngEntity.class, ing1.getId());
+        ing2 = em.find(BiRefIngEntity.class, ing2.getId());
+
+        ing1.setReference(null);
+        ing2.setReference(ed1);
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        ed2 = em.find(BiRefEdEntity.class, ed2.getId());
+        ing1 = em.find(BiRefIngEntity.class, ing1.getId());
+        ing2 = em.find(BiRefIngEntity.class, ing2.getId());
+
+        ing1.setReference(ed2);
+        ing2.setReference(null);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2, 3, 4).equals(getVersionsReader().getRevisions(BiRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 4).equals(getVersionsReader().getRevisions(BiRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(2, 3, 4).equals(getVersionsReader().getRevisions(BiRefIngEntity.class, ing1_id));
+        assert Arrays.asList(2, 3, 4).equals(getVersionsReader().getRevisions(BiRefIngEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        BiRefIngEntity ing1 = getEntityManager().find(BiRefIngEntity.class, ing1_id);
+        BiRefIngEntity ing2 = getEntityManager().find(BiRefIngEntity.class, ing2_id);
+
+        BiRefEdEntity rev1 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 1);
+        BiRefEdEntity rev2 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 2);
+        BiRefEdEntity rev3 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 3);
+        BiRefEdEntity rev4 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 4);
+
+        assert rev1.getReferencing() == null;
+        assert rev2.getReferencing().equals(ing1);
+        assert rev3.getReferencing().equals(ing2);
+        assert rev4.getReferencing() == null;
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        BiRefIngEntity ing1 = getEntityManager().find(BiRefIngEntity.class, ing1_id);
+
+        BiRefEdEntity rev1 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 1);
+        BiRefEdEntity rev2 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 2);
+        BiRefEdEntity rev3 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 3);
+        BiRefEdEntity rev4 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 4);
+
+        assert rev1.getReferencing() == null;
+        assert rev2.getReferencing() == null;
+        assert rev3.getReferencing() == null;
+        assert rev4.getReferencing().equals(ing1);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BidirectionalNoNulls.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BidirectionalNoNulls.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/BidirectionalNoNulls.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,105 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BidirectionalNoNulls extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BiRefEdEntity.class);
+        cfg.addAnnotatedClass(BiRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        BiRefEdEntity ed1 = new BiRefEdEntity(1, "data_ed_1");
+        BiRefEdEntity ed2 = new BiRefEdEntity(2, "data_ed_2");
+
+        BiRefIngEntity ing1 = new BiRefIngEntity(3, "data_ing_1");
+        BiRefIngEntity ing2 = new BiRefIngEntity(4, "data_ing_2");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing1.setReference(ed1);
+        ing2.setReference(ed2);
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing1 = em.find(BiRefIngEntity.class, ing1.getId());
+        ing2 = em.find(BiRefIngEntity.class, ing2.getId());
+
+        ed1 = em.find(BiRefEdEntity.class, ed1.getId());
+        ed2 = em.find(BiRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+        ing2.setReference(ed1);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiRefIngEntity.class, ing1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiRefIngEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        BiRefIngEntity ing1 = getEntityManager().find(BiRefIngEntity.class, ing1_id);
+        BiRefIngEntity ing2 = getEntityManager().find(BiRefIngEntity.class, ing2_id);
+
+        BiRefEdEntity rev1 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 1);
+        BiRefEdEntity rev2 = getVersionsReader().find(BiRefEdEntity.class, ed1_id, 2);
+
+        assert rev1.getReferencing().equals(ing1);
+        assert rev2.getReferencing().equals(ing2);
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        BiRefIngEntity ing1 = getEntityManager().find(BiRefIngEntity.class, ing1_id);
+        BiRefIngEntity ing2 = getEntityManager().find(BiRefIngEntity.class, ing2_id);
+
+        BiRefEdEntity rev1 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 1);
+        BiRefEdEntity rev2 = getVersionsReader().find(BiRefEdEntity.class, ed2_id, 2);
+
+        assert rev1.getReferencing().equals(ing2);
+        assert rev2.getReferencing().equals(ing1);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiEmbIdRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiEmbIdRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiEmbIdRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.EmbId;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.EmbeddedId;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class BiEmbIdRefEdEntity {
+    @EmbeddedId
+    private EmbId id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToOne(mappedBy="reference")
+    private BiEmbIdRefIngEntity referencing;
+
+    public BiEmbIdRefEdEntity() {
+    }
+
+    public BiEmbIdRefEdEntity(EmbId id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public BiEmbIdRefEdEntity(EmbId id, String data, BiEmbIdRefIngEntity referencing) {
+        this.id = id;
+        this.data = data;
+        this.referencing = referencing;
+    }
+
+    public EmbId getId() {
+        return id;
+    }
+
+    public void setId(EmbId id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public BiEmbIdRefIngEntity getReferencing() {
+        return referencing;
+    }
+
+    public void setReferencing(BiEmbIdRefIngEntity referencing) {
+        this.referencing = referencing;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BiEmbIdRefEdEntity)) return false;
+
+        BiEmbIdRefEdEntity that = (BiEmbIdRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiEmbIdRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiEmbIdRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiEmbIdRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.EmbId;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.EmbeddedId;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class BiEmbIdRefIngEntity {
+    @EmbeddedId
+    private EmbId id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToOne
+    private BiEmbIdRefEdEntity reference;
+
+    public BiEmbIdRefIngEntity() {
+    }
+
+    public BiEmbIdRefIngEntity(EmbId id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public BiEmbIdRefIngEntity(EmbId id, String data, BiEmbIdRefEdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public EmbId getId() {
+        return id;
+    }
+
+    public void setId(EmbId id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public BiEmbIdRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(BiEmbIdRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BiEmbIdRefIngEntity)) return false;
+
+        BiEmbIdRefIngEntity that = (BiEmbIdRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiMulIdRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiMulIdRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiMulIdRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,88 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.MulId;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at IdClass(MulId.class)
+public class BiMulIdRefEdEntity {
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToOne(mappedBy="reference")
+    private BiMulIdRefIngEntity referencing;
+
+    public BiMulIdRefEdEntity() {
+    }
+
+    public BiMulIdRefEdEntity(Integer id1, Integer id2, String data) {
+        this.id1 = id1;
+        this.id2 = id2;
+        this.data = data;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public BiMulIdRefIngEntity getReferencing() {
+        return referencing;
+    }
+
+    public void setReferencing(BiMulIdRefIngEntity referencing) {
+        this.referencing = referencing;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BiMulIdRefEdEntity)) return false;
+
+        BiMulIdRefEdEntity that = (BiMulIdRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiMulIdRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiMulIdRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/BiMulIdRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,88 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional.ids;
+
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.MulId;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at IdClass(MulId.class)
+public class BiMulIdRefIngEntity {
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToOne
+    private BiMulIdRefEdEntity reference;
+
+    public BiMulIdRefIngEntity() {
+    }
+
+    public BiMulIdRefIngEntity(Integer id1, Integer id2, String data) {
+        this.id1 = id1;
+        this.id2 = id2;
+        this.data = data;
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public BiMulIdRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(BiMulIdRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BiMulIdRefIngEntity)) return false;
+
+        BiMulIdRefIngEntity that = (BiMulIdRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/EmbIdBidirectional.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/EmbIdBidirectional.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/EmbIdBidirectional.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,103 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional.ids;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.ids.EmbId;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EmbIdBidirectional extends AbstractEntityTest {
+    private EmbId ed1_id;
+    private EmbId ed2_id;
+
+    private EmbId ing1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BiEmbIdRefEdEntity.class);
+        cfg.addAnnotatedClass(BiEmbIdRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        ed1_id = new EmbId(1, 2);
+        ed2_id = new EmbId(3, 4);
+
+        ing1_id = new EmbId(5, 6);
+
+        BiEmbIdRefEdEntity ed1 = new BiEmbIdRefEdEntity(ed1_id, "data_ed_1");
+        BiEmbIdRefEdEntity ed2 = new BiEmbIdRefEdEntity(ed2_id, "data_ed_2");
+
+        BiEmbIdRefIngEntity ing1 = new BiEmbIdRefIngEntity(ing1_id, "data_ing_1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing1.setReference(ed1);
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(BiEmbIdRefIngEntity.class, ing1.getId());
+        ed2 = em.find(BiEmbIdRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiEmbIdRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiEmbIdRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiEmbIdRefIngEntity.class, ing1_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        BiEmbIdRefIngEntity ing1 = getEntityManager().find(BiEmbIdRefIngEntity.class, ing1_id);
+
+        BiEmbIdRefEdEntity rev1 = getVersionsReader().find(BiEmbIdRefEdEntity.class, ed1_id, 1);
+        BiEmbIdRefEdEntity rev2 = getVersionsReader().find(BiEmbIdRefEdEntity.class, ed1_id, 2);
+
+        assert rev1.getReferencing().equals(ing1);
+        assert rev2.getReferencing() == null;
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        BiEmbIdRefIngEntity ing1 = getEntityManager().find(BiEmbIdRefIngEntity.class, ing1_id);
+
+        BiEmbIdRefEdEntity rev1 = getVersionsReader().find(BiEmbIdRefEdEntity.class, ed2_id, 1);
+        BiEmbIdRefEdEntity rev2 = getVersionsReader().find(BiEmbIdRefEdEntity.class, ed2_id, 2);
+
+        assert rev1.getReferencing() == null;
+        assert rev2.getReferencing().equals(ing1);
+    }
+
+    @Test
+    public void testHistoryOfIngId1() {
+        BiEmbIdRefEdEntity ed1 = getEntityManager().find(BiEmbIdRefEdEntity.class, ed1_id);
+        BiEmbIdRefEdEntity ed2 = getEntityManager().find(BiEmbIdRefEdEntity.class, ed2_id);
+
+        BiEmbIdRefIngEntity rev1 = getVersionsReader().find(BiEmbIdRefIngEntity.class, ing1_id, 1);
+        BiEmbIdRefIngEntity rev2 = getVersionsReader().find(BiEmbIdRefIngEntity.class, ing1_id, 2);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/MulIdBidirectional.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/MulIdBidirectional.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/bidirectional/ids/MulIdBidirectional.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,103 @@
+package org.jboss.envers.test.integration.onetoone.bidirectional.ids;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.ids.MulId;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MulIdBidirectional extends AbstractEntityTest {
+    private MulId ed1_id;
+    private MulId ed2_id;
+
+    private MulId ing1_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(BiMulIdRefEdEntity.class);
+        cfg.addAnnotatedClass(BiMulIdRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        ed1_id = new MulId(1, 2);
+        ed2_id = new MulId(3, 4);
+
+        ing1_id = new MulId(5, 6);
+
+        BiMulIdRefEdEntity ed1 = new BiMulIdRefEdEntity(ed1_id.getId1(), ed1_id.getId2(), "data_ed_1");
+        BiMulIdRefEdEntity ed2 = new BiMulIdRefEdEntity(ed2_id.getId1(), ed2_id.getId2(), "data_ed_2");
+
+        BiMulIdRefIngEntity ing1 = new BiMulIdRefIngEntity(ing1_id.getId1(), ing1_id.getId2(), "data_ing_1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing1.setReference(ed1);
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ing1 = em.find(BiMulIdRefIngEntity.class, ing1_id);
+        ed2 = em.find(BiMulIdRefEdEntity.class, ed2_id);
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiMulIdRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiMulIdRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(BiMulIdRefIngEntity.class, ing1_id));
+    }
+
+    @Test
+    public void testHistoryOfEdId1() {
+        BiMulIdRefIngEntity ing1 = getEntityManager().find(BiMulIdRefIngEntity.class, ing1_id);
+
+        BiMulIdRefEdEntity rev1 = getVersionsReader().find(BiMulIdRefEdEntity.class, ed1_id, 1);
+        BiMulIdRefEdEntity rev2 = getVersionsReader().find(BiMulIdRefEdEntity.class, ed1_id, 2);
+
+        assert rev1.getReferencing().equals(ing1);
+        assert rev2.getReferencing() == null;
+    }
+
+    @Test
+    public void testHistoryOfEdId2() {
+        BiMulIdRefIngEntity ing1 = getEntityManager().find(BiMulIdRefIngEntity.class, ing1_id);
+
+        BiMulIdRefEdEntity rev1 = getVersionsReader().find(BiMulIdRefEdEntity.class, ed2_id, 1);
+        BiMulIdRefEdEntity rev2 = getVersionsReader().find(BiMulIdRefEdEntity.class, ed2_id, 2);
+
+        assert rev1.getReferencing() == null;
+        assert rev2.getReferencing().equals(ing1);
+    }
+
+    @Test
+    public void testHistoryOfIngId1() {
+        BiMulIdRefEdEntity ed1 = getEntityManager().find(BiMulIdRefEdEntity.class, ed1_id);
+        BiMulIdRefEdEntity ed2 = getEntityManager().find(BiMulIdRefEdEntity.class, ed2_id);
+
+        BiMulIdRefIngEntity rev1 = getVersionsReader().find(BiMulIdRefIngEntity.class, ing1_id, 1);
+        BiMulIdRefIngEntity rev2 = getVersionsReader().find(BiMulIdRefIngEntity.class, ing1_id, 2);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UniRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UniRefEdEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UniRefEdEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,62 @@
+package org.jboss.envers.test.integration.onetoone.unidirectional;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Id;
+import javax.persistence.Entity;
+
+/**
+ * Unidirectional ReferencEd Entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class UniRefEdEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    public UniRefEdEntity() {
+    }
+
+    public UniRefEdEntity(Integer id, String data) {
+        this.id = id;
+        this.data = data;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UniRefEdEntity)) return false;
+
+        UniRefEdEntity that = (UniRefEdEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UniRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UniRefIngEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UniRefIngEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,76 @@
+package org.jboss.envers.test.integration.onetoone.unidirectional;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.OneToOne;
+
+/**
+ * Unidirectional ReferencIng Entity
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class UniRefIngEntity {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String data;
+
+    @Versioned
+    @OneToOne
+    private UniRefEdEntity reference;
+
+    public UniRefIngEntity() {
+    }
+
+    public UniRefIngEntity(Integer id, String data, UniRefEdEntity reference) {
+        this.id = id;
+        this.data = data;
+        this.reference = reference;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public UniRefEdEntity getReference() {
+        return reference;
+    }
+
+    public void setReference(UniRefEdEntity reference) {
+        this.reference = reference;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UniRefIngEntity)) return false;
+
+        UniRefIngEntity that = (UniRefIngEntity) o;
+
+        if (data != null ? !data.equals(that.data) : that.data != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (data != null ? data.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/Unidirectional.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/Unidirectional.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/Unidirectional.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,125 @@
+package org.jboss.envers.test.integration.onetoone.unidirectional;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Unidirectional extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+    private Integer ed3_id;
+    private Integer ed4_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(UniRefEdEntity.class);
+        cfg.addAnnotatedClass(UniRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        UniRefEdEntity ed1 = new UniRefEdEntity(1, "data_ed_1");
+        UniRefEdEntity ed2 = new UniRefEdEntity(2, "data_ed_2");
+        UniRefEdEntity ed3 = new UniRefEdEntity(3, "data_ed_2");
+        UniRefEdEntity ed4 = new UniRefEdEntity(4, "data_ed_2");
+
+        UniRefIngEntity ing1 = new UniRefIngEntity(5, "data_ing_1", ed1);
+        UniRefIngEntity ing2 = new UniRefIngEntity(6, "data_ing_2", ed3);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+        em.persist(ed3);
+        em.persist(ed4);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing1 = em.find(UniRefIngEntity.class, ing1.getId());
+        ed2 = em.find(UniRefEdEntity.class, ed2.getId());
+
+        ing1.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing2 = em.find(UniRefIngEntity.class, ing2.getId());
+        ed3 = em.find(UniRefEdEntity.class, ed3.getId());
+
+        ing2.setReference(ed4);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+        ed3_id = ed3.getId();
+        ed4_id = ed4.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(UniRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(UniRefEdEntity.class, ed2_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(UniRefEdEntity.class, ed3_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(UniRefEdEntity.class, ed4_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(UniRefIngEntity.class, ing1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(UniRefIngEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfIngId1() {
+        UniRefEdEntity ed1 = getEntityManager().find(UniRefEdEntity.class, ed1_id);
+        UniRefEdEntity ed2 = getEntityManager().find(UniRefEdEntity.class, ed2_id);
+
+        UniRefIngEntity rev1 = getVersionsReader().find(UniRefIngEntity.class, ing1_id, 1);
+        UniRefIngEntity rev2 = getVersionsReader().find(UniRefIngEntity.class, ing1_id, 2);
+        UniRefIngEntity rev3 = getVersionsReader().find(UniRefIngEntity.class, ing1_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference().equals(ed2);
+        assert rev3.getReference().equals(ed2);
+    }
+
+    @Test
+    public void testHistoryOfIngId2() {
+        UniRefEdEntity ed3 = getEntityManager().find(UniRefEdEntity.class, ed3_id);
+        UniRefEdEntity ed4 = getEntityManager().find(UniRefEdEntity.class, ed4_id);
+
+        UniRefIngEntity rev1 = getVersionsReader().find(UniRefIngEntity.class, ing2_id, 1);
+        UniRefIngEntity rev2 = getVersionsReader().find(UniRefIngEntity.class, ing2_id, 2);
+        UniRefIngEntity rev3 = getVersionsReader().find(UniRefIngEntity.class, ing2_id, 3);
+
+        assert rev1.getReference().equals(ed3);
+        assert rev2.getReference().equals(ed3);
+        assert rev3.getReference().equals(ed4);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UnidirectionalWithNulls.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UnidirectionalWithNulls.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/onetoone/unidirectional/UnidirectionalWithNulls.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,112 @@
+package org.jboss.envers.test.integration.onetoone.unidirectional;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class UnidirectionalWithNulls extends AbstractEntityTest {
+    private Integer ed1_id;
+    private Integer ed2_id;
+
+    private Integer ing1_id;
+    private Integer ing2_id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(UniRefEdEntity.class);
+        cfg.addAnnotatedClass(UniRefIngEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        UniRefEdEntity ed1 = new UniRefEdEntity(1, "data_ed_1");
+        UniRefEdEntity ed2 = new UniRefEdEntity(2, "data_ed_2");
+
+        UniRefIngEntity ing1 = new UniRefIngEntity(3, "data_ing_1", ed1);
+        UniRefIngEntity ing2 = new UniRefIngEntity(4, "data_ing_2", null);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ed1);
+        em.persist(ed2);
+
+        em.persist(ing1);
+        em.persist(ing2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing1 = em.find(UniRefIngEntity.class, ing1.getId());
+
+        ing1.setReference(null);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+
+        em = getEntityManager();
+        em.getTransaction().begin();
+
+        ing2 = em.find(UniRefIngEntity.class, ing2.getId());
+        ed2 = em.find(UniRefEdEntity.class, ed2.getId());
+
+        ing2.setReference(ed2);
+
+        em.getTransaction().commit();
+
+        //
+
+        ed1_id = ed1.getId();
+        ed2_id = ed2.getId();
+
+        ing1_id = ing1.getId();
+        ing2_id = ing2.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(UniRefEdEntity.class, ed1_id));
+        assert Arrays.asList(1).equals(getVersionsReader().getRevisions(UniRefEdEntity.class, ed2_id));
+
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(UniRefIngEntity.class, ing1_id));
+        assert Arrays.asList(1, 3).equals(getVersionsReader().getRevisions(UniRefIngEntity.class, ing2_id));
+    }
+
+    @Test
+    public void testHistoryOfIngId1() {
+        UniRefEdEntity ed1 = getEntityManager().find(UniRefEdEntity.class, ed1_id);
+
+        UniRefIngEntity rev1 = getVersionsReader().find(UniRefIngEntity.class, ing1_id, 1);
+        UniRefIngEntity rev2 = getVersionsReader().find(UniRefIngEntity.class, ing1_id, 2);
+        UniRefIngEntity rev3 = getVersionsReader().find(UniRefIngEntity.class, ing1_id, 3);
+
+        assert rev1.getReference().equals(ed1);
+        assert rev2.getReference() == null;
+        assert rev3.getReference() == null;
+    }
+
+    @Test
+    public void testHistoryOfIngId2() {
+        UniRefEdEntity ed2 = getEntityManager().find(UniRefEdEntity.class, ed2_id);
+
+        UniRefIngEntity rev1 = getVersionsReader().find(UniRefIngEntity.class, ing2_id, 1);
+        UniRefIngEntity rev2 = getVersionsReader().find(UniRefIngEntity.class, ing2_id, 2);
+        UniRefIngEntity rev3 = getVersionsReader().find(UniRefIngEntity.class, ing2_id, 3);
+
+        assert rev1.getReference() == null;
+        assert rev2.getReference() == null;
+        assert rev3.getReference().equals(ed2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/PropertiesTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/PropertiesTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/PropertiesTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,67 @@
+package org.jboss.envers.test.integration.properties;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class PropertiesTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Versioned
+    private String str;
+
+    public PropertiesTestEntity() {
+    }
+
+    public PropertiesTestEntity(String str) {
+        this.str = str;
+    }
+
+    public PropertiesTestEntity(Integer id, String str) {
+        this.id = id;
+        this.str = str;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr() {
+    return str;
+}
+
+    public void setStr(String str) {
+        this.str = str;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof PropertiesTestEntity)) return false;
+
+        PropertiesTestEntity that = (PropertiesTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str != null ? !str.equals(that.str) : that.str != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str != null ? str.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/UnversionedOptimisticLockingField.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/UnversionedOptimisticLockingField.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/UnversionedOptimisticLockingField.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,69 @@
+package org.jboss.envers.test.integration.properties;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+import javax.persistence.EntityManager;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.jboss.envers.test.AbstractEntityTest;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+/**
+ * @author Nicolas Doroskevich
+ */
+public class UnversionedOptimisticLockingField extends AbstractEntityTest {
+	private Integer id1;
+
+	public void configure(Ejb3Configuration cfg) {
+		cfg.addAnnotatedClass(UnversionedOptimisticLockingFieldEntity.class);
+
+		cfg.setProperty("org.jboss.envers.unversionedOptimisticLockingField", "true");
+	}
+
+	@BeforeClass(dependsOnMethods = "init")
+	public void initData() {
+		EntityManager em = getEntityManager();
+		em.getTransaction().begin();
+		UnversionedOptimisticLockingFieldEntity olfe = new UnversionedOptimisticLockingFieldEntity("x");
+		em.persist(olfe);
+		id1 = olfe.getId();
+		em.getTransaction().commit();
+
+		em.getTransaction().begin();
+		olfe = em.find(UnversionedOptimisticLockingFieldEntity.class, id1);
+		olfe.setStr("y");
+		em.getTransaction().commit();
+	}
+
+	@Test
+	public void testRevisionCounts() {
+		assert Arrays.asList(1, 2).equals(
+				getVersionsReader().getRevisions(UnversionedOptimisticLockingFieldEntity.class,
+						id1));
+	}
+
+	@Test
+	public void testHistoryOfId1() {
+		UnversionedOptimisticLockingFieldEntity ver1 = new UnversionedOptimisticLockingFieldEntity(id1, "x");
+		UnversionedOptimisticLockingFieldEntity ver2 = new UnversionedOptimisticLockingFieldEntity(id1, "y");
+		
+		assert getVersionsReader().find(UnversionedOptimisticLockingFieldEntity.class, id1, 1)
+				.equals(ver1);
+		assert getVersionsReader().find(UnversionedOptimisticLockingFieldEntity.class, id1, 2)
+				.equals(ver2);
+	}
+	
+	@Test
+	public void testMapping() {
+		PersistentClass pc = getCfg().getClassMapping(UnversionedOptimisticLockingFieldEntity.class.getName() + "_versions");
+		Iterator pi = pc.getPropertyIterator();
+		while(pi.hasNext()) {
+			Property p = (Property) pi.next();
+			assert !"optLocking".equals(p.getName());
+		}
+	}
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/UnversionedOptimisticLockingFieldEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/UnversionedOptimisticLockingFieldEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/UnversionedOptimisticLockingFieldEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.integration.properties;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Version;
+
+import org.jboss.envers.Versioned;
+
+/**
+ * @author Nicolas Doroskevich
+ */
+ at Versioned
+ at Entity
+public class UnversionedOptimisticLockingFieldEntity {
+	
+	 @Id
+	 @GeneratedValue
+	 private Integer id;
+	 
+	 private String str;
+	 
+	 @Version
+	 private int optLocking;
+	 
+	 public UnversionedOptimisticLockingFieldEntity() {
+	 }
+	 
+	 public UnversionedOptimisticLockingFieldEntity(String str) {
+		 this.str = str;
+	 }
+	 
+	 public UnversionedOptimisticLockingFieldEntity(Integer id, String str) {
+		 this.id = id;
+		 this.str = str;
+	 }
+	 
+	 public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getStr() {
+		return str;
+	}
+
+	public void setStr(String str) {
+		this.str = str;
+	}
+	
+	public int getOptLocking() {
+		return optLocking;
+	}
+
+	public void setOptLocking(int optLocking) {
+		this.optLocking = optLocking;
+	}
+	
+	public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UnversionedOptimisticLockingFieldEntity)) return false;
+
+        UnversionedOptimisticLockingFieldEntity that = (UnversionedOptimisticLockingFieldEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str != null ? !str.equals(that.str) : that.str != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str != null ? str.hashCode() : 0);
+        return result;
+    }
+	 
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/VersionsProperties.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/VersionsProperties.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/properties/VersionsProperties.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,55 @@
+package org.jboss.envers.test.integration.properties;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class VersionsProperties extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(PropertiesTestEntity.class);
+
+        cfg.setProperty("org.jboss.envers.versionsTablePrefix", "VP_");
+        cfg.setProperty("org.jboss.envers.versionsTableSuffix", "_VS");
+        cfg.setProperty("org.jboss.envers.idFieldName", "ver_id");
+        cfg.setProperty("org.jboss.envers.revisionFieldName", "ver_rev");
+        cfg.setProperty("org.jboss.envers.revisionTypeFieldName", "ver_rev_type");
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        PropertiesTestEntity pte = new PropertiesTestEntity("x");
+        em.persist(pte);
+        id1 = pte.getId();
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        pte = em.find(PropertiesTestEntity.class, id1);
+        pte.setStr("y");
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(PropertiesTestEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        PropertiesTestEntity ver1 = new PropertiesTestEntity(id1, "x");
+        PropertiesTestEntity ver2 = new PropertiesTestEntity(id1, "y");
+
+        assert getVersionsReader().find(PropertiesTestEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(PropertiesTestEntity.class, id1, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/AggregateQuery.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/AggregateQuery.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/AggregateQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,87 @@
+package org.jboss.envers.test.integration.query;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.IntTestEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"unchecked"})
+public class AggregateQuery extends AbstractEntityTest {
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(IntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        IntTestEntity ite1 = new IntTestEntity(2);
+        IntTestEntity ite2 = new IntTestEntity(10);
+
+        em.persist(ite1);
+        em.persist(ite2);
+
+        Integer id1 = ite1.getId();
+        Integer id2 = ite2.getId();
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        IntTestEntity ite3 = new IntTestEntity(8);
+        em.persist(ite3);
+
+        ite1 = em.find(IntTestEntity.class, id1);
+
+        ite1.setNumber(0);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        ite2 = em.find(IntTestEntity.class, id2);
+
+        ite2.setNumber(52);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testEntitiesAvgMaxQuery() {
+        Object[] ver1 = (Object[]) getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 1)
+                .addProjection("max", "number")
+                .addProjection("avg", "number")
+                .getSingleResult();
+
+        Object[] ver2 = (Object[]) getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 2)
+                .addProjection("max", "number")
+                .addProjection("avg", "number")
+                .getSingleResult();
+
+        Object[] ver3 = (Object[]) getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 3)
+                .addProjection("max", "number")
+                .addProjection("avg", "number")
+                .getSingleResult();
+
+        assert (Integer) ver1[0] == 10;
+        assert (Double) ver1[1] == 6.0;
+
+        assert (Integer) ver2[0] == 10;
+        assert (Double) ver2[1] == 6.0;
+
+        assert (Integer) ver3[0] == 52;
+        assert (Double) ver3[1] == 20.0;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/CustomRevEntityQuery.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/CustomRevEntityQuery.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/CustomRevEntityQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.integration.query;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrIntTestEntity;
+import org.jboss.envers.test.entities.reventity.CustomRevEntity;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"unchecked"})
+public class CustomRevEntityQuery extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(CustomRevEntity.class);
+        cfg.addAnnotatedClass(StrIntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrIntTestEntity site1 = new StrIntTestEntity("a", 10);
+        StrIntTestEntity site2 = new StrIntTestEntity("b", 15);
+
+        em.persist(site1);
+        em.persist(site2);
+
+        id1 = site1.getId();
+        id2 = site2.getId();
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+
+        site1.setStr1("c");
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsOfId1Query() {
+        List<Object[]> result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .add(VersionsRestrictions.idEq(id1))
+                .getResultList();
+
+        assert result.get(0)[0].equals(new StrIntTestEntity("a", 10, id1));
+        assert result.get(0)[1] instanceof CustomRevEntity;
+        assert ((CustomRevEntity) result.get(0)[1]).getCustomId() == 1;
+
+        assert result.get(1)[0].equals(new StrIntTestEntity("c", 10, id1));
+        assert result.get(1)[1] instanceof CustomRevEntity;
+        assert ((CustomRevEntity) result.get(1)[1]).getCustomId() == 2;
+    }
+
+    @Test
+    public void testRevisionsOfId2Query() {
+        List<Object[]> result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .add(VersionsRestrictions.idEq(id2))
+                .getResultList();
+
+        assert result.get(0)[0].equals(new StrIntTestEntity("b", 15, id2));
+        assert result.get(0)[1] instanceof CustomRevEntity;
+        assert ((CustomRevEntity) result.get(0)[1]).getCustomId() == 1;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/DeletedEntities.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/DeletedEntities.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/DeletedEntities.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,76 @@
+package org.jboss.envers.test.integration.query;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrIntTestEntity;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.DefaultRevisionEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DeletedEntities extends AbstractEntityTest {
+    private Integer id2;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrIntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrIntTestEntity site1 = new StrIntTestEntity("a", 10);
+        StrIntTestEntity site2 = new StrIntTestEntity("b", 11);
+
+        em.persist(site1);
+        em.persist(site2);
+
+        id2 = site2.getId();
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        site2 = em.find(StrIntTestEntity.class, id2);
+        em.remove(site2);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testProjectionsInEntitiesAtRevision() {
+        assert getVersionsReader().createQuery().forEntitiesAtRevision(StrIntTestEntity.class, 1)
+            .getResultList().size() == 2;
+        assert getVersionsReader().createQuery().forEntitiesAtRevision(StrIntTestEntity.class, 2)
+            .getResultList().size() == 1;
+
+        assert (Long) getVersionsReader().createQuery().forEntitiesAtRevision(StrIntTestEntity.class, 1)
+            .addProjection("count", "originalId.id").getResultList().get(0) == 2;
+        assert (Long) getVersionsReader().createQuery().forEntitiesAtRevision(StrIntTestEntity.class, 2)
+            .addProjection("count", "originalId.id").getResultList().get(0) == 1;
+    }
+
+    @Test
+    public void testRevisionsOfEntityWithoutDelete() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, false)
+                .add(VersionsRestrictions.idEq(id2))
+                .getResultList();
+
+        assert result.size() == 1;
+
+        assert ((Object []) result.get(0))[0].equals(new StrIntTestEntity("b", 11, id2));
+        assert ((DefaultRevisionEntity) ((Object []) result.get(0))[1]).getId() == 1;
+        assert ((Object []) result.get(0))[2].equals(RevisionType.ADD);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/MaximalizePropertyQuery.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/MaximalizePropertyQuery.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/MaximalizePropertyQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,101 @@
+package org.jboss.envers.test.integration.query;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrIntTestEntity;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.jboss.envers.query.RevisionProperty;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"unchecked"})
+public class MaximalizePropertyQuery extends AbstractEntityTest {
+    Integer id1;
+    Integer id2;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrIntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrIntTestEntity site1 = new StrIntTestEntity("a", 10);
+        StrIntTestEntity site2 = new StrIntTestEntity("b", 15);
+
+        em.persist(site1);
+        em.persist(site2);
+
+        id1 = site1.getId();
+        id2 = site2.getId();
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+        site2 = em.find(StrIntTestEntity.class, id2);
+
+        site1.setStr1("d");
+        site2.setNumber(20);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+        site2 = em.find(StrIntTestEntity.class, id2);
+
+        site1.setNumber(30);
+        site2.setStr1("z");
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+        site2 = em.find(StrIntTestEntity.class, id2);
+
+        site1.setNumber(5);
+        site2.setStr1("a");
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testMaximizeWithIdEq() {
+        List revs_id1 = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(VersionsRestrictions.maximizeProperty("number")
+                    .add(VersionsRestrictions.idEq(id2)))
+                .getResultList();
+
+        assert Arrays.asList(2, 3, 4).equals(revs_id1);
+    }
+
+    @Test
+    public void testMinimizeWithPropertyEq() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(VersionsRestrictions.minimizeProperty("number")
+                    .add(VersionsRestrictions.eq("str1", "a")))
+                .getResultList();
+
+        assert Arrays.asList(1).equals(result);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/OrderByLimitQuery.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/OrderByLimitQuery.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/OrderByLimitQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,122 @@
+package org.jboss.envers.test.integration.query;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.IntTestEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"unchecked"})
+public class OrderByLimitQuery extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+    private Integer id3;
+    private Integer id4;
+    private Integer id5;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(IntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        IntTestEntity ite1 = new IntTestEntity(12);
+        IntTestEntity ite2 = new IntTestEntity(5);
+        IntTestEntity ite3 = new IntTestEntity(8);
+        IntTestEntity ite4 = new IntTestEntity(1);
+
+        em.persist(ite1);
+        em.persist(ite2);
+        em.persist(ite3);
+        em.persist(ite4);
+
+        id1 = ite1.getId();
+        id2 = ite2.getId();
+        id3 = ite3.getId();
+        id4 = ite4.getId();
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        IntTestEntity ite5 = new IntTestEntity(3);
+        em.persist(ite5);
+        id5 = ite5.getId();
+
+        ite1 = em.find(IntTestEntity.class, id1);
+        ite1.setNumber(0);
+
+        ite4 = em.find(IntTestEntity.class, id4);
+        ite4.setNumber(15);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testEntitiesOrderLimitByQueryRev1() {
+        List res_0_to_1 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 1)
+                .addOrder("number", false)
+                .setFirstResult(0)
+                .setMaxResults(2)
+                .getResultList();
+
+        List res_2_to_3 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 1)
+                .addOrder("number", false)
+                .setFirstResult(2)
+                .setMaxResults(2)
+                .getResultList();
+
+        List res_empty = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 1)
+                .addOrder("number", false)
+                .setFirstResult(4)
+                .setMaxResults(2)
+                .getResultList();
+
+        assert Arrays.asList(new IntTestEntity(12, id1), new IntTestEntity(8, id3)).equals(res_0_to_1);
+        assert Arrays.asList(new IntTestEntity(5, id2), new IntTestEntity(1, id4)).equals(res_2_to_3);
+        assert Arrays.asList().equals(res_empty);
+    }
+
+    @Test
+    public void testEntitiesOrderLimitByQueryRev2() {
+        List res_0_to_1 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 2)
+                .addOrder("number", false)
+                .setFirstResult(0)
+                .setMaxResults(2)
+                .getResultList();
+
+        List res_2_to_3 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 2)
+                .addOrder("number", false)
+                .setFirstResult(2)
+                .setMaxResults(2)
+                .getResultList();
+
+        List res_4 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(IntTestEntity.class, 2)
+                .addOrder("number", false)
+                .setFirstResult(4)
+                .setMaxResults(2)
+                .getResultList();
+
+        assert Arrays.asList(new IntTestEntity(15, id4), new IntTestEntity(8, id3)).equals(res_0_to_1);
+        assert Arrays.asList(new IntTestEntity(5, id2), new IntTestEntity(3, id5)).equals(res_2_to_3);
+        assert Arrays.asList(new IntTestEntity(0, id1)).equals(res_4);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/RevisionConstraintQuery.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/RevisionConstraintQuery.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/RevisionConstraintQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,163 @@
+package org.jboss.envers.test.integration.query;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrIntTestEntity;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.jboss.envers.query.RevisionProperty;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"unchecked"})
+public class RevisionConstraintQuery extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrIntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrIntTestEntity site1 = new StrIntTestEntity("a", 10);
+        StrIntTestEntity site2 = new StrIntTestEntity("b", 15);
+
+        em.persist(site1);
+        em.persist(site2);
+
+        id1 = site1.getId();
+        Integer id2 = site2.getId();
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+        site2 = em.find(StrIntTestEntity.class, id2);
+
+        site1.setStr1("d");
+        site2.setNumber(20);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+        site2 = em.find(StrIntTestEntity.class, id2);
+
+        site1.setNumber(1);
+        site2.setStr1("z");
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+        site2 = em.find(StrIntTestEntity.class, id2);
+
+        site1.setNumber(5);
+        site2.setStr1("a");
+
+        em.getTransaction().commit();
+    }
+    
+    @Test
+    public void testRevisionsLtQuery() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.distinct())
+                .add(RevisionProperty.lt(3))
+                .getResultList();
+
+        assert Arrays.asList(1, 2).equals(result);
+    }
+
+    @Test
+    public void testRevisionsGeQuery() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.distinct())
+                .add(RevisionProperty.ge(2))
+                .getResultList();
+
+        assert Arrays.asList(2, 3, 4).equals(result);
+    }
+
+    @Test
+    public void testRevisionsLeWithPropertyQuery() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(RevisionProperty.le(3))
+                .add(VersionsRestrictions.eq("str1", "a"))
+                .getResultList();
+
+        assert Arrays.asList(1).equals(result);
+    }
+
+    @Test
+    public void testRevisionsGtWithPropertyQuery() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(RevisionProperty.gt(1))
+                .add(VersionsRestrictions.lt("number", 10))
+                .getResultList();
+
+        assert Arrays.asList(3, 4).equals(result);
+    }
+
+    @Test
+    public void testRevisionProjectionQuery() {
+        Object[] result = (Object[]) getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.max())
+                .addProjection(RevisionProperty.count())
+                .addProjection(RevisionProperty.countDistinct())
+                .addProjection(RevisionProperty.min())
+                .add(VersionsRestrictions.idEq(id1))
+                .getSingleResult();
+
+        assert (Integer) result[0] == 4;
+        assert (Long) result[1] == 4;
+        assert (Long) result[2] == 4;
+        assert (Integer) result[3] == 1;
+    }
+
+    @Test
+    public void testRevisionOrderQuery() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(VersionsRestrictions.idEq(id1))
+                .addOrder(RevisionProperty.desc())
+                .getResultList();
+
+        assert Arrays.asList(4, 3, 2, 1).equals(result);
+    }
+
+    @Test
+    public void testRevisionCountQuery() {
+        // The query shouldn't be ordered as always, otherwise - we get an exception.
+        Object result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.count())
+                .add(VersionsRestrictions.idEq(id1))
+                .getSingleResult();
+
+        assert (Long) result == 4;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/SimpleQuery.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/SimpleQuery.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/SimpleQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,219 @@
+package org.jboss.envers.test.integration.query;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrIntTestEntity;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.jboss.envers.query.RevisionProperty;
+import org.jboss.envers.query.RevisionTypeProperty;
+import org.jboss.envers.RevisionType;
+import org.jboss.envers.DefaultRevisionEntity;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.HashSet;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"unchecked"})
+public class SimpleQuery extends AbstractEntityTest {
+    private Integer id1;
+    private Integer id2;
+    private Integer id3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrIntTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        StrIntTestEntity site1 = new StrIntTestEntity("a", 10);
+        StrIntTestEntity site2 = new StrIntTestEntity("a", 10);
+        StrIntTestEntity site3 = new StrIntTestEntity("b", 5);
+
+        em.persist(site1);
+        em.persist(site2);
+        em.persist(site3);
+
+        id1 = site1.getId();
+        id2 = site2.getId();
+        id3 = site3.getId();
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+        site2 = em.find(StrIntTestEntity.class, id2);
+
+        site1.setStr1("c");
+        site2.setNumber(20);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        site3 = em.find(StrIntTestEntity.class, id3);
+
+        site3.setStr1("a");
+
+        em.getTransaction().commit();
+
+        // Revision 4
+        em.getTransaction().begin();
+
+        site1 = em.find(StrIntTestEntity.class, id1);
+
+        em.remove(site1);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testEntitiesIdQuery() {
+        StrIntTestEntity ver2 = (StrIntTestEntity) getVersionsReader().createQuery()
+                .forEntitiesAtRevision(StrIntTestEntity.class, 2)
+                .add(VersionsRestrictions.idEq(id2))
+                .getSingleResult();
+
+        assert ver2.equals(new StrIntTestEntity("a", 20, id2));
+    }
+
+    @Test
+    public void testEntitiesPropertyEqualsQuery() {
+        List ver1 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(StrIntTestEntity.class, 1)
+                .add(VersionsRestrictions.eq("str1", "a"))
+                .getResultList();
+
+        List ver2 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(StrIntTestEntity.class, 2)
+                .add(VersionsRestrictions.eq("str1", "a"))
+                .getResultList();
+
+        List ver3 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(StrIntTestEntity.class, 3)
+                .add(VersionsRestrictions.eq("str1", "a"))
+                .getResultList();
+
+        assert new HashSet(ver1).equals(TestTools.makeSet(new StrIntTestEntity("a", 10, id1),
+                new StrIntTestEntity("a", 10, id2)));
+        assert new HashSet(ver2).equals(TestTools.makeSet(new StrIntTestEntity("a", 20, id2)));
+        assert new HashSet(ver3).equals(TestTools.makeSet(new StrIntTestEntity("a", 20, id2),
+                new StrIntTestEntity("a", 5, id3)));
+    }
+
+    @Test
+    public void testEntitiesPropertyLeQuery() {
+        List ver1 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(StrIntTestEntity.class, 1)
+                .add(VersionsRestrictions.le("number", 10))
+                .getResultList();
+
+        List ver2 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(StrIntTestEntity.class, 2)
+                .add(VersionsRestrictions.le("number", 10))
+                .getResultList();
+
+        List ver3 = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(StrIntTestEntity.class, 3)
+                .add(VersionsRestrictions.le("number", 10))
+                .getResultList();
+
+        assert new HashSet(ver1).equals(TestTools.makeSet(new StrIntTestEntity("a", 10, id1),
+                new StrIntTestEntity("a", 10, id2), new StrIntTestEntity("b", 5, id3)));
+        assert new HashSet(ver2).equals(TestTools.makeSet(new StrIntTestEntity("c", 10, id1),
+                new StrIntTestEntity("b", 5, id3)));
+        assert new HashSet(ver3).equals(TestTools.makeSet(new StrIntTestEntity("c", 10, id1),
+                new StrIntTestEntity("a", 5, id3)));
+    }
+
+    @Test
+    public void testRevisionsPropertyEqQuery() {
+        List revs_id1 = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(VersionsRestrictions.le("str1", "a"))
+                .add(VersionsRestrictions.idEq(id1))
+                .getResultList();
+
+        List revs_id2 = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(VersionsRestrictions.le("str1", "a"))
+                .add(VersionsRestrictions.idEq(id2))
+                .getResultList();
+
+        List revs_id3 = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionProperty.revisionNumber())
+                .add(VersionsRestrictions.le("str1", "a"))
+                .add(VersionsRestrictions.idEq(id3))
+                .getResultList();
+
+        assert Arrays.asList(1).equals(revs_id1);
+        assert Arrays.asList(1, 2).equals(revs_id2);
+        assert Arrays.asList(3).equals(revs_id3);
+    }
+
+    @Test
+    public void testSelectEntitiesQuery() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, true, false)
+                .add(VersionsRestrictions.idEq(id1))
+                .getResultList();
+
+        assert result.size() == 2;
+
+        assert result.get(0).equals(new StrIntTestEntity("a", 10, id1));
+        assert result.get(1).equals(new StrIntTestEntity("c", 10, id1));
+    }
+
+    @Test
+    public void testSelectEntitiesAndRevisionsQuery() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .add(VersionsRestrictions.idEq(id1))
+                .getResultList();
+
+        assert result.size() == 3;
+
+        assert ((Object []) result.get(0))[0].equals(new StrIntTestEntity("a", 10, id1));
+        assert ((Object []) result.get(1))[0].equals(new StrIntTestEntity("c", 10, id1));
+        assert ((Object []) result.get(2))[0].equals(new StrIntTestEntity(null, null, id1));
+
+        assert ((DefaultRevisionEntity) ((Object []) result.get(0))[1]).getId() == 1;
+        assert ((DefaultRevisionEntity) ((Object []) result.get(1))[1]).getId() == 2;
+        assert ((DefaultRevisionEntity) ((Object []) result.get(2))[1]).getId() == 4;
+
+        assert ((Object []) result.get(0))[2].equals(RevisionType.ADD);
+        assert ((Object []) result.get(1))[2].equals(RevisionType.MOD);
+        assert ((Object []) result.get(2))[2].equals(RevisionType.DEL);
+    }
+
+    @Test
+    public void testSelectRevisionTypeQuery() {
+        List result = getVersionsReader().createQuery()
+                .forRevisionsOfEntity(StrIntTestEntity.class, false, true)
+                .addProjection(RevisionTypeProperty.revisionType())
+                .add(VersionsRestrictions.idEq(id1))
+                .getResultList();
+
+        assert result.size() == 3;
+
+        assert result.get(0).equals(RevisionType.ADD);
+        assert result.get(1).equals(RevisionType.MOD);
+        assert result.get(2).equals(RevisionType.DEL);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/EmbIdOneToManyQuery.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/EmbIdOneToManyQuery.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/EmbIdOneToManyQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,193 @@
+package org.jboss.envers.test.integration.query.ids;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.onetomany.ids.SetRefEdEmbIdEntity;
+import org.jboss.envers.test.entities.onetomany.ids.SetRefIngEmbIdEntity;
+import org.jboss.envers.test.entities.ids.EmbId;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"unchecked"})
+public class EmbIdOneToManyQuery extends AbstractEntityTest {
+    private EmbId id1;
+    private EmbId id2;
+
+    private EmbId id3;
+    private EmbId id4;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetRefEdEmbIdEntity.class);
+        cfg.addAnnotatedClass(SetRefIngEmbIdEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        id1 = new EmbId(0, 1);
+        id2 = new EmbId(10, 11);
+        id3 = new EmbId(20, 21);
+        id4 = new EmbId(30, 31);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        SetRefIngEmbIdEntity refIng1 = new SetRefIngEmbIdEntity(id1, "x", null);
+        SetRefIngEmbIdEntity refIng2 = new SetRefIngEmbIdEntity(id2, "y", null);
+
+        em.persist(refIng1);
+        em.persist(refIng2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        SetRefEdEmbIdEntity refEd3 = new SetRefEdEmbIdEntity(id3, "a");
+        SetRefEdEmbIdEntity refEd4 = new SetRefEdEmbIdEntity(id4, "a");
+
+        em.persist(refEd3);
+        em.persist(refEd4);
+
+        refIng1 = em.find(SetRefIngEmbIdEntity.class, id1);
+        refIng2 = em.find(SetRefIngEmbIdEntity.class, id2);
+
+        refIng1.setReference(refEd3);
+        refIng2.setReference(refEd4);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        refEd3 = em.find(SetRefEdEmbIdEntity.class, id3);
+        refIng2 = em.find(SetRefIngEmbIdEntity.class, id2);
+        refIng2.setReference(refEd3);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testEntitiesReferencedToId3() {
+        Set rev1_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 1)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .getResultList());
+
+        Set rev1 = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 1)
+                .add(VersionsRestrictions.eq("reference", new SetRefEdEmbIdEntity(id3, null)))
+                .getResultList());
+
+        Set rev2_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 2)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .getResultList());
+
+        Set rev2 = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 2)
+                .add(VersionsRestrictions.eq("reference", new SetRefEdEmbIdEntity(id3, null)))
+                .getResultList());
+
+        Set rev3_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 3)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .getResultList());
+
+        Set rev3 = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 3)
+                .add(VersionsRestrictions.eq("reference", new SetRefEdEmbIdEntity(id3, null)))
+                .getResultList());
+
+        assert rev1.equals(rev1_related);
+        assert rev2.equals(rev2_related);
+        assert rev3.equals(rev3_related);
+
+        assert rev1.equals(TestTools.makeSet());
+        assert rev2.equals(TestTools.makeSet(new SetRefIngEmbIdEntity(id1, "x", null)));
+        assert rev3.equals(TestTools.makeSet(new SetRefIngEmbIdEntity(id1, "x", null),
+                new SetRefIngEmbIdEntity(id2, "y", null)));
+    }
+
+    @Test
+    public void testEntitiesReferencedToId4() {
+        Set rev1_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 1)
+                .add(VersionsRestrictions.relatedIdEq("reference", id4))
+                .getResultList());
+
+        Set rev2_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 2)
+                .add(VersionsRestrictions.relatedIdEq("reference", id4))
+                .getResultList());
+
+        Set rev3_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 3)
+                .add(VersionsRestrictions.relatedIdEq("reference", id4))
+                .getResultList());
+
+        assert rev1_related.equals(TestTools.makeSet());
+        assert rev2_related.equals(TestTools.makeSet(new SetRefIngEmbIdEntity(id2, "y", null)));
+        assert rev3_related.equals(TestTools.makeSet());
+    }
+
+    @Test
+    public void testEntitiesReferencedByIng1ToId3() {
+        List rev1_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 1)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id1))
+                .getResultList();
+
+        Object rev2_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 2)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id1))
+                .getSingleResult();
+
+        Object rev3_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 3)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3)) 
+                .add(VersionsRestrictions.idEq(id1))
+                .getSingleResult();
+
+        assert rev1_related.size() == 0;
+        assert rev2_related.equals(new SetRefIngEmbIdEntity(id1, "x", null));
+        assert rev3_related.equals(new SetRefIngEmbIdEntity(id1, "x", null));
+    }
+
+    @Test
+    public void testEntitiesReferencedByIng2ToId3() {
+        List rev1_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 1)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id2))
+                .getResultList();
+
+        List rev2_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 2)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id2))
+                .getResultList();
+
+        Object rev3_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngEmbIdEntity.class, 3)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id2))
+                .getSingleResult();
+
+        assert rev1_related.size() == 0;
+        assert rev2_related.size() == 0;
+        assert rev3_related.equals(new SetRefIngEmbIdEntity(id2, "y", null));
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/MulIdOneToManyQuery.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/MulIdOneToManyQuery.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/query/ids/MulIdOneToManyQuery.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,193 @@
+package org.jboss.envers.test.integration.query.ids;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.tools.TestTools;
+import org.jboss.envers.test.entities.ids.MulId;
+import org.jboss.envers.test.entities.onetomany.ids.SetRefEdMulIdEntity;
+import org.jboss.envers.test.entities.onetomany.ids.SetRefIngMulIdEntity;
+import org.jboss.envers.query.VersionsRestrictions;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at SuppressWarnings({"unchecked"})
+public class MulIdOneToManyQuery extends AbstractEntityTest {
+    private MulId id1;
+    private MulId id2;
+
+    private MulId id3;
+    private MulId id4;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SetRefEdMulIdEntity.class);
+        cfg.addAnnotatedClass(SetRefIngMulIdEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        id1 = new MulId(0, 1);
+        id2 = new MulId(10, 11);
+        id3 = new MulId(20, 21);
+        id4 = new MulId(30, 31);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        SetRefIngMulIdEntity refIng1 = new SetRefIngMulIdEntity(id1, "x", null);
+        SetRefIngMulIdEntity refIng2 = new SetRefIngMulIdEntity(id2, "y", null);
+
+        em.persist(refIng1);
+        em.persist(refIng2);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        SetRefEdMulIdEntity refEd3 = new SetRefEdMulIdEntity(id3, "a");
+        SetRefEdMulIdEntity refEd4 = new SetRefEdMulIdEntity(id4, "a");
+
+        em.persist(refEd3);
+        em.persist(refEd4);
+
+        refIng1 = em.find(SetRefIngMulIdEntity.class, id1);
+        refIng2 = em.find(SetRefIngMulIdEntity.class, id2);
+
+        refIng1.setReference(refEd3);
+        refIng2.setReference(refEd4);
+
+        em.getTransaction().commit();
+
+        // Revision 3
+        em.getTransaction().begin();
+
+        refEd3 = em.find(SetRefEdMulIdEntity.class, id3);
+        refIng2 = em.find(SetRefIngMulIdEntity.class, id2);
+        refIng2.setReference(refEd3);
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testEntitiesReferencedToId3() {
+        Set rev1_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 1)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .getResultList());
+
+        Set rev1 = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 1)
+                .add(VersionsRestrictions.eq("reference", new SetRefEdMulIdEntity(id3, null)))
+                .getResultList());
+
+        Set rev2_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 2)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .getResultList());
+
+        Set rev2 = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 2)
+                .add(VersionsRestrictions.eq("reference", new SetRefEdMulIdEntity(id3, null)))
+                .getResultList());
+
+        Set rev3_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 3)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .getResultList());
+
+        Set rev3 = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 3)
+                .add(VersionsRestrictions.eq("reference", new SetRefEdMulIdEntity(id3, null)))
+                .getResultList());
+
+        assert rev1.equals(rev1_related);
+        assert rev2.equals(rev2_related);
+        assert rev3.equals(rev3_related);
+
+        assert rev1.equals(TestTools.makeSet());
+        assert rev2.equals(TestTools.makeSet(new SetRefIngMulIdEntity(id1, "x", null)));
+        assert rev3.equals(TestTools.makeSet(new SetRefIngMulIdEntity(id1, "x", null),
+                new SetRefIngMulIdEntity(id2, "y", null)));
+    }
+
+    @Test
+    public void testEntitiesReferencedToId4() {
+        Set rev1_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 1)
+                .add(VersionsRestrictions.relatedIdEq("reference", id4))
+                .getResultList());
+
+        Set rev2_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 2)
+                .add(VersionsRestrictions.relatedIdEq("reference", id4))
+                .getResultList());
+
+        Set rev3_related = new HashSet(getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 3)
+                .add(VersionsRestrictions.relatedIdEq("reference", id4))
+                .getResultList());
+
+        assert rev1_related.equals(TestTools.makeSet());
+        assert rev2_related.equals(TestTools.makeSet(new SetRefIngMulIdEntity(id2, "y", null)));
+        assert rev3_related.equals(TestTools.makeSet());
+    }
+
+    @Test
+    public void testEntitiesReferencedByIng1ToId3() {
+        List rev1_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 1)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id1))
+                .getResultList();
+
+        Object rev2_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 2)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id1))
+                .getSingleResult();
+
+        Object rev3_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 3)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id1))
+                .getSingleResult();
+
+        assert rev1_related.size() == 0;
+        assert rev2_related.equals(new SetRefIngMulIdEntity(id1, "x", null));
+        assert rev3_related.equals(new SetRefIngMulIdEntity(id1, "x", null));
+    }
+
+    @Test
+    public void testEntitiesReferencedByIng2ToId3() {
+        List rev1_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 1)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id2))
+                .getResultList();
+
+        List rev2_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 2)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id2))
+                .getResultList();
+
+        Object rev3_related = getVersionsReader().createQuery()
+                .forEntitiesAtRevision(SetRefIngMulIdEntity.class, 3)
+                .add(VersionsRestrictions.relatedIdEq("reference", id3))
+                .add(VersionsRestrictions.idEq(id2))
+                .getSingleResult();
+
+        assert rev1_related.size() == 0;
+        assert rev2_related.size() == 0;
+        assert rev3_related.equals(new SetRefIngMulIdEntity(id2, "y", null));
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Custom.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Custom.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Custom.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,111 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.entities.reventity.CustomRevEntity;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+import org.jboss.envers.VersionsReader;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Date;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Custom extends AbstractEntityTest {
+    private Integer id;
+    private long timestamp1;
+    private long timestamp2;
+    private long timestamp3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(CustomRevEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() throws InterruptedException {
+        timestamp1 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        StrTestEntity te = new StrTestEntity("x");
+        em.persist(te);
+        id = te.getId();
+        em.getTransaction().commit();
+
+        timestamp2 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 2
+        em.getTransaction().begin();
+        te = em.find(StrTestEntity.class, id);
+        te.setStr("y");
+        em.getTransaction().commit();
+
+        timestamp3 = System.currentTimeMillis();
+    }
+
+    @Test(expectedExceptions = RevisionDoesNotExistException.class)
+    public void testTimestamps1() {
+        getVersionsReader().getRevisionNumberForDate(new Date(timestamp1));
+    }
+
+    @Test
+    public void testTimestamps() {
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp2)).intValue() == 1;
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp3)).intValue() == 2;
+    }
+
+    @Test
+    public void testDatesForRevisions() {
+        VersionsReader vr = getVersionsReader();
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(1)).intValue() == 1;
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(2)).intValue() == 2;
+    }
+
+    @Test
+    public void testRevisionsForDates() {
+        VersionsReader vr = getVersionsReader();
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2))).getTime() <= timestamp2;
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2)).intValue()+1).getTime() > timestamp2;
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp3))).getTime() <= timestamp3;
+    }
+
+    @Test
+    public void testFindRevision() {
+        VersionsReader vr = getVersionsReader();
+
+        long rev1Timestamp = vr.findRevision(CustomRevEntity.class, 1).getCustomTimestamp();
+        assert rev1Timestamp > timestamp1;
+        assert rev1Timestamp <= timestamp2;
+
+        long rev2Timestamp = vr.findRevision(CustomRevEntity.class, 2).getCustomTimestamp();
+        assert rev2Timestamp > timestamp2;
+        assert rev2Timestamp <= timestamp3;
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+        StrTestEntity ver2 = new StrTestEntity("y", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomBoxed.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomBoxed.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomBoxed.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,110 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+import org.jboss.envers.VersionsReader;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Date;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class CustomBoxed extends AbstractEntityTest {
+    private Integer id;
+    private long timestamp1;
+    private long timestamp2;
+    private long timestamp3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(CustomBoxedRevEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() throws InterruptedException {
+        timestamp1 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        StrTestEntity te = new StrTestEntity("x");
+        em.persist(te);
+        id = te.getId();
+        em.getTransaction().commit();
+
+        timestamp2 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 2
+        em.getTransaction().begin();
+        te = em.find(StrTestEntity.class, id);
+        te.setStr("y");
+        em.getTransaction().commit();
+
+        timestamp3 = System.currentTimeMillis();
+    }
+
+    @Test(expectedExceptions = RevisionDoesNotExistException.class)
+    public void testTimestamps1() {
+        getVersionsReader().getRevisionNumberForDate(new Date(timestamp1));
+    }
+
+    @Test
+    public void testTimestamps() {
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp2)).intValue() == 1;
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp3)).intValue() == 2;
+    }
+
+    @Test
+    public void testDatesForRevisions() {
+        VersionsReader vr = getVersionsReader();
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(1)).intValue() == 1;
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(2)).intValue() == 2;
+    }
+
+    @Test
+    public void testRevisionsForDates() {
+        VersionsReader vr = getVersionsReader();
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2))).getTime() <= timestamp2;
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2)).intValue()+1).getTime() > timestamp2;
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp3))).getTime() <= timestamp3;
+    }
+
+    @Test
+    public void testFindRevision() {
+        VersionsReader vr = getVersionsReader();
+
+        long rev1Timestamp = vr.findRevision(CustomBoxedRevEntity.class, 1).getCustomTimestamp();
+        assert rev1Timestamp > timestamp1;
+        assert rev1Timestamp <= timestamp2;
+
+        long rev2Timestamp = vr.findRevision(CustomBoxedRevEntity.class, 2).getCustomTimestamp();
+        assert rev2Timestamp > timestamp2;
+        assert rev2Timestamp <= timestamp3;
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+        StrTestEntity ver2 = new StrTestEntity("y", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomBoxedRevEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomBoxedRevEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomBoxedRevEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,60 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.RevisionNumber;
+import org.jboss.envers.RevisionTimestamp;
+import org.jboss.envers.RevisionEntity;
+
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Entity;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at RevisionEntity
+public class CustomBoxedRevEntity {
+    @Id
+    @GeneratedValue
+    @RevisionNumber
+    private Integer customId;
+
+    @RevisionTimestamp
+    private Long customTimestamp;
+
+    public Integer getCustomId() {
+        return customId;
+    }
+
+    public void setCustomId(Integer customId) {
+        this.customId = customId;
+    }
+
+    public Long getCustomTimestamp() {
+        return customTimestamp;
+    }
+
+    public void setCustomTimestamp(Long customTimestamp) {
+        this.customTimestamp = customTimestamp;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CustomBoxedRevEntity)) return false;
+
+        CustomBoxedRevEntity that = (CustomBoxedRevEntity) o;
+
+        if (customId != null ? !customId.equals(that.customId) : that.customId != null) return false;
+        if (customTimestamp != null ? !customTimestamp.equals(that.customTimestamp) : that.customTimestamp != null)
+            return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (customId != null ? customId.hashCode() : 0);
+        result = 31 * result + (customTimestamp != null ? customTimestamp.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomPropertyAccess.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomPropertyAccess.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/CustomPropertyAccess.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,111 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.entities.reventity.CustomPropertyAccessRevEntity;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+import org.jboss.envers.VersionsReader;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Date;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class CustomPropertyAccess extends AbstractEntityTest {
+    private Integer id;
+    private long timestamp1;
+    private long timestamp2;
+    private long timestamp3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(CustomPropertyAccessRevEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() throws InterruptedException {
+        timestamp1 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        StrTestEntity te = new StrTestEntity("x");
+        em.persist(te);
+        id = te.getId();
+        em.getTransaction().commit();
+
+        timestamp2 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 2
+        em.getTransaction().begin();
+        te = em.find(StrTestEntity.class, id);
+        te.setStr("y");
+        em.getTransaction().commit();
+
+        timestamp3 = System.currentTimeMillis();
+    }
+
+    @Test(expectedExceptions = RevisionDoesNotExistException.class)
+    public void testTimestamps1() {
+        getVersionsReader().getRevisionNumberForDate(new Date(timestamp1));
+    }
+
+    @Test
+    public void testTimestamps() {
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp2)).intValue() == 1;
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp3)).intValue() == 2;
+    }
+
+    @Test
+    public void testDatesForRevisions() {
+        VersionsReader vr = getVersionsReader();
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(1)).intValue() == 1;
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(2)).intValue() == 2;
+    }
+
+    @Test
+    public void testRevisionsForDates() {
+        VersionsReader vr = getVersionsReader();
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2))).getTime() <= timestamp2;
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2)).intValue()+1).getTime() > timestamp2;
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp3))).getTime() <= timestamp3;
+    }
+
+    @Test
+    public void testFindRevision() {
+        VersionsReader vr = getVersionsReader();
+
+        long rev1Timestamp = vr.findRevision(CustomPropertyAccessRevEntity.class, 1).getCustomTimestamp();
+        assert rev1Timestamp > timestamp1;
+        assert rev1Timestamp <= timestamp2;
+
+        long rev2Timestamp = vr.findRevision(CustomPropertyAccessRevEntity.class, 2).getCustomTimestamp();
+        assert rev2Timestamp > timestamp2;
+        assert rev2Timestamp <= timestamp3;
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+        StrTestEntity ver2 = new StrTestEntity("y", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Inherited.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Inherited.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Inherited.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,110 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+import org.jboss.envers.VersionsReader;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Date;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Inherited extends AbstractEntityTest {
+    private Integer id;
+    private long timestamp1;
+    private long timestamp2;
+    private long timestamp3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(InheritedRevEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() throws InterruptedException {
+        timestamp1 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        StrTestEntity te = new StrTestEntity("x");
+        em.persist(te);
+        id = te.getId();
+        em.getTransaction().commit();
+
+        timestamp2 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 2
+        em.getTransaction().begin();
+        te = em.find(StrTestEntity.class, id);
+        te.setStr("y");
+        em.getTransaction().commit();
+
+        timestamp3 = System.currentTimeMillis();
+    }
+
+    @Test(expectedExceptions = RevisionDoesNotExistException.class)
+    public void testTimestamps1() {
+        getVersionsReader().getRevisionNumberForDate(new Date(timestamp1));
+    }
+
+    @Test
+    public void testTimestamps() {
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp2)).intValue() == 1;
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp3)).intValue() == 2;
+    }
+
+    @Test
+    public void testDatesForRevisions() {
+        VersionsReader vr = getVersionsReader();
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(1)).intValue() == 1;
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(2)).intValue() == 2;
+    }
+
+    @Test
+    public void testRevisionsForDates() {
+        VersionsReader vr = getVersionsReader();
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2))).getTime() <= timestamp2;
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2)).intValue()+1).getTime() > timestamp2;
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp3))).getTime() <= timestamp3;
+    }
+
+    @Test
+    public void testFindRevision() {
+        VersionsReader vr = getVersionsReader();
+
+        long rev1Timestamp = vr.findRevision(InheritedRevEntity.class, 1).getTimestamp();
+        assert rev1Timestamp > timestamp1;
+        assert rev1Timestamp <= timestamp2;
+
+        long rev2Timestamp = vr.findRevision(InheritedRevEntity.class, 2).getTimestamp();
+        assert rev2Timestamp > timestamp2;
+        assert rev2Timestamp <= timestamp3;
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+        StrTestEntity ver2 = new StrTestEntity("y", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/InheritedRevEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/InheritedRevEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/InheritedRevEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,15 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.RevisionEntity;
+import org.jboss.envers.DefaultRevisionEntity;
+
+import javax.persistence.Entity;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at RevisionEntity
+public class InheritedRevEntity extends DefaultRevisionEntity {
+    
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Listener.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Listener.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/Listener.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,123 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+import org.jboss.envers.VersionsReader;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Date;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Listener extends AbstractEntityTest {
+    private Integer id;
+    private long timestamp1;
+    private long timestamp2;
+    private long timestamp3;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(ListenerRevEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() throws InterruptedException {
+        timestamp1 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        TestRevisionListener.data = "data1";
+
+        StrTestEntity te = new StrTestEntity("x");
+        em.persist(te);
+        id = te.getId();
+        em.getTransaction().commit();
+
+        timestamp2 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 2
+        em.getTransaction().begin();
+        te = em.find(StrTestEntity.class, id);
+
+        TestRevisionListener.data = "data2";
+
+        te.setStr("y");
+        em.getTransaction().commit();
+
+        timestamp3 = System.currentTimeMillis();
+    }
+
+    @Test(expectedExceptions = RevisionDoesNotExistException.class)
+    public void testTimestamps1() {
+        getVersionsReader().getRevisionNumberForDate(new Date(timestamp1));
+    }
+
+    @Test
+    public void testTimestamps() {
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp2)).intValue() == 1;
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp3)).intValue() == 2;
+    }
+
+    @Test
+    public void testDatesForRevisions() {
+        VersionsReader vr = getVersionsReader();
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(1)).intValue() == 1;
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(2)).intValue() == 2;
+    }
+
+    @Test
+    public void testRevisionsForDates() {
+        VersionsReader vr = getVersionsReader();
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2))).getTime() <= timestamp2;
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2)).intValue()+1).getTime() > timestamp2;
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp3))).getTime() <= timestamp3;
+    }
+
+    @Test
+    public void testFindRevision() {
+        VersionsReader vr = getVersionsReader();
+
+        ListenerRevEntity rev1Data = vr.findRevision(ListenerRevEntity.class, 1);
+        ListenerRevEntity rev2Data = vr.findRevision(ListenerRevEntity.class, 2);
+
+        long rev1Timestamp = rev1Data.getTimestamp();
+        assert rev1Timestamp > timestamp1;
+        assert rev1Timestamp <= timestamp2;
+
+        assert "data1".equals(rev1Data.getData());
+
+        long rev2Timestamp = rev2Data.getTimestamp();
+        assert rev2Timestamp > timestamp2;
+        assert rev2Timestamp <= timestamp3;
+
+        assert "data2".equals(rev2Data.getData());
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+        StrTestEntity ver2 = new StrTestEntity("y", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/ListenerRevEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/ListenerRevEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/ListenerRevEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,69 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.RevisionNumber;
+import org.jboss.envers.RevisionTimestamp;
+import org.jboss.envers.RevisionEntity;
+
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Entity;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at RevisionEntity(TestRevisionListener.class)
+public class ListenerRevEntity {
+    @Id
+    @GeneratedValue
+    @RevisionNumber
+    private int id;
+
+    @RevisionTimestamp
+    private long timestamp;
+
+    private String data;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ListenerRevEntity)) return false;
+
+        ListenerRevEntity revEntity = (ListenerRevEntity) o;
+
+        if (id != revEntity.id) return false;
+        if (timestamp != revEntity.timestamp) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = id;
+        result = 31 * result + (int) (timestamp ^ (timestamp >>> 32));
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/LongRevNumber.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/LongRevNumber.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/LongRevNumber.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,62 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.VersionsReader;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class LongRevNumber extends AbstractEntityTest {
+    private Integer id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(LongRevNumberRevEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() throws InterruptedException {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        StrTestEntity te = new StrTestEntity("x");
+        em.persist(te);
+        id = te.getId();
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+        te = em.find(StrTestEntity.class, id);
+        te.setStr("y");
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testFindRevision() {
+        VersionsReader vr = getVersionsReader();
+
+        assert vr.findRevision(LongRevNumberRevEntity.class, 1l).getCustomId() == 1l;
+        assert vr.findRevision(LongRevNumberRevEntity.class, 2l).getCustomId() == 2l;
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1l, 2l).equals(getVersionsReader().getRevisions(StrTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        StrTestEntity ver1 = new StrTestEntity("x", id);
+        StrTestEntity ver2 = new StrTestEntity("y", id);
+
+        assert getVersionsReader().find(StrTestEntity.class, id, 1l).equals(ver1);
+        assert getVersionsReader().find(StrTestEntity.class, id, 2l).equals(ver2);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/LongRevNumberRevEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/LongRevNumberRevEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/LongRevNumberRevEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,59 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.RevisionNumber;
+import org.jboss.envers.RevisionTimestamp;
+import org.jboss.envers.RevisionEntity;
+
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Entity;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at RevisionEntity
+public class LongRevNumberRevEntity {
+    @Id
+    @GeneratedValue
+    @RevisionNumber
+    private long customId;
+
+    @RevisionTimestamp
+    private long customTimestamp;
+
+    public long getCustomId() {
+        return customId;
+    }
+
+    public void setCustomId(long customId) {
+        this.customId = customId;
+    }
+
+    public long getCustomTimestamp() {
+        return customTimestamp;
+    }
+
+    public void setCustomTimestamp(long customTimestamp) {
+        this.customTimestamp = customTimestamp;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof LongRevNumberRevEntity)) return false;
+
+        LongRevNumberRevEntity that = (LongRevNumberRevEntity) o;
+
+        if (customId != that.customId) return false;
+        if (customTimestamp != that.customTimestamp) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (int) (customId ^ (customId >>> 32));
+        result = 31 * result + (int) (customTimestamp ^ (customTimestamp >>> 32));
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/TestRevisionListener.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/TestRevisionListener.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/reventity/TestRevisionListener.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,14 @@
+package org.jboss.envers.test.integration.reventity;
+
+import org.jboss.envers.RevisionListener;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class TestRevisionListener implements RevisionListener {
+    public static String data = "data0";
+
+    public void newRevision(Object revisionEntity) {
+        ((ListenerRevEntity) revisionEntity).setData(data);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/revfordate/RevisionForDate.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/revfordate/RevisionForDate.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/revfordate/RevisionForDate.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,96 @@
+package org.jboss.envers.test.integration.revfordate;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.exception.RevisionDoesNotExistException;
+import org.jboss.envers.VersionsReader;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RevisionForDate extends AbstractEntityTest {
+    private long timestamp1;
+    private long timestamp2;
+    private long timestamp3;
+    private long timestamp4;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() throws InterruptedException {
+        timestamp1 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        StrTestEntity rfd = new StrTestEntity("x");
+        em.persist(rfd);
+        Integer id = rfd.getId();
+        em.getTransaction().commit();
+
+        timestamp2 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 2
+        em.getTransaction().begin();
+        rfd = em.find(StrTestEntity.class, id);
+        rfd.setStr("y");
+        em.getTransaction().commit();
+
+        timestamp3 = System.currentTimeMillis();
+
+        Thread.sleep(100);
+
+        // Revision 3
+        em.getTransaction().begin();
+        rfd = em.find(StrTestEntity.class, id);
+        rfd.setStr("z");
+        em.getTransaction().commit();
+
+        timestamp4 = System.currentTimeMillis();
+    }
+
+    @Test(expectedExceptions = RevisionDoesNotExistException.class)
+    public void testTimestamps1() {
+        getVersionsReader().getRevisionNumberForDate(new Date(timestamp1));
+    }
+
+    @Test
+    public void testTimestamps() {
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp2)).intValue() == 1;
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp3)).intValue() == 2;
+        assert getVersionsReader().getRevisionNumberForDate(new Date(timestamp4)).intValue() == 3;
+    }
+
+    @Test
+    public void testDatesForRevisions() {
+        VersionsReader vr = getVersionsReader();
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(1)).intValue() == 1;
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(2)).intValue() == 2;
+        assert vr.getRevisionNumberForDate(vr.getRevisionDate(3)).intValue() == 3;
+    }
+
+    @Test
+    public void testRevisionsForDates() {
+        VersionsReader vr = getVersionsReader();
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2))).getTime() <= timestamp2;
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp2)).intValue()+1).getTime() > timestamp2;
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp3))).getTime() <= timestamp3;
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp3)).intValue()+1).getTime() > timestamp3;
+
+        assert vr.getRevisionDate(vr.getRevisionNumberForDate(new Date(timestamp4))).getTime() <= timestamp4;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIdTestEntity1.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIdTestEntity1.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIdTestEntity1.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,65 @@
+package org.jboss.envers.test.integration.sameids;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SameIdTestEntity1 {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String str1;
+
+    public SameIdTestEntity1() {
+    }
+
+    public SameIdTestEntity1(String str1) {
+        this.str1 = str1;
+    }
+
+    public SameIdTestEntity1(Integer id, String str1) {
+        this.id = id;
+        this.str1 = str1;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SameIdTestEntity1)) return false;
+
+        SameIdTestEntity1 that = (SameIdTestEntity1) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIdTestEntity2.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIdTestEntity2.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIdTestEntity2.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,65 @@
+package org.jboss.envers.test.integration.sameids;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SameIdTestEntity2 {
+    @Id
+    private Integer id;
+
+    @Versioned
+    private String str1;
+
+    public SameIdTestEntity2() {
+    }
+
+    public SameIdTestEntity2(String str1) {
+        this.str1 = str1;
+    }
+
+    public SameIdTestEntity2(Integer id, String str1) {
+        this.id = id;
+        this.str1 = str1;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getStr1() {
+        return str1;
+    }
+
+    public void setStr1(String str1) {
+        this.str1 = str1;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SameIdTestEntity2)) return false;
+
+        SameIdTestEntity2 that = (SameIdTestEntity2) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (str1 != null ? !str1.equals(that.str1) : that.str1 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (str1 != null ? str1.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIds.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIds.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/sameids/SameIds.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,64 @@
+package org.jboss.envers.test.integration.sameids;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * A test which checks that if we add two different entities with the same ids in one revision, they
+ * will both be stored.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class SameIds extends AbstractEntityTest {
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SameIdTestEntity1.class);
+        cfg.addAnnotatedClass(SameIdTestEntity2.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        SameIdTestEntity1 site1 = new SameIdTestEntity1(1, "str1");
+        SameIdTestEntity2 site2 = new SameIdTestEntity2(1, "str1");
+
+        em.persist(site1);
+        em.persist(site2);
+        em.getTransaction().commit();
+
+        em.getTransaction().begin();
+        site1 = em.find(SameIdTestEntity1.class, 1);
+        site2 = em.find(SameIdTestEntity2.class, 1);
+        site1.setStr1("str2");
+        site2.setStr1("str2");
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SameIdTestEntity1.class, 1));
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SameIdTestEntity2.class, 1));
+    }
+
+    @Test
+    public void testHistoryOfSite1() {
+        SameIdTestEntity1 ver1 = new SameIdTestEntity1(1, "str1");
+        SameIdTestEntity1 ver2 = new SameIdTestEntity1(1, "str2");
+
+        assert getVersionsReader().find(SameIdTestEntity1.class, 1, 1).equals(ver1);
+        assert getVersionsReader().find(SameIdTestEntity1.class, 1, 2).equals(ver2);
+    }
+
+    @Test
+    public void testHistoryOfSite2() {
+        SameIdTestEntity2 ver1 = new SameIdTestEntity2(1, "str1");
+        SameIdTestEntity2 ver2 = new SameIdTestEntity2(1, "str2");
+
+        assert getVersionsReader().find(SameIdTestEntity2.class, 1, 1).equals(ver1);
+        assert getVersionsReader().find(SameIdTestEntity2.class, 1, 2).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/BasicSecondary.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/BasicSecondary.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/BasicSecondary.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,71 @@
+package org.jboss.envers.test.integration.secondary;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Join;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BasicSecondary extends AbstractEntityTest {
+    private Integer id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SecondaryTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        SecondaryTestEntity ste = new SecondaryTestEntity("a", "1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ste);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ste = em.find(SecondaryTestEntity.class, ste.getId());
+        ste.setS1("b");
+        ste.setS2("2");
+
+        em.getTransaction().commit();
+
+        //
+
+        id = ste.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SecondaryTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        SecondaryTestEntity ver1 = new SecondaryTestEntity(id, "a", "1");
+        SecondaryTestEntity ver2 = new SecondaryTestEntity(id, "b", "2");
+
+        assert getVersionsReader().find(SecondaryTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(SecondaryTestEntity.class, id, 2).equals(ver2);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testTableNames() {
+        assert "secondary_versions".equals(((Iterator<Join>)
+                getCfg().getClassMapping("org.jboss.envers.test.integration.secondary.SecondaryTestEntity_versions")
+                        .getJoinIterator())
+                .next().getTable().getName());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/NamingSecondary.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/NamingSecondary.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/NamingSecondary.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,71 @@
+package org.jboss.envers.test.integration.secondary;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Join;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NamingSecondary extends AbstractEntityTest {
+    private Integer id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SecondaryNamingTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        SecondaryNamingTestEntity ste = new SecondaryNamingTestEntity("a", "1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ste);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ste = em.find(SecondaryNamingTestEntity.class, ste.getId());
+        ste.setS1("b");
+        ste.setS2("2");
+
+        em.getTransaction().commit();
+
+        //
+
+        id = ste.getId();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SecondaryNamingTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        SecondaryNamingTestEntity ver1 = new SecondaryNamingTestEntity(id, "a", "1");
+        SecondaryNamingTestEntity ver2 = new SecondaryNamingTestEntity(id, "b", "2");
+
+        assert getVersionsReader().find(SecondaryNamingTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(SecondaryNamingTestEntity.class, id, 2).equals(ver2);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testTableNames() {
+        assert "sec_versions".equals(((Iterator<Join>)
+                getCfg().getClassMapping("org.jboss.envers.test.integration.secondary.SecondaryNamingTestEntity_versions")
+                        .getJoinIterator())
+                .next().getTable().getName());
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/SecondaryNamingTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/SecondaryNamingTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/SecondaryNamingTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,83 @@
+package org.jboss.envers.test.integration.secondary;
+
+import org.jboss.envers.SecondaryVersionsTable;
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at SecondaryTable(name = "secondary")
+ at SecondaryVersionsTable(secondaryTableName = "secondary", secondaryVersionsTableName = "sec_versions")
+ at Versioned
+public class SecondaryNamingTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    private String s1;
+
+    @Column(table = "secondary")
+    private String s2;
+
+    public SecondaryNamingTestEntity(Integer id, String s1, String s2) {
+        this.id = id;
+        this.s1 = s1;
+        this.s2 = s2;
+    }
+
+    public SecondaryNamingTestEntity(String s1, String s2) {
+        this.s1 = s1;
+        this.s2 = s2;
+    }
+
+    public SecondaryNamingTestEntity() {
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getS1() {
+        return s1;
+    }
+
+    public void setS1(String s1) {
+        this.s1 = s1;
+    }
+
+    public String getS2() {
+        return s2;
+    }
+
+    public void setS2(String s2) {
+        this.s2 = s2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SecondaryNamingTestEntity)) return false;
+
+        SecondaryNamingTestEntity that = (SecondaryNamingTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (s1 != null ? !s1.equals(that.s1) : that.s1 != null) return false;
+        if (s2 != null ? !s2.equals(that.s2) : that.s2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (s1 != null ? s1.hashCode() : 0);
+        result = 31 * result + (s2 != null ? s2.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/SecondaryTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/SecondaryTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/SecondaryTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,81 @@
+package org.jboss.envers.test.integration.secondary;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at SecondaryTable(name = "secondary")
+ at Versioned
+public class SecondaryTestEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    private String s1;
+
+    @Column(table = "secondary")
+    private String s2;
+
+    public SecondaryTestEntity(Integer id, String s1, String s2) {
+        this.id = id;
+        this.s1 = s1;
+        this.s2 = s2;
+    }
+
+    public SecondaryTestEntity(String s1, String s2) {
+        this.s1 = s1;
+        this.s2 = s2;
+    }
+
+    public SecondaryTestEntity() {
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getS1() {
+        return s1;
+    }
+
+    public void setS1(String s1) {
+        this.s1 = s1;
+    }
+
+    public String getS2() {
+        return s2;
+    }
+
+    public void setS2(String s2) {
+        this.s2 = s2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SecondaryTestEntity)) return false;
+
+        SecondaryTestEntity that = (SecondaryTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (s1 != null ? !s1.equals(that.s1) : that.s1 != null) return false;
+        if (s2 != null ? !s2.equals(that.s2) : that.s2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (s1 != null ? s1.hashCode() : 0);
+        result = 31 * result + (s2 != null ? s2.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/EmbIdSecondary.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/EmbIdSecondary.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/EmbIdSecondary.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,70 @@
+package org.jboss.envers.test.integration.secondary.ids;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.ids.EmbId;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Join;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class EmbIdSecondary extends AbstractEntityTest {
+    private EmbId id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SecondaryEmbIdTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        id = new EmbId(1, 2);
+
+        SecondaryEmbIdTestEntity ste = new SecondaryEmbIdTestEntity(id, "a", "1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ste);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ste = em.find(SecondaryEmbIdTestEntity.class, ste.getId());
+        ste.setS1("b");
+        ste.setS2("2");
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SecondaryEmbIdTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        SecondaryEmbIdTestEntity ver1 = new SecondaryEmbIdTestEntity(id, "a", "1");
+        SecondaryEmbIdTestEntity ver2 = new SecondaryEmbIdTestEntity(id, "b", "2");
+
+        assert getVersionsReader().find(SecondaryEmbIdTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(SecondaryEmbIdTestEntity.class, id, 2).equals(ver2);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testTableNames() {
+        assert "sec_embid_versions".equals(((Iterator<Join>)
+                getCfg().getClassMapping("org.jboss.envers.test.integration.secondary.ids.SecondaryEmbIdTestEntity_versions")
+                        .getJoinIterator())
+                .next().getTable().getName());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/MulIdSecondary.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/MulIdSecondary.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/MulIdSecondary.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,70 @@
+package org.jboss.envers.test.integration.secondary.ids;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.test.entities.ids.MulId;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.mapping.Join;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MulIdSecondary extends AbstractEntityTest {
+    private MulId id;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SecondaryMulIdTestEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        id = new MulId(1, 2);
+
+        SecondaryMulIdTestEntity ste = new SecondaryMulIdTestEntity(id, "a", "1");
+
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+
+        em.persist(ste);
+
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+
+        ste = em.find(SecondaryMulIdTestEntity.class, id);
+        ste.setS1("b");
+        ste.setS2("2");
+
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SecondaryMulIdTestEntity.class, id));
+    }
+
+    @Test
+    public void testHistoryOfId() {
+        SecondaryMulIdTestEntity ver1 = new SecondaryMulIdTestEntity(id, "a", "1");
+        SecondaryMulIdTestEntity ver2 = new SecondaryMulIdTestEntity(id, "b", "2");
+
+        assert getVersionsReader().find(SecondaryMulIdTestEntity.class, id, 1).equals(ver1);
+        assert getVersionsReader().find(SecondaryMulIdTestEntity.class, id, 2).equals(ver2);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Test
+    public void testTableNames() {
+        assert "sec_mulid_versions".equals(((Iterator<Join>)
+                getCfg().getClassMapping("org.jboss.envers.test.integration.secondary.ids.SecondaryMulIdTestEntity_versions")
+                        .getJoinIterator())
+                .next().getTable().getName());
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/SecondaryEmbIdTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/SecondaryEmbIdTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/SecondaryEmbIdTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,83 @@
+package org.jboss.envers.test.integration.secondary.ids;
+
+import org.jboss.envers.SecondaryVersionsTable;
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.EmbId;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at SecondaryTable(name = "secondary")
+ at SecondaryVersionsTable(secondaryTableName = "secondary", secondaryVersionsTableName = "sec_embid_versions")
+ at Versioned
+public class SecondaryEmbIdTestEntity {
+    @Id
+    private EmbId id;
+
+    private String s1;
+
+    @Column(table = "secondary")
+    private String s2;
+
+    public SecondaryEmbIdTestEntity(EmbId id, String s1, String s2) {
+        this.id = id;
+        this.s1 = s1;
+        this.s2 = s2;
+    }
+
+    public SecondaryEmbIdTestEntity(String s1, String s2) {
+        this.s1 = s1;
+        this.s2 = s2;
+    }
+
+    public SecondaryEmbIdTestEntity() {
+    }
+
+    public EmbId getId() {
+        return id;
+    }
+
+    public void setId(EmbId id) {
+        this.id = id;
+    }
+
+    public String getS1() {
+        return s1;
+    }
+
+    public void setS1(String s1) {
+        this.s1 = s1;
+    }
+
+    public String getS2() {
+        return s2;
+    }
+
+    public void setS2(String s2) {
+        this.s2 = s2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SecondaryEmbIdTestEntity)) return false;
+
+        SecondaryEmbIdTestEntity that = (SecondaryEmbIdTestEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (s1 != null ? !s1.equals(that.s1) : that.s1 != null) return false;
+        if (s2 != null ? !s2.equals(that.s2) : that.s2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (s1 != null ? s1.hashCode() : 0);
+        result = 31 * result + (s2 != null ? s2.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/SecondaryMulIdTestEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/SecondaryMulIdTestEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/secondary/ids/SecondaryMulIdTestEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,98 @@
+package org.jboss.envers.test.integration.secondary.ids;
+
+import org.jboss.envers.SecondaryVersionsTable;
+import org.jboss.envers.Versioned;
+import org.jboss.envers.test.entities.ids.MulId;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at SecondaryTable(name = "secondary")
+ at SecondaryVersionsTable(secondaryTableName = "secondary", secondaryVersionsTableName = "sec_mulid_versions")
+ at Versioned
+ at IdClass(MulId.class)
+public class SecondaryMulIdTestEntity {
+    @Id
+    private Integer id1;
+
+    @Id
+    private Integer id2;
+
+    private String s1;
+
+    @Column(table = "secondary")
+    private String s2;
+
+    public SecondaryMulIdTestEntity(MulId id, String s1, String s2) {
+        this.id1 = id.getId1();
+        this.id2 = id.getId2();
+        this.s1 = s1;
+        this.s2 = s2;
+    }
+
+    public SecondaryMulIdTestEntity(String s1, String s2) {
+        this.s1 = s1;
+        this.s2 = s2;
+    }
+
+    public SecondaryMulIdTestEntity() {
+    }
+
+    public Integer getId1() {
+        return id1;
+    }
+
+    public void setId1(Integer id1) {
+        this.id1 = id1;
+    }
+
+    public Integer getId2() {
+        return id2;
+    }
+
+    public void setId2(Integer id2) {
+        this.id2 = id2;
+    }
+
+    public String getS1() {
+        return s1;
+    }
+
+    public void setS1(String s1) {
+        this.s1 = s1;
+    }
+
+    public String getS2() {
+        return s2;
+    }
+
+    public void setS2(String s2) {
+        this.s2 = s2;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SecondaryMulIdTestEntity)) return false;
+
+        SecondaryMulIdTestEntity that = (SecondaryMulIdTestEntity) o;
+
+        if (id1 != null ? !id1.equals(that.id1) : that.id1 != null) return false;
+        if (id2 != null ? !id2.equals(that.id2) : that.id2 != null) return false;
+        if (s1 != null ? !s1.equals(that.s1) : that.s1 != null) return false;
+        if (s2 != null ? !s2.equals(that.s2) : that.s2 != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id1 != null ? id1.hashCode() : 0);
+        result = 31 * result + (id2 != null ? id2.hashCode() : 0);
+        result = 31 * result + (s1 != null ? s1.hashCode() : 0);
+        result = 31 * result + (s2 != null ? s2.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/MappedSubclassing.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/MappedSubclassing.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/MappedSubclassing.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,51 @@
+package org.jboss.envers.test.integration.superclass;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MappedSubclassing extends AbstractEntityTest {
+    private Integer id1;
+
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(SubclassEntity.class);
+    }
+
+    @BeforeClass(dependsOnMethods = "init")
+    public void initData() {
+        // Revision 1
+        EntityManager em = getEntityManager();
+        em.getTransaction().begin();
+        SubclassEntity se1 = new SubclassEntity("x");
+        em.persist(se1);
+        id1 = se1.getId();
+        em.getTransaction().commit();
+
+        // Revision 2
+        em.getTransaction().begin();
+        se1 = em.find(SubclassEntity.class, id1);
+        se1.setStr("y");
+        em.getTransaction().commit();
+    }
+
+    @Test
+    public void testRevisionsCounts() {
+        assert Arrays.asList(1, 2).equals(getVersionsReader().getRevisions(SubclassEntity.class, id1));
+    }
+
+    @Test
+    public void testHistoryOfId1() {
+        SubclassEntity ver1 = new SubclassEntity(id1, "x");
+        SubclassEntity ver2 = new SubclassEntity(id1, "y");
+
+        assert getVersionsReader().find(SubclassEntity.class, id1, 1).equals(ver1);
+        assert getVersionsReader().find(SubclassEntity.class, id1, 2).equals(ver2);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/SubclassEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/SubclassEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/SubclassEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,53 @@
+package org.jboss.envers.test.integration.superclass;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SubclassEntity extends SuperclassOfEntity {
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    public SubclassEntity() {
+    }
+
+    public SubclassEntity(Integer id, String str) {
+        super(str);
+        this.id = id;
+    }
+
+    public SubclassEntity(String str) {
+        super(str);
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SubclassEntity)) return false;
+        if (!super.equals(o)) return false;
+
+        SubclassEntity that = (SubclassEntity) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (id != null ? id.hashCode() : 0);
+        return result;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/SuperclassOfEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/SuperclassOfEntity.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/integration/superclass/SuperclassOfEntity.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,44 @@
+package org.jboss.envers.test.integration.superclass;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.MappedSuperclass;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at MappedSuperclass
+public class SuperclassOfEntity {
+    @Versioned
+    private String str;
+
+    public SuperclassOfEntity() {
+    }
+
+    public SuperclassOfEntity(String str) {
+        this.str = str;
+    }
+
+    public String getStr() {
+        return str;
+    }
+
+    public void setStr(String str) {
+        this.str = str;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SuperclassOfEntity)) return false;
+
+        SuperclassOfEntity that = (SuperclassOfEntity) o;
+
+        if (str != null ? !str.equals(that.str) : that.str != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (str != null ? str.hashCode() : 0);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/performance/AbstractPerformanceTest.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/performance/AbstractPerformanceTest.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/performance/AbstractPerformanceTest.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,59 @@
+package org.jboss.envers.test.performance;
+
+import org.jboss.envers.test.AbstractEntityTest;
+import org.jboss.envers.tools.Pair;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractPerformanceTest extends AbstractEntityTest {
+    protected String getSecondsString(long milliseconds) {
+        return (milliseconds/1000) + "." + (milliseconds%1000);
+    }
+
+    protected long measureTime(Runnable r) {
+        long start = System.currentTimeMillis();
+        r.run();
+        return System.currentTimeMillis() - start;
+    }
+
+    protected abstract Pair<Long, Long> doTest();
+
+    protected abstract String getName();
+
+    private long totalUnversioned;
+    private long totalVersioned;
+
+    private void printResults(long unversioned, long versioned) {
+        System.out.println("Unversioned: " + getSecondsString(unversioned));
+        System.out.println("  Versioned: " + getSecondsString(versioned));
+        System.out.println("      Delta: " + getSecondsString(versioned-unversioned));
+        System.out.println("     Factor: " + (double)versioned/unversioned);
+    }
+
+    private void test(boolean count) {
+        Pair<Long, Long> result = doTest();
+        long unversioned = result.getFirst();
+        long versioned = result.getSecond();
+
+        totalUnversioned += unversioned;
+        totalVersioned += versioned;
+
+        printResults(unversioned, versioned);
+    }
+
+    public void run(int numberOfRuns) {
+        for (int i=0; i<=numberOfRuns; i++) {
+            System.out.println("");
+            System.out.println(getName() + " TEST, RUN " + i);
+            test(i != 0);
+        }
+
+        totalUnversioned /= numberOfRuns;
+        totalVersioned /= numberOfRuns;
+
+        System.out.println("");
+        System.out.println(getName() + " TEST, AVERAGE");
+        printResults(totalUnversioned, totalVersioned);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/performance/InsertsPerformance.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/performance/InsertsPerformance.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/performance/InsertsPerformance.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,56 @@
+package org.jboss.envers.test.performance;
+
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.entities.UnversionedStrTestEntity;
+import org.jboss.envers.tools.Pair;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class InsertsPerformance extends AbstractPerformanceTest {
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(UnversionedStrTestEntity.class);
+    }
+
+    private final static int NUMBER_INSERTS = 1000;
+    
+    private void insertUnversioned() {
+        EntityManager entityManager = getEntityManager();
+        for (int i=0; i<NUMBER_INSERTS; i++) {
+            entityManager.getTransaction().begin();
+            entityManager.persist(new UnversionedStrTestEntity("x" + i));
+            entityManager.getTransaction().commit();
+        }
+    }
+
+    private void insertVersioned() {
+        EntityManager entityManager = getEntityManager();
+        for (int i=0; i<NUMBER_INSERTS; i++) {
+            entityManager.getTransaction().begin();
+            entityManager.persist(new StrTestEntity("x" + i));
+            entityManager.getTransaction().commit();
+        }
+    }
+
+    protected Pair<Long, Long> doTest() {
+        long unversioned = measureTime(new Runnable() { public void run() { insertUnversioned(); } });
+        long versioned = measureTime(new Runnable() { public void run() { insertVersioned(); } });
+
+        return Pair.make(unversioned, versioned);
+    }
+
+    protected String getName() {
+        return "INSERTS";
+    }
+
+    public static void main(String[] args) throws IOException {
+        InsertsPerformance insertsPerformance = new InsertsPerformance();
+        insertsPerformance.init();
+        insertsPerformance.run(10);
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/performance/UpdatesPerformance.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/performance/UpdatesPerformance.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/performance/UpdatesPerformance.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,87 @@
+package org.jboss.envers.test.performance;
+
+import org.jboss.envers.test.entities.StrTestEntity;
+import org.jboss.envers.test.entities.UnversionedStrTestEntity;
+import org.jboss.envers.tools.Pair;
+import org.hibernate.ejb.Ejb3Configuration;
+
+import javax.persistence.EntityManager;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class UpdatesPerformance extends AbstractPerformanceTest {
+    public void configure(Ejb3Configuration cfg) {
+        cfg.addAnnotatedClass(StrTestEntity.class);
+        cfg.addAnnotatedClass(UnversionedStrTestEntity.class);
+    }
+
+    private final static int NUMBER_UPDATES = 1000;
+    private final static int NUMBER_ENTITIES = 10;
+
+    private Random random = new Random();
+
+    private List<Integer> unversioned_ids = new ArrayList<Integer>();
+    private List<Integer> versioned_ids = new ArrayList<Integer>();
+
+    public void setup() {
+        EntityManager entityManager = getEntityManager();
+        entityManager.getTransaction().begin();
+        for (int i=0; i<NUMBER_ENTITIES; i++) {
+            UnversionedStrTestEntity testEntity = new UnversionedStrTestEntity("x" + i);
+            entityManager.persist(testEntity);
+            unversioned_ids.add(testEntity.getId());
+        }
+
+        for (int i=0; i<NUMBER_ENTITIES; i++) {
+            StrTestEntity testEntity = new StrTestEntity("x" + i);
+            entityManager.persist(testEntity);
+            versioned_ids.add(testEntity.getId());
+        }
+        entityManager.getTransaction().commit();
+    }
+
+    private void updateUnversioned() {
+        EntityManager entityManager = getEntityManager();
+        for (int i=0; i<NUMBER_UPDATES; i++) {
+            entityManager.getTransaction().begin();
+            Integer id = unversioned_ids.get(random.nextInt(NUMBER_ENTITIES));
+            UnversionedStrTestEntity testEntity = entityManager.find(UnversionedStrTestEntity.class, id);
+            testEntity.setStr("z" + i);
+            entityManager.getTransaction().commit();
+        }
+    }
+
+    private void updateVersioned() {
+        EntityManager entityManager = getEntityManager();
+        for (int i=0; i<NUMBER_UPDATES; i++) {
+            entityManager.getTransaction().begin();
+            Integer id = versioned_ids.get(random.nextInt(NUMBER_ENTITIES));
+            StrTestEntity testEntity = entityManager.find(StrTestEntity.class, id);
+            testEntity.setStr("z" + i);
+            entityManager.getTransaction().commit();
+        }
+    }
+
+    protected Pair<Long, Long> doTest() {
+        long unversioned = measureTime(new Runnable() { public void run() { updateUnversioned(); } });
+        long versioned = measureTime(new Runnable() { public void run() { updateVersioned(); } });
+
+        return Pair.make(unversioned, versioned);
+    }
+
+    protected String getName() {
+        return "UPDATES";
+    }
+
+    public static void main(String[] args) throws IOException {
+        UpdatesPerformance updatesPerformance = new UpdatesPerformance();
+        updatesPerformance.init();
+        updatesPerformance.setup();
+        updatesPerformance.run(10);
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/tools/TestTools.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/tools/TestTools.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/tools/TestTools.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,47 @@
+package org.jboss.envers.test.tools;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class TestTools {
+    public static <T> Set<T> makeSet(T... objects) {
+        Set<T> ret = new HashSet<T>();
+        //noinspection ManualArrayToCollectionCopy
+        for (T o : objects) {
+            ret.add(o);
+        }
+
+        return ret;
+    }
+
+    public static <T> List<T> makeList(T... objects) {
+        return Arrays.asList(objects);
+    }
+
+    public static Map<Object, Object> makeMap(Object... objects) {
+        Map<Object, Object> ret = new HashMap<Object, Object>();
+        // The number of objects must be divisable by 2.
+        //noinspection ManualArrayToCollectionCopy
+        for (int i=0; i<objects.length; i+=2) {
+            ret.put(objects[i], objects[i+1]);
+        }
+
+        return ret;
+    }
+
+    public static <T> boolean checkList(List<T> list, T... objects) {
+        if (list.size() != objects.length) {
+            return false;
+        }
+
+        for (T obj : objects) {
+            if (!list.contains(obj)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/various/Address.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/various/Address.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/various/Address.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,113 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.test.various;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+import java.util.Set;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class Address {
+    @Id
+    @GeneratedValue
+    private int id;
+
+    @Versioned
+    private String streetName;
+
+    @Versioned
+    private Integer houseNumber;
+
+    @Versioned
+    private Integer flatNumber;
+
+    @Versioned
+    @OneToMany(mappedBy = "address")
+    private Set<Person> persons;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getStreetName() {
+        return streetName;
+    }
+
+    public void setStreetName(String streetName) {
+        this.streetName = streetName;
+    }
+
+    public Integer getHouseNumber() {
+        return houseNumber;
+    }
+
+    public void setHouseNumber(Integer houseNumber) {
+        this.houseNumber = houseNumber;
+    }
+
+    public Integer getFlatNumber() {
+        return flatNumber;
+    }
+
+    public void setFlatNumber(Integer flatNumber) {
+        this.flatNumber = flatNumber;
+    }
+
+    public Set<Person> getPersons() {
+        return persons;
+    }
+
+    public void setPersons(Set<Person> persons) {
+        this.persons = persons;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Address)) return false;
+
+        Address address = (Address) o;
+
+        if (id != address.id) return false;
+        if (flatNumber != null ? !flatNumber.equals(address.flatNumber) : address.flatNumber != null) return false;
+        if (houseNumber != null ? !houseNumber.equals(address.houseNumber) : address.houseNumber != null) return false;
+        if (streetName != null ? !streetName.equals(address.streetName) : address.streetName != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = id;
+        result = 31 * result + (streetName != null ? streetName.hashCode() : 0);
+        result = 31 * result + (houseNumber != null ? houseNumber.hashCode() : 0);
+        result = 31 * result + (flatNumber != null ? flatNumber.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/various/HsqlTest.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/various/HsqlTest.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/various/HsqlTest.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,129 @@
+package org.jboss.envers.test.various;
+
+import org.hibernate.Session;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.persistence.EntityManager;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class HsqlTest {
+    /*
+    Query generated by Hibernate from an old demo, when criteria was used:  (works)
+
+    select
+        this_.id as id2_0_,
+        this_._revision as column2_2_0_,
+        this_._revision_type as column3_2_0_,
+        this_.name as name2_0_,
+        this_.surname as surname2_0_,
+        this_.address_id as address6_2_0_
+    from
+        Person_versions this_
+    where
+        this_._revision = (
+            select
+                max(e2_._revision) as y0_
+            from
+                Person_versions e2_
+            where
+                e2_._revision<=1
+                and this_.id=e2_.id
+        )
+        and this_.id=1
+
+    Query generated by Hibernate from a new demo, when query generator is used:
+    (throws Column not found: ID in statement because of "person_ver0_.id=person_ver1_.id")    
+
+    select
+        person_ver0_.id as id3_,
+        person_ver0_._revision as column2_3_,
+        person_ver0_._revision_type as column3_3_,
+        person_ver0_.name as name3_,
+        person_ver0_.surname as surname3_,
+        person_ver0_.address_id as address6_3_
+    from
+        Person_versions person_ver0_
+    where
+        person_ver0_._revision_type<>2
+        and person_ver0_._revision=(
+            select
+                max(person_ver1_._revision)
+            from
+                Person_versions person_ver1_
+            where
+                person_ver1_._revision<=1
+                and person_ver0_.id=person_ver1_.id
+        )
+        and person_ver0_.id=1
+
+        Both queries work from HSQL console
+        (to run: java -cp hsqldb.jar org.hsqldb.util.DatabaseManager -user sa -url jdbc:hsqldb:file:/tmp/_versions_demo.db)
+
+        TODO: post hibernate bug
+     */
+
+    public static void main(String[] argv) {
+        Map<String, String> configurationOverrides = new HashMap<String, String>();
+        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ConsolePU", configurationOverrides);
+        EntityManager entityManager = emf.createEntityManager();
+
+        populateTestData(entityManager);
+
+        entityManager.getTransaction().begin();
+
+        Session sesion = (Session) entityManager.getDelegate();
+        System.out.println(sesion.createQuery(
+                "select e from org.jboss.envers.demo.Person_versions e " +
+                        "where " +
+                        "e.originalId._revision.id =" +
+                        "(select max(e2.originalId._revision.id) " +
+                        "from org.jboss.envers.demo.Person_versions e2 " +
+                        "where e.originalId.id = :p0) ")
+                .setParameter("p0", 1)
+                .list());
+
+        entityManager.getTransaction().commit();
+
+        entityManager.close();
+        emf.close();
+    }
+
+    private static void populateTestData(EntityManager entityManager) {
+        entityManager.getTransaction().begin();
+
+        if (!hasData(entityManager)) {
+            Person p1 = new Person();
+
+            Address a1 = new Address();
+
+            p1.setName("James");
+            p1.setSurname("Bond");
+            p1.setAddress(a1);
+
+            a1.setStreetName("MI6");
+            a1.setHouseNumber(18);
+            a1.setFlatNumber(25);
+            a1.setPersons(new HashSet<Person>());
+            a1.getPersons().add(p1);
+
+            entityManager.persist(a1);
+
+            entityManager.persist(p1);
+
+            System.out.println("The DB was populated with example data.");
+        }
+
+        entityManager.getTransaction().commit();
+    }
+
+    private static boolean hasData(EntityManager entityManager) {
+        return (((Long) entityManager.createQuery("select count(a) from Address a").getSingleResult()) +
+                ((Long) entityManager.createQuery("select count(p) from Person p").getSingleResult())) > 0;
+    }
+}

Added: core/trunk/envers/src/test/java/org/jboss/envers/test/various/Person.java
===================================================================
--- core/trunk/envers/src/test/java/org/jboss/envers/test/various/Person.java	                        (rev 0)
+++ core/trunk/envers/src/test/java/org/jboss/envers/test/various/Person.java	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,99 @@
+/*
+ * Envers. http://www.jboss.org/envers
+ *
+ * Copyright 2008  Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License, v. 2.1.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT A WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License, v.2.1 along with this distribution; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Red Hat Author(s): Adam Warski
+ */
+package org.jboss.envers.test.various;
+
+import org.jboss.envers.Versioned;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class Person {
+    @Id
+    @GeneratedValue
+    private int id;
+
+    @Versioned
+    private String name;
+
+    @Versioned
+    private String surname;
+
+    @Versioned
+    @ManyToOne
+    private Address address;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSurname() {
+        return surname;
+    }
+
+    public void setSurname(String surname) {
+        this.surname = surname;
+    }
+
+    public Address getAddress() {
+        return address;
+    }
+
+    public void setAddress(Address address) {
+        this.address = address;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Person)) return false;
+
+        Person person = (Person) o;
+
+        if (id != person.id) return false;
+        if (name != null ? !name.equals(person.name) : person.name != null) return false;
+        if (surname != null ? !surname.equals(person.surname) : person.surname != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = id;
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (surname != null ? surname.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

Added: core/trunk/envers/src/test/resources/hibernate.test.cfg.xml
===================================================================
--- core/trunk/envers/src/test/resources/hibernate.test.cfg.xml	                        (rev 0)
+++ core/trunk/envers/src/test/resources/hibernate.test.cfg.xml	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,45 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE hibernate-configuration PUBLIC
+        "-//Hibernate/Hibernate Configuration DTD//EN"
+        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
+
+<!--suppress HibernateConfigDomInspection -->
+<hibernate-configuration>
+    <session-factory>
+        <property name="hbm2ddl.auto">create-drop</property>
+
+        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
+        <property name="connection.url">jdbc:mysql:///hibernate_tests?useUnicode=true&amp;characterEncoding=UTF-8</property>
+        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
+        <property name="connection.username">root</property>
+        <property name="connection.password"></property>
+
+        <!--<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
+        <property name="connection.url">jdbc:postgresql://localhost/hibernate_tests</property>
+        <property name="connection.driver_class">org.postgresql.Driver</property>
+        <property name="connection.username">postgres</property>
+        <property name="connection.password"></property>-->
+
+        <property name="show_sql">false</property>
+        <property name="format_sql">true</property>
+
+        <event type="post-insert">
+            <listener class="org.jboss.envers.event.VersionsEventListener" />
+        </event>
+        <event type="post-update">
+            <listener class="org.jboss.envers.event.VersionsEventListener" />
+        </event>
+        <event type="post-delete">
+            <listener class="org.jboss.envers.event.VersionsEventListener" />
+        </event>
+        <event type="pre-collection-update">
+            <listener class="org.jboss.envers.event.VersionsEventListener" />
+        </event>
+        <event type="pre-collection-remove">
+            <listener class="org.jboss.envers.event.VersionsEventListener" />
+        </event>
+        <event type="post-collection-recreate">
+            <listener class="org.jboss.envers.event.VersionsEventListener" />
+        </event>
+    </session-factory>
+</hibernate-configuration>
\ No newline at end of file

Added: core/trunk/envers/src/test/resources/testng.xml
===================================================================
--- core/trunk/envers/src/test/resources/testng.xml	                        (rev 0)
+++ core/trunk/envers/src/test/resources/testng.xml	2008-10-27 18:56:31 UTC (rev 15406)
@@ -0,0 +1,40 @@
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
+
+<suite name="Envers">
+    <test name="All">
+        <packages>
+            <package name="org.jboss.envers.test.integration.basic" />
+            <package name="org.jboss.envers.test.integration.cache" />
+            <package name="org.jboss.envers.test.integration.collection" />
+            <package name="org.jboss.envers.test.integration.collection.mapkey" /> 
+            <package name="org.jboss.envers.test.integration.components" />
+            <package name="org.jboss.envers.test.integration.customtype" />
+            <package name="org.jboss.envers.test.integration.data" />
+            <package name="org.jboss.envers.test.integration.flush" />
+            <package name="org.jboss.envers.test.integration.ids" />
+            <package name="org.jboss.envers.test.integration.inheritance.single" />
+            <package name="org.jboss.envers.test.integration.inheritance.single.childrelation" />
+            <package name="org.jboss.envers.test.integration.inheritance.single.relation" />
+            <package name="org.jboss.envers.test.integration.manytomany" />
+            <package name="org.jboss.envers.test.integration.manytomany.ternary" />
+            <package name="org.jboss.envers.test.integration.manytomany.unidirectional" />
+            <package name="org.jboss.envers.test.integration.naming" />
+            <package name="org.jboss.envers.test.integration.naming.ids" />
+            <package name="org.jboss.envers.test.integration.notinsertable" />
+            <package name="org.jboss.envers.test.integration.onetomany" />
+            <package name="org.jboss.envers.test.integration.onetomany.detached" />
+            <package name="org.jboss.envers.test.integration.onetoone.bidirectional" />
+            <package name="org.jboss.envers.test.integration.onetoone.bidirectional.ids" />
+            <package name="org.jboss.envers.test.integration.onetoone.unidirectional" />
+            <package name="org.jboss.envers.test.integration.properties" />
+            <package name="org.jboss.envers.test.integration.query" />
+            <package name="org.jboss.envers.test.integration.query.ids" />
+            <package name="org.jboss.envers.test.integration.reventity" />
+            <package name="org.jboss.envers.test.integration.revfordate" />
+            <package name="org.jboss.envers.test.integration.sameids" />
+            <package name="org.jboss.envers.test.integration.secondary" />
+            <package name="org.jboss.envers.test.integration.secondary.ids" />
+            <package name="org.jboss.envers.test.integration.superclass" />
+        </packages>
+    </test>
+</suite>
\ No newline at end of file

Modified: core/trunk/pom.xml
===================================================================
--- core/trunk/pom.xml	2008-10-27 14:49:08 UTC (rev 15405)
+++ core/trunk/pom.xml	2008-10-27 18:56:31 UTC (rev 15406)
@@ -53,6 +53,7 @@
         <module>cache-swarmcache</module>
         <module>connection-c3p0</module>
         <module>connection-proxool</module>
+        <module>envers</module>
         <module>jmx</module>
         <module>testing</module>
         <module>testsuite</module>
@@ -93,4 +94,4 @@
         </profile>
     </profiles>
 
-</project>
\ No newline at end of file
+</project>




More information about the hibernate-commits mailing list