/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* 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 net.ontopia.persistence.proxy;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.HashSet;
import net.ontopia.utils.OntopiaRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* INTERNAL: Class used for holding object relational mapping field
* declarations. It is used by the ObjectRelationalMapping class.<p>
*
* A field descriptor contains information about fields of object
* classes defined in object relational mappings. A field descriptor
* belongs to a single class descriptor.<p>
*/
public class FieldDescriptor {
// Define a logging category.
static Logger log = LoggerFactory.getLogger(FieldDescriptor.class.getName());
final static Set<Class<?>> PRIMITIVE_TYPES;
static {
Set<Class<?>> pt = new HashSet<Class<?>>();
pt.add(java.lang.String.class);
pt.add(java.io.Reader.class);
pt.add(java.io.InputStream.class);
pt.add(java.lang.Long.class);
pt.add(java.lang.Integer.class);
pt.add(java.lang.Boolean.class);
pt.add(java.lang.Character.class);
pt.add(java.lang.Short.class);
pt.add(java.lang.Double.class);
pt.add(java.lang.Float.class);
PRIMITIVE_TYPES = pt;
}
// -----------------------------------------------------------------------------
// Cardinality
// -----------------------------------------------------------------------------
/**
* Flag indicating that the descriptor field represents a 1:1 relationship.
*/
public static final int ONE_TO_ONE = 1;
/**
* Flag indicating that the descriptor field represents a 1:M relationship.
*/
public static final int ONE_TO_MANY = 2;
/**
* Flag indicating that the descriptor field represents a M:M relationship.
*/
public static final int MANY_TO_MANY = 3;
protected ObjectRelationalMapping mapping;
protected ClassDescriptor cdesc;
protected String name;
protected int cardinality;
protected Class value_class;
protected boolean required;
protected boolean readonly;
protected String getter;
protected String setter;
protected Method getter_method;
protected Method setter_method;
protected String[] columns; //! = new String[0];
protected String jointable;
protected String[] joinkeys; //! = new String[0];
protected String[] manykeys; //! = new String[0];
protected Class collection_class;
public FieldDescriptor(String name, ClassDescriptor cdesc) {
this.name = name;
this.cdesc = cdesc;
}
/**
* INTERNAL: Gets the class descriptor that the field descriptor
* belongs to.
*/
public ClassDescriptor getClassDescriptor() {
return cdesc;
}
/**
* INTERNAL: Returns the name of the descriptor field (the mapped
* field).
*/
public String getName() {
return name;
}
/**
* INTERNAL: Gets the field cardinality.
*/
public int getCardinality() {
return cardinality;
}
/**
* INTERNAL: Sets the field cardinality. This can be either
* ONE_TO_ONE, ONE_TO_MANY or MANY_TO_MANY.
*/
public void setCardinality(int cardinality) {
if (cardinality != ONE_TO_ONE &&
cardinality != ONE_TO_MANY &&
cardinality != MANY_TO_MANY)
throw new IllegalArgumentException("Invalid argument: " + cardinality);
this.cardinality = cardinality;
}
/**
* INTERNAL: Returns true if the field cardinality is 1:1.
*/
public boolean isOneToOne() {
return (cardinality == ONE_TO_ONE);
}
/**
* INTERNAL: Returns true if the field cardinality is 1:M.
*/
public boolean isOneToMany() {
return (cardinality == ONE_TO_MANY);
}
/**
* INTERNAL: Returns true if the field cardinality is M:M.
*/
public boolean isManyToMany() {
return (cardinality == MANY_TO_MANY);
}
/**
* INTERNAL: Returns true if the field is a collection field (a
* cardinality of 1:1 or 1:M).
*/
public boolean isCollectionField() {
if (cardinality == ONE_TO_ONE)
return false;
else
return true;
}
/**
* INTERNAL: Returns true if the field is a required field.
*/
public boolean isRequired() {
return required;
}
/**
* INTERNAL: Sets whether the field is a required field or not.
*/
public void setRequired(boolean required) {
this.required = required;
}
/**
* INTERNAL: Returns true if this field is read-only field.
*/
public boolean isReadOnly() {
return readonly;
}
/**
* INTERNAL: Sets whether the field is a read-only field or not.
*/
public void setReadOnly(boolean readonly) {
this.readonly = readonly;
}
/**
* INTERNAL: Gets the class descriptor of the field value
* class. Note that primitive value classes don't have a class info.
*/
public ClassDescriptor getValueClassDescriptor() {
return getClassDescriptor().getMapping().getDescriptorByClass(getValueClass());
}
/**
* INTERNAL: Gets the field value class. For primitive fields the
* primitive wrapper class is returned.
*/
public Class getValueClass() {
return value_class;
}
/**
* INTERNAL: Sets the field value class. For primitive fields the
* primitive wrapper class should be used.
*/
public void setValueClass(Class value_class) {
this.value_class = value_class;
}
/**
* INTERNAL: Returns true if the field is an identity field.
*/
public boolean isIdentityField() {
FieldDescriptor[] fdescs = getClassDescriptor().getIdentityFields();
for (int i=0; i < fdescs.length; i++) {
if (fdescs[i] == this) return true;
}
return false;
}
//! /**
//! * INTERNAL: Returns true if the field is a value field.
//! */
//! public boolean isValueField() {
//! return !isIdentityField();
//! }
/**
* INTERNAL: Returns true if the field is a primitive field.
*/
public boolean isPrimitiveField() {
return PRIMITIVE_TYPES.contains(value_class);
}
/**
* INTERNAL: Returns true if the field is a reference field.
*/
public boolean isReferenceField() {
return !(isPrimitiveField() || isAggregateField());
}
/**
* INTERNAL: Returns true if the field is an aggregate field.
*/
public boolean isAggregateField() {
ClassDescriptor cdesc = getValueClassDescriptor();
if (cdesc != null && cdesc.isAggregate())
return true;
else
return false;
}
/**
* INTERNAL: Returns the method name for getting the field value
* from class instances.
*/
public String getGetter() {
return getter;
}
/**
* INTERNAL: Sets the method name for getting the field value from
* class instances.
*/
public void setGetter(String getter) {
this.getter = getter;
}
/**
* INTERNAL: Returns the method name for setting the field value of
* class instances.
*/
public String getSetter() {
return setter;
}
/**
* INTERNAL: Sets the method name for setting the field value of
* class instances.
*/
public void setSetter(String setter) {
this.setter = setter;
}
/**
* INTERNAL: Returns the method for getting the field value from
* class instances.
*/
public Method getGetterMethod() {
if (getter_method == null)
try {
getter_method = FieldUtils.getGetterMethod(this);
} catch (Exception e) {
throw new OntopiaRuntimeException(e);
}
return getter_method;
}
/**
* INTERNAL: Sets the method for getting the field value from class
* instances.
*/
public void setGetterMethod(Method getter_method) {
this.getter_method = getter_method;
}
/**
* INTERNAL: Returns the method for setting the field value of class
* instances.
*/
public Method getSetterMethod() {
if (setter_method == null)
try {
setter_method = FieldUtils.getSetterMethod(this);
} catch (Exception e) {
throw new OntopiaRuntimeException(e);
}
return setter_method;
}
/**
* INTERNAL: Sets the method for setting the field value of class
* instances.
*/
public void setSetterMethod(Method setter_method) {
this.setter_method = setter_method;
}
/**
* INTERNAL: Returns the name of the table in which the field is
* stored.
*/
public String getTable() {
if (jointable != null)
return jointable;
else
return getClassDescriptor().getMasterTable();
}
/**
* INTERNAL: Returns true if the field is stored in a different
* table from the master table.
*/
public boolean isExternal() {
// Not external in the case where the join table is the same as
// the class descriptor's master table.
return (jointable != null &&
!(jointable.equals(getClassDescriptor().getMasterTable())));
}
/**
* INTERNAL: Returns the names of the columns referenced by the field.
*/
public String[] getColumns() {
return columns;
}
/**
* INTERNAL: Sets the names of the columns referenced by the field.
*/
public void setColumns(String[] columns) {
this.columns = columns;
}
/**
* INTERNAL: Gets the name of the table which needs to be joined to
* order to access the field via the master table.
*/
public String getJoinTable() {
return jointable;
}
/**
* INTERNAL: Sets the name of the table which needs to be joined to
* order to access the field via the master table.
*/
public void setJoinTable(String jointable) {
this.jointable = jointable;
}
/**
* INTERNAL: Gets the columns that contains the keys referencing the
* master table.
*/
public String[] getJoinKeys() {
return joinkeys;
}
/**
* INTERNAL: Sets the columns that contains the keys referencing the
* master table.
*/
public void setJoinKeys(String[] joinkeys) {
this.joinkeys = joinkeys;
}
/**
* INTERNAL: Gets the columns that contains the keys in the
* jointable that references the field table.
*/
public String[] getManyKeys() {
return manykeys;
}
/**
* INTERNAL: Sets the columns that contains the keys in the
* jointable that references the field table.
*/
public void setManyKeys(String[] manykeys) {
this.manykeys = manykeys;
}
/**
* INTERNAL: Returns the collection class to store the field values
* in if the field is a 1:M and M:M field.
*/
public Class getCollectionClass() {
return collection_class;
}
/**
* INTERNAL: Sets the collection class to store the field values in
* if the field is a 1:M and M:M field.
*/
public void setCollectionClass(Class collection_class) {
this.collection_class = collection_class;
}
public String toString() {
return "<FieldDescriptor " + getName() + ">";
}
}