@@ -50,7 +50,8 @@ abstract class AbstractMutableModelFile implements ModelFile {
50
50
private final Set <ShapeId > allShapeIds = new HashSet <>();
51
51
private final Map <ShapeId , AbstractShapeBuilder <?, ?>> shapes = new LinkedHashMap <>();
52
52
private final Map <ShapeId , Map <String , MemberShape .Builder >> members = new HashMap <>();
53
- private final Map <ShapeId , Set <ShapeId >> pendingShapes = new HashMap <>();
53
+ private final Map <ShapeId , List <PendingShapeModifier >> pendingModifications = new HashMap <>();
54
+ private final Map <ShapeId , Set <ShapeId >> pendingDependencies = new HashMap <>();
54
55
private final List <ValidationEvent > events = new ArrayList <>();
55
56
private final MetadataContainer metadata = new MetadataContainer (events );
56
57
private final TraitFactory traitFactory ;
@@ -106,7 +107,13 @@ void onShape(AbstractShapeBuilder<?, ?> builder) {
106
107
}
107
108
108
109
void addPendingMixin (ShapeId shape , ShapeId mixin ) {
109
- pendingShapes .computeIfAbsent (shape , id -> new LinkedHashSet <>()).add (mixin );
110
+ addPendingModification (shape , new ApplyMixin (mixin , events ));
111
+ }
112
+
113
+ void addPendingModification (ShapeId shape , PendingShapeModifier pendingModification ) {
114
+ pendingDependencies .computeIfAbsent (shape , id -> new LinkedHashSet <>())
115
+ .addAll (pendingModification .getDependencies ());
116
+ pendingModifications .computeIfAbsent (shape , id -> new ArrayList <>()).add (pendingModification );
110
117
}
111
118
112
119
private SourceException onConflict (AbstractShapeBuilder <?, ?> builder , AbstractShapeBuilder <?, ?> previous ) {
@@ -181,13 +188,15 @@ public final CreatedShapes createShapes(TraitContainer resolvedTraits) {
181
188
List <Shape > resolvedShapes = new ArrayList <>(shapes .size ());
182
189
List <PendingShape > pendingMixins = new ArrayList <>();
183
190
184
- for (Map .Entry <ShapeId , Set < ShapeId >> entry : pendingShapes .entrySet ()) {
191
+ for (Map .Entry <ShapeId , List < PendingShapeModifier >> entry : this . pendingModifications .entrySet ()) {
185
192
ShapeId subject = entry .getKey ();
186
- Set <ShapeId > mixins = entry .getValue ();
193
+ List <PendingShapeModifier > pendingModifications = entry .getValue ();
194
+ Set <ShapeId > dependencies = pendingDependencies .getOrDefault (subject , Collections .emptySet ());
187
195
AbstractShapeBuilder <?, ?> builder = shapes .get (entry .getKey ());
188
196
Map <String , MemberShape .Builder > builderMembers = claimMembersOfContainer (builder .getId ());
189
197
shapes .remove (entry .getKey ());
190
- pendingMixins .add (createPendingShape (subject , builder , builderMembers , mixins , traitContainer ));
198
+ pendingMixins .add (createPendingShape (
199
+ subject , builder , builderMembers , dependencies , pendingModifications , traitContainer ));
191
200
}
192
201
193
202
// Build members and add them to top-level shapes.
@@ -223,55 +232,21 @@ private PendingShape createPendingShape(
223
232
AbstractShapeBuilder <?, ?> builder ,
224
233
Map <String , MemberShape .Builder > builderMembers ,
225
234
Set <ShapeId > mixins ,
235
+ List <PendingShapeModifier > pendingModifications ,
226
236
TraitContainer resolvedTraits
227
237
) {
228
238
return PendingShape .create (subject , builder , mixins , shapeMap -> {
229
- // Build normal members first.
230
239
for (MemberShape .Builder memberBuilder : builderMembers .values ()) {
240
+ for (PendingShapeModifier pendingModification : pendingModifications ) {
241
+ pendingModification .modifyMember (builder , memberBuilder , resolvedTraits , shapeMap );
242
+ }
231
243
buildShape (memberBuilder , resolvedTraits ).ifPresent (builder ::addMember );
232
244
}
233
- // Add each mixin and ensure there are no member conflicts.
234
- for (ShapeId mixin : mixins ) {
235
- Shape mixinShape = shapeMap .get (mixin );
236
- for (MemberShape member : mixinShape .members ()) {
237
- ShapeId targetId = builder .getId ().withMember (member .getMemberName ());
238
- Map <ShapeId , Trait > introducedTraits = traitContainer .getTraitsForShape (targetId );
239
-
240
- MemberShape introducedMember = null ;
241
- if (builderMembers .containsKey (member .getMemberName ())) {
242
- introducedMember = builderMembers .get (member .getMemberName ())
243
- .addMixin (member )
244
- .build ();
245
-
246
- if (!introducedMember .getTarget ().equals (member .getTarget ())) {
247
- // Members cannot be redefined if their targets conflict.
248
- MemberShape .Builder conflict = builderMembers .get (member .getMemberName ());
249
- events .add (ValidationEvent .builder ()
250
- .severity (Severity .ERROR )
251
- .id (Validator .MODEL_ERROR )
252
- .shapeId (conflict .getId ())
253
- .sourceLocation (conflict .getSourceLocation ())
254
- .message ("Member conflicts with an inherited mixin member: " + member .getId ())
255
- .build ());
256
- }
257
- } else if (!introducedTraits .isEmpty ()) {
258
- // Build local member copies before adding mixins if traits
259
- // were introduced to inherited mixin members.
260
- introducedMember = MemberShape .builder ()
261
- .id (targetId )
262
- .target (member .getTarget ())
263
- .source (member .getSourceLocation ())
264
- .addTraits (introducedTraits .values ())
265
- .addMixin (member )
266
- .build ();
267
- }
268
-
269
- if (introducedMember != null ) {
270
- builder .addMember (introducedMember );
271
- }
272
- }
273
- builder .addMixin (mixinShape );
245
+
246
+ for (PendingShapeModifier pendingModification : pendingModifications ) {
247
+ pendingModification .modifyShape (builder , builderMembers , resolvedTraits , shapeMap );
274
248
}
249
+
275
250
buildShape (builder , resolvedTraits ).ifPresent (result -> shapeMap .put (result .getId (), result ));
276
251
});
277
252
}
@@ -285,10 +260,24 @@ private <S extends Shape, B extends AbstractShapeBuilder<? extends B, S>> Option
285
260
builder .addTrait (trait );
286
261
}
287
262
return Optional .of (builder .build ());
263
+ } catch (IllegalStateException e ) {
264
+ if (builder .getShapeType () == ShapeType .MEMBER && ((MemberShape .Builder ) builder ).getTarget () == null ) {
265
+ events .add (ValidationEvent .builder ()
266
+ .severity (Severity .ERROR )
267
+ .id (Validator .MODEL_ERROR )
268
+ .shapeId (builder .getId ())
269
+ .sourceLocation (builder .getSourceLocation ())
270
+ .message ("Member target was elided, but no bound resource or mixin contained a matching "
271
+ + "identifier or member name." )
272
+ .build ());
273
+ return Optional .empty ();
274
+ }
275
+ throw e ;
288
276
} catch (SourceException e ) {
289
277
events .add (ValidationEvent .fromSourceException (e , "" , builder .getId ()));
290
278
resolvedTraits .clearTraitsForShape (builder .getId ());
291
279
return Optional .empty ();
292
280
}
293
281
}
282
+
294
283
}
0 commit comments