or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-processing.mdcopy-paste-detection.mdcore-analysis.mdindex.mdlanguage-framework.mdproperties-system.mdrendering-system.mdreporting-system.mdrule-system.mdutilities.md

properties-system.mddocs/

0

# Properties System

1

2

The Properties System provides a type-safe, configurable framework for managing rule and component properties. It includes property descriptors, validation, serialization, factory methods, and source management for flexible configuration.

3

4

## Capabilities

5

6

### Property Source Interface

7

8

Base interface for objects that support configurable properties with type-safe access and management.

9

10

```java { .api }

11

/**

12

* Base interface for objects that can have configurable properties.

13

* Provides type-safe property access and management capabilities.

14

*/

15

public interface PropertySource {

16

17

/**

18

* Get property value by descriptor

19

* @param propertyDescriptor PropertyDescriptor defining the property

20

* @return Current value of the property

21

*/

22

<T> T getProperty(PropertyDescriptor<T> propertyDescriptor);

23

24

/**

25

* Set property value by descriptor

26

* @param propertyDescriptor PropertyDescriptor defining the property

27

* @param value New value for the property

28

* @throws IllegalArgumentException if value fails validation

29

*/

30

<T> void setProperty(PropertyDescriptor<T> propertyDescriptor, T value);

31

32

/**

33

* Check if property descriptor is supported

34

* @param descriptor PropertyDescriptor to check

35

* @return true if this source supports the property

36

*/

37

boolean hasDescriptor(PropertyDescriptor<?> descriptor);

38

39

/**

40

* Get all supported property descriptors

41

* @return Unmodifiable list of all PropertyDescriptor instances

42

*/

43

List<PropertyDescriptor<?>> getPropertyDescriptors();

44

45

/**

46

* Get property source for chained access

47

* @return This PropertySource instance for method chaining

48

*/

49

PropertySource getPropertySource();

50

51

/**

52

* Get all properties as map

53

* @return Map of PropertyDescriptor to current value for all properties

54

*/

55

Map<PropertyDescriptor<?>, Object> getPropertiesByPropertyDescriptor();

56

}

57

```

58

59

**Usage Examples:**

60

61

```java

62

import net.sourceforge.pmd.properties.*;

63

import net.sourceforge.pmd.lang.rule.Rule;

64

65

// Working with rule properties

66

public class PropertyExamples {

67

68

public void configureRule(Rule rule) {

69

// Get available properties

70

List<PropertyDescriptor<?>> properties = rule.getPropertyDescriptors();

71

System.out.printf("Rule %s has %d properties:%n",

72

rule.getName(), properties.size());

73

74

for (PropertyDescriptor<?> prop : properties) {

75

System.out.printf(" %s (%s): %s%n",

76

prop.name(),

77

prop.type().getSimpleName(),

78

prop.description());

79

}

80

81

// Check if rule supports specific property

82

PropertyDescriptor<Integer> thresholdProp = PropertyFactory

83

.intProperty("threshold").build();

84

85

if (rule.hasDescriptor(thresholdProp)) {

86

// Get current value

87

Integer currentThreshold = rule.getProperty(thresholdProp);

88

System.out.printf("Current threshold: %d%n", currentThreshold);

89

90

// Set new value

91

rule.setProperty(thresholdProp, 15);

92

}

93

94

// Get all property values

95

Map<PropertyDescriptor<?>, Object> allProps =

96

rule.getPropertiesByPropertyDescriptor();

97

98

allProps.forEach((desc, value) ->

99

System.out.printf("%s = %s%n", desc.name(), value));

100

}

101

102

public void bulkPropertyConfiguration(Rule rule) {

103

// Configure multiple properties at once

104

Map<PropertyDescriptor<?>, Object> newValues = new HashMap<>();

105

106

// Find and configure threshold property

107

rule.getPropertyDescriptors().stream()

108

.filter(prop -> prop.name().equals("threshold"))

109

.findFirst()

110

.ifPresent(prop -> {

111

if (prop.type() == Integer.class) {

112

@SuppressWarnings("unchecked")

113

PropertyDescriptor<Integer> intProp =

114

(PropertyDescriptor<Integer>) prop;

115

rule.setProperty(intProp, 20);

116

}

117

});

118

}

119

}

120

```

121

122

### Property Descriptor Interface

123

124

Descriptor interface defining property metadata, validation, and serialization capabilities.

125

126

```java { .api }

127

/**

128

* Describes a configurable property with type, constraints, and validation.

129

* Provides metadata and serialization capabilities for type-safe properties.

130

*/

131

public interface PropertyDescriptor<T> {

132

133

/**

134

* Get property name (unique identifier)

135

* @return Name of the property

136

*/

137

String name();

138

139

/**

140

* Get property description for documentation

141

* @return Human-readable description of property purpose

142

*/

143

String description();

144

145

/**

146

* Get property value type

147

* @return Class representing the property type

148

*/

149

Class<T> type();

150

151

/**

152

* Get default value for property

153

* @return Default value when property is not explicitly set

154

*/

155

T defaultValue();

156

157

/**

158

* Check if property is required (must be explicitly set)

159

* @return true if property must have a non-default value

160

*/

161

boolean isRequired();

162

163

/**

164

* Serialize property value to string representation

165

* @param value Property value to serialize

166

* @return String representation of the value

167

*/

168

String asDelimitedString(T value);

169

170

/**

171

* Parse property value from string representation

172

* @param propertyString String representation to parse

173

* @return Parsed property value

174

* @throws IllegalArgumentException if string cannot be parsed

175

*/

176

T valueFrom(String propertyString);

177

178

/**

179

* Validate property value and return error message

180

* @param value Value to validate

181

* @return Error message if validation fails, null if valid

182

*/

183

String errorFor(T value);

184

}

185

```

186

187

**Usage Examples:**

188

189

```java

190

import net.sourceforge.pmd.properties.*;

191

192

// Working with property descriptors

193

public class PropertyDescriptorExamples {

194

195

// Define custom property descriptors

196

private static final PropertyDescriptor<Integer> MAX_COMPLEXITY =

197

PropertyFactory.intProperty("maxComplexity")

198

.desc("Maximum allowed cyclomatic complexity")

199

.defaultValue(10)

200

.range(1, 100)

201

.build();

202

203

private static final PropertyDescriptor<String> NAMING_PATTERN =

204

PropertyFactory.stringProperty("namingPattern")

205

.desc("Regular expression for valid names")

206

.defaultValue("[a-zA-Z][a-zA-Z0-9]*")

207

.build();

208

209

private static final PropertyDescriptor<List<String>> IGNORED_ANNOTATIONS =

210

PropertyFactory.stringListProperty("ignoredAnnotations")

211

.desc("Annotations to ignore during analysis")

212

.defaultValue(Arrays.asList("SuppressWarnings", "Generated"))

213

.build();

214

215

public void demonstratePropertyUsage() {

216

// Working with property metadata

217

System.out.printf("Property: %s%n", MAX_COMPLEXITY.name());

218

System.out.printf("Type: %s%n", MAX_COMPLEXITY.type().getSimpleName());

219

System.out.printf("Description: %s%n", MAX_COMPLEXITY.description());

220

System.out.printf("Default: %s%n", MAX_COMPLEXITY.defaultValue());

221

System.out.printf("Required: %s%n", MAX_COMPLEXITY.isRequired());

222

223

// Serialization and deserialization

224

Integer value = 15;

225

String serialized = MAX_COMPLEXITY.asDelimitedString(value);

226

System.out.printf("Serialized: %s%n", serialized);

227

228

Integer deserialized = MAX_COMPLEXITY.valueFrom(serialized);

229

System.out.printf("Deserialized: %s%n", deserialized);

230

231

// Validation

232

String error = MAX_COMPLEXITY.errorFor(150); // Out of range

233

if (error != null) {

234

System.out.printf("Validation error: %s%n", error);

235

}

236

237

// Working with complex types

238

List<String> annotations = Arrays.asList("Override", "Deprecated");

239

String listSerialized = IGNORED_ANNOTATIONS.asDelimitedString(annotations);

240

System.out.printf("List serialized: %s%n", listSerialized);

241

242

List<String> listDeserialized = IGNORED_ANNOTATIONS.valueFrom(listSerialized);

243

System.out.printf("List deserialized: %s%n", listDeserialized);

244

}

245

246

public void validatePropertyValues() {

247

// Test various validation scenarios

248

testValidation(MAX_COMPLEXITY, 5, true); // Valid

249

testValidation(MAX_COMPLEXITY, 0, false); // Too low

250

testValidation(MAX_COMPLEXITY, 150, false); // Too high

251

testValidation(MAX_COMPLEXITY, null, false); // Null

252

253

testValidation(NAMING_PATTERN, "validName", true); // Valid

254

testValidation(NAMING_PATTERN, "123invalid", false); // Invalid pattern

255

testValidation(NAMING_PATTERN, "", false); // Empty

256

}

257

258

private <T> void testValidation(PropertyDescriptor<T> prop, T value, boolean expectValid) {

259

String error = prop.errorFor(value);

260

boolean isValid = (error == null);

261

262

System.out.printf("Property %s, value %s: %s%n",

263

prop.name(),

264

value,

265

isValid ? "VALID" : "INVALID (" + error + ")");

266

267

assert isValid == expectValid :

268

"Validation result mismatch for " + prop.name() + " = " + value;

269

}

270

}

271

```

272

273

### Property Factory

274

275

Factory class providing builder methods for creating typed property descriptors with validation and constraints.

276

277

```java { .api }

278

/**

279

* Factory for creating typed property descriptors with builders.

280

* Provides convenient methods for common property types with validation.

281

*/

282

public final class PropertyFactory {

283

284

/**

285

* Create string property builder

286

* @param name Property name

287

* @return StringPropertyBuilder for configuring string properties

288

*/

289

static StringPropertyBuilder stringProperty(String name);

290

291

/**

292

* Create boolean property builder

293

* @param name Property name

294

* @return BooleanPropertyBuilder for configuring boolean properties

295

*/

296

static BooleanPropertyBuilder booleanProperty(String name);

297

298

/**

299

* Create integer property builder

300

* @param name Property name

301

* @return IntegerPropertyBuilder for configuring integer properties

302

*/

303

static IntegerPropertyBuilder intProperty(String name);

304

305

/**

306

* Create long property builder

307

* @param name Property name

308

* @return LongPropertyBuilder for configuring long properties

309

*/

310

static LongPropertyBuilder longProperty(String name);

311

312

/**

313

* Create double property builder

314

* @param name Property name

315

* @return DoublePropertyBuilder for configuring double properties

316

*/

317

static DoublePropertyBuilder doubleProperty(String name);

318

319

/**

320

* Create character property builder

321

* @param name Property name

322

* @return CharacterPropertyBuilder for configuring character properties

323

*/

324

static CharacterPropertyBuilder charProperty(String name);

325

326

/**

327

* Create regex pattern property builder

328

* @param name Property name

329

* @return RegexPropertyBuilder for configuring Pattern properties

330

*/

331

static RegexPropertyBuilder regexProperty(String name);

332

333

/**

334

* Create enum property builder

335

* @param name Property name

336

* @param enumType Enum class for valid values

337

* @return EnumPropertyBuilder for configuring enum properties

338

*/

339

static <E extends Enum<E>> EnumPropertyBuilder<E> enumProperty(String name, Class<E> enumType);

340

341

/**

342

* Create file property builder

343

* @param name Property name

344

* @return FilePropertyBuilder for configuring File properties

345

*/

346

static FilePropertyBuilder fileProperty(String name);

347

348

/**

349

* Create class type property builder

350

* @param name Property name

351

* @return ClassPropertyBuilder for configuring Class<?> properties

352

*/

353

static ClassPropertyBuilder typeProperty(String name);

354

}

355

```

356

357

**Usage Examples:**

358

359

```java

360

import net.sourceforge.pmd.properties.*;

361

import java.io.File;

362

import java.util.Arrays;

363

import java.util.regex.Pattern;

364

365

// Creating property descriptors with PropertyFactory

366

public class PropertyFactoryExamples {

367

368

public void createBasicProperties() {

369

// String properties with validation

370

PropertyDescriptor<String> nameProperty = PropertyFactory

371

.stringProperty("ruleName")

372

.desc("Name of the rule")

373

.defaultValue("CustomRule")

374

.build();

375

376

// Integer properties with range validation

377

PropertyDescriptor<Integer> thresholdProperty = PropertyFactory

378

.intProperty("threshold")

379

.desc("Threshold value for rule")

380

.defaultValue(10)

381

.range(1, 100)

382

.build();

383

384

// Boolean properties

385

PropertyDescriptor<Boolean> enabledProperty = PropertyFactory

386

.booleanProperty("enabled")

387

.desc("Whether rule is enabled")

388

.defaultValue(true)

389

.build();

390

391

// Double properties with precision constraints

392

PropertyDescriptor<Double> ratioProperty = PropertyFactory

393

.doubleProperty("ratio")

394

.desc("Ratio threshold")

395

.defaultValue(0.5)

396

.range(0.0, 1.0)

397

.build();

398

}

399

400

public void createAdvancedProperties() {

401

// Enum properties

402

enum Priority { LOW, MEDIUM, HIGH }

403

PropertyDescriptor<Priority> priorityProperty = PropertyFactory

404

.enumProperty("priority", Priority.class)

405

.desc("Rule priority level")

406

.defaultValue(Priority.MEDIUM)

407

.build();

408

409

// Regular expression properties

410

PropertyDescriptor<Pattern> patternProperty = PropertyFactory

411

.regexProperty("namePattern")

412

.desc("Pattern for valid names")

413

.defaultValue(Pattern.compile("[a-zA-Z][a-zA-Z0-9]*"))

414

.build();

415

416

// File properties

417

PropertyDescriptor<File> configFileProperty = PropertyFactory

418

.fileProperty("configFile")

419

.desc("Configuration file path")

420

.defaultValue(new File("config.properties"))

421

.build();

422

423

// Class type properties

424

PropertyDescriptor<Class<?>> handlerClassProperty = PropertyFactory

425

.typeProperty("handlerClass")

426

.desc("Handler class for processing")

427

.defaultValue(Object.class)

428

.build();

429

}

430

431

public void createCollectionProperties() {

432

// String list properties

433

PropertyDescriptor<List<String>> exclusionsProperty = PropertyFactory

434

.stringListProperty("exclusions")

435

.desc("List of patterns to exclude")

436

.defaultValue(Arrays.asList("test", "generated"))

437

.delim(',') // Custom delimiter

438

.build();

439

440

// Integer list properties

441

PropertyDescriptor<List<Integer>> thresholdsProperty = PropertyFactory

442

.intListProperty("thresholds")

443

.desc("List of threshold values")

444

.defaultValue(Arrays.asList(5, 10, 15))

445

.range(1, 50) // Range applies to each element

446

.build();

447

}

448

449

public void createConstrainedProperties() {

450

// String with length constraints

451

PropertyDescriptor<String> abbreviationProperty = PropertyFactory

452

.stringProperty("abbreviation")

453

.desc("Short abbreviation")

454

.defaultValue("ABC")

455

.range(2, 5) // Length range

456

.build();

457

458

// String with choice constraints

459

PropertyDescriptor<String> modeProperty = PropertyFactory

460

.stringProperty("mode")

461

.desc("Operation mode")

462

.defaultValue("strict")

463

.validChoices("strict", "lenient", "custom")

464

.build();

465

466

// Character with constraints

467

PropertyDescriptor<Character> separatorProperty = PropertyFactory

468

.charProperty("separator")

469

.desc("Field separator character")

470

.defaultValue(',')

471

.validChoices(',', ';', '|', '\t')

472

.build();

473

}

474

475

public void demonstratePropertyBuilder() {

476

// Complex property with multiple constraints

477

PropertyDescriptor<Integer> complexProperty = PropertyFactory

478

.intProperty("complexThreshold")

479

.desc("Complex threshold with multiple constraints")

480

.defaultValue(15)

481

.range(5, 50)

482

.require() // Make property required

483

.build();

484

485

// Multi-valued property with custom validation

486

PropertyDescriptor<List<String>> packagePrefixes = PropertyFactory

487

.stringListProperty("packagePrefixes")

488

.desc("Valid package prefixes")

489

.defaultValue(Arrays.asList("com.example", "org.project"))

490

.delim(';')

491

.emptyAllowed(false) // Don't allow empty list

492

.build();

493

}

494

495

public void usePropertiesInRule() {

496

// Example of defining properties in a custom rule

497

class CustomRule extends AbstractRule {

498

private static final PropertyDescriptor<Integer> COMPLEXITY_THRESHOLD =

499

PropertyFactory.intProperty("complexityThreshold")

500

.desc("Maximum allowed complexity")

501

.defaultValue(10)

502

.range(1, 50)

503

.build();

504

505

private static final PropertyDescriptor<List<String>> IGNORED_METHODS =

506

PropertyFactory.stringListProperty("ignoredMethods")

507

.desc("Method names to ignore")

508

.defaultValue(Arrays.asList("toString", "hashCode", "equals"))

509

.build();

510

511

public CustomRule() {

512

definePropertyDescriptor(COMPLEXITY_THRESHOLD);

513

definePropertyDescriptor(IGNORED_METHODS);

514

}

515

516

@Override

517

public void apply(List<? extends Node> nodes, RuleContext ctx) {

518

int threshold = getProperty(COMPLEXITY_THRESHOLD);

519

List<String> ignored = getProperty(IGNORED_METHODS);

520

521

// Use properties in rule logic

522

for (Node node : nodes) {

523

// Rule implementation using configured values

524

}

525

}

526

}

527

}

528

}

529

```

530

531

### Property Builders

532

533

Builder interfaces for fluent construction of property descriptors with type-specific constraints and validation.

534

535

```java { .api }

536

/**

537

* Builder for string properties with length and choice constraints

538

*/

539

interface StringPropertyBuilder {

540

StringPropertyBuilder desc(String description);

541

StringPropertyBuilder defaultValue(String defaultValue);

542

StringPropertyBuilder require();

543

StringPropertyBuilder range(int minLength, int maxLength);

544

StringPropertyBuilder validChoices(String... choices);

545

PropertyDescriptor<String> build();

546

}

547

548

/**

549

* Builder for integer properties with range constraints

550

*/

551

interface IntegerPropertyBuilder {

552

IntegerPropertyBuilder desc(String description);

553

IntegerPropertyBuilder defaultValue(Integer defaultValue);

554

IntegerPropertyBuilder require();

555

IntegerPropertyBuilder range(int min, int max);

556

PropertyDescriptor<Integer> build();

557

}

558

559

/**

560

* Builder for boolean properties

561

*/

562

interface BooleanPropertyBuilder {

563

BooleanPropertyBuilder desc(String description);

564

BooleanPropertyBuilder defaultValue(Boolean defaultValue);

565

BooleanPropertyBuilder require();

566

PropertyDescriptor<Boolean> build();

567

}

568

569

/**

570

* Builder for double properties with range constraints

571

*/

572

interface DoublePropertyBuilder {

573

DoublePropertyBuilder desc(String description);

574

DoublePropertyBuilder defaultValue(Double defaultValue);

575

DoublePropertyBuilder require();

576

DoublePropertyBuilder range(double min, double max);

577

PropertyDescriptor<Double> build();

578

}

579

580

/**

581

* Builder for enum properties with type safety

582

*/

583

interface EnumPropertyBuilder<E extends Enum<E>> {

584

EnumPropertyBuilder<E> desc(String description);

585

EnumPropertyBuilder<E> defaultValue(E defaultValue);

586

EnumPropertyBuilder<E> require();

587

PropertyDescriptor<E> build();

588

}

589

590

/**

591

* Builder for regular expression pattern properties

592

*/

593

interface RegexPropertyBuilder {

594

RegexPropertyBuilder desc(String description);

595

RegexPropertyBuilder defaultValue(Pattern defaultValue);

596

RegexPropertyBuilder require();

597

PropertyDescriptor<Pattern> build();

598

}

599

600

/**

601

* Builder for file properties

602

*/

603

interface FilePropertyBuilder {

604

FilePropertyBuilder desc(String description);

605

FilePropertyBuilder defaultValue(File defaultValue);

606

FilePropertyBuilder require();

607

PropertyDescriptor<File> build();

608

}

609

610

/**

611

* Builder for list properties with element constraints

612

*/

613

interface ListPropertyBuilder<T> {

614

ListPropertyBuilder<T> desc(String description);

615

ListPropertyBuilder<T> defaultValue(List<T> defaultValue);

616

ListPropertyBuilder<T> require();

617

ListPropertyBuilder<T> delim(char delimiter);

618

ListPropertyBuilder<T> emptyAllowed(boolean allowed);

619

PropertyDescriptor<List<T>> build();

620

}

621

```

622

623

## Types

624

625

```java { .api }

626

/**

627

* Exception thrown when property operations fail

628

*/

629

class PropertyException extends RuntimeException {

630

PropertyException(String message);

631

PropertyException(String message, Throwable cause);

632

}

633

634

/**

635

* Property constraint for validation

636

*/

637

interface PropertyConstraint<T> {

638

639

/**

640

* Validate value against constraint

641

* @param value Value to validate

642

* @return Error message if invalid, null if valid

643

*/

644

String validate(T value);

645

646

/**

647

* Get constraint description

648

* @return Human-readable constraint description

649

*/

650

String getConstraintDescription();

651

}

652

653

/**

654

* Property serializer for custom types

655

*/

656

interface PropertySerializer<T> {

657

658

/**

659

* Serialize value to string

660

* @param value Value to serialize

661

* @return String representation

662

*/

663

String toString(T value);

664

665

/**

666

* Deserialize value from string

667

* @param str String representation

668

* @return Deserialized value

669

*/

670

T fromString(String str);

671

}

672

673

/**

674

* Property bundle for grouped configuration

675

*/

676

interface PropertyBundle extends PropertySource {

677

678

/**

679

* Get bundle name

680

* @return Name of the property bundle

681

*/

682

String getName();

683

684

/**

685

* Get bundle description

686

* @return Description of bundle purpose

687

*/

688

String getDescription();

689

690

/**

691

* Copy bundle with modifications

692

* @return New PropertyBundle copy

693

*/

694

PropertyBundle copy();

695

696

/**

697

* Merge with another bundle

698

* @param other PropertyBundle to merge

699

* @return New PropertyBundle with merged properties

700

*/

701

PropertyBundle merge(PropertyBundle other);

702

}

703

704

/**

705

* Property change listener for configuration updates

706

*/

707

interface PropertyChangeListener {

708

709

/**

710

* Handle property value change

711

* @param source PropertySource that changed

712

* @param property PropertyDescriptor that changed

713

* @param oldValue Previous value

714

* @param newValue New value

715

*/

716

<T> void propertyChanged(PropertySource source, PropertyDescriptor<T> property,

717

T oldValue, T newValue);

718

}

719

720

/**

721

* Manager for property configurations

722

*/

723

interface PropertyManager {

724

725

/**

726

* Register property change listener

727

* @param listener PropertyChangeListener to add

728

*/

729

void addPropertyChangeListener(PropertyChangeListener listener);

730

731

/**

732

* Remove property change listener

733

* @param listener PropertyChangeListener to remove

734

*/

735

void removePropertyChangeListener(PropertyChangeListener listener);

736

737

/**

738

* Load properties from configuration source

739

* @param source Configuration source (file, URL, etc.)

740

* @param target PropertySource to configure

741

*/

742

void loadProperties(String source, PropertySource target);

743

744

/**

745

* Save properties to configuration source

746

* @param target Configuration target (file, stream, etc.)

747

* @param source PropertySource to save

748

*/

749

void saveProperties(String target, PropertySource source);

750

}

751

```