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;
  }