Skip to content

Commit 0cb7abf

Browse files
dmealingclaude
andcommitted
ENHANCE: PlacementConstraint with Type-Safe Constant-Based API
Enhanced PlacementConstraint class with separated type/subtype/name fields and static factory methods that use existing constants instead of error-prone string literals. Key Improvements: • Added new constructor taking separate parentType, parentSubType, childType, childSubType, childName parameters • Added static factory methods: allowAttribute(), allowAttributeOnAnyField(), allowAttributeOnAnyObject(), allowChildType(), forbidAttribute() • Uses existing constants (TYPE_FIELD, SUBTYPE_STRING, ATTR_MAX_LENGTH) instead of string literals • Maintains backward compatibility with existing string-pattern constructor • Eliminates string concatenation errors and provides compile-time type safety Updated Usage Examples: • StringField constraints now use PlacementConstraint.allowAttribute() with constants • MetaField constraints use PlacementConstraint.allowAttributeOnAnyField() • MetaData constraints use PlacementConstraint.allowChildType() Benefits: • Type safety with compile-time checking • Constant reuse eliminates duplication • Clean API with readable factory methods • Unified approach with ChildRequirement • Easy migration path from string literals Tests: 8/8 enhanced constraint tests + 5/5 unified constraint examples passing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 00649dc commit 0cb7abf

7 files changed

Lines changed: 689 additions & 25 deletions

File tree

metadata/src/main/java/com/draagon/meta/MetaData.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
package com.draagon.meta;
88

99
import com.draagon.meta.attr.MetaAttribute;
10+
import com.draagon.meta.field.MetaField;
11+
import com.draagon.meta.object.MetaObject;
1012
import com.draagon.meta.loader.MetaDataLoader;
1113
// Using unified registry instead
1214
import com.draagon.meta.registry.MetaDataRegistry;
@@ -155,21 +157,19 @@ private static void setupRootAbstractConstraints() {
155157
MetaDataRegistry registry = MetaDataRegistry.getInstance();
156158

157159
// OBJECTS can be abstract or concrete under metadata.base
158-
registry.addConstraint(new PlacementConstraint(
160+
registry.addConstraint(PlacementConstraint.allowChildType(
159161
"metadata.base.objects",
160162
"metadata.base can contain objects",
161-
"metadata.base", // Parent pattern
162-
"object.*", // Child pattern
163-
true // Allowed
163+
TYPE_METADATA, SUBTYPE_BASE, // Parent: metadata.base
164+
MetaObject.TYPE_OBJECT, "*" // Child: object.*
164165
));
165166

166167
// FIELDS under metadata.base
167-
registry.addConstraint(new PlacementConstraint(
168+
registry.addConstraint(PlacementConstraint.allowChildType(
168169
"metadata.base.fields",
169170
"metadata.base can contain fields",
170-
"metadata.base", // Parent pattern
171-
"field.*", // Child pattern
172-
true // Allowed
171+
TYPE_METADATA, SUBTYPE_BASE, // Parent: metadata.base
172+
MetaField.TYPE_FIELD, "*" // Child: field.*
173173
));
174174

175175
// ATTRIBUTES under metadata.base
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# Enhanced PlacementConstraint with Constant-Based API
2+
3+
## Overview
4+
5+
The PlacementConstraint class has been enhanced with type-safe constructors and static factory methods that use existing constants instead of error-prone string literals.
6+
7+
## Key Improvements
8+
9+
### ✅ BEFORE vs AFTER Comparison
10+
11+
**BEFORE (Error-prone string literals):**
12+
```java
13+
// ❌ Old way - string concatenation with potential typos
14+
registry.addConstraint(new PlacementConstraint(
15+
"stringfield.maxlength.placement",
16+
"StringField can have maxLength attribute",
17+
"field.string", // Could have typos
18+
"attr.int[maxLength]", // Could have typos
19+
true
20+
));
21+
```
22+
23+
**AFTER (Type-safe with constants):**
24+
```java
25+
// ✅ New way - compile-time checked constants
26+
registry.addConstraint(PlacementConstraint.allowAttribute(
27+
"stringfield.maxlength.placement",
28+
"StringField can have maxLength attribute",
29+
TYPE_FIELD, SUBTYPE_STRING, // Parent: field.string
30+
IntAttribute.SUBTYPE_INT, ATTR_MAX_LENGTH // Child: attr.int[maxLength]
31+
));
32+
```
33+
34+
## New Constructor
35+
36+
```java
37+
public PlacementConstraint(String constraintId, String description,
38+
String parentType, String parentSubType,
39+
String childType, String childSubType, String childName,
40+
boolean allowed)
41+
```
42+
43+
**Parameters:**
44+
- `parentType` - Use constants like `MetaField.TYPE_FIELD`
45+
- `parentSubType` - Use constants like `StringField.SUBTYPE_STRING`
46+
- `childType` - Use constants like `MetaAttribute.TYPE_ATTR`
47+
- `childSubType` - Use constants like `StringAttribute.SUBTYPE_STRING`
48+
- `childName` - Use constants like `StringField.ATTR_MAX_LENGTH`
49+
50+
## Static Factory Methods
51+
52+
### 1. `allowAttribute()` - Allow specific attribute on specific parent
53+
54+
```java
55+
PlacementConstraint constraint = PlacementConstraint.allowAttribute(
56+
"stringfield.maxlength",
57+
"StringField can have maxLength attribute",
58+
MetaField.TYPE_FIELD, StringField.SUBTYPE_STRING, // Parent: field.string
59+
IntAttribute.SUBTYPE_INT, StringField.ATTR_MAX_LENGTH // Child: attr.int[maxLength]
60+
);
61+
```
62+
63+
### 2. `allowAttributeOnAnyField()` - Allow attribute on any field type
64+
65+
```java
66+
PlacementConstraint constraint = PlacementConstraint.allowAttributeOnAnyField(
67+
"field.required",
68+
"Any field can have required attribute",
69+
BooleanAttribute.SUBTYPE_BOOLEAN, MetaField.ATTR_REQUIRED
70+
);
71+
// Produces: parent="field.*", child="attr.boolean[required]"
72+
```
73+
74+
### 3. `allowAttributeOnAnyObject()` - Allow attribute on any object type
75+
76+
```java
77+
PlacementConstraint constraint = PlacementConstraint.allowAttributeOnAnyObject(
78+
"object.dbTable",
79+
"Any object can have dbTable attribute",
80+
StringAttribute.SUBTYPE_STRING, "dbTable"
81+
);
82+
// Produces: parent="object.*", child="attr.string[dbTable]"
83+
```
84+
85+
### 4. `allowChildType()` - Allow child type under parent (no name constraint)
86+
87+
```java
88+
PlacementConstraint constraint = PlacementConstraint.allowChildType(
89+
"metadata.fields",
90+
"Metadata can contain fields",
91+
"metadata", "base", // Parent: metadata.base
92+
MetaField.TYPE_FIELD, "*" // Child: field.*
93+
);
94+
```
95+
96+
### 5. `forbidAttribute()` - Forbid specific attribute
97+
98+
```java
99+
PlacementConstraint constraint = PlacementConstraint.forbidAttribute(
100+
"object.maxlength.forbidden",
101+
"Objects cannot have maxLength attribute",
102+
MetaObject.TYPE_OBJECT, "*",
103+
IntAttribute.SUBTYPE_INT, StringField.ATTR_MAX_LENGTH
104+
);
105+
```
106+
107+
## Constants Reference
108+
109+
### Type Constants
110+
```java
111+
MetaField.TYPE_FIELD // "field"
112+
MetaObject.TYPE_OBJECT // "object"
113+
MetaAttribute.TYPE_ATTR // "attr"
114+
```
115+
116+
### Field Subtype Constants
117+
```java
118+
StringField.SUBTYPE_STRING // "string"
119+
IntegerField.SUBTYPE_INT // "int"
120+
LongField.SUBTYPE_LONG // "long"
121+
DoubleField.SUBTYPE_DOUBLE // "double"
122+
BooleanField.SUBTYPE_BOOLEAN // "boolean"
123+
```
124+
125+
### Attribute Subtype Constants
126+
```java
127+
StringAttribute.SUBTYPE_STRING // "string"
128+
IntAttribute.SUBTYPE_INT // "int"
129+
LongAttribute.SUBTYPE_LONG // "long"
130+
DoubleAttribute.SUBTYPE_DOUBLE // "double"
131+
BooleanAttribute.SUBTYPE_BOOLEAN // "boolean"
132+
```
133+
134+
### Attribute Name Constants
135+
```java
136+
// MetaField level attributes
137+
MetaField.ATTR_REQUIRED // "required"
138+
MetaField.ATTR_DEFAULT_VALUE // "defaultValue"
139+
MetaField.ATTR_DEFAULT_VIEW // "defaultView"
140+
141+
// StringField specific attributes
142+
StringField.ATTR_PATTERN // "pattern"
143+
StringField.ATTR_MAX_LENGTH // "maxLength"
144+
StringField.ATTR_MIN_LENGTH // "minLength"
145+
```
146+
147+
## Usage Patterns
148+
149+
### Pattern 1: Field-Specific Attributes
150+
```java
151+
// StringField can have pattern attribute
152+
PlacementConstraint.allowAttribute(
153+
"stringfield.pattern",
154+
"StringField can have pattern attribute",
155+
TYPE_FIELD, SUBTYPE_STRING,
156+
StringAttribute.SUBTYPE_STRING, ATTR_PATTERN
157+
);
158+
159+
// StringField can have maxLength attribute
160+
PlacementConstraint.allowAttribute(
161+
"stringfield.maxlength",
162+
"StringField can have maxLength attribute",
163+
TYPE_FIELD, SUBTYPE_STRING,
164+
IntAttribute.SUBTYPE_INT, ATTR_MAX_LENGTH
165+
);
166+
```
167+
168+
### Pattern 2: Cross-Cutting Attributes
169+
```java
170+
// Any field can have required attribute
171+
PlacementConstraint.allowAttributeOnAnyField(
172+
"field.required",
173+
"Fields can have required attribute",
174+
BooleanAttribute.SUBTYPE_BOOLEAN, ATTR_REQUIRED
175+
);
176+
```
177+
178+
### Pattern 3: Type Containment Rules
179+
```java
180+
// Objects can contain fields
181+
PlacementConstraint.allowChildType(
182+
"object.fields",
183+
"Objects can contain fields",
184+
TYPE_OBJECT, "*",
185+
TYPE_FIELD, "*"
186+
);
187+
```
188+
189+
## Unified with ChildRequirement
190+
191+
PlacementConstraint and ChildRequirement can now use the same constants:
192+
193+
```java
194+
// PlacementConstraint (for validation)
195+
PlacementConstraint placement = PlacementConstraint.allowAttribute(
196+
"stringfield.maxlength",
197+
"StringField can have maxLength",
198+
TYPE_FIELD, SUBTYPE_STRING,
199+
IntAttribute.SUBTYPE_INT, ATTR_MAX_LENGTH
200+
);
201+
202+
// ChildRequirement (for schema generation)
203+
ChildRequirement child = ChildRequirement.optional(
204+
ATTR_MAX_LENGTH, // Same constant!
205+
TYPE_ATTR, // Same constant!
206+
IntAttribute.SUBTYPE_INT // Same constant!
207+
);
208+
```
209+
210+
## Benefits
211+
212+
1. **Type Safety**: Compile-time checking prevents typos
213+
2. **Constant Reuse**: Uses existing constants from MetaField, StringField, etc.
214+
3. **Clean API**: Static factory methods for common patterns
215+
4. **Unified Approach**: Works seamlessly with ChildRequirement
216+
5. **Backward Compatible**: Legacy string-based constructor still works
217+
6. **Easy Migration**: Can migrate usage incrementally
218+
219+
## Migration Strategy
220+
221+
1. **Immediate**: Use new factory methods for new constraints
222+
2. **Gradual**: Update existing high-frequency usage sites
223+
3. **Eventually**: Deprecate string-based constructor
224+
4. **Future**: Consider merging ChildRequirement functionality

0 commit comments

Comments
 (0)