FileDocCategorySizeDatePackage
EmbeddedAccessor.javaAPI DocGlassfish v2 API22733Tue May 22 16:54:26 BST 2007oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors

EmbeddedAccessor

public class EmbeddedAccessor extends MetadataAccessor
An embedded relationship accessor.
author
Guy Pelletier
since
TopLink EJB 3.0 Reference Implementation

Fields Summary
Constructors Summary
public EmbeddedAccessor(MetadataAccessibleObject accessibleObject, ClassAccessor classAccessor)
INTERNAL:

        super(accessibleObject, classAccessor);
    
Methods Summary
protected oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.ClassAccessorbuildAccessor(oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor embeddableDescriptor)
INTERNAL: This method is responsible for configuring the right MetadataAccessor object for processing of the Embeddable class represented by the embeddableDescriptor that is passed in. This methood associates the accessor with the descriptor as well.

param
embeddableDescriptor descriptor for the Embeddable
return
a MetadataAccessor that will be used to read metadata for this class
throws
ValidationException if the embeddable class is neither annotated as Embeddable nor specified as embeddable in XML.

        ClassAccessor embeddableAccessor = null;

        Class embeddableClass = embeddableDescriptor.getJavaClass();

        if (m_project.hasEmbeddable(embeddableClass)) {
            Node node = m_project.getEmbeddableNode(embeddableClass);
            XMLHelper helper = m_project.getEmbeddableHelper(embeddableClass);
            embeddableAccessor = new XMLClassAccessor(new MetadataClass(embeddableClass), node, helper, m_processor, embeddableDescriptor);
        } else if (MetadataHelper.isAnnotationPresent(Embeddable.class, embeddableClass, embeddableDescriptor)) {
            embeddableAccessor = new ClassAccessor(new MetadataClass(embeddableClass), m_processor, embeddableDescriptor);
        } else {
            m_validator.throwInvalidEmbeddedAttribute(getJavaClass(), m_accessibleObject.getName(), embeddableClass);
        }

        // associate the accessor to the descriptor (as per Javadoc)
        embeddableDescriptor.setClassAccessor(embeddableAccessor);
        return embeddableAccessor;
    
protected oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.EmbeddedAccessor$AccessTypecomputeAccessTypeFromAnnotation(oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor descriptor)
INTERNAL: This method computes access-type based on placement of annotations.

        Class javaClass = descriptor.getJavaClass();
        boolean fieldAccess = MetadataHelper.havePersistenceAnnotationsDefined(MetadataHelper.getFields(javaClass));
        boolean propertyAccess = MetadataHelper.havePersistenceAnnotationsDefined(MetadataHelper.getMethods(javaClass));
        
        AccessType accessType = UNDEFINED;

        if (fieldAccess && propertyAccess) {
            accessType = MIXED;
        } else if (fieldAccess) {
            accessType = FIELD;
        } else if (propertyAccess) {
            accessType = PROPERTY;
        }

        return accessType;
    
protected oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.EmbeddedAccessor$AccessTypecomputeAccessTypeFromXML(oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor descriptor)
INTERNAL: This method computes access-type based on metadata specified in mapping XML file. If there is a default value for access is specified in or and either there is no for the given class or there is specified in the , the default value is used.

        // step #1: what's PU level default
        String access = m_project.getPersistenceUnit() != null ? m_project.getPersistenceUnit().getAccess() : null;

        // step #2: what's specified in <embeddable> element

        Class javaClass = descriptor.getJavaClass();

        if (m_project.hasEmbeddable(javaClass)) {
            XMLHelper helper = m_project.getEmbeddableHelper(javaClass);
            Node node = m_project.getEmbeddableNode(javaClass);
            // override pu-level value by <embeddable> level if any
            // Note, we pass the puLevel value as the default value
            access = helper.getNodeValue(node, XMLConstants.ATT_ACCESS, access);
        }

        AccessType accessType = UNDEFINED;

        if (access != null && access.length() != 0) {
            accessType = AccessType.valueOf(access);
        }

        return accessType;
    
protected oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.EmbeddedAccessor$AccessTypedetermineAccessTypeOfEmbedded(oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor emDesc)
INTERNAL: This method is responsible for determining the access-type for an Embeddable class represented by emDescr that is passed to this method. This method should *not* be called more than once as this is quite expensive. Now the rules: Rule 1: In the *absence* of metadata in embeddable class, access-type of an embeddable is determined by the access-type of the enclosing entity. Rule 2: In the presence of metadata in embeddable class, access-type of an embeddable is determined using that metadata. This allows sharing of the embeddable in entities with conflicting access-types. Rule 3: It is an error to use a *metadata-less* embeddable class in entities with conflicting access-types as that might result in different database mapping for the same embeddable class. Rule 4: It is an error if metadata-complete == false, and metadata is present *both* in annotations and XML, and access-type as determined by each of them is *not* same. Rule 5: It is an error if *both* fields and properties of an embeddable class are annotated and metadata-complete == false.

param
emDesc descriptor for the Embeddable class

        final AccessType entityAccessType = m_descriptor.usesPropertyAccess() ? PROPERTY : FIELD;
        AccessType accessType = UNDEFINED;
        final boolean metadataComplete =emDesc.ignoreAnnotations();
        final AccessType accessTypeUsingAnnotation = computeAccessTypeFromAnnotation(emDesc);
        final AccessType accessTypeUsingXML = computeAccessTypeFromXML(emDesc);

        if (metadataComplete) {
            // metadata-complete is true, then use XML access-type if defined,
            // else use enclosing entity's access-type.
            accessType = accessTypeUsingXML != UNDEFINED ? accessTypeUsingXML : entityAccessType;
        } else {// metadata-complete is false
            if (accessTypeUsingAnnotation == UNDEFINED && accessTypeUsingAnnotation == UNDEFINED) {
                // metadata is absent, so we use enclosing entity's access-type
                accessType = entityAccessType;
            } else if (accessTypeUsingXML == UNDEFINED && accessTypeUsingAnnotation != UNDEFINED) {
                // annotation is present in embeddable class
                accessType = accessTypeUsingAnnotation;

                if (accessType == MIXED) {
                    m_validator.throwBothFieldsAndPropertiesAnnotatedException(emDesc.getJavaClass());
                }
            } else if (accessTypeUsingAnnotation == UNDEFINED && accessTypeUsingXML != UNDEFINED) {
                // access is defined using XML for embeddable class
                accessType = accessTypeUsingXML;
            } else if (accessTypeUsingAnnotation == accessTypeUsingXML) {
                // annotation is present as well as access is defined using XML
                // and they are same. So use it.
                accessType = accessTypeUsingAnnotation;
            } else {
                // annotation is present as well as access is defined using XML
                // and they are different. So report an exception.
                m_validator.throwIncorrectOverridingOfAccessType(emDesc.getJavaClass(), accessTypeUsingXML.toString(), accessTypeUsingAnnotation.toString());
            }
        }

        // we have taken every precaution to make sure that either an embeddable
        // has a well defined access-type or a suitable ValidationException
        // is thrown.
        assert(accessType != UNDEFINED && accessType != MIXED);

        return accessType;
    
public booleanisEmbedded()
INTERNAL:

        return true;
    
protected booleanisMetadataComplete(oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor emDesc)
INTERNAL: This method is used to decide if annotations should be ignored or not for the given embeddable class.

        final Class emClass = emDesc.getJavaClass();

        boolean metadataComplete = m_project.getPersistenceUnit() != null ? m_project.getPersistenceUnit().isMetadataComplete() : false;

        if (!metadataComplete) {
            // check <embeddable>
            if (m_project.hasEmbeddable(emClass)) {
                XMLHelper helper = m_project.getEmbeddableHelper(emClass);
                Node node = m_project.getEmbeddableNode(emClass);
                // check if metadata-complete at <embeddable> level
                metadataComplete = helper.getNodeValue(node, XMLConstants.ATT_METADATA_COMPLETE, false);
            }
        }

        return metadataComplete;
    
protected booleanisMetadataPresent(oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor descriptor)
INTERNAL: This method is used to decide if a class metadata or not.

        AccessType annotAccessType = computeAccessTypeFromAnnotation(descriptor);
        AccessType xmlAccessType = computeAccessTypeFromXML(descriptor);

        return annotAccessType != UNDEFINED || xmlAccessType != UNDEFINED;
    
public voidprocess()
INTERNAL: (Overridden in EmbeddedIdAccessor) Process an @Embedded or embedded element.

        // Tell the Embeddable class to process itself
        MetadataDescriptor referenceDescriptor = processEmbeddableClass();
        
        // Store this descriptor metadata. It may be needed again later on to
        // look up a mappedBy attribute.
        m_descriptor.addAggregateDescriptor(referenceDescriptor);
        
        if (m_descriptor.hasMappingForAttributeName(getAttributeName())) {
            // XML/Annotation merging. XML wins, ignore annotations.
            m_logger.logWarningMessage(m_logger.IGNORE_MAPPING, m_descriptor, this);
        } else {
            // Create an aggregate mapping and do the rest of the work.
            AggregateObjectMapping mapping = new AggregateObjectMapping();
            mapping.setIsReadOnly(false);
            mapping.setIsNullAllowed(true);
            mapping.setReferenceClassName(getReferenceClassName());
            mapping.setAttributeName(getAttributeName());    
        
            // Will check for PROPERTY access
            setAccessorMethods(mapping);
        
            // Process attribute overrides.
            processAttributeOverrides(mapping);
            
            // Process association overrides.
            processAssociationOverrides(mapping);
        
            // Add the mapping to the descriptor and we are done.
            m_descriptor.addMapping(mapping);
        }
    
protected voidprocessAssociationOverride(javax.persistence.AssociationOverride associationOverride, oracle.toplink.essentials.mappings.AggregateObjectMapping aggregateMapping)
INTERNAL: Process an @AssociationOverride for an embedded object, that is, an aggregate object mapping in TopLink. This functionality is not supported in XML, hence why this method is defined here instead of on MetadataProcessor. Also this functionality is currently optional in the EJB 3.0 spec, but since TopLink can handle it, it is implemented and assumes the user has properly configured its use since it will fail silently.

        MetadataDescriptor aggregateDescriptor = getReferenceDescriptor();
        
        // AssociationOverride.name(), the name of the attribute we want to
        // override.
        String name = associationOverride.name();
        DatabaseMapping mapping = aggregateDescriptor.getMappingForAttributeName(name);
        
        if (mapping != null && mapping.isOneToOneMapping()) {
            int index = 0;
            
            for (JoinColumn joinColumn : associationOverride.joinColumns()) {
                // We can't change the mapping from the aggregate descriptor
                // so we have to add field name translations. This needs to be
                // tested since I am not entirely sure if this will acutally
                // work.
                // In composite primary key case, how do we association the
                // foreign keys? Right now we assume the association overrides
                // are specified in the same order as the original joinColumns,
                // therefore in the same order the foreign keys were added to
                // the mapping.
                DatabaseField fkField = (DatabaseField) ((OneToOneMapping) mapping).getForeignKeyFields().elementAt(index++);
                aggregateMapping.addFieldNameTranslation(joinColumn.name(), fkField.getName());
            }   
        } else {
            // For now fail silently.
        }
	
protected voidprocessAssociationOverrides(oracle.toplink.essentials.mappings.AggregateObjectMapping mapping)
INTERNAL: Process an @AssociationOverrides for an embedded object, that is, an aggregate object mapping in TopLink. It will also look for an @AssociationOverride.

        // Look for an @AssociationOverrides.
        AssociationOverrides associationOverrides = getAnnotation(AssociationOverrides.class);
        if (associationOverrides != null) {
            for (AssociationOverride associationOverride : associationOverrides.value()) {
                processAssociationOverride(associationOverride, mapping);
            }
        }
        
        // Look for an @AssociationOverride.
        AssociationOverride associationOverride = getAnnotation(AssociationOverride.class);	
        if (associationOverride != null) {
            processAssociationOverride(associationOverride, mapping);
        }
    
protected voidprocessAttributeOverride(oracle.toplink.essentials.mappings.AggregateObjectMapping mapping, oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataColumn column)
INTERNAL: (Overridden in EmbeddedIdAccessor) Process an @AttributeOverride or attribute-override element for an embedded object, that is, an aggregate object mapping in TopLink.

        String attributeName = column.getAttributeName();
        
        // Set the attribute name on the aggregate.
        DatabaseMapping aggregateMapping = getReferenceDescriptor().getMappingForAttributeName(attributeName);
        
        if (aggregateMapping == null) {
            m_validator.throwInvalidEmbeddableAttribute(getJavaClass(), mapping.getAttributeName(), getReferenceDescriptor().getJavaClass(), attributeName);
        }
        
        // A sub-class to a mapped superclass may override an embedded attribute 
        // override.
        if (m_descriptor.hasAttributeOverrideFor(attributeName)) {
            // Update the field on this metadata column. We do that so that
            // an embedded id can associate the correct id fields.
            column.setDatabaseField(m_descriptor.getAttributeOverrideFor(attributeName).getDatabaseField());
        } 
        
        mapping.addFieldNameTranslation(column.getDatabaseField().getQualifiedName(), aggregateMapping.getField().getName());
	
protected voidprocessAttributeOverrides(oracle.toplink.essentials.mappings.AggregateObjectMapping mapping)
INTERNAL: (Overridden in XMLEmbeddedAccessor and XMLEmbeddedIdAccessor) Process an @AttributeOverrides for an embedded object, that is, an aggregate object mapping in TopLink. It will also look for an @AttributeOverride.

        // Look for an @AttributeOverrides.
        AttributeOverrides attributeOverrides = getAnnotation(AttributeOverrides.class);
        
        if (attributeOverrides != null) {
            for (AttributeOverride attributeOverride : attributeOverrides.value()) {
                processAttributeOverride(mapping, new MetadataColumn(attributeOverride.column(), attributeOverride.name(), getAnnotatedElement()));
            }
        }
        
        // Look for an @AttributeOverride.
        AttributeOverride attributeOverride = getAnnotation(AttributeOverride.class);	
        if (attributeOverride != null) {
            processAttributeOverride(mapping, new MetadataColumn(attributeOverride.column(), attributeOverride.name(), getAnnotatedElement()));
        }
    
protected oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptorprocessEmbeddableClass()
INTERNAL: (Overridden in EmbeddedIdAccessor) This method processes an embeddable class, if we have not processed it yet. The reason for lazy processing of embeddable class is because of rules governing access-type of embeddable. See GlassFish issue #831 for more details. Be careful while changing order of processing.

        final Class embeddableClass = getReferenceClass();
        MetadataDescriptor embeddableDescriptor = null;

        try {
            embeddableDescriptor = m_project.getDescriptor(embeddableClass);
        } catch (Exception exception) {
            // expected as we do lazy processing of embeddables.
        }

        if (embeddableDescriptor == null) {
            // The embeddable class is not yet processed, so process it now.
            embeddableDescriptor = new MetadataDescriptor(embeddableClass);
            // adding to projects sets up appropriate persistence-unit-defaults
            m_project.addDescriptor(embeddableDescriptor);
            embeddableDescriptor.setIgnoreAnnotations(isMetadataComplete(embeddableDescriptor));
            AccessType accessType = determineAccessTypeOfEmbedded(embeddableDescriptor);
            embeddableDescriptor.setUsesPropertyAccess(accessType == PROPERTY ? true : false);
            ClassAccessor embeddableAccessor = buildAccessor(embeddableDescriptor);
            embeddableAccessor.process();
            embeddableAccessor.setIsProcessed();
        } else {
            // We have already processed this embeddable class. let's validate 
            // that it is not used in entities with conflicting access type. 
            // Conflicting access-type is not allowed when there is no metadata 
            // in the embeddable class.
            if (!isMetadataPresent(embeddableDescriptor)) {
                boolean embeddableUsesPropertyAccess = embeddableDescriptor.usesPropertyAccess();
                boolean entityUsesPropertyAccess = m_descriptor.usesPropertyAccess();

                if (embeddableUsesPropertyAccess != entityUsesPropertyAccess) {
                    m_validator.throwConflictingAccessTypeInEmbeddable(embeddableClass);
                }
            }
        }
        
        return embeddableDescriptor;