package org.hibernate.mapping;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Objects;
import java.util.Map;
import org.hibernate.EntityMode;
import org.hibernate.MappingException;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.ExportableProducer;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.CompositeNestedGeneratedValueGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.util.collections.JoinedIterator;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
public class Component extends SimpleValue implements MetaAttributable {
private ArrayList<Property> properties = new ArrayList<>();
private String componentClassName;
private boolean embedded;
private String parentProperty;
private PersistentClass owner;
private boolean dynamic;
private Map metaAttributes;
private boolean isKey;
private String roleName;
private java.util.Map<EntityMode,String> tuplizerImpls;
@Deprecated
public Component(MetadataImplementor metadata, PersistentClass owner) throws MappingException {
this( metadata, owner.getTable(), owner );
}
@Deprecated
public Component(MetadataImplementor metadata, Component component) throws MappingException {
this( metadata, component.getTable(), component.getOwner() );
}
@Deprecated
public Component(MetadataImplementor metadata, Join join) throws MappingException {
this( metadata, join.getTable(), join.getPersistentClass() );
}
@Deprecated
public Component(MetadataImplementor metadata, Collection collection) throws MappingException {
this( metadata, collection.getCollectionTable(), collection.getOwner() );
}
@Deprecated
public Component(MetadataImplementor metadata, Table table, PersistentClass owner) throws MappingException {
super( metadata, table );
this.owner = owner;
}
public Component(MetadataBuildingContext metadata, PersistentClass owner) throws MappingException {
this( metadata, owner.getTable(), owner );
}
public Component(MetadataBuildingContext metadata, Component component) throws MappingException {
this( metadata, component.getTable(), component.getOwner() );
}
public Component(MetadataBuildingContext metadata, Join join) throws MappingException {
this( metadata, join.getTable(), join.getPersistentClass() );
}
public Component(MetadataBuildingContext metadata, Collection collection) throws MappingException {
this( metadata, collection.getCollectionTable(), collection.getOwner() );
}
public Component(MetadataBuildingContext metadata, Table table, PersistentClass owner) throws MappingException {
super( metadata, table );
this.owner = owner;
}
public int getPropertySpan() {
return properties.size();
}
public Iterator getPropertyIterator() {
return properties.iterator();
}
public void addProperty(Property p) {
properties.add( p );
}
@Override
public void addColumn(Column column) {
throw new UnsupportedOperationException("Cant add a column to a component");
}
@Override
public int getColumnSpan() {
int n=0;
Iterator iter = getPropertyIterator();
while ( iter.hasNext() ) {
Property p = (Property) iter.next();
n+= p.getColumnSpan();
}
return n;
}
@Override
@SuppressWarnings("unchecked")
public Iterator<Selectable> getColumnIterator() {
Iterator[] iters = new Iterator[ getPropertySpan() ];
Iterator iter = getPropertyIterator();
int i=0;
while ( iter.hasNext() ) {
iters[i++] = ( (Property) iter.next() ).getColumnIterator();
}
return new JoinedIterator( iters );
}
public boolean isEmbedded() {
return embedded;
}
public String getComponentClassName() {
return componentClassName;
}
public Class getComponentClass() throws MappingException {
final ClassLoaderService classLoaderService = getMetadata()
.getMetadataBuildingOptions()
.getServiceRegistry()
.getService( ClassLoaderService.class );
try {
return classLoaderService.classForName( componentClassName );
}
catch (ClassLoadingException e) {
throw new MappingException("component class not found: " + componentClassName, e);
}
}
public PersistentClass getOwner() {
return owner;
}
public String getParentProperty() {
return parentProperty;
}
public void setComponentClassName(String componentClass) {
this.componentClassName = componentClass;
}
public void setEmbedded(boolean embedded) {
this.embedded = embedded;
}
public void setOwner(PersistentClass owner) {
this.owner = owner;
}
public void setParentProperty(String parentProperty) {
this.parentProperty = parentProperty;
}
public boolean isDynamic() {
return dynamic;
}
public void setDynamic(boolean dynamic) {
this.dynamic = dynamic;
}
@Override
public Type getType() throws MappingException {
final ComponentMetamodel metamodel = new ComponentMetamodel(
this,
getMetadata().getMetadataBuildingOptions()
);
final TypeFactory factory = getMetadata().getTypeConfiguration().getTypeResolver().getTypeFactory();
return isEmbedded() ? factory.embeddedComponent( metamodel ) : factory.component( metamodel );
}
@Override
public void setTypeUsingReflection(String className, String propertyName)
throws MappingException {
}
@Override
public java.util.Map getMetaAttributes() {
return metaAttributes;
}
@Override
public MetaAttribute getMetaAttribute(String attributeName) {
return metaAttributes==null?null:(MetaAttribute) metaAttributes.get(attributeName);
}
@Override
public void setMetaAttributes(java.util.Map metas) {
this.metaAttributes = metas;
}
@Override
public Object accept(ValueVisitor visitor) {
return visitor.accept(this);
}
@Override
public boolean isSame(SimpleValue other) {
return other instanceof Component && isSame( (Component) other );
}
public boolean isSame(Component other) {
return super.isSame( other )
&& Objects.equals( properties, other.properties )
&& Objects.equals( componentClassName, other.componentClassName )
&& embedded == other.embedded
&& Objects.equals( parentProperty, other.parentProperty )
&& Objects.equals( metaAttributes, other.metaAttributes );
}
@Override
public boolean[] getColumnInsertability() {
boolean[] result = new boolean[ getColumnSpan() ];
Iterator iter = getPropertyIterator();
int i=0;
while ( iter.hasNext() ) {
Property prop = (Property) iter.next();
boolean[] chunk = prop.getValue().getColumnInsertability();
if ( prop.isInsertable() ) {
System.arraycopy(chunk, 0, result, i, chunk.length);
}
i+=chunk.length;
}
return result;
}
@Override
public boolean[] getColumnUpdateability() {
boolean[] result = new boolean[ getColumnSpan() ];
Iterator iter = getPropertyIterator();
int i=0;
while ( iter.hasNext() ) {
Property prop = (Property) iter.next();
boolean[] chunk = prop.getValue().getColumnUpdateability();
if ( prop.isUpdateable() ) {
System.arraycopy(chunk, 0, result, i, chunk.length);
}
i+=chunk.length;
}
return result;
}
public boolean isKey() {
return isKey;
}
public void setKey(boolean isKey) {
this.isKey = isKey;
}
public boolean hasPojoRepresentation() {
return componentClassName!=null;
}
public void addTuplizer(EntityMode entityMode, String implClassName) {
if ( tuplizerImpls == null ) {
tuplizerImpls = new HashMap<>();
}
tuplizerImpls.put( entityMode, implClassName );
}
public String getTuplizerImplClassName(EntityMode mode) {
if ( tuplizerImpls == null ) {
return null;
}
return tuplizerImpls.get( mode );
}
@SuppressWarnings("UnusedDeclaration")
public Map getTuplizerMap() {
if ( tuplizerImpls == null ) {
return null;
}
return java.util.Collections.unmodifiableMap( tuplizerImpls );
}
public Property getProperty(String propertyName) throws MappingException {
Iterator iter = getPropertyIterator();
while ( iter.hasNext() ) {
Property prop = (Property) iter.next();
if ( prop.getName().equals(propertyName) ) {
return prop;
}
}
throw new MappingException("component property not found: " + propertyName);
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
@Override
public String toString() {
return getClass().getName() + '(' + properties.toString() + ')';
}
private IdentifierGenerator builtIdentifierGenerator;
@Override
public IdentifierGenerator createIdentifierGenerator(
IdentifierGeneratorFactory identifierGeneratorFactory,
Dialect dialect,
String defaultCatalog,
String defaultSchema,
RootClass rootClass) throws MappingException {
if ( builtIdentifierGenerator == null ) {
builtIdentifierGenerator = buildIdentifierGenerator(
identifierGeneratorFactory,
dialect,
defaultCatalog,
defaultSchema,
rootClass
);
}
return builtIdentifierGenerator;
}
private IdentifierGenerator buildIdentifierGenerator(
IdentifierGeneratorFactory identifierGeneratorFactory,
Dialect dialect,
String defaultCatalog,
String defaultSchema,
RootClass rootClass) throws MappingException {
final boolean hasCustomGenerator = ! DEFAULT_ID_GEN_STRATEGY.equals( getIdentifierGeneratorStrategy() );
if ( hasCustomGenerator ) {
return super.createIdentifierGenerator(
identifierGeneratorFactory, dialect, defaultCatalog, defaultSchema, rootClass
);
}
final Class entityClass = rootClass.getMappedClass();
final Class attributeDeclarer;
CompositeNestedGeneratedValueGenerator.GenerationContextLocator locator;
if ( rootClass.getIdentifierMapper() != null ) {
attributeDeclarer = resolveComponentClass();
}
else if ( rootClass.getIdentifierProperty() != null ) {
attributeDeclarer = resolveComponentClass();
}
else {
attributeDeclarer = entityClass;
}
locator = new StandardGenerationContextLocator( rootClass.getEntityName() );
final CompositeNestedGeneratedValueGenerator generator = new CompositeNestedGeneratedValueGenerator( locator );
Iterator itr = getPropertyIterator();
while ( itr.hasNext() ) {
final Property property = (Property) itr.next();
if ( property.getValue().isSimpleValue() ) {
final SimpleValue value = (SimpleValue) property.getValue();
if ( DEFAULT_ID_GEN_STRATEGY.equals( value.getIdentifierGeneratorStrategy() ) ) {
continue;
}
final IdentifierGenerator valueGenerator = value.createIdentifierGenerator(
identifierGeneratorFactory,
dialect,
defaultCatalog,
defaultSchema,
rootClass
);
generator.addGeneratedValuePlan(
new ValueGenerationPlan(
valueGenerator,
injector( property, attributeDeclarer )
)
);
}
}
return generator;
}
private Setter injector(Property property, Class attributeDeclarer) {
return property.getPropertyAccessStrategy( attributeDeclarer )
.buildPropertyAccess( attributeDeclarer, property.getName() )
.getSetter();
}
private Class resolveComponentClass() {
try {
return getComponentClass();
}
catch ( Exception e ) {
return null;
}
}
public static class StandardGenerationContextLocator
implements CompositeNestedGeneratedValueGenerator.GenerationContextLocator {
private final String entityName;
public StandardGenerationContextLocator(String entityName) {
this.entityName = entityName;
}
@Override
public Serializable locateGenerationContext(SharedSessionContractImplementor session, Object incomingObject) {
return session.getEntityPersister( entityName, incomingObject ).getIdentifier( incomingObject, session );
}
}
public static class ValueGenerationPlan implements CompositeNestedGeneratedValueGenerator.GenerationPlan {
private final IdentifierGenerator subGenerator;
private final Setter injector;
public ValueGenerationPlan(
IdentifierGenerator subGenerator,
Setter injector) {
this.subGenerator = subGenerator;
this.injector = injector;
}
@Override
public void execute(SharedSessionContractImplementor session, Object incomingObject, Object injectionContext) {
final Object generatedValue = subGenerator.generate( session, incomingObject );
injector.set( injectionContext, generatedValue, session.getFactory() );
}
@Override
public void registerExportables(Database database) {
if ( ExportableProducer.class.isInstance( subGenerator ) ) {
( (ExportableProducer) subGenerator ).registerExportables( database );
}
}
}
}