mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-10 13:14:50 +00:00
HHH-7052 : Bind PluralAttributeKeyBinding
This commit is contained in:
parent
eae715584b
commit
b0f5c9758f
@ -26,8 +26,11 @@
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -58,12 +61,14 @@
|
||||
import org.hibernate.metamodel.spi.binding.SimpleValueBinding;
|
||||
import org.hibernate.metamodel.spi.binding.SingularAttributeBinding;
|
||||
import org.hibernate.metamodel.spi.binding.TypeDefinition;
|
||||
import org.hibernate.metamodel.spi.domain.Attribute;
|
||||
import org.hibernate.metamodel.spi.domain.Component;
|
||||
import org.hibernate.metamodel.spi.domain.Entity;
|
||||
import org.hibernate.metamodel.spi.domain.PluralAttribute;
|
||||
import org.hibernate.metamodel.spi.domain.SingularAttribute;
|
||||
import org.hibernate.metamodel.spi.relational.Column;
|
||||
import org.hibernate.metamodel.spi.relational.DerivedValue;
|
||||
import org.hibernate.metamodel.spi.relational.ForeignKey;
|
||||
import org.hibernate.metamodel.spi.relational.Identifier;
|
||||
import org.hibernate.metamodel.spi.relational.Schema;
|
||||
import org.hibernate.metamodel.spi.relational.SimpleValue;
|
||||
@ -179,7 +184,8 @@ private EntityBinding doCreateEntityBinding(EntitySource entitySource, EntityBin
|
||||
final EntityBinding entityBinding = createBasicEntityBinding( entitySource, superEntityBinding );
|
||||
|
||||
bindSecondaryTables( entitySource, entityBinding );
|
||||
bindAttributes( entitySource, entityBinding );
|
||||
Deque<TableSpecification> tableStack = new ArrayDeque<TableSpecification>( );
|
||||
bindAttributes( entitySource, entityBinding, tableStack );
|
||||
|
||||
bindTableUniqueConstraints( entitySource, entityBinding );
|
||||
|
||||
@ -359,7 +365,9 @@ private void bindIdentifier(RootEntitySource entitySource, EntityBinding entityB
|
||||
}
|
||||
}
|
||||
|
||||
private void bindSimpleIdentifier(SimpleIdentifierSource identifierSource, EntityBinding entityBinding) {
|
||||
private void bindSimpleIdentifier(
|
||||
SimpleIdentifierSource identifierSource,
|
||||
EntityBinding entityBinding) {
|
||||
final BasicAttributeBinding idAttributeBinding = doBasicSingularAttributeBindingCreation(
|
||||
identifierSource.getIdentifierAttributeSource(), entityBinding
|
||||
);
|
||||
@ -439,7 +447,10 @@ private void bindDiscriminatorValue(SubclassEntitySource entitySource, EntityBin
|
||||
entityBinding.setDiscriminatorMatchValue( discriminatorValue );
|
||||
}
|
||||
|
||||
private void bindAttributes(AttributeSourceContainer attributeSourceContainer, AttributeBindingContainer attributeBindingContainer) {
|
||||
private void bindAttributes(
|
||||
AttributeSourceContainer attributeSourceContainer,
|
||||
AttributeBindingContainer attributeBindingContainer,
|
||||
Deque<TableSpecification> tableStack) {
|
||||
// todo : we really need the notion of a Stack here for the table from which the columns come for binding these attributes.
|
||||
// todo : adding the concept (interface) of a source of attribute metadata would allow reuse of this method for entity, component, unique-key, etc
|
||||
// for now, simply assume all columns come from the base table....
|
||||
@ -448,19 +459,22 @@ private void bindAttributes(AttributeSourceContainer attributeSourceContainer, A
|
||||
if ( attributeSource.isSingular() ) {
|
||||
final SingularAttributeSource singularAttributeSource = (SingularAttributeSource) attributeSource;
|
||||
if ( singularAttributeSource.getNature() == SingularAttributeNature.COMPONENT ) {
|
||||
bindComponent( (ComponentAttributeSource) singularAttributeSource, attributeBindingContainer );
|
||||
bindComponent( (ComponentAttributeSource) singularAttributeSource, attributeBindingContainer, tableStack );
|
||||
}
|
||||
else {
|
||||
doBasicSingularAttributeBindingCreation( singularAttributeSource, attributeBindingContainer );
|
||||
}
|
||||
}
|
||||
else {
|
||||
bindPersistentCollection( (PluralAttributeSource) attributeSource, attributeBindingContainer );
|
||||
bindPersistentCollection( (PluralAttributeSource) attributeSource, attributeBindingContainer, tableStack );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bindComponent(ComponentAttributeSource attributeSource, AttributeBindingContainer container) {
|
||||
private void bindComponent(
|
||||
ComponentAttributeSource attributeSource,
|
||||
AttributeBindingContainer container,
|
||||
Deque<TableSpecification> tableStack) {
|
||||
final String attributeName = attributeSource.getName();
|
||||
SingularAttribute attribute = container.getAttributeContainer().locateComponentAttribute( attributeName );
|
||||
if ( attribute == null ) {
|
||||
@ -485,10 +499,13 @@ private void bindComponent(ComponentAttributeSource attributeSource, AttributeBi
|
||||
buildMetaAttributeContext( attributeSource.metaAttributes(), container.getMetaAttributeContext() )
|
||||
);
|
||||
|
||||
bindAttributes( attributeSource, componentAttributeBinding );
|
||||
bindAttributes( attributeSource, componentAttributeBinding, tableStack );
|
||||
}
|
||||
|
||||
private void bindPersistentCollection(PluralAttributeSource attributeSource, AttributeBindingContainer attributeBindingContainer) {
|
||||
private void bindPersistentCollection(
|
||||
PluralAttributeSource attributeSource,
|
||||
AttributeBindingContainer attributeBindingContainer,
|
||||
Deque<TableSpecification> tableStack) {
|
||||
final PluralAttribute existingAttribute = attributeBindingContainer.getAttributeContainer()
|
||||
.locatePluralAttribute( attributeSource.getName() );
|
||||
final AbstractPluralAttributeBinding pluralAttributeBinding;
|
||||
@ -521,7 +538,7 @@ else if ( attributeSource.getPluralAttributeNature() == PluralAttributeNature.SE
|
||||
bindCollectionTable( attributeSource, pluralAttributeBinding );
|
||||
bindSortingAndOrdering( attributeSource, pluralAttributeBinding );
|
||||
|
||||
bindCollectionKey( attributeSource, pluralAttributeBinding );
|
||||
bindCollectionKey( attributeSource, pluralAttributeBinding, tableStack );
|
||||
bindCollectionElement( attributeSource, pluralAttributeBinding );
|
||||
bindCollectionIndex( attributeSource, pluralAttributeBinding );
|
||||
|
||||
@ -654,16 +671,82 @@ private void bindCollectionTable(
|
||||
}
|
||||
|
||||
private void bindCollectionKey(
|
||||
PluralAttributeSource attributeSource,
|
||||
AbstractPluralAttributeBinding pluralAttributeBinding) {
|
||||
PluralAttributeSource attributeSource,
|
||||
AbstractPluralAttributeBinding pluralAttributeBinding,
|
||||
Deque<TableSpecification> tableStack) {
|
||||
|
||||
TableSpecification targetTable = tableStack.peekLast();
|
||||
pluralAttributeBinding.getPluralAttributeKeyBinding().prepareForeignKey(
|
||||
attributeSource.getKeySource().getExplicitForeignKeyName(),
|
||||
//tableStack.peekLast().getLogicalName()
|
||||
null // todo : handle secondary table names
|
||||
);
|
||||
pluralAttributeBinding.getPluralAttributeKeyBinding().getForeignKey().setDeleteRule(
|
||||
attributeSource.getKeySource().getOnDeleteAction()
|
||||
);
|
||||
// todo : need to bind "relational values", account for property-ref
|
||||
final ForeignKey foreignKey = pluralAttributeBinding.getPluralAttributeKeyBinding().getForeignKey();
|
||||
|
||||
Iterator<SimpleValueBinding> targetValueBindings = null;
|
||||
if ( attributeSource.getKeySource().getReferencedEntityAttributeName() != null ) {
|
||||
final EntityBinding ownerEntityBinding = pluralAttributeBinding.getContainer().seekEntityBinding();
|
||||
final AttributeBinding referencedAttributeBinding =
|
||||
ownerEntityBinding
|
||||
.locateAttributeBinding( attributeSource.getKeySource().getReferencedEntityAttributeName() );
|
||||
if ( ! referencedAttributeBinding.getAttribute().isSingular() ) {
|
||||
throw new MappingException(
|
||||
String.format(
|
||||
"Collection (%s) property-ref is a plural attribute (%s); must be singular.",
|
||||
pluralAttributeBinding.getAttribute().getRole(),
|
||||
referencedAttributeBinding
|
||||
),
|
||||
currentBindingContext.getOrigin()
|
||||
);
|
||||
}
|
||||
targetValueBindings = ( ( SingularAttributeBinding) referencedAttributeBinding ).getSimpleValueBindings().iterator();
|
||||
}
|
||||
for ( RelationalValueSource valueSource : attributeSource.getKeySource().getValueSources() ) {
|
||||
SimpleValue targetValue = null;
|
||||
if ( targetValueBindings != null ) {
|
||||
if ( ! targetValueBindings.hasNext() ) {
|
||||
throw new MappingException(
|
||||
String.format(
|
||||
"More collection key source columns than target columns for collection: %s",
|
||||
pluralAttributeBinding.getAttribute().getRole()
|
||||
),
|
||||
currentBindingContext.getOrigin()
|
||||
);
|
||||
}
|
||||
targetValue = targetValueBindings.next().getSimpleValue();
|
||||
}
|
||||
if ( ColumnSource.class.isInstance( valueSource ) ) {
|
||||
final ColumnSource columnSource = ColumnSource.class.cast( valueSource );
|
||||
final Column column = makeColumn( columnSource, pluralAttributeBinding.getCollectionTable() );
|
||||
if ( targetValue != null && ! Column.class.isInstance( targetValue ) ) {
|
||||
throw new MappingException(
|
||||
String.format(
|
||||
"Type mismatch between collection key source and target; collection: %s; source column (%s) corresponds with target derived value (%s).",
|
||||
pluralAttributeBinding.getAttribute().getRole(),
|
||||
columnSource.getName(),
|
||||
DerivedValue.class.cast( targetValue ).getExpression()
|
||||
),
|
||||
currentBindingContext.getOrigin()
|
||||
);
|
||||
}
|
||||
foreignKey.addColumnMapping( column, Column.class.cast( targetValue ) );
|
||||
}
|
||||
else {
|
||||
// TODO: deal with formulas???
|
||||
}
|
||||
}
|
||||
if ( targetValueBindings != null && targetValueBindings.hasNext() ) {
|
||||
throw new MappingException(
|
||||
String.format(
|
||||
"More collection key target columns than source columns for collection: %s",
|
||||
pluralAttributeBinding.getAttribute().getRole()
|
||||
),
|
||||
currentBindingContext.getOrigin()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindCollectionElement(
|
||||
@ -678,7 +761,10 @@ private void bindCollectionElement(
|
||||
pluralAttributeBinding.getAttribute(),
|
||||
basicCollectionElement
|
||||
);
|
||||
// todo : temp
|
||||
bindBasicPluralElementRelationalValues(
|
||||
basicElementSource,
|
||||
basicCollectionElement
|
||||
);
|
||||
return;
|
||||
}
|
||||
// todo : implement
|
||||
@ -690,6 +776,18 @@ private void bindCollectionElement(
|
||||
);
|
||||
}
|
||||
|
||||
private void bindBasicPluralElementRelationalValues(
|
||||
RelationalValueSourceContainer relationalValueSourceContainer,
|
||||
BasicPluralAttributeElementBinding elementBinding) {
|
||||
elementBinding.setSimpleValueBindings(
|
||||
createSimpleRelationalValues(
|
||||
relationalValueSourceContainer,
|
||||
elementBinding.getPluralAttributeBinding().getContainer(),
|
||||
elementBinding.getPluralAttributeBinding().getAttribute()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void bindCollectionIndex(
|
||||
PluralAttributeSource attributeSource,
|
||||
AbstractPluralAttributeBinding pluralAttributeBinding) {
|
||||
@ -986,12 +1084,25 @@ private void bindTableUniqueConstraints(EntitySource entitySource, EntityBinding
|
||||
private void bindRelationalValues(
|
||||
RelationalValueSourceContainer relationalValueSourceContainer,
|
||||
SingularAttributeBinding attributeBinding) {
|
||||
attributeBinding.setSimpleValueBindings(
|
||||
createSimpleRelationalValues(
|
||||
relationalValueSourceContainer,
|
||||
attributeBinding.getContainer(),
|
||||
attributeBinding.getAttribute()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private List<SimpleValueBinding> createSimpleRelationalValues(
|
||||
RelationalValueSourceContainer relationalValueSourceContainer,
|
||||
AttributeBindingContainer attributeBindingContainer,
|
||||
Attribute attribute) {
|
||||
|
||||
List<SimpleValueBinding> valueBindings = new ArrayList<SimpleValueBinding>();
|
||||
|
||||
if ( !relationalValueSourceContainer.relationalValueSources().isEmpty() ) {
|
||||
for ( RelationalValueSource valueSource : relationalValueSourceContainer.relationalValueSources() ) {
|
||||
final TableSpecification table = attributeBinding.getContainer()
|
||||
final TableSpecification table = attributeBindingContainer
|
||||
.seekEntityBinding()
|
||||
.locateTable( valueSource.getContainingTableName() );
|
||||
|
||||
@ -1018,9 +1129,9 @@ private void bindRelationalValues(
|
||||
else {
|
||||
String name = metadata.getOptions()
|
||||
.getNamingStrategy()
|
||||
.propertyToColumnName( attributeBinding.getAttribute().getName() );
|
||||
.propertyToColumnName( attribute.getName() );
|
||||
name = quoteIdentifier( name );
|
||||
Column column = attributeBinding.getContainer()
|
||||
Column column = attributeBindingContainer
|
||||
.seekEntityBinding()
|
||||
.getPrimaryTable()
|
||||
.locateOrCreateColumn( name );
|
||||
@ -1033,7 +1144,7 @@ private void bindRelationalValues(
|
||||
)
|
||||
);
|
||||
}
|
||||
attributeBinding.setSimpleValueBindings( valueBindings );
|
||||
return valueBindings;
|
||||
}
|
||||
|
||||
private String quoteIdentifier(String identifier) {
|
||||
|
@ -52,12 +52,12 @@ public String getContainingTableName() {
|
||||
|
||||
@Override
|
||||
public boolean isIncludedInInsertByDefault() {
|
||||
return true;
|
||||
return BasicPluralAttributeElementSourceImpl.this.areValuesIncludedInInsertByDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIncludedInUpdateByDefault() {
|
||||
return true;
|
||||
return BasicPluralAttributeElementSourceImpl.this.areValuesIncludedInUpdateByDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -107,10 +107,25 @@ public PluralAttributeElementNature getNature() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RelationalValueSource> getValueSources() {
|
||||
public List<RelationalValueSource> relationalValueSources() {
|
||||
return valueSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areValuesIncludedInInsertByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areValuesIncludedInUpdateByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areValuesNullableByDefault() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplicitHibernateTypeSource getExplicitHibernateTypeSource() {
|
||||
return typeSource;
|
||||
|
@ -65,6 +65,10 @@ public Attribute getAttribute() {
|
||||
return attribute;
|
||||
}
|
||||
|
||||
protected String getRole() {
|
||||
return getContainer().getPathBase() + '.' + getAttribute().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HibernateTypeDescriptor getHibernateTypeDescriptor() {
|
||||
return hibernateTypeDescriptor;
|
||||
|
@ -48,7 +48,6 @@ public abstract class AbstractPluralAttributeBinding extends AbstractAttributeBi
|
||||
|
||||
private Caching caching;
|
||||
|
||||
private boolean inverse;
|
||||
private boolean mutable = true;
|
||||
|
||||
private Class<? extends CollectionPersister> collectionPersisterClass;
|
||||
@ -108,7 +107,6 @@ private AbstractPluralAttributeElementBinding interpretNature(PluralAttributeEle
|
||||
// extraLazy = state.isExtraLazy();
|
||||
// pluralAttributeElementBinding.setNodeName( state.getElementNodeName() );
|
||||
// pluralAttributeElementBinding.setTypeName( state.getElementTypeName() );
|
||||
// inverse = state.isInverse();
|
||||
// mutable = state.isMutable();
|
||||
// subselectLoadable = state.isSubselectLoadable();
|
||||
// if ( isSubselectLoadable() ) {
|
||||
@ -244,15 +242,6 @@ public void setWhere(String where) {
|
||||
this.where = where;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInverse() {
|
||||
return inverse;
|
||||
}
|
||||
|
||||
public void setInverse(boolean inverse) {
|
||||
this.inverse = inverse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return mutable;
|
||||
|
@ -78,10 +78,6 @@ public void setSimpleValueBindings(Iterable<SimpleValueBinding> simpleValueBindi
|
||||
}
|
||||
}
|
||||
|
||||
private String getRole() {
|
||||
return getContainer().getPathBase() + '.' + getAttribute().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSimpleValueSpan() {
|
||||
checkValueBinding();
|
||||
|
@ -23,6 +23,13 @@
|
||||
*/
|
||||
package org.hibernate.metamodel.spi.binding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.metamodel.spi.relational.SimpleValue;
|
||||
import org.hibernate.metamodel.spi.relational.Tuple;
|
||||
import org.hibernate.metamodel.spi.relational.Value;
|
||||
|
||||
/**
|
||||
* Describes plural attributes of {@link PluralAttributeElementNature#BASIC} elements
|
||||
*
|
||||
@ -30,6 +37,11 @@
|
||||
* @author Gail Badner
|
||||
*/
|
||||
public class BasicPluralAttributeElementBinding extends AbstractPluralAttributeElementBinding {
|
||||
private Value value;
|
||||
private List<SimpleValueBinding> simpleValueBindings = new ArrayList<SimpleValueBinding>();
|
||||
|
||||
private boolean hasDerivedValue;
|
||||
private boolean isNullable = true;
|
||||
|
||||
public BasicPluralAttributeElementBinding(AbstractPluralAttributeBinding binding) {
|
||||
super( binding );
|
||||
@ -39,4 +51,24 @@ public BasicPluralAttributeElementBinding(AbstractPluralAttributeBinding binding
|
||||
public PluralAttributeElementNature getPluralAttributeElementNature() {
|
||||
return PluralAttributeElementNature.BASIC;
|
||||
}
|
||||
|
||||
public void setSimpleValueBindings(Iterable<SimpleValueBinding> simpleValueBindings) {
|
||||
List<SimpleValue> values = new ArrayList<SimpleValue>();
|
||||
for ( SimpleValueBinding simpleValueBinding : simpleValueBindings ) {
|
||||
this.simpleValueBindings.add( simpleValueBinding );
|
||||
values.add( simpleValueBinding.getSimpleValue() );
|
||||
this.hasDerivedValue = this.hasDerivedValue || simpleValueBinding.isDerived();
|
||||
this.isNullable = this.isNullable && simpleValueBinding.isNullable();
|
||||
}
|
||||
if ( values.size() == 1 ) {
|
||||
this.value = values.get( 0 );
|
||||
}
|
||||
else {
|
||||
final Tuple tuple = values.get( 0 ).getTable().createTuple( getPluralAttributeBinding().getRole() );
|
||||
for ( SimpleValue value : values ) {
|
||||
tuple.addValue( value );
|
||||
}
|
||||
this.value = tuple;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.hibernate.metamodel.spi.relational.Table;
|
||||
import org.hibernate.metamodel.spi.relational.Value;
|
||||
|
||||
/**
|
||||
* Describes plural attributes of {@link PluralAttributeElementNature#MANY_TO_MANY} elements
|
||||
*
|
||||
@ -35,7 +38,8 @@ public class ManyToManyPluralAttributeElementBinding extends AbstractPluralAttri
|
||||
private final java.util.Map manyToManyFilters = new HashMap();
|
||||
private String manyToManyWhere;
|
||||
private String manyToManyOrderBy;
|
||||
|
||||
// TODO: really should have value defined (which defines table), but may not know
|
||||
private Value value;
|
||||
|
||||
ManyToManyPluralAttributeElementBinding(AbstractPluralAttributeBinding binding) {
|
||||
super( binding );
|
||||
@ -61,4 +65,12 @@ public String getManyToManyOrderBy() {
|
||||
public void setManyToManyOrderBy(String manyToManyOrderBy) {
|
||||
this.manyToManyOrderBy = manyToManyOrderBy;
|
||||
}
|
||||
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Value value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
@ -57,13 +57,6 @@ public interface PluralAttributeBinding extends AttributeBinding {
|
||||
*/
|
||||
public PluralAttributeElementBinding getPluralAttributeElementBinding();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public TableSpecification getCollectionTable();
|
||||
|
||||
public boolean isMutable();
|
||||
@ -92,7 +85,5 @@ public interface PluralAttributeBinding extends AttributeBinding {
|
||||
|
||||
java.util.Map getFilterMap();
|
||||
|
||||
boolean isInverse();
|
||||
|
||||
String getOrderBy();
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.metamodel.spi.relational.ForeignKey;
|
||||
import org.hibernate.metamodel.spi.relational.Table;
|
||||
import org.hibernate.metamodel.spi.relational.TableSpecification;
|
||||
|
||||
/**
|
||||
@ -81,6 +82,10 @@ public boolean isInverse() {
|
||||
return inverse;
|
||||
}
|
||||
|
||||
public void setInverse(boolean inverse) {
|
||||
this.inverse = inverse;
|
||||
}
|
||||
|
||||
public HibernateTypeDescriptor getHibernateTypeDescriptor() {
|
||||
return hibernateTypeDescriptor;
|
||||
}
|
||||
|
@ -28,7 +28,6 @@
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface BasicPluralAttributeElementSource extends PluralAttributeElementSource {
|
||||
public List<RelationalValueSource> getValueSources();
|
||||
public interface BasicPluralAttributeElementSource extends PluralAttributeElementSource, RelationalValueSourceContainer {
|
||||
public ExplicitHibernateTypeSource getExplicitHibernateTypeSource();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user