AbstractMVCGroup.java
001 /*
002  * Copyright 2008-2015 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 
032 import javax.annotation.Nonnull;
033 import javax.annotation.Nullable;
034 import java.util.ArrayList;
035 import java.util.Collections;
036 import java.util.LinkedHashMap;
037 import java.util.List;
038 import java.util.Map;
039 import java.util.UUID;
040 
041 import static griffon.util.GriffonClassUtils.requireState;
042 import static griffon.util.GriffonClassUtils.setPropertyOrFieldValue;
043 import static griffon.util.GriffonNameUtils.isBlank;
044 import static griffon.util.GriffonNameUtils.requireNonBlank;
045 import static java.util.Collections.unmodifiableMap;
046 import static java.util.Objects.requireNonNull;
047 
048 /**
049  * Base implementation of the {@code MVCGroup} interface
050  *
051  @author Andres Almiray
052  @since 2.0.0
053  */
054 public abstract class AbstractMVCGroup extends AbstractMVCHandler implements MVCGroup {
055     protected final MVCGroupConfiguration configuration;
056     protected final String mvcId;
057     protected final Context context;
058     protected final Map<String, Object> members = new LinkedHashMap<>();
059     protected final Map<String, MVCGroup> children = new LinkedHashMap<>();
060     private final Object[] lock = new Object[0];
061     protected MVCGroup parentGroup;
062     private boolean alive;
063 
064     public AbstractMVCGroup(@Nonnull MVCGroupManager mvcGroupManager, @Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> members, @Nullable MVCGroup parentGroup) {
065         super(mvcGroupManager);
066         this.configuration = requireNonNull(configuration, "Argument 'configuration' must not be null");
067         this.mvcId = isBlank(mvcId? configuration.getMvcType() "-" + UUID.randomUUID().toString() : mvcId;
068         this.members.putAll(requireNonNull(members, "Argument 'members' must not be null"));
069         this.alive = true;
070         this.parentGroup = parentGroup;
071         this.context = mvcGroupManager.newContext(parentGroup);
072 
073         for (Object o : this.members.values()) {
074             if (instanceof GriffonMvcArtifact) {
075                 setPropertyOrFieldValue(o, "mvcGroup"this);
076             }
077         }
078     }
079 
080     @Nonnull
081     @Override
082     public Context getContext() {
083         return context;
084     }
085 
086     @Nullable
087     @Override
088     public MVCGroup getParentGroup() {
089         return parentGroup;
090     }
091 
092     @Nonnull
093     @Override
094     public MVCGroupConfiguration getConfiguration() {
095         return configuration;
096     }
097 
098     @Nonnull
099     @Override
100     public String getMvcType() {
101         return configuration.getMvcType();
102     }
103 
104     @Nonnull
105     @Override
106     public String getMvcId() {
107         return mvcId;
108     }
109 
110     @Nullable
111     @Override
112     public GriffonModel getModel() {
113         return (GriffonModelgetMember(GriffonModelClass.TYPE);
114     }
115 
116     @Nullable
117     @Override
118     public GriffonView getView() {
119         return (GriffonViewgetMember(GriffonViewClass.TYPE);
120     }
121 
122     @Nullable
123     @Override
124     public GriffonController getController() {
125         return (GriffonControllergetMember(GriffonControllerClass.TYPE);
126     }
127 
128     @Nullable
129     @Override
130     public Object getMember(@Nonnull String name) {
131         requireNonBlank(name, "Argument 'name' must not be blank");
132         checkIfAlive();
133         return members.get(name);
134     }
135 
136     @Nonnull
137     @Override
138     public Map<String, Object> getMembers() {
139         checkIfAlive();
140         return unmodifiableMap(members);
141     }
142 
143     @Override
144     public void destroy() {
145         if (isAlive()) {
146             List<String> childrenIds = new ArrayList<>(children.keySet());
147             Collections.reverse(childrenIds);
148             for (String id : childrenIds) {
149                 getMvcGroupManager().destroyMVCGroup(id);
150             }
151             getMvcGroupManager().destroyMVCGroup(mvcId);
152             members.clear();
153             children.clear();
154             if (parentGroup != null) {
155                 parentGroup.notifyMVCGroupDestroyed(mvcId);
156             }
157             parentGroup = null;
158             context.destroy();
159             synchronized (lock) {
160                 alive = false;
161             }
162         }
163     }
164 
165     @Override
166     public void notifyMVCGroupDestroyed(@Nonnull String mvcId) {
167         requireNonBlank(mvcId, "Argument 'mvcId' must not be blank");
168         children.remove(mvcId);
169     }
170 
171     @Override
172     public boolean isAlive() {
173         synchronized (lock) {
174             return alive;
175         }
176     }
177 
178     protected void checkIfAlive() {
179         requireState(isAlive()"Group " + getMvcType() ":" + mvcId + " has been destroyed already.");
180     }
181 
182     @Nonnull
183     @Override
184     public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
185         return manageChildGroup(super.createMVCGroup(injectParentGroup(args), mvcType));
186     }
187 
188     @Nonnull
189     @Override
190     public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
191         return manageChildGroup(super.createMVCGroup(injectParentGroup(args), mvcType, mvcId));
192     }
193 
194     @Nonnull
195     @Override
196     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
197         return manageChildGroup(super.createMVCGroup(mvcType, injectParentGroup(args)));
198     }
199 
200     @Nonnull
201     @Override
202     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
203         return manageChildGroup(super.createMVCGroup(mvcType, mvcId, injectParentGroup(args)));
204     }
205 
206     @Nonnull
207     @Override
208     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
209         return manageChildGroup(super.createMVC(injectParentGroup(args), mvcType));
210     }
211 
212     @Nonnull
213     @Override
214     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
215         return manageChildGroup(super.createMVC(injectParentGroup(args), mvcType, mvcId));
216     }
217 
218     @Nonnull
219     @Override
220     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
221         return manageChildGroup(super.createMVC(mvcType, injectParentGroup(args)));
222     }
223 
224     @Nonnull
225     @Override
226     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
227         return manageChildGroup(super.createMVC(mvcType, mvcId, injectParentGroup(args)));
228     }
229 
230     @Override
231     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) {
232         super.withMVC(injectParentGroup(args), mvcType, new MVCFunctionDecorator<>(handler));
233     }
234 
235     @Override
236     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) {
237         super.withMVC(injectParentGroup(args), mvcType, mvcId, new MVCFunctionDecorator<>(handler));
238     }
239 
240     @Override
241     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) {
242         super.withMVC(mvcType, injectParentGroup(args)new MVCFunctionDecorator<>(handler));
243     }
244 
245     @Override
246     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) {
247         super.withMVC(mvcType, mvcId, injectParentGroup(args)new MVCFunctionDecorator<>(handler));
248     }
249 
250     @Override
251     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull MVCFunction<M, V, C> handler) {
252         super.withMVC(mvcType, injectParentGroup()new MVCFunctionDecorator<>(handler));
253     }
254 
255     @Override
256     public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) {
257         super.withMVC(mvcType, mvcId, injectParentGroup()new MVCFunctionDecorator<>(handler));
258     }
259 
260     @Override
261     public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull MVCGroupFunction handler) {
262         super.withMVCGroup(injectParentGroup(args), mvcType, new MVCGroupFunctionDecorator(handler));
263     }
264 
265     @Override
266     public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCGroupFunction handler) {
267         super.withMVCGroup(injectParentGroup(args), mvcType, mvcId, new MVCGroupFunctionDecorator(handler));
268     }
269 
270     @Override
271     public void withMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
272         super.withMVCGroup(mvcType, injectParentGroup(args)new MVCGroupFunctionDecorator(handler));
273     }
274 
275     @Override
276     public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
277         super.withMVCGroup(mvcType, mvcId, injectParentGroup(args)new MVCGroupFunctionDecorator(handler));
278     }
279 
280     @Override
281     public void withMVCGroup(@Nonnull String mvcType, @Nonnull MVCGroupFunction handler) {
282         super.withMVCGroup(mvcType, injectParentGroup()new MVCGroupFunctionDecorator(handler));
283     }
284 
285     @Override
286     public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, final @Nonnull MVCGroupFunction handler) {
287         super.withMVCGroup(mvcType, mvcId, injectParentGroup()new MVCGroupFunctionDecorator(handler));
288     }
289 
290     @Nonnull
291     @Override
292     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId) {
293         return manageChildGroup(super.createMVC(mvcType, mvcId, injectParentGroup()));
294     }
295 
296     @Nonnull
297     @Override
298     public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType) {
299         return manageChildGroup(super.createMVC(mvcType, injectParentGroup()));
300     }
301 
302     @Nonnull
303     @Override
304     public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId) {
305         return manageChildGroup(super.createMVCGroup(mvcType, mvcId, injectParentGroup()));
306     }
307 
308     @Nonnull
309     @Override
310     public MVCGroup createMVCGroup(@Nonnull String mvcType) {
311         return manageChildGroup(super.createMVCGroup(mvcType, injectParentGroup()));
312     }
313 
314     @Nonnull
315     private MVCGroup manageChildGroup(@Nonnull MVCGroup group) {
316         children.put(group.getMvcId(), group);
317         return group;
318     }
319 
320     @Nonnull
321     private List<? extends GriffonMvcArtifact> manageChildGroup(@Nonnull List<? extends GriffonMvcArtifact> artifacts) {
322         MVCGroup group = null;
323         for (GriffonMvcArtifact artifact : artifacts) {
324             if (artifact != null) {
325                 group = artifact.getMvcGroup();
326                 break;
327             }
328         }
329         if (group != null) {
330             children.put(group.getMvcId(), group);
331         }
332         return artifacts;
333     }
334 
335     @Nonnull
336     @Override
337     public Map<String, MVCGroup> getChildrenGroups() {
338         return unmodifiableMap(children);
339     }
340 
341     @Nonnull
342     private Map<String, Object> injectParentGroup() {
343         return injectParentGroup(new LinkedHashMap<String, Object>());
344     }
345 
346     @Nonnull
347     private Map<String, Object> injectParentGroup(@Nonnull Map<String, Object> args) {
348         Map<String, Object> map = new LinkedHashMap<>();
349         map.put("parentGroup"this);
350         map.putAll(args);
351         return map;
352     }
353 
354     private final class MVCFunctionDecorator<M extends GriffonModel, V extends GriffonView, C extends GriffonController> implements MVCFunction<M, V, C> {
355         private final MVCFunction<M, V, C> delegate;
356 
357         private MVCFunctionDecorator(MVCFunction<M, V, C> delegate) {
358             this.delegate = delegate;
359         }
360 
361         @Override
362         public void apply(@Nullable M model, @Nullable V view, @Nullable C controller) {
363             MVCGroup group = null;
364             if (model != nullgroup = model.getMvcGroup();
365             if (view != nullgroup = view.getMvcGroup();
366             if (controller != nullgroup = controller.getMvcGroup();
367 
368             if (group != nullchildren.put(group.getMvcId(), group);
369             delegate.apply(model, view, controller);
370             if (group != nullchildren.remove(group.getMvcId());
371         }
372     }
373 
374     private final class MVCGroupFunctionDecorator implements MVCGroupFunction {
375         private final MVCGroupFunction delegate;
376 
377         private MVCGroupFunctionDecorator(MVCGroupFunction delegate) {
378             this.delegate = delegate;
379         }
380 
381         @Override
382         public void apply(@Nullable MVCGroup group) {
383             children.put(group.getMvcId(), group);
384             delegate.apply(group);
385             children.remove(group.getMvcId());
386         }
387     }
388 }