CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-github-dozermapper--dozer-core

Java Bean to Java Bean mapper that recursively copies data from one object to another

Pending
Overview
Eval results
Files

metadata-access.mddocs/

Metadata Access

Runtime access to mapping metadata and configuration for debugging, introspection, and dynamic behavior based on mapping definitions.

Capabilities

Mapping Metadata Interface

Primary interface for accessing mapping metadata and configuration information.

/**
 * Provides read-only access to mapping metadata and configuration
 */
public interface MappingMetadata {
    /**
     * Gets all class mappings defined in the mapper
     * @return list of all class mapping metadata
     */
    List<ClassMappingMetadata> getClassMappings();
    
    /**
     * Gets class mappings by source class name
     * @param sourceClassName fully qualified source class name
     * @return list of class mappings for the specified source class
     */
    List<ClassMappingMetadata> getClassMappingsBySourceName(String sourceClassName);
    
    /**
     * Gets class mappings by destination class name
     * @param destinationClassName fully qualified destination class name
     * @return list of class mappings for the specified destination class
     */
    List<ClassMappingMetadata> getClassMappingsByDestinationName(String destinationClassName);
    
    /**
     * Gets specific class mapping by source and destination class names
     * @param sourceClassName fully qualified source class name
     * @param destinationClassName fully qualified destination class name
     * @return class mapping metadata or null if not found
     */
    ClassMappingMetadata getClassMappingByName(String sourceClassName, String destinationClassName);
    
    /**
     * Gets class mappings by source class
     * @param sourceClass source class type
     * @return list of class mappings for the specified source class
     */
    List<ClassMappingMetadata> getClassMappingsBySource(Class<?> sourceClass);
    
    /**
     * Gets class mappings by destination class
     * @param destinationClass destination class type
     * @return list of class mappings for the specified destination class
     */
    List<ClassMappingMetadata> getClassMappingsByDestination(Class<?> destinationClass);
    
    /**
     * Gets specific class mapping by source and destination classes
     * @param sourceClass source class type
     * @param destinationClass destination class type
     * @return class mapping metadata or null if not found
     */
    ClassMappingMetadata getClassMapping(Class<?> sourceClass, Class<?> destinationClass);
}

Class Mapping Metadata Interface

Provides metadata about individual class-to-class mappings.

/**
 * Provides metadata about class-level mappings
 */
public interface ClassMappingMetadata {
    /**
     * Gets the source class name
     * @return fully qualified source class name
     */
    String getSourceClassName();
    
    /**
     * Gets the destination class name
     * @return fully qualified destination class name
     */
    String getDestinationClassName();
    
    /**
     * Gets the source class type
     * @return source class
     */
    Class<?> getSourceClass();
    
    /**
     * Gets the destination class type
     * @return destination class
     */
    Class<?> getDestinationClass();
    
    /**
     * Gets all field mappings for this class mapping
     * @return list of field mapping metadata
     */
    List<FieldMappingMetadata> getFieldMappings();
    
    /**
     * Gets field mapping by source field name
     * @param sourceFieldName source field name
     * @return field mapping metadata or null if not found
     */
    FieldMappingMetadata getFieldMappingBySource(String sourceFieldName);
    
    /**
     * Gets field mapping by destination field name
     * @param destinationFieldName destination field name
     * @return field mapping metadata or null if not found
     */
    FieldMappingMetadata getFieldMappingByDestination(String destinationFieldName);
    
    /**
     * Gets the mapping ID if specified
     * @return mapping ID or null if not specified
     */
    String getMappingId();
    
    /**
     * Indicates if this is a bidirectional mapping
     * @return true if bidirectional, false otherwise
     */
    boolean isBidirectional();
    
    /**
     * Gets custom converter class if specified for this mapping
     * @return custom converter class or null if not specified
     */
    Class<? extends CustomConverter> getCustomConverter();
}

Field Mapping Metadata Interface

Provides metadata about individual field-to-field mappings.

/**
 * Provides metadata about field-level mappings
 */
public interface FieldMappingMetadata {
    /**
     * Gets the source field name
     * @return source field name
     */
    String getSourceFieldName();
    
    /**
     * Gets the destination field name
     * @return destination field name
     */
    String getDestinationFieldName();
    
    /**
     * Gets the source field type
     * @return source field class type
     */
    Class<?> getSourceFieldType();
    
    /**
     * Gets the destination field type
     * @return destination field class type
     */
    Class<?> getDestinationFieldType();
    
    /**
     * Gets custom converter class if specified for this field
     * @return custom converter class or null if not specified
     */
    Class<? extends CustomConverter> getCustomConverter();
    
    /**
     * Gets custom converter ID if specified for this field
     * @return custom converter ID or null if not specified
     */
    String getCustomConverterId();
    
    /**
     * Indicates if this field is mapped by reference
     * @return true if copy by reference, false if deep copy
     */
    boolean isCopyByReference();
    
    /**
     * Gets date format if specified for date field conversions
     * @return date format string or null if not specified
     */
    String getDateFormat();
    
    /**
     * Indicates if this field mapping is conditional
     * @return true if conditional, false otherwise
     */
    boolean isConditional();
    
    /**
     * Gets the condition expression if this is a conditional mapping
     * @return condition expression or null if not conditional
     */
    String getCondition();
    
    /**
     * Indicates if this field is excluded from mapping
     * @return true if excluded, false otherwise
     */
    boolean isExcluded();
}

Accessing Metadata

From Mapper Instance

import com.github.dozermapper.core.Mapper;
import com.github.dozermapper.core.metadata.MappingMetadata;
import com.github.dozermapper.core.metadata.ClassMappingMetadata;

Mapper mapper = DozerBeanMapperBuilder.buildDefault();

// Get metadata - this initializes mappings if not already done
MappingMetadata metadata = mapper.getMappingMetadata();

// List all class mappings
List<ClassMappingMetadata> allMappings = metadata.getClassMappings();
for (ClassMappingMetadata classMapping : allMappings) {
    System.out.println("Mapping: " + classMapping.getSourceClassName() + 
                      " -> " + classMapping.getDestinationClassName());
}

Query Specific Mappings

// Find mappings for a specific source class
List<ClassMappingMetadata> userMappings = 
    metadata.getClassMappingsBySource(User.class);

// Find mappings for a specific destination class  
List<ClassMappingMetadata> dtoMappings = 
    metadata.getClassMappingsByDestination(UserDto.class);

// Find specific mapping between two classes
ClassMappingMetadata specificMapping = 
    metadata.getClassMapping(User.class, UserDto.class);

Inspect Field Mappings

ClassMappingMetadata classMapping = metadata.getClassMapping(User.class, UserDto.class);
if (classMapping != null) {
    List<FieldMappingMetadata> fieldMappings = classMapping.getFieldMappings();
    
    for (FieldMappingMetadata fieldMapping : fieldMappings) {
        System.out.println("Field: " + fieldMapping.getSourceFieldName() + 
                          " -> " + fieldMapping.getDestinationFieldName());
        
        if (fieldMapping.getCustomConverter() != null) {
            System.out.println("  Custom converter: " + 
                fieldMapping.getCustomConverter().getSimpleName());
        }
        
        if (fieldMapping.isCopyByReference()) {
            System.out.println("  Copy by reference");
        }
        
        if (fieldMapping.isConditional()) {
            System.out.println("  Condition: " + fieldMapping.getCondition());
        }
    }
}

Practical Use Cases

Dynamic Mapping Validation

public class MappingValidator {
    
    public void validateMappings(Mapper mapper) {
        MappingMetadata metadata = mapper.getMappingMetadata();
        List<ClassMappingMetadata> mappings = metadata.getClassMappings();
        
        for (ClassMappingMetadata mapping : mappings) {
            validateClassMapping(mapping);
        }
    }
    
    private void validateClassMapping(ClassMappingMetadata mapping) {
        // Validate source and destination classes exist
        try {
            Class<?> sourceClass = mapping.getSourceClass();
            Class<?> destClass = mapping.getDestinationClass();
            
            // Check if classes are compatible
            if (!areClassesCompatible(sourceClass, destClass)) {
                logger.warn("Potentially incompatible mapping: {} -> {}", 
                    sourceClass.getSimpleName(), destClass.getSimpleName());
            }
            
            // Validate field mappings
            validateFieldMappings(mapping.getFieldMappings());
            
        } catch (Exception e) {
            logger.error("Invalid mapping configuration: {} -> {}", 
                mapping.getSourceClassName(), mapping.getDestinationClassName(), e);
        }
    }
    
    private void validateFieldMappings(List<FieldMappingMetadata> fieldMappings) {
        for (FieldMappingMetadata fieldMapping : fieldMappings) {
            // Check if field types are compatible
            if (!areTypesCompatible(fieldMapping.getSourceFieldType(), 
                                   fieldMapping.getDestinationFieldType())) {
                
                // Ensure custom converter exists for incompatible types
                if (fieldMapping.getCustomConverter() == null && 
                    fieldMapping.getCustomConverterId() == null) {
                    logger.warn("No converter for incompatible types: {} -> {}", 
                        fieldMapping.getSourceFieldType().getSimpleName(),
                        fieldMapping.getDestinationFieldType().getSimpleName());
                }
            }
        }
    }
    
    private boolean areClassesCompatible(Class<?> source, Class<?> dest) {
        // Implementation for class compatibility check
        return true; // Simplified
    }
    
    private boolean areTypesCompatible(Class<?> sourceType, Class<?> destType) {
        // Implementation for type compatibility check
        return sourceType.equals(destType) || 
               destType.isAssignableFrom(sourceType) ||
               isAutoConvertible(sourceType, destType);
    }
    
    private boolean isAutoConvertible(Class<?> source, Class<?> dest) {
        // Check for built-in conversions (String <-> primitives, etc.)
        return false; // Simplified
    }
}

Mapping Documentation Generator

public class MappingDocumentationGenerator {
    
    public void generateDocumentation(Mapper mapper, PrintWriter writer) {
        MappingMetadata metadata = mapper.getMappingMetadata();
        List<ClassMappingMetadata> mappings = metadata.getClassMappings();
        
        writer.println("# Dozer Mapping Documentation");
        writer.println();
        
        for (ClassMappingMetadata mapping : mappings) {
            generateClassMappingDoc(mapping, writer);
        }
    }
    
    private void generateClassMappingDoc(ClassMappingMetadata mapping, PrintWriter writer) {
        writer.println("## " + mapping.getSourceClass().getSimpleName() + 
                      " → " + mapping.getDestinationClass().getSimpleName());
        writer.println();
        
        if (mapping.getMappingId() != null) {
            writer.println("**Mapping ID:** " + mapping.getMappingId());
            writer.println();
        }
        
        writer.println("**Source:** " + mapping.getSourceClassName());
        writer.println("**Destination:** " + mapping.getDestinationClassName());
        writer.println();
        
        List<FieldMappingMetadata> fieldMappings = mapping.getFieldMappings();
        if (!fieldMappings.isEmpty()) {
            writer.println("### Field Mappings");
            writer.println();
            writer.println("| Source Field | Destination Field | Type | Notes |");
            writer.println("|--------------|-------------------|------|-------|");
            
            for (FieldMappingMetadata fieldMapping : fieldMappings) {
                generateFieldMappingDoc(fieldMapping, writer);
            }
            writer.println();
        }
    }
    
    private void generateFieldMappingDoc(FieldMappingMetadata fieldMapping, PrintWriter writer) {
        String sourceField = fieldMapping.getSourceFieldName();
        String destField = fieldMapping.getDestinationFieldName();
        String type = getFieldMappingType(fieldMapping);
        String notes = getFieldMappingNotes(fieldMapping);
        
        writer.printf("| %s | %s | %s | %s |%n", 
            sourceField, destField, type, notes);
    }
    
    private String getFieldMappingType(FieldMappingMetadata fieldMapping) {
        if (fieldMapping.getCustomConverter() != null) {
            return "Custom";
        } else if (fieldMapping.isCopyByReference()) {
            return "Reference";
        } else if (fieldMapping.isConditional()) {
            return "Conditional";
        } else {
            return "Auto";
        }
    }
    
    private String getFieldMappingNotes(FieldMappingMetadata fieldMapping) {
        StringBuilder notes = new StringBuilder();
        
        if (fieldMapping.getCustomConverter() != null) {
            notes.append("Converter: ").append(fieldMapping.getCustomConverter().getSimpleName());
        }
        
        if (fieldMapping.getCustomConverterId() != null) {
            if (notes.length() > 0) notes.append(", ");
            notes.append("Converter ID: ").append(fieldMapping.getCustomConverterId());
        }
        
        if (fieldMapping.getDateFormat() != null) {
            if (notes.length() > 0) notes.append(", ");
            notes.append("Date format: ").append(fieldMapping.getDateFormat());
        }
        
        if (fieldMapping.isConditional()) {
            if (notes.length() > 0) notes.append(", ");
            notes.append("Condition: ").append(fieldMapping.getCondition());
        }
        
        return notes.toString();
    }
}

Runtime Mapping Discovery

public class MappingDiscoveryService {
    
    public boolean canMap(Mapper mapper, Class<?> sourceClass, Class<?> destClass) {
        MappingMetadata metadata = mapper.getMappingMetadata();
        ClassMappingMetadata mapping = metadata.getClassMapping(sourceClass, destClass);
        return mapping != null;
    }
    
    public List<Class<?>> getAvailableDestinations(Mapper mapper, Class<?> sourceClass) {
        MappingMetadata metadata = mapper.getMappingMetadata();
        List<ClassMappingMetadata> mappings = metadata.getClassMappingsBySource(sourceClass);
        
        return mappings.stream()
            .map(ClassMappingMetadata::getDestinationClass)
            .collect(Collectors.toList());
    }
    
    public List<String> getCustomFields(Mapper mapper, Class<?> sourceClass, Class<?> destClass) {
        MappingMetadata metadata = mapper.getMappingMetadata();
        ClassMappingMetadata mapping = metadata.getClassMapping(sourceClass, destClass);
        
        if (mapping == null) {
            return Collections.emptyList();
        }
        
        return mapping.getFieldMappings().stream()
            .filter(field -> field.getCustomConverter() != null || 
                           field.getCustomConverterId() != null)
            .map(FieldMappingMetadata::getDestinationFieldName)
            .collect(Collectors.toList());
    }
}

Mapping Performance Analysis

public class MappingAnalyzer {
    
    public void analyzeMappings(Mapper mapper) {
        MappingMetadata metadata = mapper.getMappingMetadata();
        List<ClassMappingMetadata> mappings = metadata.getClassMappings();
        
        int totalMappings = mappings.size();
        int customConverterMappings = 0;
        int conditionalMappings = 0;
        int referenceMappings = 0;
        
        Map<Class<? extends CustomConverter>, Integer> converterUsage = new HashMap<>();
        
        for (ClassMappingMetadata mapping : mappings) {
            for (FieldMappingMetadata field : mapping.getFieldMappings()) {
                if (field.getCustomConverter() != null) {
                    customConverterMappings++;
                    converterUsage.merge(field.getCustomConverter(), 1, Integer::sum);
                }
                
                if (field.isConditional()) {
                    conditionalMappings++;
                }
                
                if (field.isCopyByReference()) {
                    referenceMappings++;
                }
            }
        }
        
        System.out.println("Mapping Analysis:");
        System.out.println("Total class mappings: " + totalMappings);
        System.out.println("Fields with custom converters: " + customConverterMappings);
        System.out.println("Conditional field mappings: " + conditionalMappings);
        System.out.println("Reference copy mappings: " + referenceMappings);
        
        System.out.println("\nConverter Usage:");
        converterUsage.entrySet().stream()
            .sorted(Map.Entry.<Class<? extends CustomConverter>, Integer>comparingByValue().reversed())
            .forEach(entry -> System.out.println(entry.getKey().getSimpleName() + ": " + entry.getValue()));
    }
}

Error Handling

/**
 * Exception thrown when metadata lookup fails
 */
public class MetadataLookupException extends MappingException {
    public MetadataLookupException(String message);
    public MetadataLookupException(String message, Throwable cause);
    public MetadataLookupException(Throwable cause);
}

Safe Metadata Access

public class SafeMetadataAccess {
    
    public Optional<ClassMappingMetadata> findMapping(Mapper mapper, 
                                                     Class<?> source, Class<?> dest) {
        try {
            MappingMetadata metadata = mapper.getMappingMetadata();
            ClassMappingMetadata mapping = metadata.getClassMapping(source, dest);
            return Optional.ofNullable(mapping);
        } catch (MetadataLookupException e) {
            logger.warn("Failed to lookup mapping metadata", e);
            return Optional.empty();
        }
    }
}

Best Practices

Performance Considerations

  • Metadata access initializes mappings if not already done - cache results when possible
  • Avoid repeated metadata queries in performance-critical code
  • Use metadata for development/debugging tools rather than runtime logic

Threading

  • Metadata interfaces are thread-safe for read operations
  • Cache metadata results in thread-safe collections for concurrent access

Usage Patterns

  • Use metadata for validation during application startup
  • Generate documentation from metadata in build processes
  • Implement mapping discovery services for dynamic applications
  • Create debugging tools that inspect mapping configurations

Install with Tessl CLI

npx tessl i tessl/maven-com-github-dozermapper--dozer-core

docs

bean-factory.md

builder-configuration.md

core-mapping.md

custom-conversion.md

event-system.md

index.md

metadata-access.md

programmatic-mapping.md

tile.json