Package org.hibernate.cfg

Source Code of org.hibernate.cfg.PropertyContainer

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, 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
*/

// $Id$

package org.hibernate.cfg;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.persistence.Access;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Transient;

import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.Target;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;

import org.jboss.logging.Logger;

/**
* A helper class to keep the {@code XProperty}s of a class ordered by access type.
*
* @author Hardy Ferentschik
*/
class PropertyContainer {

    static {
        System.setProperty("jboss.i18n.generate-proxies", "true");
    }

    private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, PropertyContainer.class.getName());

  private final AccessType explicitClassDefinedAccessType;

  /**
   * Constains the properties which must be returned in case the class is accessed via {@code AccessType.FIELD}. Note,
   * this does not mean that all {@code XProperty}s in this map are fields. Due to JPA access rules single properties
   * can have different access type than the overall class access type.
   */
  private final TreeMap<String, XProperty> fieldAccessMap;

  /**
   * Constains the properties which must be returned in case the class is accessed via {@code AccessType.Property}. Note,
   * this does not mean that all {@code XProperty}s in this map are properties/methods. Due to JPA access rules single properties
   * can have different access type than the overall class access type.
   */
  private final TreeMap<String, XProperty> propertyAccessMap;

  /**
   * The class for which this container is created.
   */
  private final XClass xClass;
  private final XClass entityAtStake;

  PropertyContainer(XClass clazz, XClass entityAtStake) {
    this.xClass = clazz;
    this.entityAtStake = entityAtStake;

    explicitClassDefinedAccessType = determineClassDefinedAccessStrategy();

    // first add all properties to field and property map
    fieldAccessMap = initProperties( AccessType.FIELD );
    propertyAccessMap = initProperties( AccessType.PROPERTY );

    considerExplicitFieldAndPropertyAccess();
  }

  public XClass getEntityAtStake() {
    return entityAtStake;
  }

  public XClass getDeclaringClass() {
    return xClass;
  }

  public AccessType getExplicitAccessStrategy() {
    return explicitClassDefinedAccessType;
  }

  public boolean hasExplicitAccessStrategy() {
    return !explicitClassDefinedAccessType.equals( AccessType.DEFAULT );
  }

  public Collection<XProperty> getProperties(AccessType accessType) {
    assertTypesAreResolvable( accessType );
    if ( AccessType.DEFAULT == accessType || AccessType.PROPERTY == accessType ) {
      return Collections.unmodifiableCollection( propertyAccessMap.values() );
    }
    else {
      return Collections.unmodifiableCollection( fieldAccessMap.values() );
    }
  }

  private void assertTypesAreResolvable(AccessType access) {
    Map<String, XProperty> xprops;
    if ( AccessType.PROPERTY.equals( access ) || AccessType.DEFAULT.equals( access ) ) {
      xprops = propertyAccessMap;
    }
    else {
      xprops = fieldAccessMap;
    }
    for ( XProperty property : xprops.values() ) {
      if ( !property.isTypeResolved() && !discoverTypeWithoutReflection( property ) ) {
        String msg = "Property " + StringHelper.qualify( xClass.getName(), property.getName() ) +
            " has an unbound type and no explicit target entity. Resolve this Generic usage issue" +
            " or set an explicit target attribute (eg @OneToMany(target=) or use an explicit @Type";
        throw new AnnotationException( msg );
      }
    }
  }

  private void considerExplicitFieldAndPropertyAccess() {
    for ( XProperty property : fieldAccessMap.values() ) {
      Access access = property.getAnnotation( Access.class );
      if ( access == null ) {
        continue;
      }

      // see "2.3.2 Explicit Access Type" of JPA 2 spec
      // the access type for this property is explicitly set to AccessType.FIELD, hence we have to
      // use field access for this property even if the default access type for the class is AccessType.PROPERTY
      AccessType accessType = AccessType.getAccessStrategy( access.value() );
            if (accessType == AccessType.FIELD) {
        propertyAccessMap.put(property.getName(), property);
      }
            else {
        LOG.debug( "Placing @Access(AccessType.FIELD) on a field does not have any effect." );
      }
    }

    for ( XProperty property : propertyAccessMap.values() ) {
      Access access = property.getAnnotation( Access.class );
      if ( access == null ) {
        continue;
      }

      AccessType accessType = AccessType.getAccessStrategy( access.value() );

      // see "2.3.2 Explicit Access Type" of JPA 2 spec
      // the access type for this property is explicitly set to AccessType.PROPERTY, hence we have to
      // return use method access even if the default class access type is AccessType.FIELD
            if (accessType == AccessType.PROPERTY) {
        fieldAccessMap.put(property.getName(), property);
      }
            else {
        LOG.debug( "Placing @Access(AccessType.PROPERTY) on a field does not have any effect." );
      }
    }
  }

  /**
   * Retrieves all properties from the {@code xClass} with the specified access type. This method does not take
   * any jpa access rules/annotations into account yet.
   *
   * @param access The access type - {@code AccessType.FIELD}  or {@code AccessType.Property}
   *
   * @return A maps of the properties with the given access type keyed against their property name
   */
  private TreeMap<String, XProperty> initProperties(AccessType access) {
    if ( !( AccessType.PROPERTY.equals( access ) || AccessType.FIELD.equals( access ) ) ) {
      throw new IllegalArgumentException( "Access type has to be AccessType.FIELD or AccessType.Property" );
    }

    //order so that property are used in the same order when binding native query
    TreeMap<String, XProperty> propertiesMap = new TreeMap<String, XProperty>();
    List<XProperty> properties = xClass.getDeclaredProperties( access.getType() );
    for ( XProperty property : properties ) {
      if ( mustBeSkipped( property ) ) {
        continue;
      }
      propertiesMap.put( property.getName(), property );
    }
    return propertiesMap;
  }

  private AccessType determineClassDefinedAccessStrategy() {
    AccessType classDefinedAccessType;

    AccessType hibernateDefinedAccessType = AccessType.DEFAULT;
    AccessType jpaDefinedAccessType = AccessType.DEFAULT;

    org.hibernate.annotations.AccessType accessType = xClass.getAnnotation( org.hibernate.annotations.AccessType.class );
    if ( accessType != null ) {
      hibernateDefinedAccessType = AccessType.getAccessStrategy( accessType.value() );
    }

    Access access = xClass.getAnnotation( Access.class );
    if ( access != null ) {
      jpaDefinedAccessType = AccessType.getAccessStrategy( access.value() );
    }

    if ( hibernateDefinedAccessType != AccessType.DEFAULT
        && jpaDefinedAccessType != AccessType.DEFAULT
        && hibernateDefinedAccessType != jpaDefinedAccessType ) {
      throw new MappingException(
          "@AccessType and @Access specified with contradicting values. Use of @Access only is recommended. "
      );
    }

    if ( hibernateDefinedAccessType != AccessType.DEFAULT ) {
      classDefinedAccessType = hibernateDefinedAccessType;
    }
    else {
      classDefinedAccessType = jpaDefinedAccessType;
    }
    return classDefinedAccessType;
  }

  private static boolean discoverTypeWithoutReflection(XProperty p) {
    if ( p.isAnnotationPresent( OneToOne.class ) && !p.getAnnotation( OneToOne.class )
        .targetEntity()
        .equals( void.class ) ) {
      return true;
    }
    else if ( p.isAnnotationPresent( OneToMany.class ) && !p.getAnnotation( OneToMany.class )
        .targetEntity()
        .equals( void.class ) ) {
      return true;
    }
    else if ( p.isAnnotationPresent( ManyToOne.class ) && !p.getAnnotation( ManyToOne.class )
        .targetEntity()
        .equals( void.class ) ) {
      return true;
    }
    else if ( p.isAnnotationPresent( ManyToMany.class ) && !p.getAnnotation( ManyToMany.class )
        .targetEntity()
        .equals( void.class ) ) {
      return true;
    }
    else if ( p.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
      return true;
    }
    else if ( p.isAnnotationPresent( ManyToAny.class ) ) {
      if ( !p.isCollection() && !p.isArray() ) {
        throw new AnnotationException( "@ManyToAny used on a non collection non array property: " + p.getName() );
      }
      return true;
    }
    else if ( p.isAnnotationPresent( Type.class ) ) {
      return true;
    }
    else if ( p.isAnnotationPresent( Target.class ) ) {
      return true;
    }
    return false;
  }

  private static boolean mustBeSkipped(XProperty property) {
    //TODO make those hardcoded tests more portable (through the bytecode provider?)
    return property.isAnnotationPresent( Transient.class )
        || "net.sf.cglib.transform.impl.InterceptFieldCallback".equals( property.getType().getName() )
        || "org.hibernate.bytecode.internal.javassist.FieldHandler".equals( property.getType().getName() );
  }
}

TOP

Related Classes of org.hibernate.cfg.PropertyContainer

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.