Search in sources :

Example 1 with PropertyType

use of com.datastax.oss.driver.internal.mapper.processor.util.generation.PropertyType in project java-driver by datastax.

the class EntityHelperGetMethodGenerator method convertUdtsIntoEntities.

/**
 * Generates the code to convert a collection of UDT instances, for example a {@code Map<String,
 * UdtValue>} into a {@code Map<String, Product>}.
 *
 * @param rawObjectName the name of the local variable containing the value to convert.
 * @param mappedObjectName the name of the local variable that will hold the converted value (it
 *     already exists).
 * @param type the type of the value.
 * @param getBuilder the method where the generated code will be appended.
 */
private void convertUdtsIntoEntities(String rawObjectName, String mappedObjectName, PropertyType type, MethodSpec.Builder getBuilder) {
    if (type instanceof PropertyType.SingleEntity) {
        ClassName entityClass = ((PropertyType.SingleEntity) type).entityName;
        String entityHelperName = enclosingClass.addEntityHelperField(entityClass);
        getBuilder.addStatement("$L = $L.get($L, lenient)", mappedObjectName, entityHelperName, rawObjectName);
    } else if (type instanceof PropertyType.EntityList) {
        getBuilder.addStatement("$L = $T.newArrayListWithExpectedSize($L.size())", mappedObjectName, Lists.class, rawObjectName);
        PropertyType mappedElementType = ((PropertyType.EntityList) type).elementType;
        TypeName rawElementType = mappedElementType.asRawTypeName();
        String rawElementName = enclosingClass.getNameIndex().uniqueField("rawElement");
        getBuilder.beginControlFlow("for ($T $L: $L)", rawElementType, rawElementName, rawObjectName);
        String mappedElementName = enclosingClass.getNameIndex().uniqueField("mappedElement");
        getBuilder.addStatement("$T $L", mappedElementType.asTypeName(), mappedElementName);
        convertUdtsIntoEntities(rawElementName, mappedElementName, mappedElementType, getBuilder);
        getBuilder.addStatement("$L.add($L)", mappedObjectName, mappedElementName).endControlFlow();
    } else if (type instanceof PropertyType.EntitySet) {
        getBuilder.addStatement("$L = $T.newLinkedHashSetWithExpectedSize($L.size())", mappedObjectName, Sets.class, rawObjectName);
        PropertyType mappedElementType = ((PropertyType.EntitySet) type).elementType;
        TypeName rawElementType = mappedElementType.asRawTypeName();
        String rawElementName = enclosingClass.getNameIndex().uniqueField("rawElement");
        getBuilder.beginControlFlow("for ($T $L: $L)", rawElementType, rawElementName, rawObjectName);
        String mappedElementName = enclosingClass.getNameIndex().uniqueField("mappedElement");
        getBuilder.addStatement("$T $L", mappedElementType.asTypeName(), mappedElementName);
        convertUdtsIntoEntities(rawElementName, mappedElementName, mappedElementType, getBuilder);
        getBuilder.addStatement("$L.add($L)", mappedObjectName, mappedElementName).endControlFlow();
    } else if (type instanceof PropertyType.EntityMap) {
        getBuilder.addStatement("$L = $T.newLinkedHashMapWithExpectedSize($L.size())", mappedObjectName, Maps.class, rawObjectName);
        PropertyType mappedKeyType = ((PropertyType.EntityMap) type).keyType;
        PropertyType mappedValueType = ((PropertyType.EntityMap) type).valueType;
        String rawEntryName = enclosingClass.getNameIndex().uniqueField("rawEntry");
        getBuilder.beginControlFlow("for ($T $L: $L.entrySet())", ParameterizedTypeName.get(ClassName.get(Map.Entry.class), mappedKeyType.asRawTypeName(), mappedValueType.asRawTypeName()), rawEntryName, rawObjectName);
        String rawKeyName = CodeBlock.of("$L.getKey()", rawEntryName).toString();
        String mappedKeyName;
        if (mappedKeyType instanceof PropertyType.Simple) {
            // no conversion, use the instance as-is
            mappedKeyName = rawKeyName;
        } else {
            mappedKeyName = enclosingClass.getNameIndex().uniqueField("mappedKey");
            getBuilder.addStatement("$T $L", mappedKeyType.asTypeName(), mappedKeyName);
            convertUdtsIntoEntities(rawKeyName, mappedKeyName, mappedKeyType, getBuilder);
        }
        String rawValueName = CodeBlock.of("$L.getValue()", rawEntryName).toString();
        String mappedValueName;
        if (mappedValueType instanceof PropertyType.Simple) {
            mappedValueName = rawValueName;
        } else {
            mappedValueName = enclosingClass.getNameIndex().uniqueField("mappedValue");
            getBuilder.addStatement("$T $L", mappedValueType.asTypeName(), mappedValueName);
            convertUdtsIntoEntities(rawValueName, mappedValueName, mappedValueType, getBuilder);
        }
        getBuilder.addStatement("$L.put($L, $L)", mappedObjectName, mappedKeyName, mappedValueName).endControlFlow();
    } else {
        throw new AssertionError("Unsupported type " + type.asTypeName());
    }
}
Also used : ParameterizedTypeName(com.squareup.javapoet.ParameterizedTypeName) TypeName(com.squareup.javapoet.TypeName) PropertyType(com.datastax.oss.driver.internal.mapper.processor.util.generation.PropertyType) Maps(com.datastax.oss.driver.shaded.guava.common.collect.Maps) Lists(com.datastax.oss.driver.shaded.guava.common.collect.Lists) ClassName(com.squareup.javapoet.ClassName)

Example 2 with PropertyType

use of com.datastax.oss.driver.internal.mapper.processor.util.generation.PropertyType in project java-driver by datastax.

the class EntityHelperGetMethodGenerator method generate.

@Override
public Optional<MethodSpec> generate() {
    MethodSpec.Builder getBuilder = MethodSpec.methodBuilder("get").addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(ParameterSpec.builder(ClassName.get(GettableByName.class), "source").build()).addParameter(ParameterSpec.builder(TypeName.BOOLEAN, "lenient").build()).returns(entityDefinition.getClassName());
    TypeName returnType = entityDefinition.getClassName();
    String resultName = "returnValue";
    boolean mutable = entityDefinition.isMutable();
    if (mutable) {
        // Create an instance now, we'll call the setters as we go through the properties
        getBuilder.addStatement("$1T $2L = new $1T()", returnType, resultName);
    }
    // We store each read property into a local variable, store the names here (this is only used if
    // the entity is immutable, we'll call the all-arg constructor at the end).
    List<String> propertyValueNames = new ArrayList<>();
    for (PropertyDefinition property : entityDefinition.getAllValues()) {
        PropertyType type = property.getType();
        CodeBlock cqlName = property.getCqlName();
        String setterName = property.getSetterName();
        String propertyValueName = enclosingClass.getNameIndex().uniqueField("propertyValue");
        propertyValueNames.add(propertyValueName);
        if (type instanceof PropertyType.Simple) {
            TypeName typeName = ((PropertyType.Simple) type).typeName;
            String primitiveAccessor = GeneratedCodePatterns.PRIMITIVE_ACCESSORS.get(typeName);
            if (primitiveAccessor != null) {
                // int propertyValue1 = source.getInt("length");
                if (mutable) {
                    getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName).addStatement("$T $L = source.get$L($L)", typeName, propertyValueName, primitiveAccessor, cqlName).addStatement("$L.$L($L)", resultName, setterName, propertyValueName).endControlFlow();
                } else {
                    getBuilder.addStatement("$T $L = !lenient || hasProperty(source, $L) ? source.get$L($L) : $L", typeName, propertyValueName, cqlName, primitiveAccessor, cqlName, typeName.equals(TypeName.BOOLEAN) ? false : 0);
                }
            } else if (typeName instanceof ClassName) {
                // UUID propertyValue1 = source.get("id", UUID.class);
                if (mutable) {
                    getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName).addStatement("$T $L = source.get($L, $T.class)", typeName, propertyValueName, cqlName, typeName).addStatement("$L.$L($L)", resultName, setterName, propertyValueName).endControlFlow();
                } else {
                    getBuilder.addStatement("$T $L = !lenient || hasProperty(source, $L) ? source.get($L, $T.class) : null", typeName, propertyValueName, cqlName, cqlName, typeName);
                }
            } else {
                // advantage in calling them instead of the generic get().
                if (mutable) {
                    getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName).addStatement("$T $L = source.get($L, $L)", typeName, propertyValueName, cqlName, enclosingClass.addGenericTypeConstant(typeName)).addStatement("$L.$L($L)", resultName, setterName, propertyValueName).endControlFlow();
                } else {
                    getBuilder.addStatement("$T $L = !lenient || hasProperty(source, $L) ? source.get($L, $L) : null", typeName, propertyValueName, cqlName, cqlName, enclosingClass.addGenericTypeConstant(typeName));
                }
            }
        } else if (type instanceof PropertyType.SingleEntity) {
            ClassName entityClass = ((PropertyType.SingleEntity) type).entityName;
            // Other entity class: the CQL column is a mapped UDT:
            // Dimensions propertyValue1;
            // UdtValue udtValue1 = source.getUdtValue("dimensions");
            // propertyValue1 = udtValue1 == null ? null : dimensionsHelper.get(udtValue1);
            String udtValueName = enclosingClass.getNameIndex().uniqueField("udtValue");
            if (mutable) {
                getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName);
                getBuilder.addStatement("$T $L", entityClass, propertyValueName);
            } else {
                getBuilder.addStatement("$T $L = null", entityClass, propertyValueName);
                getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName);
            }
            getBuilder.addStatement("$T $L = source.getUdtValue($L)", UdtValue.class, udtValueName, cqlName);
            // Get underlying udt object and set it on return type
            String childHelper = enclosingClass.addEntityHelperField(entityClass);
            getBuilder.addStatement("$L = $L == null ? null : $L.get($L, lenient)", propertyValueName, udtValueName, childHelper, udtValueName);
            if (mutable) {
                getBuilder.addStatement("$L.$L($L)", resultName, setterName, propertyValueName);
            }
            getBuilder.endControlFlow();
        } else {
            // }
            if (mutable) {
                getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName);
                getBuilder.addStatement("$T $L", type.asTypeName(), propertyValueName);
            } else {
                getBuilder.addStatement("$T $L = null", type.asTypeName(), propertyValueName);
                getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName);
            }
            String rawCollectionName = enclosingClass.getNameIndex().uniqueField("rawCollection");
            TypeName rawCollectionType = type.asRawTypeName();
            getBuilder.addStatement("$T $L = source.get($L, $L)", rawCollectionType, rawCollectionName, cqlName, enclosingClass.addGenericTypeConstant(rawCollectionType));
            getBuilder.beginControlFlow("if ($L == null)", rawCollectionName).addStatement("$L = null", propertyValueName).nextControlFlow("else");
            convertUdtsIntoEntities(rawCollectionName, propertyValueName, type, getBuilder);
            getBuilder.endControlFlow();
            if (mutable) {
                getBuilder.addStatement("$L.$L($L)", resultName, setterName, propertyValueName);
            }
            getBuilder.endControlFlow();
        }
    }
    if (mutable) {
        // We've already created an instance and filled the properties as we went
        getBuilder.addStatement("return returnValue");
    } else {
        // Assume an all-arg constructor exists, and call it with all the temporary variables
        getBuilder.addCode("$[return new $T(", returnType);
        for (int i = 0; i < propertyValueNames.size(); i++) {
            getBuilder.addCode((i == 0 ? "\n$L" : ",\n$L"), propertyValueNames.get(i));
        }
        getBuilder.addCode(")$];");
    }
    return Optional.of(getBuilder.build());
}
Also used : ParameterizedTypeName(com.squareup.javapoet.ParameterizedTypeName) TypeName(com.squareup.javapoet.TypeName) UdtValue(com.datastax.oss.driver.api.core.data.UdtValue) MethodSpec(com.squareup.javapoet.MethodSpec) GettableByName(com.datastax.oss.driver.api.core.data.GettableByName) ArrayList(java.util.ArrayList) CodeBlock(com.squareup.javapoet.CodeBlock) PropertyType(com.datastax.oss.driver.internal.mapper.processor.util.generation.PropertyType) ClassName(com.squareup.javapoet.ClassName)

Example 3 with PropertyType

use of com.datastax.oss.driver.internal.mapper.processor.util.generation.PropertyType in project java-driver by datastax.

the class DefaultEntityFactory method getDefinition.

@Override
public EntityDefinition getDefinition(TypeElement processedClass) {
    Set<TypeMirror> types = HierarchyScanner.resolveTypeHierarchy(processedClass, context);
    Set<TypeElement> typeHierarchy = Sets.newLinkedHashSet();
    for (TypeMirror type : types) {
        typeHierarchy.add((TypeElement) context.getTypeUtils().asElement(type));
    }
    Language language = Language.detect(typeHierarchy);
    Optional<PropertyStrategy> propertyStrategy = getPropertyStrategy(typeHierarchy);
    GetterStyle getterStyle = propertyStrategy.map(PropertyStrategy::getterStyle).orElse(language.defaultGetterStyle);
    SetterStyle setterStyle = propertyStrategy.map(PropertyStrategy::setterStyle).orElse(language.defaultSetterStyle);
    boolean mutable = propertyStrategy.map(PropertyStrategy::mutable).orElse(language.defaultMutable);
    CqlNameGenerator cqlNameGenerator = buildCqlNameGenerator(typeHierarchy);
    Set<String> transientProperties = getTransientPropertyNames(typeHierarchy);
    Set<String> encounteredPropertyNames = Sets.newHashSet();
    SortedMap<Integer, PropertyDefinition> partitionKey = new TreeMap<>();
    SortedMap<Integer, PropertyDefinition> clusteringColumns = new TreeMap<>();
    ImmutableList.Builder<PropertyDefinition> regularColumns = ImmutableList.builder();
    ImmutableList.Builder<PropertyDefinition> computedValues = ImmutableList.builder();
    // scan hierarchy for properties
    for (TypeElement typeElement : typeHierarchy) {
        for (Element child : typeElement.getEnclosedElements()) {
            Set<Modifier> modifiers = child.getModifiers();
            if (child.getKind() != ElementKind.METHOD || modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.PRIVATE)) {
                continue;
            }
            ExecutableElement getMethod = (ExecutableElement) child;
            if (!getMethod.getParameters().isEmpty()) {
                continue;
            }
            TypeMirror typeMirror = getMethod.getReturnType();
            if (typeMirror.getKind() == TypeKind.VOID) {
                continue;
            }
            String getMethodName = getMethod.getSimpleName().toString();
            // hashCode() and a few Scala or Kotlin methods.
            if (getMethodName.equals("toString") || getMethodName.equals("hashCode") || (language == Language.SCALA_CASE_CLASS && (getMethodName.equals("productPrefix") || getMethodName.equals("productArity") || getMethodName.equals("productIterator") || getMethodName.equals("productElementNames") || getMethodName.startsWith("copy$default$"))) || (language == Language.KOTLIN_DATA_CLASS && getMethodName.matches("component[0-9]+"))) {
                continue;
            }
            String propertyName = inferPropertyName(getMethodName, getterStyle, typeMirror);
            if (propertyName == null) {
                // getMethodName does not follow a known pattern => this is not a getter, skip
                continue;
            }
            // skip properties we've already encountered.
            if (encounteredPropertyNames.contains(propertyName)) {
                continue;
            }
            String setMethodName;
            if (mutable) {
                setMethodName = inferSetMethodName(propertyName, setterStyle);
                ExecutableElement setMethod = findSetMethod(typeHierarchy, setMethodName, typeMirror);
                if (setMethod == null) {
                    // must have both
                    continue;
                }
            } else {
                setMethodName = null;
            }
            VariableElement field = findField(typeHierarchy, propertyName, typeMirror);
            Map<Class<? extends Annotation>, Annotation> propertyAnnotations = scanPropertyAnnotations(typeHierarchy, getMethod, field);
            if (isTransient(propertyAnnotations, propertyName, transientProperties, getMethod, field)) {
                continue;
            }
            int partitionKeyIndex = getPartitionKeyIndex(propertyAnnotations);
            int clusteringColumnIndex = getClusteringColumnIndex(propertyAnnotations);
            Optional<String> customCqlName = getCustomCqlName(propertyAnnotations);
            Optional<String> computedFormula = getComputedFormula(propertyAnnotations, getMethod, field);
            PropertyType propertyType = PropertyType.parse(typeMirror, context);
            PropertyDefinition property = new DefaultPropertyDefinition(propertyName, customCqlName, computedFormula, getMethodName, setMethodName, propertyType, cqlNameGenerator);
            encounteredPropertyNames.add(propertyName);
            if (partitionKeyIndex >= 0) {
                PropertyDefinition previous = partitionKey.putIfAbsent(partitionKeyIndex, property);
                if (previous != null) {
                    context.getMessager().error(getMethod, "Duplicate partition key index: if multiple properties are annotated " + "with @%s, the annotation must be parameterized with an integer " + "indicating the position. Found duplicate index %d for %s and %s.", PartitionKey.class.getSimpleName(), partitionKeyIndex, previous.getGetterName(), property.getGetterName());
                }
            } else if (clusteringColumnIndex >= 0) {
                PropertyDefinition previous = clusteringColumns.putIfAbsent(clusteringColumnIndex, property);
                if (previous != null) {
                    context.getMessager().error(getMethod, "Duplicate clustering column index: if multiple properties are annotated " + "with @%s, the annotation must be parameterized with an integer " + "indicating the position. Found duplicate index %d for %s and %s.", ClusteringColumn.class.getSimpleName(), clusteringColumnIndex, previous.getGetterName(), property.getGetterName());
                }
            } else if (computedFormula.isPresent()) {
                computedValues.add(property);
            } else {
                regularColumns.add(property);
            }
        }
    }
    if (encounteredPropertyNames.isEmpty()) {
        context.getMessager().error(processedClass, "@%s-annotated class must have at least one property defined.", Entity.class.getSimpleName());
    }
    String entityName = Capitalizer.decapitalize(processedClass.getSimpleName().toString());
    String defaultKeyspace = processedClass.getAnnotation(Entity.class).defaultKeyspace();
    EntityDefinition entityDefinition = new DefaultEntityDefinition(ClassName.get(processedClass), entityName, defaultKeyspace.isEmpty() ? null : defaultKeyspace, Optional.ofNullable(processedClass.getAnnotation(CqlName.class)).map(CqlName::value), ImmutableList.copyOf(partitionKey.values()), ImmutableList.copyOf(clusteringColumns.values()), regularColumns.build(), computedValues.build(), cqlNameGenerator, mutable);
    validateConstructor(entityDefinition, processedClass);
    return entityDefinition;
}
Also used : Entity(com.datastax.oss.driver.api.mapper.annotations.Entity) SetterStyle(com.datastax.oss.driver.api.mapper.entity.naming.SetterStyle) ImmutableList(com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList) VariableElement(javax.lang.model.element.VariableElement) TypeElement(javax.lang.model.element.TypeElement) ExecutableElement(javax.lang.model.element.ExecutableElement) Element(javax.lang.model.element.Element) ExecutableElement(javax.lang.model.element.ExecutableElement) VariableElement(javax.lang.model.element.VariableElement) PropertyType(com.datastax.oss.driver.internal.mapper.processor.util.generation.PropertyType) GetterStyle(com.datastax.oss.driver.api.mapper.entity.naming.GetterStyle) TypeMirror(javax.lang.model.type.TypeMirror) Modifier(javax.lang.model.element.Modifier) CqlName(com.datastax.oss.driver.api.mapper.annotations.CqlName) TypeElement(javax.lang.model.element.TypeElement) TreeMap(java.util.TreeMap) ResolvedAnnotation(com.datastax.oss.driver.internal.mapper.processor.util.ResolvedAnnotation) Annotation(java.lang.annotation.Annotation) PropertyStrategy(com.datastax.oss.driver.api.mapper.annotations.PropertyStrategy) PartitionKey(com.datastax.oss.driver.api.mapper.annotations.PartitionKey)

Aggregations

PropertyType (com.datastax.oss.driver.internal.mapper.processor.util.generation.PropertyType)3 ClassName (com.squareup.javapoet.ClassName)2 ParameterizedTypeName (com.squareup.javapoet.ParameterizedTypeName)2 TypeName (com.squareup.javapoet.TypeName)2 GettableByName (com.datastax.oss.driver.api.core.data.GettableByName)1 UdtValue (com.datastax.oss.driver.api.core.data.UdtValue)1 CqlName (com.datastax.oss.driver.api.mapper.annotations.CqlName)1 Entity (com.datastax.oss.driver.api.mapper.annotations.Entity)1 PartitionKey (com.datastax.oss.driver.api.mapper.annotations.PartitionKey)1 PropertyStrategy (com.datastax.oss.driver.api.mapper.annotations.PropertyStrategy)1 GetterStyle (com.datastax.oss.driver.api.mapper.entity.naming.GetterStyle)1 SetterStyle (com.datastax.oss.driver.api.mapper.entity.naming.SetterStyle)1 ResolvedAnnotation (com.datastax.oss.driver.internal.mapper.processor.util.ResolvedAnnotation)1 ImmutableList (com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList)1 Lists (com.datastax.oss.driver.shaded.guava.common.collect.Lists)1 Maps (com.datastax.oss.driver.shaded.guava.common.collect.Maps)1 CodeBlock (com.squareup.javapoet.CodeBlock)1 MethodSpec (com.squareup.javapoet.MethodSpec)1 Annotation (java.lang.annotation.Annotation)1 ArrayList (java.util.ArrayList)1