protected FieldDescriptorImpl createFieldDesc(final Class javaClass, final FieldMapping fieldMap)
      throws MappingException {

    // If not an SQL field, return a stock field descriptor.
    Sql sql = fieldMap.getSql();
    if (sql == null) {
      return super.createFieldDesc(javaClass, fieldMap);
    }

    String fieldName = fieldMap.getName();

    // If the field type is supplied, grab it and use it to locate the
    // field/accessor.
    Class fieldType = null;
    if (fieldMap.getType() != null) {
      fieldType = resolveType(fieldMap.getType());
    }

    // If the field is declared as a collection, grab the collection type as
    // well and use it to locate the field/accessor.
    CollectionHandler colHandler = null;
    boolean isLazy = fieldMap.getLazy();
    if (fieldMap.getCollection() != null) {
      Class colType = CollectionHandlers.getCollectionType(fieldMap.getCollection().toString());
      colHandler = CollectionHandlers.getHandler(colType);

      if (colType.getName().equals("java.util.Iterator") && isLazy) {
        String err = "Lazy loading not supported for collection type 'iterator'";
        throw new MappingException(err);
      }

      if (colType.getName().equals("java.util.Enumeration") && isLazy) {
        String err = "Lazy loading not supported for collection type 'enumerate'";
        throw new MappingException(err);
      }
    }

    TypeInfo typeInfo = getTypeInfo(fieldType, colHandler, fieldMap);

    ExtendedFieldHandler exfHandler = null;
    FieldHandler handler = null;

    // -- check for user supplied FieldHandler
    if (fieldMap.getHandler() != null) {

      Class handlerClass = resolveType(fieldMap.getHandler());

      if (!FieldHandler.class.isAssignableFrom(handlerClass)) {
        String err =
            "The class '"
                + fieldMap.getHandler()
                + "' must implement "
                + FieldHandler.class.getName();
        throw new MappingException(err);
      }

      // -- get default constructor to invoke. We can't use the
      // -- newInstance method unfortunately becaue FieldHandler
      // -- overloads this method
      Constructor constructor = null;
      try {
        constructor = handlerClass.getConstructor(new Class[0]);
        handler = (FieldHandler) constructor.newInstance(new Object[0]);
      } catch (Exception except) {
        String err =
            "The class '" + handlerClass.getName() + "' must have a default public constructor.";
        throw new MappingException(err);
      }

      // -- ExtendedFieldHandler?
      if (handler instanceof ExtendedFieldHandler) {
        exfHandler = (ExtendedFieldHandler) handler;
      }

      // -- Fix for Castor JDO from Steve Vaughan, Castor JDO
      // -- requires FieldHandlerImpl or a ClassCastException
      // -- will be thrown... [KV 20030131 - also make sure this new handler
      // -- doesn't use it's own CollectionHandler otherwise
      // -- it'll cause unwanted calls to the getValue method during
      // -- unmarshalling]
      colHandler = typeInfo.getCollectionHandler();
      typeInfo.setCollectionHandler(null);
      handler = new FieldHandlerImpl(handler, typeInfo);
      typeInfo.setCollectionHandler(colHandler);
      // -- End Castor JDO fix

    }

    boolean generalized = (exfHandler instanceof GeneralizedFieldHandler);

    // -- if generalized we need to change the fieldType to whatever
    // -- is specified in the GeneralizedFieldHandler so that the
    // -- correct getter/setter methods can be found
    FieldHandler custom = handler;
    if (generalized) {
      fieldType = ((GeneralizedFieldHandler) exfHandler).getFieldType();
    }

    if (generalized || (handler == null)) {
      // -- create TypeInfoRef to get new TypeInfo from call
      // -- to createFieldHandler
      TypeInfoReference typeInfoRef = new TypeInfoReference();
      typeInfoRef.typeInfo = typeInfo;
      handler = createFieldHandler(javaClass, fieldType, fieldMap, typeInfoRef);
      if (custom != null) {
        ((GeneralizedFieldHandler) exfHandler).setFieldHandler(handler);
        handler = custom;
      } else {
        typeInfo = typeInfoRef.typeInfo;
      }
    }

    String[] sqlName = sql.getName();

    String[] sqlTypes = getSqlTypes(fieldMap);

    int[] sqlTypeNum;
    if (sqlTypes.length > 0) {
      sqlTypeNum = new int[sqlTypes.length];
      for (int i = 0; i < sqlTypes.length; i++) {
        String sqlTypeString = definition2type(sqlTypes[i]);
        Class sqlType = SQLTypeInfos.sqlTypeName2javaType(sqlTypeString);
        if (_factory != null) {
          sqlType = _factory.adjustSqlType(sqlType);
        }
        sqlTypeNum[i] = SQLTypeInfos.javaType2sqlTypeNum(sqlType);
      }
    } else {
      Class sqlType = typeInfo.getFieldType();
      if (_factory != null) {
        sqlType = _factory.adjustSqlType(sqlType);
      }
      sqlTypeNum = new int[] {SQLTypeInfos.javaType2sqlTypeNum(sqlType)};
    }

    // create FieldDescriptor(Impl) instance, and apply JDO nature
    FieldDescriptorImpl fieldDescriptor =
        new FieldDescriptorImpl(fieldName, typeInfo, handler, fieldMap.getTransient());
    fieldDescriptor.addNature(FieldDescriptorJDONature.class.getName());

    fieldDescriptor.setRequired(fieldMap.getRequired());

    // If we're using an ExtendedFieldHandler we need to set the FieldDescriptor
    if (exfHandler != null) {
      ((FieldHandlerFriend) exfHandler).setFieldDescriptor(fieldDescriptor);
    }

    // if SQL mapping declares transient
    // TODO: cross-check that this should be implemented like this
    if (sql.getTransient()) {
      fieldDescriptor.setTransient(true);
    }

    // set collection type as specified in mapping file
    fieldDescriptor.setCollection(fieldMap.getCollection());

    fieldDescriptor.setComparator(fieldMap.getComparator());
    fieldDescriptor.setCreateMethod(fieldMap.getCreateMethod());
    fieldDescriptor.setGetMethod(fieldMap.getGetMethod());
    fieldDescriptor.setSetMethod(fieldMap.getSetMethod());
    fieldDescriptor.setDirect(fieldMap.getDirect());

    // extract values for 'laziness' from field mapping
    fieldDescriptor.setLazy(isLazy);

    FieldDescriptorJDONature fieldJdoNature = new FieldDescriptorJDONature(fieldDescriptor);

    fieldJdoNature.setTypeConvertor(typeInfo.getConvertorFrom());
    if (sqlName.length > 0) {
      fieldJdoNature.setSQLName(sqlName);
    }
    fieldJdoNature.setSQLType(sqlTypeNum);
    fieldJdoNature.setManyTable(sql.getManyTable());
    if (sql.getManyKey().length > 0) {
      fieldJdoNature.setManyKey(sql.getManyKey());
    }
    fieldJdoNature.setDirtyCheck(!SqlDirtyType.IGNORE.equals(sql.getDirty()));
    fieldJdoNature.setReadOnly(sql.getReadOnly());

    fieldJdoNature.setCascading(sql.getCascading());
    fieldJdoNature.setTransient(sql.getTransient());

    return fieldDescriptor;
  }
  protected TypeInfo getTypeInfo(
      final Class fieldType, final CollectionHandler colHandler, final FieldMapping fieldMap)
      throws MappingException {
    Class internalFieldType = Types.typeFromPrimitive(fieldType);
    String typeName = null;
    Class sqlType = null;
    String sqlParam = null;

    String[] sqlTypes = getSqlTypes(fieldMap);
    if ((fieldMap.getSql() != null) && (sqlTypes.length > 0)) {
      // --TO Check
      typeName = sqlTypes[0];
      sqlType = SQLTypeInfos.sqlTypeName2javaType(definition2type(typeName));
      sqlParam = definition2param(typeName);
    } else {
      sqlType = internalFieldType;
    }

    if (_factory != null) {
      sqlType = _factory.adjustSqlType(sqlType);
    }

    TypeConvertor convertorTo = null;
    TypeConvertor convertorFrom = null;

    if (internalFieldType != sqlType) {
      try {
        convertorTo = _typeConvertorRegistry.getConvertor(sqlType, internalFieldType, sqlParam);
      } catch (MappingException ex) {
        boolean isTypeSafeEnum = false;
        // -- check for type-safe enum style classes
        if ((internalFieldType != null) && (!isPrimitive(internalFieldType))) {
          // -- make sure no default constructor
          Constructor cons = null;
          try {
            cons = internalFieldType.getConstructor(EMPTY_ARGS);
            if (!Modifier.isPublic(cons.getModifiers())) {
              cons = null;
            }
          } catch (NoSuchMethodException nsmx) {
            // -- Do nothing
          }

          if (cons == null) {
            // -- make sure a valueOf factory method exists
            try {
              Method method = internalFieldType.getMethod(VALUE_OF, STRING_ARG);
              Class returnType = method.getReturnType();
              if ((returnType != null) && internalFieldType.isAssignableFrom(returnType)) {
                int mods = method.getModifiers();
                if (Modifier.isStatic(mods)) {
                  // create individual SQLTypeConverter
                  convertorTo = new String2EnumTypeConvertor(sqlType, internalFieldType, method);

                  Types.addEnumType(internalFieldType);

                  isTypeSafeEnum = true;
                }
              }
            } catch (NoSuchMethodException nsmx) {
              // -- Do nothing
            }
          }

          if (isTypeSafeEnum) {
            // -- check if name() method should be used instead of toString()
            try {
              Method method = internalFieldType.getMethod(NAME);
              if (String.class == method.getReturnType()) {
                convertorFrom = new Enum2StringTypeConvertor(internalFieldType, sqlType, method);
              }
            } catch (NoSuchMethodException nsmx) {
              // -- Do nothing
            }
          }
        }

        if (!isTypeSafeEnum) {
          throw new MappingException(
              "mapping.noConvertor", sqlType.getName(), internalFieldType.getName());
        }
      }

      if (convertorFrom == null) {
        convertorFrom = _typeConvertorRegistry.getConvertor(internalFieldType, sqlType, sqlParam);
      }

      if ((convertorTo != null) && (convertorFrom != null)) {
        Types.addConvertibleType(internalFieldType);
      }
    }

    return new TypeInfo(
        internalFieldType, convertorTo, convertorFrom, fieldMap.getRequired(), null, colHandler);
  }