Package org.springframework.data.neo4j.support.mapping

Source Code of org.springframework.data.neo4j.support.mapping.Neo4jPersistentPropertyImpl

/**
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.data.neo4j.support.mapping;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.neo4j.annotation.EndNode;
import org.springframework.data.neo4j.annotation.Fetch;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.GraphProperty;
import org.springframework.data.neo4j.annotation.Indexed;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.annotation.RelatedTo;
import org.springframework.data.neo4j.annotation.RelatedToVia;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.neo4j.annotation.RelationshipType;
import org.springframework.data.neo4j.annotation.StartNode;
import org.springframework.data.neo4j.mapping.IndexInfo;
import org.springframework.data.neo4j.mapping.ManagedEntity;
import org.springframework.data.neo4j.mapping.MappingPolicy;
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.neo4j.mapping.RelationshipInfo;
import org.springframework.data.neo4j.support.DoReturn;
import org.springframework.data.util.TypeInformation;

/**
* Implementation of {@link org.springframework.data.neo4j.mapping.Neo4jPersistentProperty}.
*
* @author Oliver Gierke
*/
class Neo4jPersistentPropertyImpl extends AnnotationBasedPersistentProperty<Neo4jPersistentProperty> implements
        Neo4jPersistentProperty {

    private final static Logger log = LoggerFactory.getLogger(Neo4jPersistentProperty.class);

    private final RelationshipInfo relationshipInfo;
    private final boolean isIdProperty;
    private IndexInfo indexInfo;
    private Association<Neo4jPersistentProperty> myAssociation;
    private String defaultValue;
    private Class<?> propertyType;
    private String query;
    private final boolean isNeo4jEntityType;
    private Boolean isAssociation;
    private final String neo4jPropertyName;
    private final int hash;

    public Neo4jPersistentPropertyImpl(Field field, PropertyDescriptor propertyDescriptor,
                                       PersistentEntity<?, Neo4jPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder, Neo4jMappingContext ctx) {
        super(field, propertyDescriptor, owner, simpleTypeHolder);
        this.hash = field == null ? propertyDescriptor.hashCode() : field.hashCode();
        this.relationshipInfo = extractRelationshipInfo(field, ctx);
        this.isNeo4jEntityType = isNeo4jPropertyType(getType());
        this.propertyType = extractPropertyType();
        this.neo4jPropertyName = createNeo4jPropertyName();
        this.indexInfo = extractIndexInfo();
        this.isIdProperty = super.isIdProperty() || getAnnotation(GraphId.class) != null;
        this.defaultValue = extractDefaultValue();
        this.myAssociation = isAssociation() ? super.getAssociation() == null ? createAssociation() : super.getAssociation() : null;
        this.query = extractQuery();
    }

    private String extractQuery() {
        final Query query = getAnnotation(Query.class);
        if (query == null) return null;
        String value = query.value();
        return value.trim().isEmpty() ? null : value;
    }

    private Class<?> extractPropertyType() {
        final GraphProperty graphProperty = getAnnotation(GraphProperty.class);
        if (graphProperty==null) return null;
        return graphProperty.propertyType();
    }

    private String extractDefaultValue() {
        final GraphProperty graphProperty = getAnnotation(GraphProperty.class);
        if (graphProperty==null) return null;
        final String value = graphProperty.defaultValue();
        if (value.equals(GraphProperty.UNSET_DEFAULT)) return null;
        return value;
    }

    @Override
    public Association<Neo4jPersistentProperty> getAssociation() {
        return myAssociation;
    }

    private IndexInfo extractIndexInfo() {
        final Indexed annotation = getAnnotation(Indexed.class);
        return annotation!=null ? new IndexInfo(annotation,this) : null;
    }

    public <T extends Annotation> T getAnnotation(Class<? extends T> annotationType) {
        return findAnnotation(annotationType);
    }

    private RelationshipInfo extractRelationshipInfo(final Field field, Neo4jMappingContext ctx) {
        if (isAnnotationPresent(RelatedTo.class)) {
            return RelationshipInfo.fromField(getName(), getAnnotation(RelatedTo.class), getTypeInformation(), ctx);
        }

        if (isAnnotationPresent(RelatedToVia.class)) {
            return RelationshipInfo.fromField(getName(), getAnnotation(RelatedToVia.class), getTypeInformation(),ctx);
        }
        if (hasAnnotation(getTypeInformation(), NodeEntity.class)) {
            return RelationshipInfo.fromField(getName(), getTypeInformation(), ctx);
        }
        return null;
    }


    @Override
    public void setValue(Object entity, Object newValue) {
     
      BeanWrapper<Object> wrapper = BeanWrapper.create(entity, null);
      wrapper.setProperty(this, newValue);
    }

    private static boolean hasAnnotation(TypeInformation<?> typeInformation, final Class<NodeEntity> annotationClass) {
        return typeInformation.getActualType().getType().isAnnotationPresent(annotationClass);
    }

    @Override
    protected Association<Neo4jPersistentProperty> createAssociation() {
        return new Association<Neo4jPersistentProperty>(this, null);
    }

    @Override
    public boolean isAssociation() {
     
      if (this.isAssociation == null) {
        this.isAssociation = super.isAssociation();
      }
     
      return this.isAssociation || isRelationship();
    }

    @Override
    public boolean isRelationship() {
        return this.relationshipInfo != null;
    }

    @Override
    public RelationshipInfo getRelationshipInfo() {
        return relationshipInfo;
    }

    @Override
    public boolean isIndexed() {
        return indexInfo != null;
    }

    @Override
    public IndexInfo getIndexInfo() {
        return indexInfo;
    }

    public String getNeo4jPropertyName() {
        return neo4jPropertyName;
    }

    private String createNeo4jPropertyName() {
        final Neo4jPersistentEntity<?> entityClass = (Neo4jPersistentEntity<?>) getOwner();
        final GraphProperty annotation = getAnnotation(GraphProperty.class);
        if (annotation != null && annotation.propertyName() != null && !annotation.propertyName().isEmpty()) return annotation.propertyName();
        if (entityClass.useShortNames()) return getName();
        return String.format("%s.%s", entityClass.getType().getSimpleName(), getName());
    }

    public boolean mustConvert() {
        return !isNeo4jPropertyType() || propertyType != null;
    }


    public boolean isSerializablePropertyField(final ConversionService conversionService) {
        if (isRelationship()) return false;
        if (!mustConvert()) return false;
        final Class<?> type = getType();
        if (getTypeInformation().isCollectionLike()) {
            return isConvertible(conversionService, getComponentType());
        }
        return isConvertible(conversionService, type);
    }

    private boolean isConvertible(ConversionService conversionService, Class<?> type) {
        Class targetType = getPropertyTypeOrDefault();
        return conversionService.canConvert(type, targetType) && conversionService.canConvert(targetType, type);
    }

    private Class<?> getPropertyTypeOrDefault() {
        return propertyType == null ? DEFAULT_NEO4J_PROPERTY_TYPE : propertyType;
    }

    @Override
    public boolean isNeo4jPropertyType() {
        return isNeo4jEntityType;
    }

    public static boolean isNumeric(final Class<?> fieldType) {
        return (fieldType.isPrimitive() && !fieldType.equals(boolean.class) && !fieldType.equals(void.class))
            || fieldType.equals(Character.class)
            || (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType));
    }

    public boolean isIndexedNumerically() {
        if (!isIndexed() || !getIndexInfo().isNumeric()) return false;
        return isNumeric(getType()) || isNumeric(getPropertyType()) ||
               (getType().isArray() && !getType().getComponentType().isArray() && isNumeric(getType().getComponentType()));
    }

    private static boolean isNeo4jPropertyType(final Class<?> fieldType) {
        // todo: add array support
        return fieldType.equals(String.class)
                || fieldType.equals(Boolean.class)
                || fieldType.equals(boolean.class)
                || isNumeric(fieldType)
                || (fieldType.isArray() && !fieldType.getComponentType().isArray() && isNeo4jPropertyType(fieldType.getComponentType()));
    }

    @Override
    public boolean isNeo4jPropertyValue(Object value) {
    if (value == null) {
        return false;
    }
    return isNeo4jPropertyType(value.getClass());
    }

    public boolean isSyntheticField() {
        return getName().contains("$");
    }

    public Object getValue(final Object entity, final MappingPolicy mappingPolicy) {
        if (entity instanceof ManagedEntity && !mappingPolicy.accessField()) {
            return DoReturn.unwrap(((ManagedEntity<?,?>) entity).getEntityState().getValue(this, mappingPolicy));
        }
        return getValueFromEntity(entity, mappingPolicy);
    }

    @Override
    public Object getValueFromEntity(Object entity, final MappingPolicy mappingPolicy) {
     
      BeanWrapper<Object> wrapper = BeanWrapper.create(entity, null);
      return wrapper.getProperty(this);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getDefaultValue(ConversionService conversionService, final Class<T> targetType) {
        if (defaultValue == null) return (T) getDefaultValue(targetType);
        if (targetType.isAssignableFrom(String.class)) return (T) defaultValue;
        if (conversionService == null) return (T) getDefaultValue(targetType);
        return conversionService.convert(defaultValue, targetType);
    }

    @Override
    public Class<?> getPropertyType() {
        return propertyType;
    }

    public boolean isUnique() {
        return isIndexed() && getIndexInfo().isUnique();
    }

    private Object getDefaultValue(Class<?> type) {
        if (type!=null && type.isPrimitive()) {
            if (type.equals(boolean.class)) return false;
            return 0;
        }
        return null;
    }


    @Override
    public String toString() {
        return getType() +" "+ getName() + " rel: "+isRelationship()+ " idx: "+isIndexed();
    }

    @Override
    public Neo4jPersistentEntity<?> getOwner() {
        return (Neo4jPersistentEntity<?>) super.getOwner();
    }
    @Override
    public boolean isEntity() {
        return super.isEntity() && (isRelationshipEntity(getType()) || isNodeEntity(getType()));
    }

    private boolean isRelationshipEntity(final Class<?> type) {
        return type.isAnnotationPresent(RelationshipEntity.class);
    }

    private boolean isNodeEntity(Class<?> type) {
        return type.isAnnotationPresent(NodeEntity.class);
    }

    public String getIndexKey() {
        return getIndexInfo().getIndexKey();
    }

    @Override
    public MappingPolicy getMappingPolicy() {
        if (isAnnotationPresent(Fetch.class))
            return MappingPolicy.LOAD_POLICY;
        else
            return MappingPolicy.DEFAULT_POLICY;
    }

    public boolean isTransient() {

        boolean primaryIsTransient = super.isTransient();
        // DATAGRAPH-257
        // We need to ignore @Query annotated fields for the moment. Entities should be able
        // to be marked with the Java transient modified so that they are not serialized,
        // however the still need @Query functionality to be able to execute and this will
        // not be done if the property is marked as transient
        if (field != null && field.isAnnotationPresent(Query.class)) {
            return false;
        }

      if (primaryIsTransient) {
        return true;
      }
     
      return field == null ? false : Modifier.isTransient(field.getModifiers());
    }

    @Override
    public Iterable<? extends TypeInformation<?>> getPersistentEntityType() {
        final Iterable<? extends TypeInformation<?>> result = super.getPersistentEntityType();
        for (Iterator<? extends TypeInformation<?>> it = result.iterator(); it.hasNext(); ) {
            final TypeInformation<?> typeInformation = it.next();
            final Class<?> type = typeInformation.getType();
            if (isNodeEntity(type) || isRelationshipEntity(type)) continue;
            if (log.isInfoEnabled()) log.info("ignoring "+getName()+" "+type+" "+typeInformation.getActualType().getType());
            it.remove();
        }
        return result;
    }

    public MappingPolicy obtainMappingPolicy(MappingPolicy providedMappingPolicy) {
        if (providedMappingPolicy != null) return providedMappingPolicy;
        return getMappingPolicy();
    }
    public int hashCode() {
        return hash;
    }

    public boolean hasQuery() {
        return this.query!=null;
    }

    public String getQuery() {
        return query;
    }

    @Override
    public Class<?> getTargetType() {
        if (! isTargetTypeEnforced()) return null;

        if (isCollectionLike()) return getComponentType();

        return getType();
    }

    @Override
    public boolean isTargetTypeEnforced() {
        return getAnnotation( RelatedTo.class ) != null && getAnnotation( RelatedTo.class ).enforceTargetType();
    }
   
  /*
   * (non-Javadoc)
   * @see org.springframework.data.neo4j.mapping.Neo4jPersistentProperty#isStartNode()
   */
  @Override
  public boolean isStartNode() {
    return isAnnotationPresent(StartNode.class);
  }
 
  /*
   * (non-Javadoc)
   * @see org.springframework.data.neo4j.mapping.Neo4jPersistentProperty#isEndNode()
   */
  @Override
  public boolean isEndNode() {
    return isAnnotationPresent(EndNode.class);
  }
 
  /*
   * (non-Javadoc)
   * @see org.springframework.data.neo4j.mapping.Neo4jPersistentProperty#isRelationshipType()
   */
  @Override
  public boolean isRelationshipType() {
    return isAnnotationPresent(RelationshipType.class);
  }
 
  /*
   * (non-Javadoc)
   * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isIdProperty()
   */
  @Override
  public boolean isIdProperty() {
    return isIdProperty;
  }
}
TOP

Related Classes of org.springframework.data.neo4j.support.mapping.Neo4jPersistentPropertyImpl

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.