private ToOne establishDomainClassRelationship()

in grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/config/GormMappingConfigurationStrategy.java [569:698]


    private ToOne establishDomainClassRelationship(PersistentEntity entity, PropertyDescriptor property, MappingContext context, Map hasOneMap, boolean embedded) {
        ToOne association = null;
        Class propType = property.getPropertyType();

        if (embedded && !isPersistentEntity(propType)) {
            // uni-directional to embedded non-entity
            PersistentEntity associatedEntity = getOrCreateEmbeddedEntity(entity, context, propType);
            association = propertyFactory.createEmbedded(entity, context, property);
            association.setAssociatedEntity(associatedEntity);
            return association;
        }

        ClassPropertyFetcher relatedCpf = ClassPropertyFetcher.forClass(propType);

        // establish relationship to type
        Map relatedClassRelationships = getAllAssociationMap(relatedCpf);
        Map mappedBy = getMapStaticProperty(relatedCpf, MAPPED_BY);

        Class<?> relatedClassPropertyType = null;

        // if there is a relationships map use that to find out
        // whether it is mapped to a Set
        String relatedClassPropertyName = null;

        if (!forceUnidirectional(property, mappedBy)) {
            if (relatedClassRelationships != null && !relatedClassRelationships.isEmpty()) {

                PropertyDescriptor[] descriptors = ReflectionUtils.getPropertiesOfType(entity.getJavaClass(), propType);
                relatedClassPropertyName = findOneToManyThatMatchesType(entity, property, relatedClassRelationships, mappedBy, relatedCpf);

                Object mappedByValue = relatedClassPropertyName != null ? mappedBy.get(relatedClassPropertyName) : null;
                if(mappedByValue != null && property.getName().equals(mappedByValue)) {

                    relatedClassPropertyType = relatedCpf.getPropertyType(relatedClassPropertyName, true);
                }
                else {

                    // if there is only one property on many-to-one side of the relationship then
                    // try to establish if it is bidirectional
                    if (descriptors.length == 1 && isNotMappedToDifferentProperty(property,relatedClassPropertyName, mappedBy)) {
                        if (StringUtils.hasText(relatedClassPropertyName)) {
                            // get the type of the property
                            PropertyDescriptor potentialProperty = relatedCpf.getPropertyDescriptor(relatedClassPropertyName);

                            // ensure circular links are not possible between one-to-one associations
                            if(!potentialProperty.equals(property)) {
                                relatedClassPropertyType = potentialProperty.getPropertyType();
                            }
                        }
                    }
                    // if there is more than one property on the many-to-one side then we need to either
                    // find out if there is a mappedBy property or whether a convention is used to decide
                    // on the mapping property
                    else if (descriptors.length > 1) {
                        if (mappedBy.containsValue(property.getName())) {
                            for (Object o : mappedBy.keySet()) {
                                String mappedByPropertyName = (String) o;
                                if (property.getName().equals(mappedBy.get(mappedByPropertyName))) {
                                    Class<?> mappedByRelatedType = (Class<?>) relatedClassRelationships.get(mappedByPropertyName);
                                    if (mappedByRelatedType != null && propType.isAssignableFrom(mappedByRelatedType))
                                        relatedClassPropertyType = relatedCpf.getPropertyType(mappedByPropertyName);
                                }
                            }
                        }
                        else if(relatedClassPropertyName != null) {
                            // in this case no mappedBy is found so check if the the property name is the same as the class name (eg. 'Foo' would be come 'foo')
                            // using this convention we consider this the default property to map to 
                            String classNameAsProperty = Introspector.decapitalize(propType.getSimpleName());
                            if (property.getName().equals(classNameAsProperty) && !mappedBy.containsKey(relatedClassPropertyName)) {
                                relatedClassPropertyType = relatedCpf.getPropertyType(relatedClassPropertyName);
                            }
                        }
                    }
                }
            }

            // otherwise retrieve all the properties of the type from the associated class
            if (relatedClassPropertyType == null) {
                List<PropertyDescriptor> descriptors = getPropertiesAssignableFromType(entity.getJavaClass(), relatedCpf);

                // if there is only one then the association is established
                if (descriptors.size() == 1) {
                    PropertyDescriptor first = descriptors.get(0);
                    // ensure circular links are not possible between one-to-one associations
                    if(!first.equals(property)) {
                        String otherSidePropertyName = first.getName();
                        if(mappedBy.containsKey(otherSidePropertyName)) {
                            Object mapping = mappedBy.get(otherSidePropertyName);
                            if(mapping != null && mapping.equals(property.getName())) {
                                relatedClassPropertyType = first.getPropertyType();
                                relatedClassPropertyName = otherSidePropertyName;
                            }
                        }
                        else {
                            relatedClassPropertyType = first.getPropertyType();
                            relatedClassPropertyName = otherSidePropertyName;
                        }
                    }
                }
            }
        }

        //    establish relationship based on this type

        final boolean isAssociationEntity = isPersistentEntity(relatedClassPropertyType);
        // one-to-one
        if (relatedClassPropertyType == null || isAssociationEntity) {
            association = embedded ? propertyFactory.createEmbedded(entity, context, property) :
                    propertyFactory.createOneToOne(entity, context, property);

            if (hasOneMap.containsKey(property.getName()) && !embedded) {
                association.setForeignKeyInChild(true);
            }
        }
        // bi-directional many-to-one
        else if (!embedded && Collection.class.isAssignableFrom(relatedClassPropertyType)||Map.class.isAssignableFrom(relatedClassPropertyType)) {
            association = propertyFactory.createManyToOne(entity, context, property);
        }

        // bi-directional
        if (association != null) {
            PersistentEntity associatedEntity = getOrCreateAssociatedEntity(entity, context, propType);
            association.setAssociatedEntity(associatedEntity);
            if (relatedClassPropertyName != null && relatedClassPropertyType != null) {
                association.setReferencedPropertyName(relatedClassPropertyName);
            }
        }

        return association;
    }