Package org.hibernate.envers.configuration.internal.metadata.reader

Source Code of org.hibernate.envers.configuration.internal.metadata.reader.AuditedPropertiesReader$ComponentPropertiesSource

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*/
package org.hibernate.envers.configuration.internal.metadata.reader;

import javax.persistence.JoinColumn;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.envers.AuditJoinTable;
import org.hibernate.envers.AuditMappedBy;
import org.hibernate.envers.AuditOverride;
import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.Audited;
import org.hibernate.envers.ModificationStore;
import org.hibernate.envers.NotAudited;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.configuration.internal.metadata.MetadataTools;
import org.hibernate.envers.internal.tools.MappingTools;
import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;

import static org.hibernate.envers.internal.tools.Tools.newHashMap;
import static org.hibernate.envers.internal.tools.Tools.newHashSet;

/**
* Reads persistent properties form a {@link PersistentPropertiesSource} and adds the ones that are audited to a
* {@link AuditedPropertiesHolder}, filling all the auditing data.
*
* @author Adam Warski (adam at warski dot org)
* @author Erik-Berndt Scheper
* @author Hern&aacut;n Chanfreau
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @author Michal Skowronek (mskowr at o2 dot pl)
*/
public class AuditedPropertiesReader {
  protected final ModificationStore defaultStore;
  private final PersistentPropertiesSource persistentPropertiesSource;
  private final AuditedPropertiesHolder auditedPropertiesHolder;
  private final GlobalConfiguration globalCfg;
  private final ReflectionManager reflectionManager;
  private final String propertyNamePrefix;

  private final Set<String> propertyAccessedPersistentProperties;
  private final Set<String> fieldAccessedPersistentProperties;
  // Mapping class field to corresponding <properties> element.
  private final Map<String, String> propertiesGroupMapping;

  private final Set<XProperty> overriddenAuditedProperties;
  private final Set<XProperty> overriddenNotAuditedProperties;

  private final Set<XClass> overriddenAuditedClasses;
  private final Set<XClass> overriddenNotAuditedClasses;

  public AuditedPropertiesReader(
      ModificationStore defaultStore,
      PersistentPropertiesSource persistentPropertiesSource,
      AuditedPropertiesHolder auditedPropertiesHolder,
      GlobalConfiguration globalCfg,
      ReflectionManager reflectionManager,
      String propertyNamePrefix) {
    this.defaultStore = defaultStore;
    this.persistentPropertiesSource = persistentPropertiesSource;
    this.auditedPropertiesHolder = auditedPropertiesHolder;
    this.globalCfg = globalCfg;
    this.reflectionManager = reflectionManager;
    this.propertyNamePrefix = propertyNamePrefix;

    propertyAccessedPersistentProperties = newHashSet();
    fieldAccessedPersistentProperties = newHashSet();
    propertiesGroupMapping = newHashMap();

    overriddenAuditedProperties = newHashSet();
    overriddenNotAuditedProperties = newHashSet();

    overriddenAuditedClasses = newHashSet();
    overriddenNotAuditedClasses = newHashSet();
  }

  public void read() {
    // First reading the access types for the persistent properties.
    readPersistentPropertiesAccess();

    // Retrieve classes and properties that are explicitly marked for auditing process by any superclass
    // of currently mapped entity or itself.
    final XClass clazz = persistentPropertiesSource.getXClass();
    readAuditOverrides( clazz );

    // Adding all properties from the given class.
    addPropertiesFromClass( clazz );
  }

  /**
   * Recursively constructs sets of audited and not audited properties and classes which behavior has been overridden
   * using {@link AuditOverride} annotation.
   *
   * @param clazz Class that is being processed. Currently mapped entity shall be passed during first invocation.
   */
  private void readAuditOverrides(XClass clazz) {
    /* TODO: Code to remove with @Audited.auditParents - start. */
    final Audited allClassAudited = clazz.getAnnotation( Audited.class );
    if ( allClassAudited != null && allClassAudited.auditParents().length > 0 ) {
      for ( Class c : allClassAudited.auditParents() ) {
        final XClass parentClass = reflectionManager.toXClass( c );
        checkSuperclass( clazz, parentClass );
        if ( !overriddenNotAuditedClasses.contains( parentClass ) ) {
          // If the class has not been marked as not audited by the subclass.
          overriddenAuditedClasses.add( parentClass );
        }
      }
    }
        /* TODO: Code to remove with @Audited.auditParents - finish. */
    final List<AuditOverride> auditOverrides = computeAuditOverrides( clazz );
    for ( AuditOverride auditOverride : auditOverrides ) {
      if ( auditOverride.forClass() != void.class ) {
        final XClass overrideClass = reflectionManager.toXClass( auditOverride.forClass() );
        checkSuperclass( clazz, overrideClass );
        final String propertyName = auditOverride.name();
        if ( !StringTools.isEmpty( propertyName ) ) {
          // Override @Audited annotation on property level.
          final XProperty property = getProperty( overrideClass, propertyName );
          if ( auditOverride.isAudited() ) {
            if ( !overriddenNotAuditedProperties.contains( property ) ) {
              // If the property has not been marked as not audited by the subclass.
              overriddenAuditedProperties.add( property );
            }
          }
          else {
            if ( !overriddenAuditedProperties.contains( property ) ) {
              // If the property has not been marked as audited by the subclass.
              overriddenNotAuditedProperties.add( property );
            }
          }
        }
        else {
          // Override @Audited annotation on class level.
          if ( auditOverride.isAudited() ) {
            if ( !overriddenNotAuditedClasses.contains( overrideClass ) ) {
              // If the class has not been marked as not audited by the subclass.
              overriddenAuditedClasses.add( overrideClass );
            }
          }
          else {
            if ( !overriddenAuditedClasses.contains( overrideClass ) ) {
              // If the class has not been marked as audited by the subclass.
              overriddenNotAuditedClasses.add( overrideClass );
            }
          }
        }
      }
    }
    final XClass superclass = clazz.getSuperclass();
    if ( !clazz.isInterface() && !Object.class.getName().equals( superclass.getName() ) ) {
      readAuditOverrides( superclass );
    }
  }

  /**
   * @param clazz Source class.
   *
   * @return List of @AuditOverride annotations applied at class level.
   */
  private List<AuditOverride> computeAuditOverrides(XClass clazz) {
    final AuditOverrides auditOverrides = clazz.getAnnotation( AuditOverrides.class );
    final AuditOverride auditOverride = clazz.getAnnotation( AuditOverride.class );
    if ( auditOverrides == null && auditOverride != null ) {
      return Arrays.asList( auditOverride );
    }
    else if ( auditOverrides != null && auditOverride == null ) {
      return Arrays.asList( auditOverrides.value() );
    }
    else if ( auditOverrides != null && auditOverride != null ) {
      throw new MappingException(
          "@AuditOverrides annotation should encapsulate all @AuditOverride declarations. " +
              "Please revise Envers annotations applied to class " + clazz.getName() + "."
      );
    }
    return Collections.emptyList();
  }

  /**
   * Checks whether one class is assignable from another. If not {@link MappingException} is thrown.
   *
   * @param child Subclass.
   * @param parent Superclass.
   */
  private void checkSuperclass(XClass child, XClass parent) {
    if ( !parent.isAssignableFrom( child ) ) {
      throw new MappingException(
          "Class " + parent.getName() + " is not assignable from " + child.getName() + ". " +
              "Please revise Envers annotations applied to " + child.getName() + " type."
      );
    }
  }

  /**
   * Checks whether class contains property with a given name. If not {@link MappingException} is thrown.
   *
   * @param clazz Class.
   * @param propertyName Property name.
   *
   * @return Property object.
   */
  private XProperty getProperty(XClass clazz, String propertyName) {
    final XProperty property = ReflectionTools.getProperty( clazz, propertyName );
    if ( property == null ) {
      throw new MappingException(
          "Property '" + propertyName + "' not found in class " + clazz.getName() + ". " +
              "Please revise Envers annotations applied to class " + persistentPropertiesSource.getXClass() + "."
      );
    }
    return property;
  }

  private void readPersistentPropertiesAccess() {
    final Iterator<Property> propertyIter = persistentPropertiesSource.getPropertyIterator();
    while ( propertyIter.hasNext() ) {
      final Property property = propertyIter.next();
      addPersistentProperty( property );
      if ( "embedded".equals( property.getPropertyAccessorName() ) && property.getName()
          .equals( property.getNodeName() ) ) {
        // If property name equals node name and embedded accessor type is used, processing component
        // has been defined with <properties> tag. See HHH-6636 JIRA issue.
        createPropertiesGroupMapping( property );
      }
    }
  }

  private void addPersistentProperty(Property property) {
    if ( "field".equals( property.getPropertyAccessorName() ) ) {
      fieldAccessedPersistentProperties.add( property.getName() );
    }
    else {
      propertyAccessedPersistentProperties.add( property.getName() );
    }
  }

  @SuppressWarnings("unchecked")
  private void createPropertiesGroupMapping(Property property) {
    final Component component = (Component) property.getValue();
    final Iterator<Property> componentProperties = component.getPropertyIterator();
    while ( componentProperties.hasNext() ) {
      final Property componentProperty = componentProperties.next();
      propertiesGroupMapping.put( componentProperty.getName(), component.getNodeName() );
    }
  }

  /**
   * @param clazz Class which properties are currently being added.
   *
   * @return {@link Audited} annotation of specified class. If processed type hasn't been explicitly marked, method
   *         checks whether given class exists in {@link AuditedPropertiesReader#overriddenAuditedClasses} collection.
   *         In case of success, {@link Audited} configuration of currently mapped entity is returned, otherwise
   *         {@code null}. If processed type exists in {@link AuditedPropertiesReader#overriddenNotAuditedClasses}
   *         collection, the result is also {@code null}.
   */
  private Audited computeAuditConfiguration(XClass clazz) {
    Audited allClassAudited = clazz.getAnnotation( Audited.class );
    // If processed class is not explicitly marked with @Audited annotation, check whether auditing is
    // forced by any of its child entities configuration (@AuditedOverride.forClass).
    if ( allClassAudited == null && overriddenAuditedClasses.contains( clazz ) ) {
      // Declared audited parent copies @Audited.modStore and @Audited.targetAuditMode configuration from
      // currently mapped entity.
      allClassAudited = persistentPropertiesSource.getXClass().getAnnotation( Audited.class );
      if ( allClassAudited == null ) {
        // If parent class declares @Audited on the field/property level.
        allClassAudited = DEFAULT_AUDITED;
      }
    }
    else if ( allClassAudited != null && overriddenNotAuditedClasses.contains( clazz ) ) {
      return null;
    }
    return allClassAudited;
  }

  /**
   * Recursively adds all audited properties of entity class and its superclasses.
   *
   * @param clazz Currently processed class.
   */
  private void addPropertiesFromClass(XClass clazz) {
    final Audited allClassAudited = computeAuditConfiguration( clazz );

    //look in the class
    addFromProperties(
        clazz.getDeclaredProperties( "field" ),
        "field",
        fieldAccessedPersistentProperties,
        allClassAudited
    );
    addFromProperties(
        clazz.getDeclaredProperties( "property" ),
        "property",
        propertyAccessedPersistentProperties,
        allClassAudited
    );

    if ( allClassAudited != null || !auditedPropertiesHolder.isEmpty() ) {
      final XClass superclazz = clazz.getSuperclass();
      if ( !clazz.isInterface() && !"java.lang.Object".equals( superclazz.getName() ) ) {
        addPropertiesFromClass( superclazz );
      }
    }
  }

  private void addFromProperties(
      Iterable<XProperty> properties,
      String accessType,
      Set<String> persistentProperties,
      Audited allClassAudited) {
    for ( XProperty property : properties ) {
      // If this is not a persistent property, with the same access type as currently checked,
      // it's not audited as well.
      // If the property was already defined by the subclass, is ignored by superclasses
      if ( persistentProperties.contains( property.getName() )
          && !auditedPropertiesHolder.contains( property.getName() ) ) {
        final Value propertyValue = persistentPropertiesSource.getProperty( property.getName() ).getValue();
        if ( propertyValue instanceof Component ) {
          this.addFromComponentProperty( property, accessType, (Component) propertyValue, allClassAudited );
        }
        else {
          this.addFromNotComponentProperty( property, accessType, allClassAudited );
        }
      }
      else if ( propertiesGroupMapping.containsKey( property.getName() ) ) {
        // Retrieve embedded component name based on class field.
        final String embeddedName = propertiesGroupMapping.get( property.getName() );
        if ( !auditedPropertiesHolder.contains( embeddedName ) ) {
          // Manage properties mapped within <properties> tag.
          final Value propertyValue = persistentPropertiesSource.getProperty( embeddedName ).getValue();
          this.addFromPropertiesGroup(
              embeddedName,
              property,
              accessType,
              (Component) propertyValue,
              allClassAudited
          );
        }
      }
    }
  }

  private void addFromPropertiesGroup(
      String embeddedName,
      XProperty property,
      String accessType,
      Component propertyValue,
      Audited allClassAudited) {
    final ComponentAuditingData componentData = new ComponentAuditingData();
    final boolean isAudited = fillPropertyData( property, componentData, accessType, allClassAudited );
    if ( isAudited ) {
      // EntityPersister.getPropertyNames() returns name of embedded component instead of class field.
      componentData.setName( embeddedName );
      // Marking component properties as placed directly in class (not inside another component).
      componentData.setBeanName( null );

      final PersistentPropertiesSource componentPropertiesSource = new ComponentPropertiesSource(
          reflectionManager,
          propertyValue
      );
      final AuditedPropertiesReader audPropReader = new AuditedPropertiesReader(
          ModificationStore.FULL, componentPropertiesSource, componentData, globalCfg, reflectionManager,
          propertyNamePrefix + MappingTools.createComponentPrefix( embeddedName )
      );
      audPropReader.read();

      auditedPropertiesHolder.addPropertyAuditingData( embeddedName, componentData );
    }
  }

  private void addFromComponentProperty(
      XProperty property,
      String accessType,
      Component propertyValue,
      Audited allClassAudited) {
    final ComponentAuditingData componentData = new ComponentAuditingData();
    final boolean isAudited = fillPropertyData( property, componentData, accessType, allClassAudited );

    if ( propertyValue.isDynamic() ) {
      if ( isAudited ) {
        throw new MappingException(
            "Audited dynamic-component properties are not supported. Consider applying @NotAudited annotation to "
                + propertyValue.getOwner().getEntityName() + "#" + property + "."
        );
      }
      return;
    }

    final PersistentPropertiesSource componentPropertiesSource = new ComponentPropertiesSource(
        reflectionManager, propertyValue
    );

    final ComponentAuditedPropertiesReader audPropReader = new ComponentAuditedPropertiesReader(
        ModificationStore.FULL,
        componentPropertiesSource,
        componentData,
        globalCfg,
        reflectionManager,
        propertyNamePrefix + MappingTools.createComponentPrefix( property.getName() )
    );
    audPropReader.read();

    if ( isAudited ) {
      // Now we know that the property is audited
      auditedPropertiesHolder.addPropertyAuditingData( property.getName(), componentData );
    }
  }

  private void addFromNotComponentProperty(XProperty property, String accessType, Audited allClassAudited) {
    final PropertyAuditingData propertyData = new PropertyAuditingData();
    final boolean isAudited = fillPropertyData( property, propertyData, accessType, allClassAudited );

    if ( isAudited ) {
      // Now we know that the property is audited
      auditedPropertiesHolder.addPropertyAuditingData( property.getName(), propertyData );
    }
  }


  /**
   * Checks if a property is audited and if yes, fills all of its data.
   *
   * @param property Property to check.
   * @param propertyData Property data, on which to set this property's modification store.
   * @param accessType Access type for the property.
   *
   * @return False if this property is not audited.
   */
  private boolean fillPropertyData(
      XProperty property,
      PropertyAuditingData propertyData,
      String accessType,
      Audited allClassAudited) {

    // check if a property is declared as not audited to exclude it
    // useful if a class is audited but some properties should be excluded
    final NotAudited unVer = property.getAnnotation( NotAudited.class );
    if ( (unVer != null
        && !overriddenAuditedProperties.contains( property ))
        || overriddenNotAuditedProperties.contains( property ) ) {
      return false;
    }
    else {
      // if the optimistic locking field has to be unversioned and the current property
      // is the optimistic locking field, don't audit it
      if ( globalCfg.isDoNotAuditOptimisticLockingField() ) {
        final Version jpaVer = property.getAnnotation( Version.class );
        if ( jpaVer != null ) {
          return false;
        }
      }
    }


    if ( !this.checkAudited( property, propertyData, allClassAudited ) ) {
      return false;
    }

    final String propertyName = propertyNamePrefix + property.getName();
    propertyData.setName( propertyName );
    propertyData.setModifiedFlagName(
        MetadataTools.getModifiedFlagPropertyName(
            propertyName,
            globalCfg.getModifiedFlagSuffix()
        )
    );
    propertyData.setBeanName( property.getName() );
    propertyData.setAccessType( accessType );

    addPropertyJoinTables( property, propertyData );
    addPropertyAuditingOverrides( property, propertyData );
    if ( !processPropertyAuditingOverrides( property, propertyData ) ) {
      // not audited due to AuditOverride annotation
      return false;
    }
    addPropertyMapKey( property, propertyData );
    setPropertyAuditMappedBy( property, propertyData );
    setPropertyRelationMappedBy( property, propertyData );

    return true;
  }


  protected boolean checkAudited(
      XProperty property,
      PropertyAuditingData propertyData, Audited allClassAudited) {
    // Checking if this property is explicitly audited or if all properties are.
    Audited aud = (property.isAnnotationPresent( Audited.class ))
        ? property.getAnnotation( Audited.class )
        : allClassAudited;
    if ( aud == null
        && overriddenAuditedProperties.contains( property )
        && !overriddenNotAuditedProperties.contains( property ) ) {
      // Assigning @Audited defaults. If anyone needs to customize those values in the future,
      // appropriate fields shall be added to @AuditOverride annotation.
      aud = DEFAULT_AUDITED;
    }
    if ( aud != null ) {
      propertyData.setStore( aud.modStore() );
      propertyData.setRelationTargetAuditMode( aud.targetAuditMode() );
      propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) );
      return true;
    }
    else {
      return false;
    }
  }

  protected boolean checkUsingModifiedFlag(Audited aud) {
    return globalCfg.hasSettingForUsingModifiedFlag() ?
        globalCfg.isGlobalWithModifiedFlag() : aud.withModifiedFlag();
  }

  private void setPropertyRelationMappedBy(XProperty property, PropertyAuditingData propertyData) {
    final OneToMany oneToMany = property.getAnnotation( OneToMany.class );
    if ( oneToMany != null && !"".equals( oneToMany.mappedBy() ) ) {
      propertyData.setRelationMappedBy( oneToMany.mappedBy() );
    }
  }

  private void setPropertyAuditMappedBy(XProperty property, PropertyAuditingData propertyData) {
    final AuditMappedBy auditMappedBy = property.getAnnotation( AuditMappedBy.class );
    if ( auditMappedBy != null ) {
      propertyData.setAuditMappedBy( auditMappedBy.mappedBy() );
      if ( !"".equals( auditMappedBy.positionMappedBy() ) ) {
        propertyData.setPositionMappedBy( auditMappedBy.positionMappedBy() );
      }
    }
  }

  private void addPropertyMapKey(XProperty property, PropertyAuditingData propertyData) {
    final MapKey mapKey = property.getAnnotation( MapKey.class );
    if ( mapKey != null ) {
      propertyData.setMapKey( mapKey.name() );
    }
  }

  private void addPropertyJoinTables(XProperty property, PropertyAuditingData propertyData) {
    // first set the join table based on the AuditJoinTable annotation
    final AuditJoinTable joinTable = property.getAnnotation( AuditJoinTable.class );
    if ( joinTable != null ) {
      propertyData.setJoinTable( joinTable );
    }
    else {
      propertyData.setJoinTable( DEFAULT_AUDIT_JOIN_TABLE );
    }
  }

  /**
   * Add the {@link AuditOverride} annotations.
   *
   * @param property the property being processed
   * @param propertyData the Envers auditing data for this property
   */
  private void addPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) {
    final AuditOverride annotationOverride = property.getAnnotation( AuditOverride.class );
    if ( annotationOverride != null ) {
      propertyData.addAuditingOverride( annotationOverride );
    }
    final AuditOverrides annotationOverrides = property.getAnnotation( AuditOverrides.class );
    if ( annotationOverrides != null ) {
      propertyData.addAuditingOverrides( annotationOverrides );
    }
  }

  /**
   * Process the {@link AuditOverride} annotations for this property.
   *
   * @param property the property for which the {@link AuditOverride}
   * annotations are being processed
   * @param propertyData the Envers auditing data for this property
   *
   * @return {@code false} if isAudited() of the override annotation was set to
   */
  private boolean processPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) {
    // if this property is part of a component, process all override annotations
    if ( this.auditedPropertiesHolder instanceof ComponentAuditingData ) {
      final List<AuditOverride> overrides = ( (ComponentAuditingData) this.auditedPropertiesHolder ).getAuditingOverrides();
      for ( AuditOverride override : overrides ) {
        if ( property.getName().equals( override.name() ) ) {
          // the override applies to this property
          if ( !override.isAudited() ) {
            return false;
          }
          else {
            if ( override.auditJoinTable() != null ) {
              propertyData.setJoinTable( override.auditJoinTable() );
            }
          }
        }
      }

    }
    return true;
  }

  private static final Audited DEFAULT_AUDITED = new Audited() {
    @Override
    public ModificationStore modStore() {
      return ModificationStore.FULL;
    }

    @Override
    public RelationTargetAuditMode targetAuditMode() {
      return RelationTargetAuditMode.AUDITED;
    }

    @Override
    public Class[] auditParents() {
      return new Class[0];
    }

    @Override
    public boolean withModifiedFlag() {
      return false;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
      return this.getClass();
    }
  };

  private static final AuditJoinTable DEFAULT_AUDIT_JOIN_TABLE = new AuditJoinTable() {
    @Override
    public String name() {
      return "";
    }

    @Override
    public String schema() {
      return "";
    }

    @Override
    public String catalog() {
      return "";
    }

    @Override
    public JoinColumn[] inverseJoinColumns() {
      return new JoinColumn[0];
    }

    @Override
    public Class<? extends Annotation> annotationType() {
      return this.getClass();
    }
  };

  public static class ComponentPropertiesSource implements PersistentPropertiesSource {
    private final XClass xclass;
    private final Component component;

    public ComponentPropertiesSource(ReflectionManager reflectionManager, Component component) {
      try {
        this.xclass = reflectionManager.classForName( component.getComponentClassName(), this.getClass() );
      }
      catch (ClassNotFoundException e) {
        throw new MappingException( e );
      }

      this.component = component;
    }

    @Override
    @SuppressWarnings({"unchecked"})
    public Iterator<Property> getPropertyIterator() {
      return component.getPropertyIterator();
    }

    @Override
    public Property getProperty(String propertyName) {
      return component.getProperty( propertyName );
    }

    @Override
    public XClass getXClass() {
      return xclass;
    }
  }
}
TOP

Related Classes of org.hibernate.envers.configuration.internal.metadata.reader.AuditedPropertiesReader$ComponentPropertiesSource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.