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