blob: 53db8d0121c9cd8e40487a389a6f96fa99dc752b [file] [log] [blame]
/*
* Copyright (c) 2010, Stanislav Muhametsin. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zest.index.sql.support.common;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.apache.zest.api.association.AssociationDescriptor;
import org.apache.zest.api.common.QualifiedName;
import org.apache.zest.api.property.PropertyDescriptor;
/**
* A helper interface to encapsulate information about qualified name and how it appears in
* database.
*
* @author Stanislav Muhametsin
*/
public final class QNameInfo
{
/**
* Currently all possible types of qualified names: {@link #PROPERTY} for properties,
* {@link #ASSOCIATION} for associations, and {@link #MANY_ASSOCIATION} for many-associations.
*/
public enum QNameType
{
PROPERTY,
ASSOCIATION,
MANY_ASSOCIATION
}
private static final List<Class<?>> EMPTY_COLL_CLASSES = new ArrayList<>();
private String _tableName;
/**
* Store also information about collection types - in case it is required in future.
*/
private final List<Class<?>> _collectionClasses;
private final QualifiedName _qName;
private final QNameType _qNameType;
private final Type _finalType;
private final Boolean _isFinalTypePrimitive;
private final PropertyDescriptor _propertyDescriptor;
private final AssociationDescriptor _associationDescriptor;
private final AssociationDescriptor _manyAssociationDescriptor;
private QNameInfo( QualifiedName qName, //
QNameType qNameType, //
List<Class<?>> collectionClasses, //
String tableName, //
Type finalType, //
PropertyDescriptor propertyDescriptor, //
AssociationDescriptor associationDescriptor, //
AssociationDescriptor manyAssociationDescriptor //
)
{
if( ( propertyDescriptor != null && associationDescriptor == null && manyAssociationDescriptor == null )
|| ( propertyDescriptor == null && associationDescriptor != null && manyAssociationDescriptor == null )
|| ( propertyDescriptor == null && associationDescriptor == null && manyAssociationDescriptor != null ) )
{
this._propertyDescriptor = propertyDescriptor;
this._associationDescriptor = associationDescriptor;
this._manyAssociationDescriptor = manyAssociationDescriptor;
}
else
{
throw new IllegalArgumentException(
"Exactly one of property, association, or many-association descriptors must be non-null." );
}
this._qName = qName;
this._qNameType = qNameType;
this._collectionClasses = ( collectionClasses == null || collectionClasses.isEmpty()
? EMPTY_COLL_CLASSES
: collectionClasses );
this._tableName = tableName;
this._finalType = finalType;
Boolean isFinalTypePrimitive = false;
if( finalType instanceof Class<?> )
{
Class<?> finalClass = (Class<?>) finalType;
isFinalTypePrimitive = //
Number.class.isAssignableFrom( finalClass )//
|| Boolean.class.isAssignableFrom( finalClass ) //
|| Character.class.isAssignableFrom( finalClass ) //
|| Date.class.isAssignableFrom( finalClass ) //
|| Enum.class.isAssignableFrom( finalClass ) //
|| String.class.isAssignableFrom( finalClass )//
;
}
this._isFinalTypePrimitive = isFinalTypePrimitive;
}
/**
* If qualified name represented by this interface is a property with collection as type,
* returns the {@code amount of nested collections + 1}. That is, assuming {@code X} is not a
* collection, for type {@code Property<Set<X>>} this returns {@code 1}, for
* {@code Property<Set<Set<X>>>} this returns {@code 2}, etc. If qualified name represented by
* this interface is not a property or a property with no collection type, this method returns
* {@code 0}.
*
* @return The collection depth ({@code > 0}) of qualified name, if this interface represents
* qualified name with collection property; {@code 0} otherwise.
*/
public Integer getCollectionDepth()
{
return this._collectionClasses.size();
}
/**
* Returns the non-collection type of this qualified name. That is, for {@code Property<X>} this
* returns {@code X} if {@code X} is not a collection type, and for {@code Property<Set<Y>>}
* this returns {@code Y} if {@code Y} is not a collection type.
*
* @return The non-collection type of this qualified name.
*/
public Type getFinalType()
{
return this._finalType;
}
/**
* Gets the qualified name this interface represents.
*
* @return The qualified name this interface represents.
* @see QualifiedName
*/
public QualifiedName getQName()
{
return this._qName;
}
/**
* Gets the table name in database, used to store values of the qualified name this interface
* represents.
*
* @return The table name in database, used to store values of the qualified name this interface
* represents. May be {@code null} if it is not yet decided.
*/
public String getTableName()
{
return this._tableName;
}
/**
* Sets the previously undecided table name to some specific one. This method only works when
* argument is {@code non-null} and current table name is {@code null}.
*
* @param tableName The new table name. Must be {@code non-null}.
* @throws IllegalArgumentException If {@code tableName} is {@code null}.
* @throws IllegalStateException If current table name is {@code non-null}.
*/
public void setTableName( String tableName )
{
if( tableName == null )
{
throw new IllegalArgumentException( "Table name must not be null." );
}
if( this._tableName == null )
{
this._tableName = tableName;
}
else
{
throw new IllegalStateException( "Can only set table name when it is null." );
}
}
/**
* Returns whether the final (non-collection) type of this qualified name is not seen as value
* composite. Always returns {@code false} for qualified names of type
* {@link QNameType#ASSOCIATION} and {@link QNameType#MANY_ASSOCIATION}.
*
* @return {@code true} if {@link #getFinalType()} is not seen as value composite type;
* {@code false} otherwise.
*/
public Boolean isFinalTypePrimitive()
{
return this._isFinalTypePrimitive;
}
/**
* Returns {@link PropertyDescriptor} associated with this property, if this qualified name info
* represents a property. Returns {@code null} otherwise.
*
* @return {@link PropertyDescriptor} if this qualified name info is associated with property,
* {@code null} otherwise.
*/
public PropertyDescriptor getPropertyDescriptor()
{
return this._propertyDescriptor;
}
/**
* Returns {@link AssociationDescriptor} associated with this association, if this qualified
* name info represents an association. Returns {@code null} otherwise.
*
* @return {@link AssociationDescriptor} if this qualified name info is associated with
* association, {@code null} otherwise.
*/
public AssociationDescriptor getAssociationDescriptor()
{
return this._associationDescriptor;
}
/**
* Returns {@link AssociationDescriptor} associated with this many-association, if this
* qualified name info represents a many-association. Returns {@code null} otherwise.
*
* @return {@link AssociationDescriptor} if this qualified name info is associated with
* many-association, {@code null} otherwise.
*/
public AssociationDescriptor getManyAssociationDescriptor()
{
return this._manyAssociationDescriptor;
}
/**
* Gets the type of represented qualified name: either {@link QNameType#PROPERTY} for
* properties, {@link QNameType#ASSOCIATION} for associations, or
* {@link QNameType#MANY_ASSOCIATION} for many-associations.
*
* @return The type of represented qualified name: either {@link QNameType#PROPERTY},
* {@link QNameType#ASSOCIATION}, or {@link QNameType#MANY_ASSOCIATION}.
*/
public QNameType getQNameType()
{
return this._qNameType;
}
/**
* Creates information about specified qualified name which represents a property.
*
* @param qName The qualified name of property.
* @param tableName The table name where the values of all instances of propertiy with this
* qualified name will be stored. May be {@code null} if it is to be decided later.
* @param propertyDescriptor {@link PropertyDescriptor} of this property.
* @return An object representing information about property with this qualified name, and how
* instances of this property are stored in database.
*/
public static QNameInfo fromProperty( //
QualifiedName qName, //
String tableName, //
PropertyDescriptor propertyDescriptor //
)
{
Type vType = propertyDescriptor.type();
List<Class<?>> collectionClasses = new ArrayList<>();
while( vType instanceof ParameterizedType
&& Collection.class.isAssignableFrom( (Class<?>) ( (ParameterizedType) vType )
.getRawType() ) )
{
collectionClasses.add( (Class<?>) ( (ParameterizedType) vType ).getRawType() );
vType = ( (ParameterizedType) vType ).getActualTypeArguments()[0];
}
return new QNameInfo( qName, QNameType.PROPERTY, collectionClasses, tableName, vType,
propertyDescriptor, null,
null );
}
/**
* Creates information about specified qualified name which represents an association.
*
* @param qName The qualified name of the association.
* @param tableName The table name where the values of all instances of association with this
* qualified name will be stored. May be {@code null} if it is to be decided later.
* @param assoDescriptor {@link AssociationDescriptor} of this association.
* @return An object representing information about association with this qualified name, and
* how instances of this association are stored in database.
*/
public static QNameInfo fromAssociation( //
QualifiedName qName, //
String tableName, //
AssociationDescriptor assoDescriptor //
)
{
return new QNameInfo( qName, QNameType.ASSOCIATION, null, tableName, assoDescriptor.type(),
null,
assoDescriptor, null );
}
/**
* Creates information about specified qualified name which represents a many-association.
*
* @param qName The qualified name of the many-association.
* @param tableName The table name where the values of all instances of many-association with
* this qualified name will be stored. May be {@code null} if it is to be decided later.
* @param manyAssoDescriptor {@link AssociationDescriptor} of this many-association.
* @return An object representing information about many-association with this qualified name,
* and how instances of this many-association are stored in database.
*/
public static QNameInfo fromManyAssociation( //
QualifiedName qName, //
String tableName, //
AssociationDescriptor manyAssoDescriptor //
)
{
return new QNameInfo( qName, QNameType.MANY_ASSOCIATION, null, tableName,
manyAssoDescriptor.type(), null,
null, manyAssoDescriptor );
}
@Override
public String toString()
{
return "[table: " + this._tableName + ", final type: " + this._finalType + ", qNameType: "
+ this._qNameType + ", qName: " + this._qName + "]";
}
}