package org.etk.orm.plugins.bean.mapping; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import org.etk.orm.plugins.bean.BeanFilter; import org.etk.orm.plugins.bean.BeanInfo; import org.etk.orm.plugins.bean.BeanInfoBuilder; import org.etk.orm.plugins.bean.BeanValueInfo; import org.etk.orm.plugins.bean.PropertyInfo; import org.etk.orm.plugins.bean.SimpleValueInfo; import org.etk.orm.plugins.bean.ValueInfo; import org.etk.orm.plugins.bean.ValueKind; import org.etk.orm.plugins.bean.mapping.jcr.PropertyDefinitionMapping; import org.etk.orm.plugins.bean.mapping.jcr.PropertyMetaType; import org.etk.orm.plugins.bean.type.SimpleTypeMapping; import org.etk.orm.plugins.bean.type.SimpleTypeResolver; import org.etk.reflect.api.ClassTypeInfo; import org.etk.reflect.api.MethodInfo; import org.etk.reflect.api.TypeInfo; import org.etk.reflect.api.TypeResolver; import org.etk.reflect.api.VoidTypeInfo; import org.etk.reflect.api.annotation.AnnotationInfo; import org.etk.reflect.api.annotation.AnnotationParameterInfo; import org.etk.reflect.api.introspection.AnnotationTarget; import org.etk.reflect.api.introspection.MethodIntrospector; import org.etk.reflect.api.visit.HierarchyScope; import org.etk.reflect.core.AnnotationType; import org.etk.reflect.core.TypeResolverImpl; import org.etk.reflect.jlr.metadata.JLReflectionMetadata; import org.etk.orm.api.NameConflictResolution; import org.etk.orm.api.RelationshipType; import org.etk.orm.api.annotations.AutoCreated; import org.etk.orm.api.annotations.Create; import org.etk.orm.api.annotations.DefaultValue; import org.etk.orm.api.annotations.Destroy; import org.etk.orm.api.annotations.FindById; import org.etk.orm.api.annotations.FormattedBy; import org.etk.orm.api.annotations.Id; import org.etk.orm.api.annotations.Mandatory; import org.etk.orm.api.annotations.ManyToOne; import org.etk.orm.api.annotations.MappedBy; import org.etk.orm.api.annotations.MixinType; import org.etk.orm.api.annotations.Name; import org.etk.orm.api.annotations.NamingPolicy; import org.etk.orm.api.annotations.NamingPrefix; import org.etk.orm.api.annotations.OneToMany; import org.etk.orm.api.annotations.OneToOne; import org.etk.orm.api.annotations.Owner; import org.etk.orm.api.annotations.Path; import org.etk.orm.api.annotations.PrimaryType; import org.etk.orm.api.annotations.Properties; import org.etk.orm.api.annotations.Property; import org.etk.orm.api.annotations.WorkspaceName; /** * The bean mapping builder * * @author thanh_vucong * */ public class BeanMappingBuilder { /** Used for receiving {@code java.lang.Object} */ private final TypeResolver<Type> domain = TypeResolverImpl.create(JLReflectionMetadata.newInstance()); private final ClassTypeInfo FORMATTED_BY = (ClassTypeInfo) domain.resolve(FormattedBy.class); private final AnnotationType<AnnotationInfo, ?> FORMATTED_BY_ANNOTATION_TYPE = AnnotationType.get(FORMATTED_BY); private final SimpleTypeResolver simpleTypeResolver; public BeanMappingBuilder() { this(new SimpleTypeResolver()); } public BeanMappingBuilder(SimpleTypeResolver simpleTypeResolver) { this.simpleTypeResolver = simpleTypeResolver; } public Map<ClassTypeInfo, BeanMapping> build(Set<ClassTypeInfo> classTypes) { // Clone for modification classTypes = new HashSet<ClassTypeInfo>(classTypes); // Build beans final AtomicReference<ClassTypeInfo> objectCTI = new AtomicReference<ClassTypeInfo>(); BeanFilter filter = new BeanFilter() { public boolean accept(ClassTypeInfo cti) { boolean accept = false; if (cti.getName().equals(Object.class.getName())) { objectCTI.set(cti); accept = true; } else { accept |= cti.getDeclaredAnnotation(AnnotationType.get(PrimaryType.class)) != null; accept |= cti.getDeclaredAnnotation(AnnotationType.get(MixinType.class)) != null; } return accept; } }; Map<ClassTypeInfo, BeanInfo> beans = new BeanInfoBuilder(simpleTypeResolver, filter).build(classTypes); // Create context Context ctx = new Context(new SimpleTypeResolver(), new HashSet<BeanInfo>(beans.values())); // Build object bean info ahead as it does not contain any annotation if (objectCTI.get() != null) { BeanInfo objectBean = beans.remove(objectCTI.get()); BeanMapping objectMapping = new BeanMapping( objectBean, NodeTypeKind.PRIMARY, "nt:base", NameConflictResolution.FAIL, null, false, true, null); ctx.beanMappings.put(objectBean, objectMapping); ctx.beans.remove(objectBean); } // Build mappings Map<BeanInfo, BeanMapping> beanMappings = ctx.build(); // Resolve relationships for (BeanMapping beanMapping : beanMappings.values()) { for (PropertyMapping propertyMapping : beanMapping.getProperties().values()) { if (propertyMapping instanceof RelationshipMapping<?, ?, ?>) { RelationshipMapping<?, ?, ?> relationshipMapping = (RelationshipMapping<?, ?, ?>)propertyMapping; relationshipMapping.resolve(); } } } // Map<ClassTypeInfo, BeanMapping> classTypeMappings = new HashMap<ClassTypeInfo, BeanMapping>(); for (Map.Entry<BeanInfo, BeanMapping> beanMapping : beanMappings.entrySet()) { classTypeMappings.put(beanMapping.getKey().getClassType(), beanMapping.getValue()); } // return classTypeMappings; } private class Context { /** . */ final SimpleTypeResolver typeResolver; /** . */ final Map<ClassTypeInfo, BeanInfo> beanClassTypeMap; /** . */ final Set<BeanInfo> beans; /** . */ final Map<BeanInfo, BeanMapping> beanMappings; private Context(SimpleTypeResolver typeResolver, Set<BeanInfo> beans) { // Map<ClassTypeInfo, BeanInfo> beanClassTypeMap = new HashMap<ClassTypeInfo, BeanInfo>(); for (BeanInfo bean : beans) { beanClassTypeMap.put(bean.getClassType(), bean); } // this.typeResolver = typeResolver; this.beanClassTypeMap = beanClassTypeMap; this.beans = beans; this.beanMappings = new HashMap<BeanInfo, BeanMapping>(); } public Map<BeanInfo, BeanMapping> build() { while (true) { Iterator<BeanInfo> iterator = beans.iterator(); if (iterator.hasNext()) { BeanInfo bean = iterator.next(); resolve(bean); } else { return beanMappings; } } } private BeanMapping resolve(ClassTypeInfo classType) { BeanInfo bean = beanClassTypeMap.get(classType); if (bean != null) { return resolve(bean); } else { return null; } } private BeanMapping resolve(BeanInfo bean) { BeanMapping mapping = beanMappings.get(bean); if (mapping == null) { if (beans.remove(bean)) { mapping = create(bean); beanMappings.put(bean, mapping); build(mapping); } else { // It does not resolve } } return mapping; } private BeanMapping create(BeanInfo bean) { Collection<? extends Annotation> annotations = bean.getAnnotations(PrimaryType.class, MixinType.class); if (annotations.size() != 1) { throw new InvalidMappingException(bean.getClassType(), "Class is not annotated with a primary type of mixin type"); } // Annotation mappingAnnotation = annotations.iterator().next(); // NameConflictResolution onDuplicate = NameConflictResolution.FAIL; NamingPolicy namingPolicy = bean.getAnnotation(NamingPolicy.class); if (namingPolicy != null) { onDuplicate = namingPolicy.onDuplicate(); } // ClassTypeInfo formatter = null; AnnotationInfo formattedBy = bean.getAnnotation(FORMATTED_BY_ANNOTATION_TYPE); if (formattedBy != null) { AnnotationParameterInfo<ClassTypeInfo> valueParameter = (AnnotationParameterInfo<ClassTypeInfo>)formattedBy.getParameter("value"); formatter = valueParameter.getValue(); } // NamingPrefix namingPrefix = bean.getAnnotation(NamingPrefix.class); String prefix = null; if (namingPrefix != null) { prefix = namingPrefix.value(); } // NodeTypeKind nodeTypeKind; String nodeTypeName; boolean orderable; boolean abstract_; if (mappingAnnotation instanceof PrimaryType) { PrimaryType typeAnnotation = (PrimaryType)mappingAnnotation; nodeTypeKind = NodeTypeKind.PRIMARY; nodeTypeName = typeAnnotation.name(); orderable = typeAnnotation.orderable(); abstract_ = typeAnnotation.abstract_(); } else { MixinType typeAnnotation = (MixinType)mappingAnnotation; nodeTypeKind = NodeTypeKind.MIXIN; nodeTypeName = typeAnnotation.name(); orderable = false; abstract_ = true; } // return new BeanMapping(bean, nodeTypeKind, nodeTypeName, onDuplicate, formatter, orderable, abstract_, prefix); } private void build(BeanMapping beanMapping) { BeanInfo bean = beanMapping.bean; // First build the parent mapping if any if (bean.getParent() != null) { beanMapping.parent = resolve(bean.getParent()); } // Map<String, PropertyMapping<?, ?, ?>> properties = new HashMap<String, PropertyMapping<?, ?, ?>>(); for (PropertyInfo<?, ?> property : bean.getProperties().values()) { // Determine kind Collection<? extends Annotation> annotations = property.getAnnotations( Property.class, Properties.class, OneToOne.class, OneToMany.class, ManyToOne.class, Id.class, Path.class, Name.class, WorkspaceName.class ); // if (annotations.size() > 1) { throw new InvalidMappingException(bean.getClassType(), "The property " + property + " declares too many annotations " + annotations); } // Build the correct mapping or fail PropertyMapping<?, ?, ?> mapping = null; if (annotations.size() == 1) { Annotation annotation = annotations.iterator().next(); ValueInfo value = property.getValue(); if (property.getValueKind() == ValueKind.SINGLE) { if (value instanceof SimpleValueInfo<?>) { SimpleValueInfo<?> simpleValue = (SimpleValueInfo<?>)value; if (annotation instanceof Property) { Property propertyAnnotation = (Property)annotation; if (simpleValue.getValueKind() instanceof ValueKind.Single) { PropertyInfo<SimpleValueInfo<ValueKind.Single>, ValueKind.Single> a = (PropertyInfo<SimpleValueInfo<ValueKind.Single>, ValueKind.Single>)property; mapping = createValueMapping(propertyAnnotation, a); } else { PropertyInfo<SimpleValueInfo<ValueKind.Multi>, ValueKind.Single> a = (PropertyInfo<SimpleValueInfo<ValueKind.Multi>, ValueKind.Single>)property; mapping = createValueMapping(propertyAnnotation, a); } } else if (annotation instanceof Id) { mapping = createAttribute((PropertyInfo<SimpleValueInfo, ValueKind.Single>)property, NodeAttributeType.ID); } else if (annotation instanceof Path) { mapping = createAttribute((PropertyInfo<SimpleValueInfo, ValueKind.Single>)property, NodeAttributeType.PATH); } else if (annotation instanceof Name) { mapping = createAttribute((PropertyInfo<SimpleValueInfo, ValueKind.Single>)property, NodeAttributeType.NAME); } else if (annotation instanceof WorkspaceName) { mapping = createAttribute((PropertyInfo<SimpleValueInfo, ValueKind.Single>)property, NodeAttributeType.WORKSPACE_NAME); } else { throw new InvalidMappingException(bean.getClassType(), "The property " + property + " is not annotated"); } } else if (value instanceof BeanValueInfo) { if (annotation instanceof OneToOne) { OneToOne oneToOne = (OneToOne)annotation; switch (oneToOne.type()) { case HIERARCHIC: mapping = createHierarchicOneToOne(beanMapping, oneToOne, (PropertyInfo<BeanValueInfo, ValueKind.Single>) property); break; case EMBEDDED: mapping = createEmbeddedOneToOne((PropertyInfo<BeanValueInfo, ValueKind.Single>) property); break; default: throw new InvalidMappingException(bean.getClassType(), "Expecting that the @OneToOne property " + property + " to be annotated with " + RelationshipType.HIERARCHIC + " or " + RelationshipType.EMBEDDED + " instead of " + oneToOne.type()); } } else if (annotation instanceof ManyToOne) { ManyToOne manyToOne = (ManyToOne)annotation; switch (manyToOne.type()) { case HIERARCHIC: mapping = createHierarchicManyToOne(beanMapping, manyToOne, (PropertyInfo<BeanValueInfo, ValueKind.Single>)property); break; case PATH: case REFERENCE: mapping = createReferenceManyToOne(manyToOne, (PropertyInfo<BeanValueInfo, ValueKind.Single>)property); break; default: throw new InvalidMappingException(bean.getClassType(), "Expecting that the @ManyToOne property " + property + " to be annotated with " + RelationshipType.HIERARCHIC + ", " + RelationshipType.PATH + " or " + RelationshipType.REFERENCE + " instead of " + manyToOne.type()); } } else { throw new InvalidMappingException(bean.getClassType(), "Annotation " + annotation + " is forbidden " + " on property " + property); } } else { throw new AssertionError(); } } else if (property.getValueKind() instanceof ValueKind.Multi) { if (value instanceof SimpleValueInfo) { SimpleValueInfo<?> simpleValue = (SimpleValueInfo<?>)value; if (annotation instanceof Property) { Property propertyAnnotation = (Property)annotation; if (simpleValue.getValueKind() instanceof ValueKind.Single) { PropertyInfo<SimpleValueInfo<ValueKind.Single>, ValueKind.Single> a = (PropertyInfo<SimpleValueInfo<ValueKind.Single>, ValueKind.Single>)property; mapping = createValueMapping(propertyAnnotation, a); } else { PropertyInfo<SimpleValueInfo<ValueKind.Multi>, ValueKind.Single> a = (PropertyInfo<SimpleValueInfo<ValueKind.Multi>, ValueKind.Single>)property; mapping = createValueMapping(propertyAnnotation, a); } } else if (annotation instanceof Properties) { mapping = createProperties((PropertyInfo<?, ValueKind.Map>)property); } else { throw new InvalidMappingException(bean.getClassType(), "Annotation " + annotation + " is forbidden " + " on property " + property); } } else if (value instanceof BeanValueInfo) { if (annotation instanceof OneToMany) { OneToMany oneToMany = (OneToMany)annotation; switch (oneToMany.type()) { case HIERARCHIC: mapping = createHierarchicOneToMany(beanMapping, oneToMany, (PropertyInfo<BeanValueInfo, ?>)property); break; case PATH: case REFERENCE: mapping = createReferenceOneToMany(oneToMany, (PropertyInfo<BeanValueInfo, ?>)property); break; default: throw new InvalidMappingException(bean.getClassType(), "Expecting that the @OneToMany property " + property + " to be annotated with " + RelationshipType.HIERARCHIC + ", " + RelationshipType.PATH + " or " + RelationshipType.REFERENCE + " instead of " + oneToMany.type()); } } else if (annotation instanceof Properties) { mapping = createProperties((PropertyInfo<?, ValueKind.Map>)property); } else { throw new InvalidMappingException(bean.getClassType(), "Annotation " + annotation + " is forbidden " + " on property " + property); } } else { throw new AssertionError(); } } else { throw new AssertionError(); } } // if (mapping != null) { // Resolve parent property without any check for now PropertyInfo parentProperty = property.getParent(); if (parentProperty != null) { BeanInfo ancestor = parentProperty.getOwner(); BeanMapping ancestorMapping = resolve(ancestor); mapping.parent = ancestorMapping.properties.get(parentProperty.getName()); } // properties.put(mapping.property.getName(), mapping); } } // Wire beanMapping.properties.putAll(properties); for (PropertyMapping<?, ?, ?> propertyMapping : beanMapping.properties.values()) { propertyMapping.owner = beanMapping; } // Take care of methods MethodIntrospector introspector = new MethodIntrospector(HierarchyScope.ALL); Set<MethodMapping> methodMappings = new HashSet<MethodMapping>(); // Create for (AnnotationTarget<MethodInfo, Create> annotatedMethods : introspector.resolveMethods(bean.getClassType(), Constants.CREATE)) { MethodInfo method = annotatedMethods.getTarget(); if (!method.isStatic()) { List<TypeInfo> parameterTypes = method.getParameterTypes(); if (parameterTypes.size() < 2) { if (parameterTypes.size() == 1) { TypeInfo argTI = parameterTypes.get(0); if (argTI instanceof ClassTypeInfo) { ClassTypeInfo argCTI = (ClassTypeInfo)argTI; if (!argCTI.getName().equals(String.class.getName())) { throw new InvalidMappingException(bean.getClassType(), "The argument of the @Create method " + method + " must be a java.lang.String instead of " + method.getSignature()); } } else { throw new InvalidMappingException(bean.getClassType(), "The argument of the @Create method " + method + " must be a java.lang.String instead of " + method.getSignature()); } } ClassTypeInfo returnTypeInfo = bean.resolveToClass(method.getReturnType()); if (returnTypeInfo != null) { BeanMapping createBeanMapping = resolve(returnTypeInfo); if (createBeanMapping == null) { throw new InvalidMappingException(bean.getClassType(), "Could not resolve the return type " + returnTypeInfo + " to a chromattic bean among beans " + beans + " and mappings " + beanMappings.values()); } methodMappings.add(new CreateMapping(method, createBeanMapping)); } else { throw new InvalidMappingException(bean.getClassType(), "Invalid @Create method " + method + " return type " + returnTypeInfo); } } else { throw new InvalidMappingException(bean.getClassType(), "The signature of the @Create method " + method + "should have zero or one argument instead of " + method.getSignature()); } } else { throw new InvalidMappingException(bean.getClassType(), "The @Create method " + method + " must not be static"); } } // Destroy for (AnnotationTarget<MethodInfo, Destroy> annotatedMethods : introspector.resolveMethods(bean.getClassType(), Constants.DESTROY)) { MethodInfo method = annotatedMethods.getTarget(); if (!method.isStatic()) { List<TypeInfo> parameterTypes = method.getParameterTypes(); if (parameterTypes.size() != 0) { throw new InvalidMappingException(bean.getClassType(), "The @Destroy method " + method + " must have no arguments"); } if (!(method.getReturnType() instanceof VoidTypeInfo)) { throw new InvalidMappingException(bean.getClassType(), "The @Destroy method " + method + " must have a void return type"); } methodMappings.add(new DestroyMapping(method)); } else { throw new InvalidMappingException(bean.getClassType(), "The @Destroy method " + method + " must not be static"); } } // Find by id for (AnnotationTarget<MethodInfo, FindById> annotatedMethods : introspector.resolveMethods(bean.getClassType(), Constants.FIND_BY_ID)) { MethodInfo method = annotatedMethods.getTarget(); if (!method.isStatic()) { List<TypeInfo> parameterTypes = method.getParameterTypes(); if (parameterTypes.size() == 1) { TypeInfo argTI = parameterTypes.get(0); if (argTI instanceof ClassTypeInfo) { ClassTypeInfo argCTI = (ClassTypeInfo)argTI; if (argCTI.getName().equals(String.class.getName())) { ClassTypeInfo cti = (ClassTypeInfo)bean.getClassType().resolve(method.getReturnType()); methodMappings.add(new FindByIdMapping(method, cti)); } else { throw new InvalidMappingException(bean.getClassType(), "The argument of the @FindById method " + method + " must be a java.lang.String instead of " + method.getSignature()); } } else { throw new InvalidMappingException(bean.getClassType(), "The argument of the @FindById method " + method + " must be a java.lang.String instead of " + method.getSignature()); } } else { throw new InvalidMappingException(bean.getClassType(), "The signature of the @FindById method " + method + "should a single java.lang.String argument instead of " + method.getSignature()); } } else { throw new InvalidMappingException(bean.getClassType(), "The @FindById method " + method + " must not be static"); } } // beanMapping.methods.addAll(methodMappings); } private AttributeMapping createAttribute(PropertyInfo<SimpleValueInfo, ValueKind.Single> property, NodeAttributeType type) { TypeInfo effectiveType = property.getValue().getEffectiveType(); if (!(effectiveType instanceof ClassTypeInfo)) { throw new InvalidMappingException(property.getOwner().getClassType(), "The property " + property + " must be of type java.lang.String"); } ClassTypeInfo effectiveClassType = (ClassTypeInfo)effectiveType; if (!effectiveClassType.getName().equals(String.class.getName())) { throw new InvalidMappingException(property.getOwner().getClassType(), "The property " + property + " must be of type java.lang.String"); } return new AttributeMapping(property, type); } private <V extends ValueInfo> PropertiesMapping<V> createProperties(PropertyInfo<V, ValueKind.Map> property) { if (property.getValueKind() != ValueKind.MAP) { throw new InvalidMappingException(property.getOwner().getClassType(), "The @Properties " + property + " must be of type java.util.Map instead of " + property.getValue().getEffectiveType()); } TypeInfo type = property.getValue().getEffectiveType(); // PropertyMetaType<?> mt = null; ValueKind valueKind; ValueInfo vi = property.getValue(); if (vi instanceof SimpleValueInfo<?>) { SimpleValueInfo<?> svi = (SimpleValueInfo<?>)vi; if (svi.getTypeMapping() != null) { mt = svi.getTypeMapping().getPropertyMetaType(); } valueKind = svi.getValueKind(); } else { if (type.getName().equals(Object.class.getName())) { mt = null; } valueKind = ValueKind.SINGLE; } // String prefix = null; NamingPrefix namingPrefix = property.getAnnotation(NamingPrefix.class); if (namingPrefix != null) { prefix = namingPrefix.value(); } // return new PropertiesMapping<V>(property, prefix, mt, valueKind); } private <K extends ValueKind> ValueMapping<K> createValueMapping( Property propertyAnnotation, PropertyInfo<SimpleValueInfo<K>, ValueKind.Single> property) { // PropertyMetaType<?> propertyMetaType = PropertyMetaType.get(propertyAnnotation.type()); // SimpleTypeMapping resolved = typeResolver.resolveType(property.getValue().getDeclaredType(), propertyMetaType); if (resolved == null) { throw new InvalidMappingException(property.getOwner().getClassType(), "No simple type mapping " + property.getValue().getDeclaredType() + " for property " + property); } // List<String> defaultValueList = null; DefaultValue defaultValueAnnotation = property.getAnnotation(DefaultValue.class); if (defaultValueAnnotation != null) { String[] defaultValues = defaultValueAnnotation.value(); defaultValueList = new ArrayList<String>(defaultValues.length); defaultValueList.addAll(Arrays.asList(defaultValues)); defaultValueList = Collections.unmodifiableList(defaultValueList); } // PropertyDefinitionMapping<?> propertyDefinition = new PropertyDefinitionMapping( propertyAnnotation.name(), resolved.getPropertyMetaType(), defaultValueList, false); // return new ValueMapping<K>(property, propertyDefinition); } private RelationshipMapping.OneToMany.Reference createReferenceOneToMany(OneToMany annotation, PropertyInfo<BeanValueInfo, ?> property) { MappedBy mappedBy = property.getAnnotation(MappedBy.class); if (mappedBy == null) { throw new InvalidMappingException(property.getOwner().getClassType(), "The reference @OneToMany relationship " + property + "must carry an @MappedBy annotation"); } RelationshipMapping.OneToMany.Reference mapping; mapping = new RelationshipMapping.OneToMany.Reference(property, mappedBy.value(), annotation.type()); mapping.relatedBeanMapping = resolve(property.getValue().getBean()); return mapping; } private RelationshipMapping.OneToMany.Hierarchic createHierarchicOneToMany(BeanMapping beanMapping, OneToMany annotation, PropertyInfo<BeanValueInfo, ?> property) { RelationshipMapping.OneToMany.Hierarchic mapping; NamingPrefix namingPrefix = property.getAnnotation(NamingPrefix.class); String declaredPrefix = namingPrefix != null ? namingPrefix.value() : null; String prefix = declaredPrefix == null ? beanMapping.getPrefix() : declaredPrefix; mapping = new RelationshipMapping.OneToMany.Hierarchic(property, declaredPrefix, prefix); mapping.relatedBeanMapping = resolve(property.getValue().getBean()); return mapping; } private RelationshipMapping.ManyToOne.Reference createReferenceManyToOne(ManyToOne annotation, PropertyInfo<BeanValueInfo, ValueKind.Single> property) { MappedBy mappedBy = property.getAnnotation(MappedBy.class); if (mappedBy == null) { throw new InvalidMappingException(property.getOwner().getClassType(), "The reference @ManyToOne relationship " + property + "must carry an @MappedBy annotation"); } RelationshipMapping.ManyToOne.Reference mapping; mapping = new RelationshipMapping.ManyToOne.Reference(property, mappedBy.value(), annotation.type()); mapping.relatedBeanMapping = resolve(property.getValue().getBean()); return mapping; } private RelationshipMapping.ManyToOne.Hierarchic createHierarchicManyToOne(BeanMapping beanMapping, ManyToOne annotation, PropertyInfo<BeanValueInfo, ValueKind.Single> property) { RelationshipMapping.ManyToOne.Hierarchic mapping; NamingPrefix namingPrefix = property.getAnnotation(NamingPrefix.class); String declaredPrefix = namingPrefix != null ? namingPrefix.value() : null; String prefix = declaredPrefix == null ? beanMapping.getPrefix() : declaredPrefix; mapping = new RelationshipMapping.ManyToOne.Hierarchic(property, declaredPrefix, prefix); mapping.relatedBeanMapping = resolve(property.getValue().getBean()); return mapping; } private RelationshipMapping.OneToOne.Embedded createEmbeddedOneToOne(PropertyInfo<BeanValueInfo, ValueKind.Single> property) { RelationshipMapping.OneToOne.Embedded mapping; boolean owner = property.getAnnotation(Owner.class) != null; mapping = new RelationshipMapping.OneToOne.Embedded(property, owner); mapping.relatedBeanMapping = resolve(property.getValue().getBean()); return mapping; } private RelationshipMapping.OneToOne.Hierarchic createHierarchicOneToOne( BeanMapping beanMapping, OneToOne annotation, PropertyInfo<BeanValueInfo, ValueKind.Single> property) { MappedBy mappedBy = property.getAnnotation(MappedBy.class); if (mappedBy == null) { throw new InvalidMappingException(property.getOwner().getClassType(), "The @OneToOne relationship " + property + "must carry an @MappedBy annotation"); } boolean owner = property.getAnnotation(Owner.class) != null; boolean autocreated = property.getAnnotation(AutoCreated.class) != null; boolean mandatory = property.getAnnotation(Mandatory.class) != null; String declaredPrefix; String localName; int index = mappedBy.value().indexOf(':'); if (index == -1) { declaredPrefix = null; localName = mappedBy.value(); } else { declaredPrefix = mappedBy.value().substring(0, index); localName = mappedBy.value().substring(index + 1); } String prefix = declaredPrefix == null ? beanMapping.getPrefix() : declaredPrefix; RelationshipMapping.OneToOne.Hierarchic mapping; mapping = new RelationshipMapping.OneToOne.Hierarchic(property, owner, declaredPrefix, prefix, localName, mandatory, autocreated); mapping.relatedBeanMapping = resolve(property.getValue().getBean()); return mapping; } } }