public EntityDefinition getDefinition()

in mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/entity/DefaultEntityFactory.java [87:259]


  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();

        // Skip methods that test as false positives with the fluent getter style: 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) {
            continue; // must have both
          }
        } 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;
  }