or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bean-factory.mdbuilder-configuration.mdcore-mapping.mdcustom-conversion.mdevent-system.mdindex.mdmetadata-access.mdprogrammatic-mapping.md

metadata-access.mddocs/

0

# Metadata Access

1

2

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

3

4

## Capabilities

5

6

### Mapping Metadata Interface

7

8

Primary interface for accessing mapping metadata and configuration information.

9

10

```java { .api }

11

/**

12

* Provides read-only access to mapping metadata and configuration

13

*/

14

public interface MappingMetadata {

15

/**

16

* Gets all class mappings defined in the mapper

17

* @return list of all class mapping metadata

18

*/

19

List<ClassMappingMetadata> getClassMappings();

20

21

/**

22

* Gets class mappings by source class name

23

* @param sourceClassName fully qualified source class name

24

* @return list of class mappings for the specified source class

25

*/

26

List<ClassMappingMetadata> getClassMappingsBySourceName(String sourceClassName);

27

28

/**

29

* Gets class mappings by destination class name

30

* @param destinationClassName fully qualified destination class name

31

* @return list of class mappings for the specified destination class

32

*/

33

List<ClassMappingMetadata> getClassMappingsByDestinationName(String destinationClassName);

34

35

/**

36

* Gets specific class mapping by source and destination class names

37

* @param sourceClassName fully qualified source class name

38

* @param destinationClassName fully qualified destination class name

39

* @return class mapping metadata or null if not found

40

*/

41

ClassMappingMetadata getClassMappingByName(String sourceClassName, String destinationClassName);

42

43

/**

44

* Gets class mappings by source class

45

* @param sourceClass source class type

46

* @return list of class mappings for the specified source class

47

*/

48

List<ClassMappingMetadata> getClassMappingsBySource(Class<?> sourceClass);

49

50

/**

51

* Gets class mappings by destination class

52

* @param destinationClass destination class type

53

* @return list of class mappings for the specified destination class

54

*/

55

List<ClassMappingMetadata> getClassMappingsByDestination(Class<?> destinationClass);

56

57

/**

58

* Gets specific class mapping by source and destination classes

59

* @param sourceClass source class type

60

* @param destinationClass destination class type

61

* @return class mapping metadata or null if not found

62

*/

63

ClassMappingMetadata getClassMapping(Class<?> sourceClass, Class<?> destinationClass);

64

}

65

```

66

67

### Class Mapping Metadata Interface

68

69

Provides metadata about individual class-to-class mappings.

70

71

```java { .api }

72

/**

73

* Provides metadata about class-level mappings

74

*/

75

public interface ClassMappingMetadata {

76

/**

77

* Gets the source class name

78

* @return fully qualified source class name

79

*/

80

String getSourceClassName();

81

82

/**

83

* Gets the destination class name

84

* @return fully qualified destination class name

85

*/

86

String getDestinationClassName();

87

88

/**

89

* Gets the source class type

90

* @return source class

91

*/

92

Class<?> getSourceClass();

93

94

/**

95

* Gets the destination class type

96

* @return destination class

97

*/

98

Class<?> getDestinationClass();

99

100

/**

101

* Gets all field mappings for this class mapping

102

* @return list of field mapping metadata

103

*/

104

List<FieldMappingMetadata> getFieldMappings();

105

106

/**

107

* Gets field mapping by source field name

108

* @param sourceFieldName source field name

109

* @return field mapping metadata or null if not found

110

*/

111

FieldMappingMetadata getFieldMappingBySource(String sourceFieldName);

112

113

/**

114

* Gets field mapping by destination field name

115

* @param destinationFieldName destination field name

116

* @return field mapping metadata or null if not found

117

*/

118

FieldMappingMetadata getFieldMappingByDestination(String destinationFieldName);

119

120

/**

121

* Gets the mapping ID if specified

122

* @return mapping ID or null if not specified

123

*/

124

String getMappingId();

125

126

/**

127

* Indicates if this is a bidirectional mapping

128

* @return true if bidirectional, false otherwise

129

*/

130

boolean isBidirectional();

131

132

/**

133

* Gets custom converter class if specified for this mapping

134

* @return custom converter class or null if not specified

135

*/

136

Class<? extends CustomConverter> getCustomConverter();

137

}

138

```

139

140

### Field Mapping Metadata Interface

141

142

Provides metadata about individual field-to-field mappings.

143

144

```java { .api }

145

/**

146

* Provides metadata about field-level mappings

147

*/

148

public interface FieldMappingMetadata {

149

/**

150

* Gets the source field name

151

* @return source field name

152

*/

153

String getSourceFieldName();

154

155

/**

156

* Gets the destination field name

157

* @return destination field name

158

*/

159

String getDestinationFieldName();

160

161

/**

162

* Gets the source field type

163

* @return source field class type

164

*/

165

Class<?> getSourceFieldType();

166

167

/**

168

* Gets the destination field type

169

* @return destination field class type

170

*/

171

Class<?> getDestinationFieldType();

172

173

/**

174

* Gets custom converter class if specified for this field

175

* @return custom converter class or null if not specified

176

*/

177

Class<? extends CustomConverter> getCustomConverter();

178

179

/**

180

* Gets custom converter ID if specified for this field

181

* @return custom converter ID or null if not specified

182

*/

183

String getCustomConverterId();

184

185

/**

186

* Indicates if this field is mapped by reference

187

* @return true if copy by reference, false if deep copy

188

*/

189

boolean isCopyByReference();

190

191

/**

192

* Gets date format if specified for date field conversions

193

* @return date format string or null if not specified

194

*/

195

String getDateFormat();

196

197

/**

198

* Indicates if this field mapping is conditional

199

* @return true if conditional, false otherwise

200

*/

201

boolean isConditional();

202

203

/**

204

* Gets the condition expression if this is a conditional mapping

205

* @return condition expression or null if not conditional

206

*/

207

String getCondition();

208

209

/**

210

* Indicates if this field is excluded from mapping

211

* @return true if excluded, false otherwise

212

*/

213

boolean isExcluded();

214

}

215

```

216

217

## Accessing Metadata

218

219

### From Mapper Instance

220

221

```java

222

import com.github.dozermapper.core.Mapper;

223

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

224

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

225

226

Mapper mapper = DozerBeanMapperBuilder.buildDefault();

227

228

// Get metadata - this initializes mappings if not already done

229

MappingMetadata metadata = mapper.getMappingMetadata();

230

231

// List all class mappings

232

List<ClassMappingMetadata> allMappings = metadata.getClassMappings();

233

for (ClassMappingMetadata classMapping : allMappings) {

234

System.out.println("Mapping: " + classMapping.getSourceClassName() +

235

" -> " + classMapping.getDestinationClassName());

236

}

237

```

238

239

### Query Specific Mappings

240

241

```java

242

// Find mappings for a specific source class

243

List<ClassMappingMetadata> userMappings =

244

metadata.getClassMappingsBySource(User.class);

245

246

// Find mappings for a specific destination class

247

List<ClassMappingMetadata> dtoMappings =

248

metadata.getClassMappingsByDestination(UserDto.class);

249

250

// Find specific mapping between two classes

251

ClassMappingMetadata specificMapping =

252

metadata.getClassMapping(User.class, UserDto.class);

253

```

254

255

### Inspect Field Mappings

256

257

```java

258

ClassMappingMetadata classMapping = metadata.getClassMapping(User.class, UserDto.class);

259

if (classMapping != null) {

260

List<FieldMappingMetadata> fieldMappings = classMapping.getFieldMappings();

261

262

for (FieldMappingMetadata fieldMapping : fieldMappings) {

263

System.out.println("Field: " + fieldMapping.getSourceFieldName() +

264

" -> " + fieldMapping.getDestinationFieldName());

265

266

if (fieldMapping.getCustomConverter() != null) {

267

System.out.println(" Custom converter: " +

268

fieldMapping.getCustomConverter().getSimpleName());

269

}

270

271

if (fieldMapping.isCopyByReference()) {

272

System.out.println(" Copy by reference");

273

}

274

275

if (fieldMapping.isConditional()) {

276

System.out.println(" Condition: " + fieldMapping.getCondition());

277

}

278

}

279

}

280

```

281

282

## Practical Use Cases

283

284

### Dynamic Mapping Validation

285

286

```java

287

public class MappingValidator {

288

289

public void validateMappings(Mapper mapper) {

290

MappingMetadata metadata = mapper.getMappingMetadata();

291

List<ClassMappingMetadata> mappings = metadata.getClassMappings();

292

293

for (ClassMappingMetadata mapping : mappings) {

294

validateClassMapping(mapping);

295

}

296

}

297

298

private void validateClassMapping(ClassMappingMetadata mapping) {

299

// Validate source and destination classes exist

300

try {

301

Class<?> sourceClass = mapping.getSourceClass();

302

Class<?> destClass = mapping.getDestinationClass();

303

304

// Check if classes are compatible

305

if (!areClassesCompatible(sourceClass, destClass)) {

306

logger.warn("Potentially incompatible mapping: {} -> {}",

307

sourceClass.getSimpleName(), destClass.getSimpleName());

308

}

309

310

// Validate field mappings

311

validateFieldMappings(mapping.getFieldMappings());

312

313

} catch (Exception e) {

314

logger.error("Invalid mapping configuration: {} -> {}",

315

mapping.getSourceClassName(), mapping.getDestinationClassName(), e);

316

}

317

}

318

319

private void validateFieldMappings(List<FieldMappingMetadata> fieldMappings) {

320

for (FieldMappingMetadata fieldMapping : fieldMappings) {

321

// Check if field types are compatible

322

if (!areTypesCompatible(fieldMapping.getSourceFieldType(),

323

fieldMapping.getDestinationFieldType())) {

324

325

// Ensure custom converter exists for incompatible types

326

if (fieldMapping.getCustomConverter() == null &&

327

fieldMapping.getCustomConverterId() == null) {

328

logger.warn("No converter for incompatible types: {} -> {}",

329

fieldMapping.getSourceFieldType().getSimpleName(),

330

fieldMapping.getDestinationFieldType().getSimpleName());

331

}

332

}

333

}

334

}

335

336

private boolean areClassesCompatible(Class<?> source, Class<?> dest) {

337

// Implementation for class compatibility check

338

return true; // Simplified

339

}

340

341

private boolean areTypesCompatible(Class<?> sourceType, Class<?> destType) {

342

// Implementation for type compatibility check

343

return sourceType.equals(destType) ||

344

destType.isAssignableFrom(sourceType) ||

345

isAutoConvertible(sourceType, destType);

346

}

347

348

private boolean isAutoConvertible(Class<?> source, Class<?> dest) {

349

// Check for built-in conversions (String <-> primitives, etc.)

350

return false; // Simplified

351

}

352

}

353

```

354

355

### Mapping Documentation Generator

356

357

```java

358

public class MappingDocumentationGenerator {

359

360

public void generateDocumentation(Mapper mapper, PrintWriter writer) {

361

MappingMetadata metadata = mapper.getMappingMetadata();

362

List<ClassMappingMetadata> mappings = metadata.getClassMappings();

363

364

writer.println("# Dozer Mapping Documentation");

365

writer.println();

366

367

for (ClassMappingMetadata mapping : mappings) {

368

generateClassMappingDoc(mapping, writer);

369

}

370

}

371

372

private void generateClassMappingDoc(ClassMappingMetadata mapping, PrintWriter writer) {

373

writer.println("## " + mapping.getSourceClass().getSimpleName() +

374

" → " + mapping.getDestinationClass().getSimpleName());

375

writer.println();

376

377

if (mapping.getMappingId() != null) {

378

writer.println("**Mapping ID:** " + mapping.getMappingId());

379

writer.println();

380

}

381

382

writer.println("**Source:** " + mapping.getSourceClassName());

383

writer.println("**Destination:** " + mapping.getDestinationClassName());

384

writer.println();

385

386

List<FieldMappingMetadata> fieldMappings = mapping.getFieldMappings();

387

if (!fieldMappings.isEmpty()) {

388

writer.println("### Field Mappings");

389

writer.println();

390

writer.println("| Source Field | Destination Field | Type | Notes |");

391

writer.println("|--------------|-------------------|------|-------|");

392

393

for (FieldMappingMetadata fieldMapping : fieldMappings) {

394

generateFieldMappingDoc(fieldMapping, writer);

395

}

396

writer.println();

397

}

398

}

399

400

private void generateFieldMappingDoc(FieldMappingMetadata fieldMapping, PrintWriter writer) {

401

String sourceField = fieldMapping.getSourceFieldName();

402

String destField = fieldMapping.getDestinationFieldName();

403

String type = getFieldMappingType(fieldMapping);

404

String notes = getFieldMappingNotes(fieldMapping);

405

406

writer.printf("| %s | %s | %s | %s |%n",

407

sourceField, destField, type, notes);

408

}

409

410

private String getFieldMappingType(FieldMappingMetadata fieldMapping) {

411

if (fieldMapping.getCustomConverter() != null) {

412

return "Custom";

413

} else if (fieldMapping.isCopyByReference()) {

414

return "Reference";

415

} else if (fieldMapping.isConditional()) {

416

return "Conditional";

417

} else {

418

return "Auto";

419

}

420

}

421

422

private String getFieldMappingNotes(FieldMappingMetadata fieldMapping) {

423

StringBuilder notes = new StringBuilder();

424

425

if (fieldMapping.getCustomConverter() != null) {

426

notes.append("Converter: ").append(fieldMapping.getCustomConverter().getSimpleName());

427

}

428

429

if (fieldMapping.getCustomConverterId() != null) {

430

if (notes.length() > 0) notes.append(", ");

431

notes.append("Converter ID: ").append(fieldMapping.getCustomConverterId());

432

}

433

434

if (fieldMapping.getDateFormat() != null) {

435

if (notes.length() > 0) notes.append(", ");

436

notes.append("Date format: ").append(fieldMapping.getDateFormat());

437

}

438

439

if (fieldMapping.isConditional()) {

440

if (notes.length() > 0) notes.append(", ");

441

notes.append("Condition: ").append(fieldMapping.getCondition());

442

}

443

444

return notes.toString();

445

}

446

}

447

```

448

449

### Runtime Mapping Discovery

450

451

```java

452

public class MappingDiscoveryService {

453

454

public boolean canMap(Mapper mapper, Class<?> sourceClass, Class<?> destClass) {

455

MappingMetadata metadata = mapper.getMappingMetadata();

456

ClassMappingMetadata mapping = metadata.getClassMapping(sourceClass, destClass);

457

return mapping != null;

458

}

459

460

public List<Class<?>> getAvailableDestinations(Mapper mapper, Class<?> sourceClass) {

461

MappingMetadata metadata = mapper.getMappingMetadata();

462

List<ClassMappingMetadata> mappings = metadata.getClassMappingsBySource(sourceClass);

463

464

return mappings.stream()

465

.map(ClassMappingMetadata::getDestinationClass)

466

.collect(Collectors.toList());

467

}

468

469

public List<String> getCustomFields(Mapper mapper, Class<?> sourceClass, Class<?> destClass) {

470

MappingMetadata metadata = mapper.getMappingMetadata();

471

ClassMappingMetadata mapping = metadata.getClassMapping(sourceClass, destClass);

472

473

if (mapping == null) {

474

return Collections.emptyList();

475

}

476

477

return mapping.getFieldMappings().stream()

478

.filter(field -> field.getCustomConverter() != null ||

479

field.getCustomConverterId() != null)

480

.map(FieldMappingMetadata::getDestinationFieldName)

481

.collect(Collectors.toList());

482

}

483

}

484

```

485

486

### Mapping Performance Analysis

487

488

```java

489

public class MappingAnalyzer {

490

491

public void analyzeMappings(Mapper mapper) {

492

MappingMetadata metadata = mapper.getMappingMetadata();

493

List<ClassMappingMetadata> mappings = metadata.getClassMappings();

494

495

int totalMappings = mappings.size();

496

int customConverterMappings = 0;

497

int conditionalMappings = 0;

498

int referenceMappings = 0;

499

500

Map<Class<? extends CustomConverter>, Integer> converterUsage = new HashMap<>();

501

502

for (ClassMappingMetadata mapping : mappings) {

503

for (FieldMappingMetadata field : mapping.getFieldMappings()) {

504

if (field.getCustomConverter() != null) {

505

customConverterMappings++;

506

converterUsage.merge(field.getCustomConverter(), 1, Integer::sum);

507

}

508

509

if (field.isConditional()) {

510

conditionalMappings++;

511

}

512

513

if (field.isCopyByReference()) {

514

referenceMappings++;

515

}

516

}

517

}

518

519

System.out.println("Mapping Analysis:");

520

System.out.println("Total class mappings: " + totalMappings);

521

System.out.println("Fields with custom converters: " + customConverterMappings);

522

System.out.println("Conditional field mappings: " + conditionalMappings);

523

System.out.println("Reference copy mappings: " + referenceMappings);

524

525

System.out.println("\nConverter Usage:");

526

converterUsage.entrySet().stream()

527

.sorted(Map.Entry.<Class<? extends CustomConverter>, Integer>comparingByValue().reversed())

528

.forEach(entry -> System.out.println(entry.getKey().getSimpleName() + ": " + entry.getValue()));

529

}

530

}

531

```

532

533

## Error Handling

534

535

```java { .api }

536

/**

537

* Exception thrown when metadata lookup fails

538

*/

539

public class MetadataLookupException extends MappingException {

540

public MetadataLookupException(String message);

541

public MetadataLookupException(String message, Throwable cause);

542

public MetadataLookupException(Throwable cause);

543

}

544

```

545

546

### Safe Metadata Access

547

548

```java

549

public class SafeMetadataAccess {

550

551

public Optional<ClassMappingMetadata> findMapping(Mapper mapper,

552

Class<?> source, Class<?> dest) {

553

try {

554

MappingMetadata metadata = mapper.getMappingMetadata();

555

ClassMappingMetadata mapping = metadata.getClassMapping(source, dest);

556

return Optional.ofNullable(mapping);

557

} catch (MetadataLookupException e) {

558

logger.warn("Failed to lookup mapping metadata", e);

559

return Optional.empty();

560

}

561

}

562

}

563

```

564

565

## Best Practices

566

567

### Performance Considerations

568

- Metadata access initializes mappings if not already done - cache results when possible

569

- Avoid repeated metadata queries in performance-critical code

570

- Use metadata for development/debugging tools rather than runtime logic

571

572

### Threading

573

- Metadata interfaces are thread-safe for read operations

574

- Cache metadata results in thread-safe collections for concurrent access

575

576

### Usage Patterns

577

- Use metadata for validation during application startup

578

- Generate documentation from metadata in build processes

579

- Implement mapping discovery services for dynamic applications

580

- Create debugging tools that inspect mapping configurations