/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* 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, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY 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
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.configuration.internal.metadata;
import java.util.Iterator;
import org.hibernate.MappingException;
import org.hibernate.envers.ModificationStore;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.internal.entities.IdMappingData;
import org.hibernate.envers.internal.entities.PropertyData;
import org.hibernate.envers.internal.entities.mapper.SimpleMapperBuilder;
import org.hibernate.envers.internal.entities.mapper.id.EmbeddedIdMapper;
import org.hibernate.envers.internal.entities.mapper.id.MultipleIdMapper;
import org.hibernate.envers.internal.entities.mapper.id.SimpleIdMapperBuilder;
import org.hibernate.envers.internal.entities.mapper.id.SingleIdMapper;
import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.Type;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
/**
* Generates metadata for primary identifiers (ids) of versions entities.
*
* @author Adam Warski (adam at warski dot org)
*/
public final class IdMetadataGenerator {
private final AuditMetadataGenerator mainGenerator;
IdMetadataGenerator(AuditMetadataGenerator auditMetadataGenerator) {
mainGenerator = auditMetadataGenerator;
}
@SuppressWarnings({"unchecked"})
private boolean addIdProperties(
Element parent,
Iterator<Property> properties,
SimpleMapperBuilder mapper,
boolean key,
boolean audited) {
while ( properties.hasNext() ) {
final Property property = properties.next();
final Type propertyType = property.getType();
if ( !"_identifierMapper".equals( property.getName() ) ) {
boolean added = false;
if ( propertyType instanceof ManyToOneType ) {
added = mainGenerator.getBasicMetadataGenerator().addManyToOne(
parent,
getIdPersistentPropertyAuditingData( property ),
property.getValue(),
mapper
);
}
else {
// Last but one parameter: ids are always insertable
added = mainGenerator.getBasicMetadataGenerator().addBasic(
parent,
getIdPersistentPropertyAuditingData( property ),
property.getValue(),
mapper,
true,
key
);
}
if ( !added ) {
// If the entity is audited, and a non-supported id component is used, throwing an exception.
// If the entity is not audited, then we simply don't support this entity, even in
// target relation mode not audited.
if ( audited ) {
throw new MappingException( "Type not supported: " + propertyType.getClass().getName() );
}
else {
return false;
}
}
}
}
return true;
}
@SuppressWarnings({"unchecked"})
IdMappingData addId(PersistentClass pc, boolean audited) {
// Xml mapping which will be used for relations
final Element relIdMapping = new DefaultElement( "properties" );
// Xml mapping which will be used for the primary key of the versions table
final Element origIdMapping = new DefaultElement( "composite-id" );
final Property idProp = pc.getIdentifierProperty();
final Component idMapper = pc.getIdentifierMapper();
// Checking if the id mapping is supported
if ( idMapper == null && idProp == null ) {
return null;
}
SimpleIdMapperBuilder mapper;
if ( idMapper != null ) {
// Multiple id
final Class componentClass = ReflectionTools.loadClass(
( (Component) pc.getIdentifier() ).getComponentClassName(),
mainGenerator.getClassLoaderService()
);
mapper = new MultipleIdMapper( componentClass );
if ( !addIdProperties(
relIdMapping,
(Iterator<Property>) idMapper.getPropertyIterator(),
mapper,
false,
audited
) ) {
return null;
}
// null mapper - the mapping where already added the first time, now we only want to generate the xml
if ( !addIdProperties(
origIdMapping,
(Iterator<Property>) idMapper.getPropertyIterator(),
null,
true,
audited
) ) {
return null;
}
}
else if ( idProp.isComposite() ) {
// Embedded id
final Component idComponent = (Component) idProp.getValue();
final Class embeddableClass = ReflectionTools.loadClass(
idComponent.getComponentClassName(),
mainGenerator.getClassLoaderService()
);
mapper = new EmbeddedIdMapper( getIdPropertyData( idProp ), embeddableClass );
if ( !addIdProperties(
relIdMapping,
(Iterator<Property>) idComponent.getPropertyIterator(),
mapper,
false,
audited
) ) {
return null;
}
// null mapper - the mapping where already added the first time, now we only want to generate the xml
if ( !addIdProperties(
origIdMapping,
(Iterator<Property>) idComponent.getPropertyIterator(),
null,
true,
audited
) ) {
return null;
}
}
else {
// Single id
mapper = new SingleIdMapper();
// Last but one parameter: ids are always insertable
mainGenerator.getBasicMetadataGenerator().addBasic(
relIdMapping,
getIdPersistentPropertyAuditingData( idProp ),
idProp.getValue(),
mapper,
true,
false
);
// null mapper - the mapping where already added the first time, now we only want to generate the xml
mainGenerator.getBasicMetadataGenerator().addBasic(
origIdMapping,
getIdPersistentPropertyAuditingData( idProp ),
idProp.getValue(),
null,
true,
true
);
}
origIdMapping.addAttribute( "name", mainGenerator.getVerEntCfg().getOriginalIdPropName() );
// Adding a relation to the revision entity (effectively: the "revision number" property)
mainGenerator.addRevisionInfoRelation( origIdMapping );
return new IdMappingData( mapper, origIdMapping, relIdMapping );
}
private PropertyData getIdPropertyData(Property property) {
return new PropertyData(
property.getName(), property.getName(), property.getPropertyAccessorName(),
ModificationStore.FULL
);
}
private PropertyAuditingData getIdPersistentPropertyAuditingData(Property property) {
return new PropertyAuditingData(
property.getName(), property.getPropertyAccessorName(),
ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null, false
);
}
}