package com.venky.swf.db.model.reflection;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import com.venky.cache.Cache;
import com.venky.core.collections.IgnoreCaseList;
import com.venky.core.collections.IgnoreCaseMap;
import com.venky.core.collections.IgnoreCaseSet;
import com.venky.core.collections.SequenceSet;
import com.venky.core.log.SWFLogger;
import com.venky.core.log.TimerStatistics.Timer;
import com.venky.core.string.StringUtil;
import com.venky.core.util.ObjectUtil;
import com.venky.extension.Registry;
import com.venky.reflection.Reflector.MethodMatcher;
import com.venky.swf.db.Database;
import com.venky.swf.db.JdbcTypeHelper;
import com.venky.swf.db.JdbcTypeHelper.TypeRef;
import com.venky.swf.db.annotations.column.COLUMN_DEF;
import com.venky.swf.db.annotations.column.COLUMN_NAME;
import com.venky.swf.db.annotations.column.COLUMN_SIZE;
import com.venky.swf.db.annotations.column.DATA_TYPE;
import com.venky.swf.db.annotations.column.DECIMAL_DIGITS;
import com.venky.swf.db.annotations.column.HOUSEKEEPING;
import com.venky.swf.db.annotations.column.IS_AUTOINCREMENT;
import com.venky.swf.db.annotations.column.IS_NULLABLE;
import com.venky.swf.db.annotations.column.IS_VIRTUAL;
import com.venky.swf.db.annotations.column.PASSWORD;
import com.venky.swf.db.annotations.column.UNIQUE_KEY;
import com.venky.swf.db.annotations.column.defaulting.CLONING_PROTECT;
import com.venky.swf.db.annotations.column.defaulting.StandardDefault;
import com.venky.swf.db.annotations.column.indexing.Index;
import com.venky.swf.db.annotations.column.pm.PARTICIPANT;
import com.venky.swf.db.annotations.column.ui.CONTENT_TYPE;
import com.venky.swf.db.annotations.column.ui.HIDDEN;
import com.venky.swf.db.annotations.column.ui.PROTECTION;
import com.venky.swf.db.annotations.column.ui.PROTECTION.Kind;
import com.venky.swf.db.annotations.column.ui.mimes.MimeType;
import com.venky.swf.db.annotations.column.validations.Enumeration;
import com.venky.swf.db.annotations.model.EXPORTABLE;
import com.venky.swf.db.annotations.model.HAS_DESCRIPTION_FIELD;
import com.venky.swf.db.annotations.model.ORDER_BY;
import com.venky.swf.db.model.Count;
import com.venky.swf.db.model.Model;
import com.venky.swf.db.model.reflection.TableReflector.MReflector;
import com.venky.swf.db.model.reflection.uniquekey.UniqueKey;
import com.venky.swf.db.model.reflection.uniquekey.UniqueKeyFieldDescriptor;
import com.venky.swf.db.table.Record;
import com.venky.swf.db.table.Table.ColumnDescriptor;
import com.venky.swf.routing.Config;
import com.venky.swf.sql.Conjunction;
import com.venky.swf.sql.Expression;
import com.venky.swf.sql.Operator;
import com.venky.swf.sql.Select;
import com.venky.swf.util.WordWrapUtil;
public class ModelReflector<M extends Model> {
@SuppressWarnings("rawtypes")
private static final Map<Class<? extends Model> , ModelReflector> modelReflectorByModelClass = new HashMap<Class<? extends Model>, ModelReflector>();
@SuppressWarnings("unchecked")
public static <M extends Model> ModelReflector<M> instance(Class<M> modelClass){
ModelReflector<M> ref = modelReflectorByModelClass.get(modelClass);
if (ref == null){
synchronized (modelReflectorByModelClass) {
ref = modelReflectorByModelClass.get(modelClass);
if (ref == null){
ref = new ModelReflector<M>(modelClass, TableReflector.instance(modelClass)) ;
modelReflectorByModelClass.put(modelClass, ref);
}
}
}
return ref;
}
private Cache<String, SequenceSet<String>> extensionPointsCache = new Cache<String, SequenceSet<String>>() {
/**
*
*/
private static final long serialVersionUID = 3938846263913578958L;
@Override
protected SequenceSet<String> getValue(String k) {
StringTokenizer tok = new StringTokenizer(k, "@");
String prefix = tok.nextToken();
String suffix = tok.nextToken();
SequenceSet<String> extnPoints = new SequenceSet<String>();
for (Class<? extends Model> inHierarchy : getClassHierarchies()){
String extnPoint = prefix + "."+ inHierarchy.getSimpleName() + "." + suffix;
if (Registry.instance().hasExtensions(extnPoint)){
extnPoints.add(extnPoint);
}
}
return extnPoints;
}
};
/**
* Find extension points of the form prefix.<i>modelClass.getSimpleName()</i>.suffix for all relevant models in the right sequence.
* @param prefix
* @param suffix
* @return
*/
public List<String> getExtensionPoints(String prefix,String suffix){
return extensionPointsCache.get(prefix+"@"+suffix);
}
private final Class<M> modelClass ;
private final TableReflector reflector ;
private ModelReflector(Class<M> modelClass,TableReflector reflector){
this.modelClass = modelClass;
this.reflector = reflector;
this.cat = Config.instance().getLogger(modelClass.getName());
}
public Class<M> getModelClass(){
return modelClass;
}
public String getTableName() {
return reflector.getTableName();
}
public Class<? extends Model> getRealModelClass(){
return TableReflector.getRealModelClass(getModelClass());
}
public SequenceSet<Class<? extends Model>> getModelClasses(){
return reflector.getModelClasses();
}
public boolean reflects(Class<? extends Model> referredModelClass) {
return reflector.reflects(referredModelClass);
}
public boolean canReflect(Object o) {
return reflector.canReflect(o);
}
public String getDescriptionField(){
HAS_DESCRIPTION_FIELD descColumn = getAnnotation(HAS_DESCRIPTION_FIELD.class);
if (descColumn != null){
String column = descColumn.value();
if (getFields().contains(column)){
return column;
}
}
if (getFields().contains("NAME")){
return "NAME";
}
return "ID";
}
@SuppressWarnings("unchecked")
public <T extends Object> T get(Object o, String fieldName){
if (o == null){
return null;
}
Model record = null;
Record rawRecord = null ;
if (Record.class.isInstance(o)){
rawRecord = (Record)o;
}else if (Proxy.isProxyClass(o.getClass()) && (o instanceof Model)){
record = (Model)o;
}else {
throw new RuntimeException ("Don't know how to get " + fieldName );
}
if (rawRecord == null){
rawRecord = record.getRawRecord();
}
Timer timer = cat.startTimer();
try {
T ret = (T)rawRecord.get(fieldName);
if (ret == null){
ColumnDescriptor cd = getColumnDescriptor(fieldName);
if (!cd.isVirtual()){
ret = (T)rawRecord.get(cd.getName());
}
}
Method getter = getFieldGetter(fieldName);
if (ret == null || !(getter.getReturnType().isAssignableFrom(ret.getClass()))) {
if (record == null){
record = rawRecord.getAsProxy(getModelClass());
}
ret = (T)getter.invoke(record);
}
return ret;
} catch (Exception e1) {
throw new RuntimeException(e1);
} finally {
timer.stop();
}
}
public void set(Model record, String fieldName, Object value){
Timer timer = cat.startTimer();
try {
Method getter = getFieldGetter(fieldName);
Method setter = getFieldSetter(fieldName);
TypeRef<?> typeRef = Database.getJdbcTypeHelper(getPool()).getTypeRef(getter.getReturnType());
if (!ObjectUtil.isVoid(value) || getter.getReturnType().isPrimitive() ){
setter.invoke(record, typeRef.getTypeConverter().valueOf(value));
}else {
setter.invoke(record, getter.getReturnType().cast(null));
}
} catch (Exception e1) {
throw new RuntimeException(fieldName +":" + value , e1);
} finally {
timer.stop();
}
}
public void loadMethods(List<Method> into, MethodMatcher matcher) {
Timer timer = cat.startTimer();
try {
reflector.loadMethods(getModelClass(), into, matcher);
}finally{
timer.stop();
}
}
private SequenceSet<Method> fieldGetters = null;
public List<Method> getFieldGetters(){
if (fieldGetters == null){
synchronized (this) {
if (fieldGetters == null){
SequenceSet<Method> fieldGetters = new SequenceSet<Method>();
loadMethods(fieldGetters, getFieldGetterMatcher());
this.fieldGetters = fieldGetters;
}
}
}
return fieldGetters;
}
private SequenceSet<String> fieldGetterSignatures = null;
public List<String> getFieldGetterSignatures(){
if (fieldGetterSignatures == null){
synchronized (this) {
if (fieldGetterSignatures == null ){
SequenceSet<String> fieldGetterSignatures = new SequenceSet<String>();
for (Method m : getFieldGetters()){
fieldGetterSignatures.add(getSignature(m));
}
this.fieldGetterSignatures = fieldGetterSignatures;
}
}
}
return fieldGetterSignatures;
}
private SequenceSet<Method> indexedFieldGetters = null;
public List<Method> getIndexedFieldGetters(){
if (indexedFieldGetters == null) {
synchronized (this) {
if (indexedFieldGetters == null){
SequenceSet<Method> indexedFieldGetters = new SequenceSet<Method>();
loadMethods(indexedFieldGetters, getIndexedFieldGetterMatcher());
this.indexedFieldGetters = indexedFieldGetters;
}
}
}
return indexedFieldGetters;
}
private SequenceSet<Method> fieldSetters = null;
public List<Method> getFieldSetters(){
if (fieldSetters == null) {
synchronized (this) {
if (fieldSetters == null) {
SequenceSet<Method> fieldSetters = new SequenceSet<Method>() ;
loadMethods(fieldSetters, getFieldSetterMatcher());
this.fieldSetters = fieldSetters;
}
}
}
return fieldSetters;
}
private SequenceSet<Method> referredModelGetters = null;
public List<Method> getReferredModelGetters(){
if (referredModelGetters == null ){
synchronized (this) {
if (referredModelGetters == null) {
SequenceSet<Method> referredModelGetters = new SequenceSet<Method>();
loadMethods(referredModelGetters, getReferredModelGetterMatcher());
this.referredModelGetters = referredModelGetters;
}
}
}
return referredModelGetters;
}
private SequenceSet<Method> participantModelGetters = null ;
public List<Method> getParticipantModelGetters(){
if (participantModelGetters == null){
synchronized (this) {
if (participantModelGetters == null){
SequenceSet<Method> participantModelGetters = new SequenceSet<Method>();
loadMethods(participantModelGetters, getParticipantModelGetterMatcher());
this.participantModelGetters = participantModelGetters;
}
}
}
return participantModelGetters;
}
private SequenceSet<Method> childModelGetters = null ;
public List<Method> getChildGetters(){
if (childModelGetters == null){
synchronized (this) {
if (childModelGetters == null) {
SequenceSet<Method> childModelGetters = new SequenceSet<Method>();
loadMethods(childModelGetters,getChildrenGetterMatcher());
this.childModelGetters = childModelGetters;
}
}
}
return childModelGetters;
}
public List<Class<? extends Model>> getChildModels(){
return getChildModels(false,false);
}
public List<Class<? extends Model>> getChildModels(boolean onlyMultipleChildren ,boolean onlyVisible){
SequenceSet<Class<? extends Model>> childModels = new SequenceSet<Class<? extends Model>>();
for (Method childGetter: getChildGetters()){
if (onlyMultipleChildren && !List.class.isAssignableFrom(childGetter.getReturnType())){
continue;
}
HIDDEN hidden = getAnnotation(childGetter, HIDDEN.class);
if (onlyVisible && hidden != null && hidden.value()){
continue;
}
Class<? extends Model> childModelClass = getChildModelClass(childGetter);
childModels.add(childModelClass);
}
return childModels;
}
private List<String> allfields = null;
private Map<String,List<String>> columnFields = new IgnoreCaseMap<List<String>>();
private Map<String,String> fieldColumn = new IgnoreCaseMap<String>();
private void loadAllFields(){
Timer timer = cat.startTimer();
try {
if (allfields != null){
return;
}
synchronized (this) {
if (allfields != null){
return;
}
List<Method> fieldGetters = getFieldGetters();
List<String> allfields = new IgnoreCaseList(false);
for (Method fieldGetter : fieldGetters){
String fieldName = getFieldName(fieldGetter);
Map<Class<? extends Annotation>,Annotation> map = getAnnotationMap(fieldGetter);
COLUMN_NAME name = (COLUMN_NAME)map.get(COLUMN_NAME.class);
String columnName = ( name == null ? fieldName : name.value());
allfields.add(fieldName);
List<String> fields = columnFields.get(columnName);
if (fields == null){
fields = new IgnoreCaseList(false);
columnFields.put(columnName, fields);
}
fields.add(fieldName);
fieldColumn.put(fieldName, columnName);
}
this.allfields=allfields;
}
}finally {
timer.stop();
}
}
public List<String> getFields(){
Timer timer = cat.startTimer();
try {
loadAllFields();
return new IgnoreCaseList(false,allfields);
}finally{
timer.stop();
}
}
private SequenceSet<String> indexedColumns = null;
public List<String> getIndexedColumns(){
if (indexedColumns == null){
synchronized (this) {
if (indexedColumns == null) {
SequenceSet<String> indexedColumns = new SequenceSet<String>();
for (Method indexedFieldGetter : getIndexedFieldGetters()){
String indexColumnName = getColumnDescriptor(getFieldName(indexedFieldGetter)).getName();
indexedColumns.add(indexColumnName);
}
this.indexedColumns = indexedColumns;
}
}
}
return indexedColumns;
}
private SequenceSet<String> indexedFields = null ;
public SequenceSet<String> getIndexedFields(){
if (indexedFields == null){
synchronized (this) {
if (indexedFields == null){
SequenceSet<String> indexedFields = new SequenceSet<String>();
for (Method indexedFieldGetter : getIndexedFieldGetters()){
indexedFields.add(getFieldName(indexedFieldGetter));
}
this.indexedFields = indexedFields;
}
}
}
return indexedFields;
}
private Cache<String,UniqueKey<M>> uniqueKeys = null;
public Collection<UniqueKey<M>> getUniqueKeys(){
if (uniqueKeys == null){
synchronized (this) {
if (uniqueKeys == null){
Cache<String, UniqueKey<M>> uniqueKeys = new Cache<String, UniqueKey<M>>() {
private static final long serialVersionUID = 1892299842617679145L;
@Override
protected UniqueKey<M> getValue(String keyName) {
return new UniqueKey<M>(getModelClass(), keyName);
}
};
for (Method fieldGetter : getFieldGetters()){
UNIQUE_KEY key = this.getAnnotation(fieldGetter, UNIQUE_KEY.class);
if (key != null){
String fieldName = getFieldName(fieldGetter);
StringTokenizer keys = new StringTokenizer(key.value(),",");
while (keys.hasMoreTokens()){
String keyName = keys.nextToken();
UniqueKey<M> uk = uniqueKeys.get(keyName);
uk.addField(fieldName,key.exportable(),key.allowMultipleRecordsWithNull());
}
}
}
this.uniqueKeys = uniqueKeys;
}
}
}
return uniqueKeys.values();
}
public <T> boolean isDirty(T recordOrProxy,String fieldName) {
Model record = null;
Record rawRecord = null ;
if (Record.class.isInstance(recordOrProxy)){
rawRecord = (Record)recordOrProxy;
}else if (Proxy.isProxyClass(recordOrProxy.getClass()) && (recordOrProxy instanceof Model)){
record = (Model)recordOrProxy;
}else {
throw new RuntimeException ("Don't know how to get " + fieldName );
}
if (rawRecord == null){
rawRecord = record.getRawRecord();
}
return rawRecord.isFieldDirty(fieldName);
}
public <T> Collection<Expression> sgetUniqueKeyConditions(T recordOrProxy){
return getUniqueKeyConditions(recordOrProxy, false);
}
public <T> Collection<Expression> getUniqueKeyConditions(T recordOrProxy,boolean onlyIfContainsDirtyFields){
List<Expression> col = new ArrayList<Expression>();
for (UniqueKey<M> key : getUniqueKeys() ){
Expression where = new Expression(getPool(),Conjunction.AND);
boolean ignoreWhereClause = false;
boolean ret = false;
for (UniqueKeyFieldDescriptor<M> fd: key.getFields()){
String fieldName = fd.getFieldName();
ret = ret || !onlyIfContainsDirtyFields || isDirty(recordOrProxy,fieldName);
Object value = get(recordOrProxy, fieldName);
if (value != null){
where.add(new Expression(getPool(),getColumnDescriptor(fd.getFieldName()).getName(),Operator.EQ, value));
}else {
if (!fd.isMultipleRecordsWithNullAllowed()){
where.add(new Expression(getPool(),getColumnDescriptor(fd.getFieldName()).getName(),Operator.EQ));
}else {
ignoreWhereClause = true;
break;
}
}
}
if (!ignoreWhereClause && ret){
col.add(where);
}
}
return col;
}
public List<String> getUniqueFields(){
return getUniqueFields(null);
}
public List<String> getUniqueFields(String keyName){
SequenceSet<String> uniqueFields = new SequenceSet<String>();
for (UniqueKey<M> key : getUniqueKeys()) {
if (keyName == null || key.getKeyName().equalsIgnoreCase(keyName)){
for (UniqueKeyFieldDescriptor<M> fd: key.getFields()){
uniqueFields.add(fd.getFieldName());
}
if (keyName != null) {
break;
}
}
}
return uniqueFields;
}
private List<UniqueKey<M>> singleColumnUniqueKeys = null;
public Collection<UniqueKey<M>> getSingleColumnUniqueKeys(){
if (singleColumnUniqueKeys == null){
synchronized (this) {
if (singleColumnUniqueKeys == null){
ArrayList<UniqueKey<M>> singleColumnUniqueKeys = new ArrayList<UniqueKey<M>>();
for (UniqueKey<M> key: getUniqueKeys()){
if (key.size() == 1){
singleColumnUniqueKeys.add(key);
}
}
this.singleColumnUniqueKeys = singleColumnUniqueKeys;
}
}
}
return singleColumnUniqueKeys;
}
private List<String> editableFields = null;
public List<String> getEditableFields(){
if (editableFields == null){
synchronized (this) {
SequenceSet<String> editableFields = new SequenceSet<String>();
for (String field:getFields()){
if (isFieldEditable(field)){
editableFields.add(field);
}
}
this.editableFields = editableFields;
}
}
return editableFields;
}
public List<String> getRealFields(){
return getFields(new RealFieldMatcher());
}
public List<String> getVirtualFields(){
return getFields(new VirtualFieldMatcher());
}
public List<String> getFields(FieldMatcher matcher){
loadAllFields();
List<String> fields = new IgnoreCaseList(false);
for (String field: allfields){
if (matcher == null || matcher.matches(getColumnDescriptor(field))){
fields.add(field);
}
}
return fields;
}
public List<String> getRealColumns(){
return getColumns(new RealFieldMatcher());
}
public List<String> getColumns(FieldMatcher matcher){
Timer timer = cat.startTimer();
try {
List<String> fields = getFields(matcher);
List<String> columns = new IgnoreCaseList(false);
for (String field:fields){
columns.add(getColumnDescriptor(field).getName());
}
return columns;
}finally{
timer.stop();
}
}
public boolean isFieldExportable(String fieldName){
Method fieldGetter = getFieldGetter(fieldName);
EXPORTABLE exportable = getAnnotation(fieldGetter,EXPORTABLE.class);
if (exportable != null && !exportable.value()){
return false;
}
if (isHouseKeepingField(fieldName)){
if (getUniqueKeys().size() > 0 || !"ID".equals(fieldName)){
return false;
}
}
return true;
}
public boolean isFieldCopiedWhileCloning(String fieldName){
Method fieldGetter = getFieldGetter(fieldName);
CLONING_PROTECT cloningProtect = getAnnotation(fieldGetter, CLONING_PROTECT.class);
boolean protectedFromCloning = false;
if (cloningProtect == null){
protectedFromCloning = isHouseKeepingField(fieldName);
}else {
protectedFromCloning = cloningProtect.value();
}
return !protectedFromCloning;
}
public boolean isFieldMandatory(String fieldName){
return !getColumnDescriptor(fieldName).isNullable();
}
public boolean isFieldEditable(String fieldName){
return isFieldVisible(fieldName) && isFieldSettable(fieldName) && !isFieldProtected(fieldName) ;
}
public boolean isFieldSettable(String fieldName){
loadFieldSetters();
return fieldSetterMap.containsKey(fieldName);
}
public boolean isFieldGettable(String fieldName){
loadFieldGetters();
return fieldGetterMap.containsKey(fieldName);
}
public boolean isFieldVisible(String fieldName) {
return !isFieldHidden(fieldName);
}
public boolean isFieldHidden(String fieldName){
Method getter = getFieldGetter(fieldName);
HIDDEN hidden = getAnnotation(getter,HIDDEN.class);
boolean isHidden = (hidden == null ? false : hidden.value());
if (!isHidden){
boolean hideHouseKeepingFields = (Boolean) Database.getJdbcTypeHelper(getPool()).getTypeRef(Boolean.class).getTypeConverter().valueOf(Config.instance().getProperty("swf.hide.housekeeping.fields","N"));
isHidden = hideHouseKeepingFields && isHouseKeepingField(fieldName);
}
return isHidden;
}
public String getFieldName(final String columnOrFieldName){
Timer timer = cat.startTimer();
try {
loadAllFields();
//Mostly column name and fieldnames are same.
if (!fieldColumn.containsKey(columnOrFieldName)){
List<String> fields = columnFields.get(columnOrFieldName);
int numFields = fields == null ? 0 : fields.size();
if (numFields == 1){
return fields.get(0);
}else if (numFields == 0){
cat.finest("Field not found for Column " + columnOrFieldName + " in " + getTableName() + ", Model Is " + getModelClass().getName());
return null;
}
}
return columnOrFieldName;
}finally{
timer.stop();
}
}
private SWFLogger cat = null;
public boolean isHouseKeepingField(String fieldName){
Method getter = getFieldGetter(fieldName);
return isAnnotationPresent(getter,HOUSEKEEPING.class);
}
public boolean isFieldPassword(String fieldName){
Method getter = getFieldGetter(fieldName);
return isAnnotationPresent(getter,PASSWORD.class);
}
public boolean isFieldProtected(String fieldName){
return (getFieldProtection(fieldName) != Kind.EDITABLE);
}
public boolean isFieldDisabled(String fieldName){
return (getFieldProtection(fieldName) == Kind.DISABLED || !isFieldSettable(fieldName));
}
public Kind getFieldProtection(String fieldName){
Method getter = getFieldGetter(fieldName);
PROTECTION p = getAnnotation(getter,PROTECTION.class);
return p == null ? Kind.EDITABLE : p.value();
}
public boolean isFieldVirtual(String fieldName){
Method getter = getFieldGetter(fieldName);
IS_VIRTUAL p = getAnnotation(getter,IS_VIRTUAL.class);
return (p == null ? false : p.value());
}
public boolean isFieldEnumeration(String fieldName){
Method getter = getFieldGetter(fieldName);
return isAnnotationPresent(getter,Enumeration.class);
}
public boolean isVirtual(){
IS_VIRTUAL isVirtual = getAnnotation(IS_VIRTUAL.class);
if (isVirtual != null && isVirtual.value()) {
return true;
}
return false;
}
public String getPool(){
return reflector.getPool();
}
public int getMaxDataLength(String fieldName){
ColumnDescriptor fieldDescriptor = getColumnDescriptor(fieldName);
String fieldColumnName = fieldDescriptor.getName();
int size = fieldDescriptor.getSize();
Class<?> javaClass = getFieldGetter(fieldName).getReturnType();
if (String.class.isAssignableFrom(javaClass) || Reader.class.isAssignableFrom(javaClass)){
List<Count> counts = new ArrayList<Count>();
if (!isVirtual() && !fieldDescriptor.isVirtual()){
counts = new Select("MAX(LENGTH("+ fieldColumnName + ")) AS COUNT").from(modelClass).execute(Count.class);
if (!counts.isEmpty()){
size = counts.get(0).getCount();
}else {
size = MAX_DATA_LENGTH_FOR_TEXT_BOX;
}
}else {
size = fieldDescriptor.getSize();
size = size == 0 ? MAX_DATA_LENGTH_FOR_TEXT_BOX + 1 : size;
}
}
return size;
}
public boolean isFieldDisplayLongForTextBox(String fieldName){
if (isFieldValueALongText(fieldName)){
return true;
}else {
Method getter = getFieldGetter(fieldName);
Method referredModelGetter = getReferredModelGetterFor(getter);
if (referredModelGetter != null){
Class<? extends Model> referredModelClass = getReferredModelClass(referredModelGetter);
ModelReflector<? extends Model> referredModelReflector = ModelReflector.instance(referredModelClass);
if (referredModelReflector != this || !ObjectUtil.equals(fieldName,referredModelReflector.getDescriptionField())){
return referredModelReflector.isFieldValueALongText(referredModelReflector.getDescriptionField());
}
}
return false;
}
}
public static final int MAX_DATA_LENGTH_FOR_TEXT_BOX = 80 ;
public boolean isFieldValueALongText(String fieldName){
return isFieldValueALongText(fieldName,null);
}
public boolean isFieldValueALongText(String fieldName,Object fieldValue){
Method getter = getFieldGetter(fieldName);
Class<?> returnType = getter.getReturnType();
if (Reader.class.isAssignableFrom(returnType) ||
(String.class.isAssignableFrom(returnType))){
int len = getMaxDataLength(fieldName);
if (!isFieldEditable(fieldName) && String.class.isAssignableFrom(returnType)){
len = WordWrapUtil.getNumRowsRequired(StringUtil.valueOf(fieldValue),MAX_DATA_LENGTH_FOR_TEXT_BOX) * MAX_DATA_LENGTH_FOR_TEXT_BOX;
}
return (len > MAX_DATA_LENGTH_FOR_TEXT_BOX) ;
}
return false;
}
private Cache<Method,String> fieldNameCache = new Cache<Method, String>() {
/**
*
*/
private static final long serialVersionUID = 4626497380273214264L;
@Override
protected String getValue(Method method) {
String fieldName = null;
if (getFieldGetterMatcher().matches(method) ){
for (String getterPrefix:getterPrefixes){
if (method.getName().startsWith(getterPrefix)){
fieldName = StringUtil.underscorize(method.getName().substring(getterPrefix.length()));
break;
}
}
}else if(getFieldSetterMatcher().matches(method)){
fieldName = StringUtil.underscorize(method.getName().substring(3));
}
return fieldName;
}
};
public String getFieldName(Method method){
Timer timer = cat.startTimer();
try {
return fieldNameCache.get(method);
}finally{
timer.stop();
}
}
public String getContentType(Model record, String fieldName){
String mimeType = null ;
List<String> fields = getFields();
if (fields.contains(fieldName)){
Method getter = getFieldGetter(fieldName);
if (InputStream.class.isAssignableFrom(getter.getReturnType())){
CONTENT_TYPE ct = getAnnotation(getter,CONTENT_TYPE.class);
if (ct != null){
mimeType = ct.value().toString();
}else {
String contentTypeFieldName = fieldName + "_CONTENT_TYPE";
if (getFields().contains(contentTypeFieldName)){
mimeType = get(record,contentTypeFieldName);
}
}
}
}
if (mimeType == null){
mimeType = getDefaultContentType();
}
return mimeType;
}
public String getDefaultContentType(){
return MimeType.APPLICATION_OCTET_STREAM.toString();
}
public String getContentName(Model record, String fieldName){
List<String> fields = getFields();
if (fields.contains(fieldName)){
Method getter = getFieldGetter(fieldName);
if (InputStream.class.isAssignableFrom(getter.getReturnType())){
String fileName = fieldName + "_CONTENT_NAME";
if (fields.contains(fileName)){
return get(record,fileName);
}
}
}
return null;
}
public int getContentSize(Model record, String fieldName){
List<String> fields = getFields();
if (fields.contains(fieldName)){
Method getter = getFieldGetter(fieldName);
if (InputStream.class.isAssignableFrom(getter.getReturnType())){
String sizeFieldName = fieldName + "_CONTENT_SIZE";
if (fields.contains(sizeFieldName)){
Integer i = get(record,sizeFieldName);
return i;
}else {
InputStream is = get(record,fieldName);
try {
if (is != null){
return is.available();
}else {
return 0;
}
} catch (IOException e) {
//
}
}
}
}
return -1;
}
private static final String[] getterPrefixes = new String[]{"get" , "is"};
private Map<String,Method> fieldGetterMap = null ;
public void loadFieldGetters(){
if (fieldGetterMap == null){
synchronized (this) {
if (fieldGetterMap == null) {
IgnoreCaseMap<Method> fieldGetterMap = new IgnoreCaseMap<Method>();
List<Method> fieldGetters = getFieldGetters();
for (Method fieldGetter: fieldGetters){
fieldGetterMap.put(getFieldName(fieldGetter), fieldGetter);
}
this.fieldGetterMap = fieldGetterMap;
}
}
}
}
public Method getFieldGetter(String fieldName){
Timer timer = cat.startTimer();
try {
loadFieldGetters();
Method getter = fieldGetterMap.get(fieldName);
if (getter == null){
String getterName = "get/is" + StringUtil.camelize(fieldName);
throw new FieldGetterMissingException("Method " + getterName + "() with appropriate return type is missing");
}
return getter;
}finally{
timer.stop();
}
}
private Map<String,Method> fieldSetterMap = null;
private void loadFieldSetters(){
if (fieldSetterMap == null){
synchronized (this) {
if (fieldSetterMap == null){
IgnoreCaseMap<Method> fieldSetterMap = new IgnoreCaseMap<Method>();
List<Method> fieldSetters = getFieldSetters();
for (Method fieldSetter: fieldSetters){
fieldSetterMap.put(getFieldName(fieldSetter), fieldSetter);
}
this.fieldSetterMap = fieldSetterMap;
}
}
}
}
public Method getFieldSetter(String fieldName){
Timer timer = cat.startTimer();
try {
loadFieldSetters();
Method setter = fieldSetterMap.get(fieldName);
if (setter == null){
Method getter = getFieldGetter(fieldName);
String setterName = "set"+StringUtil.camelize(fieldName) +"(" + getter.getReturnType().getName() + ")";
throw new FieldSetterMissingException("Method: public void " + setterName + " missing!");
}
return setter;
}finally{
timer.stop();
}
}
public static class FieldSetterMissingException extends RuntimeException {
private static final long serialVersionUID = 5976842300991239658L;
public FieldSetterMissingException(String message){
super(message);
}
}
public static class FieldGetterMissingException extends RuntimeException {
private static final long serialVersionUID = 5976842300991239658L;
public FieldGetterMissingException(String message){
super(message);
}
}
public String getSignature(Method method){
return reflector.getSignature(method);
}
private Map<String,Map<Class<? extends Annotation>, Annotation>> annotationMap = new HashMap<String, Map<Class<? extends Annotation>,Annotation>>();
private Map<Class<? extends Annotation>, Annotation> getAnnotationMap(Method getter){
Timer timer = cat.startTimer();
try {
String signature = getSignature(getter);
Map<Class<? extends Annotation>, Annotation> map = annotationMap.get(signature);
if (map != null){
return map;
}
synchronized (this) {
map = annotationMap.get(signature);
if (map == null) {
map = new HashMap<Class<? extends Annotation>, Annotation>();
for (Class<? extends Model> modelClass:reflector.getSiblingModelClasses(getModelClass())){
MReflector<? extends Model> ref = MReflector.instance(modelClass);
//We could have simple called getAnnotation(getter,Annotation class) but that would mean looping multiple times for
//each annotation needed. hence this optimization.
if (map.get(COLUMN_NAME.class) == null){ map.put(COLUMN_NAME.class,ref.getAnnotation(getter,COLUMN_NAME.class)); }
if (map.get(COLUMN_SIZE.class) == null){ map.put(COLUMN_SIZE.class,ref.getAnnotation(getter,COLUMN_SIZE.class)); }
if (map.get(DATA_TYPE.class ) == null){ map.put(DATA_TYPE.class ,ref.getAnnotation(getter, DATA_TYPE.class)); }
if (map.get(DECIMAL_DIGITS.class) == null){ map.put(DECIMAL_DIGITS.class,ref.getAnnotation(getter,DECIMAL_DIGITS.class)); }
if (map.get(IS_NULLABLE.class) == null){ map.put(IS_NULLABLE.class,ref.getAnnotation(getter,IS_NULLABLE.class)); }
if (map.get(IS_AUTOINCREMENT.class) == null){ map.put(IS_AUTOINCREMENT.class,ref.getAnnotation(getter,IS_AUTOINCREMENT.class)); }
if (map.get(IS_VIRTUAL.class) == null){ map.put(IS_VIRTUAL.class,ref.getAnnotation(getter,IS_VIRTUAL.class)); }
if (map.get(COLUMN_DEF.class) == null){ map.put(COLUMN_DEF.class,ref.getAnnotation(getter,COLUMN_DEF.class)); }
}
annotationMap.put(signature, map);
}
}
return map;
}finally{
timer.stop();
}
}
public ColumnDescriptor getColumnDescriptor(String fieldName){
Timer timer =cat.startTimer(null, Config.instance().isTimerAdditive());
try {
return getColumnDescriptors().get(fieldName);
}finally{
timer.stop();
}
}
/*
public ColumnDescriptor getColumnDescriptor(Method fieldGetter){
Timer timer =cat.startTimer(null, Config.instance().isTimerAdditive());
try {
return getColumnDescriptor(getFieldName(fieldGetter));
}finally{
timer.stop();
}
}*/
public boolean hasMultipleAccess(String columnName){
Timer timer = cat.startTimer();
try {
List<String> fields = columnFields.get(columnName);
return (fields != null && fields.size() > 1);
}finally{
timer.stop();
}
}
private class ColumnDescriptorCache extends Cache<String,ColumnDescriptor>{
private static final long serialVersionUID = 302310307824397179L;
public ColumnDescriptorCache(){
super(Cache.MAX_ENTRIES_UNLIMITED,0);
loadAllFields();
}
@Override
protected ColumnDescriptor getValue(String fieldName) {
Timer timer =cat.startTimer(null, Config.instance().isTimerAdditive());
try {
Method fieldGetter = getFieldGetter(fieldName);
if (!getFieldGetters().contains(fieldGetter)){
throw new RuntimeException("Method:" + fieldGetter.getName() + " is not recognizable as a a FieldGetter");
}
//String fieldName = getFieldName(fieldGetter);
String columnName = fieldColumn.get(fieldName);
if (hasMultipleAccess(columnName)){
if (!columnFields.get(columnName).contains(columnName)){
throw new RuntimeException(columnName + " has multiple access while none of the field has the same name!(" + columnFields.get(columnName).toString() + ")");
}else if (!columnName.equalsIgnoreCase(fieldName)){
return get(columnName);
}
}
Map<Class<? extends Annotation>, Annotation> map = getAnnotationMap(fieldGetter);
COLUMN_SIZE size = (COLUMN_SIZE) map.get(COLUMN_SIZE.class);
DATA_TYPE type = (DATA_TYPE) map.get(DATA_TYPE.class);
DECIMAL_DIGITS digits = (DECIMAL_DIGITS) map.get(DECIMAL_DIGITS.class);
IS_NULLABLE isNullable = (IS_NULLABLE)map.get(IS_NULLABLE.class);
IS_AUTOINCREMENT isAutoIncrement = (IS_AUTOINCREMENT)map.get(IS_AUTOINCREMENT.class);
IS_VIRTUAL isVirtual = (IS_VIRTUAL)map.get(IS_VIRTUAL.class);
COLUMN_DEF colDef = (COLUMN_DEF)map.get(COLUMN_DEF.class);
ColumnDescriptor cd = new ColumnDescriptor(getPool());
cd.setName(columnName);
JdbcTypeHelper helper = Database.getJdbcTypeHelper(getPool());
TypeRef<?> typeRef = helper.getTypeRef(fieldGetter.getReturnType());
assert typeRef != null;
cd.setJDBCType(type == null ? typeRef.getJdbcType() : type.value());
cd.setNullable(isNullable != null ? isNullable.value() : !fieldGetter.getReturnType().isPrimitive());
cd.setSize(size == null? typeRef.getSize() : size.value());
cd.setScale(digits == null ? typeRef.getScale() : digits.value());
cd.setAutoIncrement(isAutoIncrement == null? false : true);
cd.setVirtual(isVirtual == null ? false : isVirtual.value());
if (colDef != null && colDef.value() != StandardDefault.NONE){
cd.setColumnDefault(toDefaultKW(typeRef,colDef));
}
return cd;
}finally {
timer.stop();
}
}
}
private ColumnDescriptorCache columnDescriptors = null;
private ColumnDescriptorCache getColumnDescriptors(){
if (columnDescriptors == null){
synchronized (this) {
if (columnDescriptors == null) {
columnDescriptors = new ColumnDescriptorCache();
}
}
}
return columnDescriptors;
}
private String toDefaultKW(TypeRef<?> ref, COLUMN_DEF def){
return Database.getJdbcTypeHelper(getPool()).toDefaultKW(ref,def);
}
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass){
return getAnnotation(annotationClass) != null;
}
public boolean isAnnotationPresent(Method method, Class<? extends Annotation> annotationClass ){
return getAnnotation(method, annotationClass) != null;
}
private Cache<Class<? extends Annotation>,Annotation> classAnnotationCache = new Cache<Class<? extends Annotation>, Annotation>(){
/**
*
*/
private static final long serialVersionUID = -4698644911072168124L;
@Override
protected Annotation getValue(Class<? extends Annotation> annotationClass) {
Annotation a = reflector.getAnnotation(getModelClass(), annotationClass);
return a;
}
};
@SuppressWarnings("unchecked")
public <A extends Annotation> A getAnnotation(Class<A> annotationClass){
return (A)classAnnotationCache.get(annotationClass);
}
private Cache<Method,Cache<Class<? extends Annotation>,Annotation>> methodAnnotationCache = new Cache<Method, Cache<Class<? extends Annotation>,Annotation>>(){
/**
*
*/
private static final long serialVersionUID = 4256698883995018084L;
@Override
protected Cache<Class<? extends Annotation>, Annotation> getValue(final Method k) {
Timer timer = cat.startTimer();
try {
return new Cache<Class<? extends Annotation>, Annotation>() {
/**
*
*/
private static final long serialVersionUID = 4851400562811398820L;
@Override
protected Annotation getValue(
Class<? extends Annotation> annotationClass) {
Timer timer = cat.startTimer();
try {
return reflector.getAnnotation(getModelClass(),k,
annotationClass);
} finally {
timer.stop();
}
}
};
} finally {
timer.stop();
}
}
};
@SuppressWarnings("unchecked")
public <A extends Annotation> A getAnnotation(Method method, Class<A> annotationClass){
return (A)methodAnnotationCache.get(method).get(annotationClass);
}
public Class<? extends Model> getChildModelClass(Method method){
Timer timer = cat.startTimer();
try {
Class<?> possibleChildClass = null;
if (!getClassForests().contains(method.getDeclaringClass())){
return null;
}
if (getGetterMatcher().matches(method)){
Class<?> retType = method.getReturnType();
if (List.class.isAssignableFrom(retType)){
ParameterizedType parameterizedType = (ParameterizedType)method.getGenericReturnType();
possibleChildClass = (Class<?>)parameterizedType.getActualTypeArguments()[0];
}
if (possibleChildClass != null && Model.class.isAssignableFrom(possibleChildClass)){
// Validate That child has a parentReferenceId.
@SuppressWarnings("unchecked")
Class<? extends Model> childClass = (Class<? extends Model>)possibleChildClass;
ModelReflector<? extends Model> childReflector = ModelReflector.instance(childClass);
if (!childReflector.getReferenceFields(getModelClass()).isEmpty()){
return childClass;
}
}
}
return null;
}finally{
timer.stop();
}
}
public List<String> getReferenceFields(Class<? extends Model> referredModelClass){
Timer timer = cat.startTimer();
try {
List<String> names = new ArrayList<String>();
for (Method referredModelGetter : getReferredModelGetters(referredModelClass)){
names.add(getReferenceField(referredModelGetter));
}
return names;
}finally{
timer.stop();
}
}
@SuppressWarnings("unchecked")
public List<Method> getReferredModelGetters(final Class<? extends Model> referredModelClass){
Timer timer = cat.startTimer();
try {
ModelReflector<? extends Model> referredModelReflector = ModelReflector.instance(referredModelClass);
List<Method> referredModelGetters = getReferredModelGetters();
List<Method> ret = new ArrayList<Method>();
for (Method aReferredModelGetter: referredModelGetters){
if (referredModelReflector.reflects((Class <? extends Model>)aReferredModelGetter.getReturnType())){
ret.add(aReferredModelGetter);
}
}
return ret;
}finally{
timer.stop();
}
}
@SuppressWarnings("unchecked")
public Class<? extends Model> getReferredModelClass(Method referredModelGetter) {
Timer timer = cat.startTimer();
try {
if (!getClassForests().contains(referredModelGetter.getDeclaringClass())) {
return null;
}
Class<? extends Model> referredModelClass = null;
Class<?> possibleReferredModelClass = referredModelGetter.getReturnType();
if (Model.class.isAssignableFrom(possibleReferredModelClass)
&& getGetterMatcher().matches(referredModelGetter)) {
String referredIdFieldName = getReferenceField(referredModelGetter);
if (getFields().contains(referredIdFieldName)) {
referredModelClass = (Class<? extends Model>) possibleReferredModelClass;
}
}
return referredModelClass;
} finally {
timer.stop();
}
}
Map<Method,String> referredModelGetterToReferenceFieldMap = new HashMap<Method, String>();
public String getReferenceField(Method parentGetter){
String field = referredModelGetterToReferenceFieldMap.get(parentGetter);
if (field == null && !referredModelGetterToReferenceFieldMap.containsKey(parentGetter)){
field = StringUtil.underscorize(parentGetter.getName().substring(3) + "Id");
referredModelGetterToReferenceFieldMap.put(parentGetter, field);
}
return field;
}
private Map<Method,Method> referredModelIdGetterToReferredModelGetterMap = new HashMap<Method, Method>();
public Method getReferredModelGetterFor(Method referredModelIdGetter) {
if (!getFieldGetters().contains(referredModelIdGetter)) {
return null;
}
Method ret = referredModelIdGetterToReferredModelGetterMap.get(referredModelIdGetter);
if (ret == null && !referredModelIdGetterToReferredModelGetterMap.containsKey(referredModelIdGetter)) {
String methodName = referredModelIdGetter.getName();
if (methodName.startsWith("get")
&& methodName.endsWith("Id")
&& methodName.length() > 5 // Not = getId
&& (referredModelIdGetter.getReturnType() == int.class || referredModelIdGetter
.getReturnType() == Integer.class)) {
String referredModelMethodName = methodName.substring(0,
methodName.length() - "Id".length());
for (Class<? extends Model> modelClass : reflector
.getSiblingModelClasses(getModelClass())) {
try {
Method referredModelGetter = modelClass
.getMethod(referredModelMethodName);
if (Model.class.isAssignableFrom(referredModelGetter
.getReturnType())) {
ret = referredModelGetter;
break;
}
} catch (NoSuchMethodException ex) {
//
}
}
}
referredModelIdGetterToReferredModelGetterMap.put(referredModelIdGetter, ret);
}
return ret;
}
private SequenceSet<Class<? extends Model>> classHierarchies = null;
public SequenceSet<Class<? extends Model>> getClassHierarchies() {
if (classHierarchies == null){
synchronized (this) {
if (classHierarchies == null) {
classHierarchies = reflector.getClassHierarchies(getModelClass());
}
}
}
return classHierarchies;
}
private SequenceSet<Class<?>> classForests = null;
public SequenceSet<Class<?>> getClassForests(){
if (classForests == null){
synchronized (this) {
if (classForests == null){
classForests = reflector.getClassForests(getModelClass());
}
}
}
return classForests;
}
//Field MAtchers
public static interface FieldMatcher {
public boolean matches(ColumnDescriptor cd);
}
private class RealFieldMatcher implements FieldMatcher {
public boolean matches(ColumnDescriptor cd) {
return !cd.isVirtual();
}
}
private class VirtualFieldMatcher implements FieldMatcher {
public boolean matches(ColumnDescriptor cd) {
return cd.isVirtual();
}
}
//Method Matcher
private final MethodMatcher getterMatcher = new GetterMatcher();
public MethodMatcher getGetterMatcher(){
return getterMatcher;
}
public class GetterMatcher implements MethodMatcher{
public boolean matches(Method method){
Timer timer = cat.startTimer();
try {
String mName = method.getName();
Class<?> retType = method.getReturnType();
Class<?>[] paramTypes = method.getParameterTypes();
if ( ((mName.startsWith("get") && retType != Void.TYPE) ||
mName.startsWith("is") && (boolean.class == retType || Boolean.class == retType) ) &&
(paramTypes == null || paramTypes.length == 0)){
return true;
}
return false;
} finally {
timer.stop();
}
}
}
private final MethodMatcher fieldGetterMatcher = new FieldGetterMatcher();
public MethodMatcher getFieldGetterMatcher() {
return fieldGetterMatcher;
}
private final MethodMatcher indexedFieldGetterMatcher = new IndexedFieldGetterMatcher();
public MethodMatcher getIndexedFieldGetterMatcher() {
return indexedFieldGetterMatcher;
}
public class IndexedFieldGetterMatcher extends FieldGetterMatcher {
@Override
public boolean matches(Method method){
return super.matches(method) && isAnnotationPresent(method,Index.class) ;
}
}
public class FieldGetterMatcher extends GetterMatcher{
@Override
public boolean matches(Method method){
Timer timer = cat.startTimer();
try {
if (super.matches(method) &&
!Model.class.isAssignableFrom(method.getReturnType()) &&
Database.getJdbcTypeHelper(getPool()).getTypeRef(method.getReturnType()) != null){
return true;
}
return false;
}finally{
timer.stop();
}
}
}
public class SetterMatcher implements MethodMatcher{
public boolean matches(Method method){
Timer timer = cat.startTimer();
try {
String mName = method.getName();
Class<?> retType = method.getReturnType();
Class<?>[] paramTypes = method.getParameterTypes();
if (mName.startsWith("set") && (Void.TYPE == retType) &&
(paramTypes != null && paramTypes.length == 1) ){
return true;
}
return false;
}finally{
timer.stop();
}
}
}
private final MethodMatcher fieldSetterMatcher = new FieldSetterMatcher();
public MethodMatcher getFieldSetterMatcher() {
return fieldSetterMatcher;
}
public class FieldSetterMatcher extends SetterMatcher{
@Override
public boolean matches(Method method){
Timer timer = cat.startTimer();
try {
if (super.matches(method)
&& Database.getJdbcTypeHelper(getPool()).getTypeRef(
method.getParameterTypes()[0]) != null) {
return true;
}
return false;
} finally {
timer.stop();
}
}
}
//RelationShip Methods matchers
private final MethodMatcher referredModelGetterMatcher= new ReferredModelGetterMatcher();
public MethodMatcher getReferredModelGetterMatcher(){
return referredModelGetterMatcher;
}
private class ReferredModelGetterMatcher implements MethodMatcher{
public boolean matches(Method method){
Timer timer = cat.startTimer();
try {
return getReferredModelClass(method) != null;
} finally {
timer.stop();
}
}
}
private final MethodMatcher participantModelGetterMatcher = new ParticipantModelGetterMatcher();
public MethodMatcher getParticipantModelGetterMatcher(){
return participantModelGetterMatcher;
}
private class ParticipantModelGetterMatcher extends ReferredModelGetterMatcher{
public boolean matches(Method method){
Timer timer = cat.startTimer();
try {
if (super.matches(method)){
return isAnnotationPresent(getFieldGetter(getReferenceField(method)), PARTICIPANT.class);
}
return false;
} finally {
timer.stop();
}
}
}
private final MethodMatcher childrenGetterMatcher= new ChildrenGetterMatcher();
public MethodMatcher getChildrenGetterMatcher(){
return childrenGetterMatcher;
}
private class ChildrenGetterMatcher implements MethodMatcher{
public boolean matches(Method method){
return (getChildModelClass(method) != null);
}
}
public String getOrderBy(){
ORDER_BY order = getAnnotation(ORDER_BY.class);
String orderBy = ORDER_BY.DEFAULT;
if (order != null){
orderBy = order.value();
}
return orderBy;
}
private Cache<String,String> participatingRole = new Cache<String, String>() {
/**
*
*/
private static final long serialVersionUID = 163426440760440104L;
@Override
protected String getValue(String referencedModelIdFieldName) {
return referencedModelIdFieldName.substring(0, referencedModelIdFieldName.length()-3) ; //Remove "_ID" from the end.
}
};
public String getParticipatingRole(String referencedModelIdFieldName){
return participatingRole.get(referencedModelIdFieldName);
}
private Set<String> participatableRoles = null;
public Set<String> getParticipatableRoles(){
if (participatableRoles == null){
synchronized (this) {
if (participatableRoles == null){
HashSet<String> participatableRoles = new HashSet<String>();
for (Method m : getParticipantModelGetters()){
participatableRoles.add(getParticipatingRole(getReferenceField(m)));
}
this.participatableRoles = participatableRoles;
}
}
}
return new HashSet<String>(participatableRoles);
}
public static void dispose() {
ModelReflector.modelReflectorByModelClass.clear();
}
private Set<String> autoIncrementColumns = null;
public Set<String> getAutoIncrementColumns() {
if (autoIncrementColumns == null){
synchronized (this) {
if (autoIncrementColumns == null){
autoIncrementColumns = new IgnoreCaseSet();
for (String f : getFields()){
ColumnDescriptor d = getColumnDescriptor(f);
if (d.isAutoIncrement()){
autoIncrementColumns.add(d.getName());
}
}
}
}
}
return Collections.unmodifiableSet(autoIncrementColumns);
}
}