AbstractMVCGroup.java
001 /*
002  * Copyright 2008-2017 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.core.mvc;
017 
018 import griffon.core.Context;
019 import griffon.core.artifact.GriffonController;
020 import griffon.core.artifact.GriffonControllerClass;
021 import griffon.core.artifact.GriffonModel;
022 import griffon.core.artifact.GriffonModelClass;
023 import griffon.core.artifact.GriffonMvcArtifact;
024 import griffon.core.artifact.GriffonView;
025 import griffon.core.artifact.GriffonViewClass;
026 import griffon.core.mvc.MVCFunction;
027 import griffon.core.mvc.MVCGroup;
028 import griffon.core.mvc.MVCGroupConfiguration;
029 import griffon.core.mvc.MVCGroupFunction;
030 import griffon.core.mvc.MVCGroupManager;
031 import griffon.core.mvc.TypedMVCGroup;
032 import griffon.core.mvc.TypedMVCGroupFunction;
033 
034 import javax.annotation.Nonnull;
035 import javax.annotation.Nullable;
036 import java.util.ArrayList;
037 import java.util.Collections;
038 import java.util.LinkedHashMap;
039 import java.util.List;
040 import java.util.Map;
041 import java.util.UUID;
042 
043 import static griffon.util.GriffonClassUtils.requireState;
044 import static griffon.util.GriffonClassUtils.setPropertyOrFieldValue;
045 import static griffon.util.GriffonNameUtils.isBlank;
046 import static griffon.util.GriffonNameUtils.requireNonBlank;
047 import static java.util.Collections.unmodifiableMap;
048 import static java.util.Objects.requireNonNull;
049 
050 /**
051  * Base implementation of the {@code MVCGroup} interface
052  *
053  @author Andres Almiray
054  @since 2.0.0
055  */
056 public abstract class AbstractMVCGroup extends AbstractMVCHandler implements MVCGroup {
057     protected final MVCGroupConfiguration configuration;
058     protected final String mvcId;
059     protected final Context context;
060     protected final Map<String, Object> members = new LinkedHashMap<>();
061     protected final Map<String, MVCGroup> children = new LinkedHashMap<>();
062     private final Object[] lock = new Object[0];
063     protected MVCGroup parentGroup;
064     private boolean alive;
065     private final List<Object> injectedInstances = new ArrayList<>();
066 
067     public AbstractMVCGroup(@Nonnull MVCGroupManager mvcGroupManager, @Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> members, @Nullable MVCGroup parentGroup) {
068         super(mvcGroupManager);
069         this.configuration = requireNonNull(configuration, "Argument 'configuration' must not be null");
070         this.mvcId = isBlank(mvcId? configuration.getMvcType() "-" + UUID.randomUUID().toString() : mvcId;
071         this.members.putAll(requireNonNull(members, "Argument 'members' must not be null"));
072         this.alive = true;
073         this.parentGroup = parentGroup;
074         this.context = mvcGroupManager.newContext(parentGroup);
075 
076         for (Object o : this.members.values()) {
077             if (instanceof GriffonMvcArtifact) {
078                 setPropertyOrFieldValue(o, "mvcGroup"this);
079             }
080         }
081     }
082 
083     @Nonnull
084     public List<Object> getInjectedInstances() {
085         return injectedInstances;
086     }
087 
088     @Nonnull
089     @Override
090     public Context getContext() {
091         return context;
092     }
093 
094     @Nullable
095     @Override
096     public MVCGroup getParentGroup() {
097         return parentGroup;
098     }
099 
100     @Nonnull
101     @Override
102     public MVCGroupConfiguration getConfiguration() {
103         return configuration;
104     }
105 
106     @Nonnull
107     @Override
108     public String getMvcType() {
109         return configuration.getMvcType();
110     }
111 
112     @Nonnull
113     @Override
114     public String getMvcId() {
115         return mvcId;
116     }
117 
118     @Nullable
119     @Override
120     public GriffonModel getModel() {
121         return (GriffonModelgetMember(GriffonModelClass.TYPE);
122     }
123 
124     @Nullable
125     @Override
126     public GriffonView getView() {
127         return (GriffonViewgetMember(GriffonViewClass.TYPE);
128     }
129 
130     @Nullable
131     @Override
132     public GriffonController getController() {
133         return (GriffonControllergetMember(GriffonControllerClass.TYPE);
134     }
135 
136     @Nullable
137     @Override
138     public Object getMember(@Nonnull String name) {
139         requireNonBlank(name, "Argument 'name' must not be blank");
140         checkIfAlive();
141         return members.get(name);
142     }
143 
144     @Nonnull
145     @Override
146     public Map<String, Object> getMembers() {
147         checkIfAlive();
148         return unmodifiableMap(members);
149     }
150 
151     @Override
152     public void destroy() {
153         if (isAlive()) {
154             List<String> childrenIds = new ArrayList<>(children.keySet());
155             Collections.reverse(childrenIds);
156             for (String id : childrenIds) {
157                 getMvcGroupManager().destroyMVCGroup(id);
158             }
159             getMvcGroupManager().destroyMVCGroup(mvcId);
160             members.clear();
161             children.clear();
162             if (parentGroup != null) {
163                 parentGroup.notifyMVCGroupDestroyed(mvcId);
164             }
165             parentGroup = null;
166             context.destroy();
167             synchronized (lock) {
168                 alive = false;
169             }
170         }
171     }
172 
173     @Override
174     public void notifyMVCGroupDestroyed(@Nonnull String mvcId) {
175         requireNonBlank(mvcId, "Argument 'mvcId' must not be blank");
176         children.remove(mvcId);
177     }
178 
179     @Override
180     public boolean isAlive() {
181         synchronized (lock) {
182             return alive;
183         }
184     }
185 
186     protected void checkIfAlive() {
187         requireState(isAlive()"Group " + getMvcType() ":" + mvcId + " has been destroyed already.");
188     }
189 
190 
191     @Nonnull
192     @Override
193     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId) {
194         return manageChildGroup(super.createMVCGroup(mvcType, mvcId, injectParentGroup()));
195     }
196 
197     @Nonnull
198     @Override
199     public MVCGroup createMVCGroup(@Nonnull String mvcType) {
200         return manageChildGroup(super.createMVCGroup(mvcType, injectParentGroup()));
201     }
202 
203     @Nonnull
204     @Override
205     public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
206         return manageChildGroup(super.createMVCGroup(injectParentGroup(args), mvcType));
207     }
208 
209     @Nonnull
210     @Override
211     public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
212         return manageChildGroup(super.createMVCGroup(injectParentGroup(args), mvcType, mvcId));
213     }
214 
215     @Nonnull
216     @Override
217     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
218         return manageChildGroup(super.createMVCGroup(mvcType, injectParentGroup(args)));
219     }
220 
221     @Nonnull
222     @Override
223     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
224         return manageChildGroup(super.createMVCGroup(mvcType, mvcId, injectParentGroup(args)));
225     }
226 
227     @Nonnull
228     @Override
229     public <MVC extends TypedMVCGroup> MVC createMVCGroup(@Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId) {
230         return manageTypedChildGroup(super.createMVCGroup(mvcType, mvcId, injectParentGroup()));
231     }
232 
233     @Nonnull
234     @Override
235     public <MVC extends TypedMVCGroup> MVC createMVCGroup(@Nonnull Class<? extends MVC> mvcType) {
236         return manageTypedChildGroup(super.createMVCGroup(mvcType, injectParentGroup()));
237     }
238 
239     @Nonnull
240     @Override
241     public <MVC extends TypedMVCGroup> MVC createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull Class<? extends MVC> mvcType) {
242         return manageTypedChildGroup(super.createMVCGroup(injectParentGroup(args), mvcType));
243     }
244 
245     @Nonnull
246     @Override
247     public <MVC extends TypedMVCGroup> MVC createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId) {
248         return manageTypedChildGroup(super.createMVCGroup(injectParentGroup(args), mvcType, mvcId));
249     }
250 
251     @Nonnull
252     @Override
253     public <MVC extends TypedMVCGroup> MVC createMVCGroup(@Nonnull Class<? extends MVC> mvcType, @Nonnull Map<String, Object> args) {
254         return manageTypedChildGroup(super.createMVCGroup(mvcType, injectParentGroup(args)));
255     }
256 
257     @Nonnull
258     @Override
259     public <MVC extends TypedMVCGroup> MVC createMVCGroup(@Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
260         return manageTypedChildGroup(super.createMVCGroup(mvcType, mvcId, injectParentGroup(args)));
261     }
262 
263     @Nonnull
264     @Override
265     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId) {
266         return manageChildGroup(super.createMVC(mvcType, mvcId, injectParentGroup()));
267     }
268 
269     @Nonnull
270     @Override
271     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType) {
272         return manageChildGroup(super.createMVC(mvcType, injectParentGroup()));
273     }
274 
275     @Nonnull
276     @Override
277     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
278         return manageChildGroup(super.createMVC(injectParentGroup(args), mvcType));
279     }
280 
281     @Nonnull
282     @Override
283     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
284         return manageChildGroup(super.createMVC(injectParentGroup(args), mvcType, mvcId));
285     }
286 
287     @Nonnull
288     @Override
289     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
290         return manageChildGroup(super.createMVC(mvcType, injectParentGroup(args)));
291     }
292 
293     @Nonnull
294     @Override
295     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
296         return manageChildGroup(super.createMVC(mvcType, mvcId, injectParentGroup(args)));
297     }
298 
299     @Nonnull
300     @Override
301     public <MVC extends TypedMVCGroup> List<? extends GriffonMvcArtifact> createMVC(@Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId) {
302         return manageChildGroup(super.createMVC(mvcType, mvcId, injectParentGroup()));
303     }
304 
305     @Nonnull
306     @Override
307     public <MVC extends TypedMVCGroup> List<? extends GriffonMvcArtifact> createMVC(@Nonnull Class<? extends MVC> mvcType) {
308         return manageChildGroup(super.createMVC(mvcType, injectParentGroup()));
309     }
310 
311     @Nonnull
312     @Override
313     public <MVC extends TypedMVCGroup> List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull Class<? extends MVC> mvcType) {
314         return manageChildGroup(super.createMVC(injectParentGroup(args), mvcType));
315     }
316 
317     @Nonnull
318     @Override
319     public <MVC extends TypedMVCGroup> List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId) {
320         return manageChildGroup(super.createMVC(injectParentGroup(args), mvcType, mvcId));
321     }
322 
323     @Nonnull
324     @Override
325     public <MVC extends TypedMVCGroup> List<? extends GriffonMvcArtifact> createMVC(@Nonnull Class<? extends MVC> mvcType, @Nonnull Map<String, Object> args) {
326         return manageChildGroup(super.createMVC(mvcType, injectParentGroup(args)));
327     }
328 
329     @Nonnull
330     @Override
331     public <MVC extends TypedMVCGroup> List<? extends GriffonMvcArtifact> createMVC(@Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
332         return manageChildGroup(super.createMVC(mvcType, mvcId, injectParentGroup(args)));
333     }
334 
335     @Override
336     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull MVCFunction<M, V, C> handler) {
337         super.withMVC(injectParentGroup(args), mvcType, new MVCFunctionDecorator<>(handler));
338     }
339 
340     @Override
341     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) {
342         super.withMVC(injectParentGroup(args), mvcType, mvcId, new MVCFunctionDecorator<>(handler));
343     }
344 
345     @Override
346     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCFunction<M, V, C> handler) {
347         super.withMVC(mvcType, injectParentGroup(args)new MVCFunctionDecorator<>(handler));
348     }
349 
350     @Override
351     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCFunction<M, V, C> handler) {
352         super.withMVC(mvcType, mvcId, injectParentGroup(args)new MVCFunctionDecorator<>(handler));
353     }
354 
355     @Override
356     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull MVCFunction<M, V, C> handler) {
357         super.withMVC(mvcType, injectParentGroup()new MVCFunctionDecorator<>(handler));
358     }
359 
360     @Override
361     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) {
362         super.withMVC(mvcType, mvcId, injectParentGroup()new MVCFunctionDecorator<>(handler));
363     }
364 
365     @Override
366     public <MVC extends TypedMVCGroup, M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Map<String, Object> args, @Nonnull Class<? extends MVC> mvcType, @Nonnull MVCFunction<M, V, C> handler) {
367         super.withMVC(injectParentGroup(args), mvcType, new MVCFunctionDecorator<>(handler));
368     }
369 
370     @Override
371     public <MVC extends TypedMVCGroup, M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Map<String, Object> args, @Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) {
372         super.withMVC(injectParentGroup(args), mvcType, mvcId, new MVCFunctionDecorator<>(handler));
373     }
374 
375     @Override
376     public <MVC extends TypedMVCGroup, M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Class<? extends MVC> mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCFunction<M, V, C> handler) {
377         super.withMVC(mvcType, injectParentGroup(args)new MVCFunctionDecorator<>(handler));
378     }
379 
380     @Override
381     public <MVC extends TypedMVCGroup, M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCFunction<M, V, C> handler) {
382         super.withMVC(mvcType, mvcId, injectParentGroup(args)new MVCFunctionDecorator<>(handler));
383     }
384 
385     @Override
386     public <MVC extends TypedMVCGroup, M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Class<? extends MVC> mvcType, @Nonnull MVCFunction<M, V, C> handler) {
387         super.withMVC(mvcType, injectParentGroup()new MVCFunctionDecorator<>(handler));
388     }
389 
390     @Override
391     public <MVC extends TypedMVCGroup, M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) {
392         super.withMVC(mvcType, mvcId, injectParentGroup()new MVCFunctionDecorator<>(handler));
393     }
394 
395     @Override
396     public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull MVCGroupFunction handler) {
397         super.withMVCGroup(injectParentGroup(args), mvcType, new MVCGroupFunctionDecorator(handler));
398     }
399 
400     @Override
401     public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCGroupFunction handler) {
402         super.withMVCGroup(injectParentGroup(args), mvcType, mvcId, new MVCGroupFunctionDecorator(handler));
403     }
404 
405     @Override
406     public void withMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
407         super.withMVCGroup(mvcType, injectParentGroup(args)new MVCGroupFunctionDecorator(handler));
408     }
409 
410     @Override
411     public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
412         super.withMVCGroup(mvcType, mvcId, injectParentGroup(args)new MVCGroupFunctionDecorator(handler));
413     }
414 
415     @Override
416     public void withMVCGroup(@Nonnull String mvcType, @Nonnull MVCGroupFunction handler) {
417         super.withMVCGroup(mvcType, injectParentGroup()new MVCGroupFunctionDecorator(handler));
418     }
419 
420     @Override
421     public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull final MVCGroupFunction handler) {
422         super.withMVCGroup(mvcType, mvcId, injectParentGroup()new MVCGroupFunctionDecorator(handler));
423     }
424 
425     @Override
426     public <MVC extends TypedMVCGroup> void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull Class<? extends MVC> mvcType, @Nonnull TypedMVCGroupFunction<MVC> handler) {
427         super.withMVCGroup(injectParentGroup(args), mvcType, new TypedMVCGroupFunctionDecorator<>(handler));
428     }
429 
430     @Override
431     public <MVC extends TypedMVCGroup> void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId, @Nonnull TypedMVCGroupFunction<MVC> handler) {
432         super.withMVCGroup(injectParentGroup(args), mvcType, mvcId, new TypedMVCGroupFunctionDecorator<>(handler));
433     }
434 
435     @Override
436     public <MVC extends TypedMVCGroup> void withMVCGroup(@Nonnull Class<? extends MVC> mvcType, @Nonnull Map<String, Object> args, @Nonnull TypedMVCGroupFunction<MVC> handler) {
437         super.withMVCGroup(mvcType, injectParentGroup(args)new TypedMVCGroupFunctionDecorator<>(handler));
438     }
439 
440     @Override
441     public <MVC extends TypedMVCGroup> void withMVCGroup(@Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull TypedMVCGroupFunction<MVC> handler) {
442         super.withMVCGroup(mvcType, mvcId, injectParentGroup(args)new TypedMVCGroupFunctionDecorator<>(handler));
443     }
444 
445     @Override
446     public <MVC extends TypedMVCGroup> void withMVCGroup(@Nonnull Class<? extends MVC> mvcType, @Nonnull TypedMVCGroupFunction<MVC> handler) {
447         super.withMVCGroup(mvcType, injectParentGroup()new TypedMVCGroupFunctionDecorator<>(handler));
448     }
449 
450     @Override
451     public <MVC extends TypedMVCGroup> void withMVCGroup(@Nonnull Class<? extends MVC> mvcType, @Nonnull String mvcId, @Nonnull final TypedMVCGroupFunction<MVC> handler) {
452         super.withMVCGroup(mvcType, mvcId, injectParentGroup()new TypedMVCGroupFunctionDecorator<>(handler));
453     }
454 
455     @Nonnull
456     private MVCGroup manageChildGroup(@Nonnull MVCGroup group) {
457         children.put(group.getMvcId(), group);
458         return group;
459     }
460 
461     @Nonnull
462     private <MVC extends TypedMVCGroup> MVC manageTypedChildGroup(@Nonnull MVC group) {
463         children.put(group.getMvcId(), group);
464         return group;
465     }
466 
467     @Nonnull
468     private List<? extends GriffonMvcArtifact> manageChildGroup(@Nonnull List<? extends GriffonMvcArtifact> artifacts) {
469         MVCGroup group = null;
470         for (GriffonMvcArtifact artifact : artifacts) {
471             if (artifact != null) {
472                 group = artifact.getMvcGroup();
473                 break;
474             }
475         }
476         if (group != null) {
477             children.put(group.getMvcId(), group);
478         }
479         return artifacts;
480     }
481 
482     @Nonnull
483     @Override
484     public Map<String, MVCGroup> getChildrenGroups() {
485         return unmodifiableMap(children);
486     }
487 
488     @Nonnull
489     private Map<String, Object> injectParentGroup() {
490         return injectParentGroup(new LinkedHashMap<String, Object>());
491     }
492 
493     @Nonnull
494     private Map<String, Object> injectParentGroup(@Nonnull Map<String, Object> args) {
495         Map<String, Object> map = new LinkedHashMap<>();
496         map.put("parentGroup"this);
497         map.putAll(args);
498         return map;
499     }
500 
501     private final class MVCFunctionDecorator<M extends GriffonModel, V extends GriffonView, C extends GriffonController> implements MVCFunction<M, V, C> {
502         private final MVCFunction<M, V, C> delegate;
503 
504         private MVCFunctionDecorator(MVCFunction<M, V, C> delegate) {
505             this.delegate = delegate;
506         }
507 
508         @Override
509         public void apply(@Nullable M model, @Nullable V view, @Nullable C controller) {
510             MVCGroup group = null;
511             if (model != null) { group = model.getMvcGroup()}
512             if (view != null) { group = view.getMvcGroup()}
513             if (controller != null) { group = controller.getMvcGroup()}
514 
515             if (group != null) { children.put(group.getMvcId(), group)}
516             delegate.apply(model, view, controller);
517             if (group != null) { children.remove(group.getMvcId())}
518         }
519     }
520 
521     private final class MVCGroupFunctionDecorator implements MVCGroupFunction {
522         private final MVCGroupFunction delegate;
523 
524         private MVCGroupFunctionDecorator(MVCGroupFunction delegate) {
525             this.delegate = delegate;
526         }
527 
528         @Override
529         public void apply(@Nonnull MVCGroup group) {
530             children.put(group.getMvcId(), group);
531             delegate.apply(group);
532             children.remove(group.getMvcId());
533         }
534     }
535 
536     private final class TypedMVCGroupFunctionDecorator<MVC extends TypedMVCGroup> implements TypedMVCGroupFunction<MVC> {
537         private final TypedMVCGroupFunction<MVC> delegate;
538 
539         private TypedMVCGroupFunctionDecorator(TypedMVCGroupFunction<MVC> delegate) {
540             this.delegate = delegate;
541         }
542 
543         @Override
544         public void apply(@Nonnull MVC group) {
545             children.put(group.getMvcId(), group);
546             delegate.apply(group);
547             children.remove(group.getMvcId());
548         }
549     }
550 }