/*
* Copyright 2008-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.griffon.runtime.core.mvc;
import griffon.core.Context;
import griffon.core.artifact.GriffonController;
import griffon.core.artifact.GriffonControllerClass;
import griffon.core.artifact.GriffonModel;
import griffon.core.artifact.GriffonModelClass;
import griffon.core.artifact.GriffonMvcArtifact;
import griffon.core.artifact.GriffonView;
import griffon.core.artifact.GriffonViewClass;
import griffon.core.mvc.MVCFunction;
import griffon.core.mvc.MVCGroup;
import griffon.core.mvc.MVCGroupConfiguration;
import griffon.core.mvc.MVCGroupFunction;
import griffon.core.mvc.MVCGroupManager;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static griffon.util.GriffonClassUtils.requireState;
import static griffon.util.GriffonClassUtils.setPropertyOrFieldValue;
import static griffon.util.GriffonNameUtils.isBlank;
import static griffon.util.GriffonNameUtils.requireNonBlank;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
/**
* Base implementation of the {@code MVCGroup} interface
*
* @author Andres Almiray
* @since 2.0.0
*/
public abstract class AbstractMVCGroup extends AbstractMVCHandler implements MVCGroup {
protected final MVCGroupConfiguration configuration;
protected final String mvcId;
protected final Context context;
protected final Map<String, Object> members = new LinkedHashMap<>();
protected final Map<String, MVCGroup> children = new LinkedHashMap<>();
private final Object[] lock = new Object[0];
protected MVCGroup parentGroup;
private boolean alive;
private final List<Object> injectedInstances = new ArrayList<>();
public AbstractMVCGroup(@Nonnull MVCGroupManager mvcGroupManager, @Nonnull MVCGroupConfiguration configuration, @Nullable String mvcId, @Nonnull Map<String, Object> members, @Nullable MVCGroup parentGroup) {
super(mvcGroupManager);
this.configuration = requireNonNull(configuration, "Argument 'configuration' must not be null");
this.mvcId = isBlank(mvcId) ? configuration.getMvcType() + "-" + UUID.randomUUID().toString() : mvcId;
this.members.putAll(requireNonNull(members, "Argument 'members' must not be null"));
this.alive = true;
this.parentGroup = parentGroup;
this.context = mvcGroupManager.newContext(parentGroup);
for (Object o : this.members.values()) {
if (o instanceof GriffonMvcArtifact) {
setPropertyOrFieldValue(o, "mvcGroup", this);
}
}
}
@Nonnull
public List<Object> getInjectedInstances() {
return injectedInstances;
}
@Nonnull
@Override
public Context getContext() {
return context;
}
@Nullable
@Override
public MVCGroup getParentGroup() {
return parentGroup;
}
@Nonnull
@Override
public MVCGroupConfiguration getConfiguration() {
return configuration;
}
@Nonnull
@Override
public String getMvcType() {
return configuration.getMvcType();
}
@Nonnull
@Override
public String getMvcId() {
return mvcId;
}
@Nullable
@Override
public GriffonModel getModel() {
return (GriffonModel) getMember(GriffonModelClass.TYPE);
}
@Nullable
@Override
public GriffonView getView() {
return (GriffonView) getMember(GriffonViewClass.TYPE);
}
@Nullable
@Override
public GriffonController getController() {
return (GriffonController) getMember(GriffonControllerClass.TYPE);
}
@Nullable
@Override
public Object getMember(@Nonnull String name) {
requireNonBlank(name, "Argument 'name' must not be blank");
checkIfAlive();
return members.get(name);
}
@Nonnull
@Override
public Map<String, Object> getMembers() {
checkIfAlive();
return unmodifiableMap(members);
}
@Override
public void destroy() {
if (isAlive()) {
List<String> childrenIds = new ArrayList<>(children.keySet());
Collections.reverse(childrenIds);
for (String id : childrenIds) {
getMvcGroupManager().destroyMVCGroup(id);
}
getMvcGroupManager().destroyMVCGroup(mvcId);
members.clear();
children.clear();
if (parentGroup != null) {
parentGroup.notifyMVCGroupDestroyed(mvcId);
}
parentGroup = null;
context.destroy();
synchronized (lock) {
alive = false;
}
}
}
@Override
public void notifyMVCGroupDestroyed(@Nonnull String mvcId) {
requireNonBlank(mvcId, "Argument 'mvcId' must not be blank");
children.remove(mvcId);
}
@Override
public boolean isAlive() {
synchronized (lock) {
return alive;
}
}
protected void checkIfAlive() {
requireState(isAlive(), "Group " + getMvcType() + ":" + mvcId + " has been destroyed already.");
}
@Nonnull
@Override
public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
return manageChildGroup(super.createMVCGroup(injectParentGroup(args), mvcType));
}
@Nonnull
@Override
public MVCGroup createMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
return manageChildGroup(super.createMVCGroup(injectParentGroup(args), mvcType, mvcId));
}
@Nonnull
@Override
public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
return manageChildGroup(super.createMVCGroup(mvcType, injectParentGroup(args)));
}
@Nonnull
@Override
public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
return manageChildGroup(super.createMVCGroup(mvcType, mvcId, injectParentGroup(args)));
}
@Nonnull
@Override
public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType) {
return manageChildGroup(super.createMVC(injectParentGroup(args), mvcType));
}
@Nonnull
@Override
public List<? extends GriffonMvcArtifact> createMVC(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId) {
return manageChildGroup(super.createMVC(injectParentGroup(args), mvcType, mvcId));
}
@Nonnull
@Override
public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull Map<String, Object> args) {
return manageChildGroup(super.createMVC(mvcType, injectParentGroup(args)));
}
@Nonnull
@Override
public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args) {
return manageChildGroup(super.createMVC(mvcType, mvcId, injectParentGroup(args)));
}
@Override
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) {
super.withMVC(injectParentGroup(args), mvcType, new MVCFunctionDecorator<>(handler));
}
@Override
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) {
super.withMVC(injectParentGroup(args), mvcType, mvcId, new MVCFunctionDecorator<>(handler));
}
@Override
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) {
super.withMVC(mvcType, injectParentGroup(args), new MVCFunctionDecorator<>(handler));
}
@Override
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) {
super.withMVC(mvcType, mvcId, injectParentGroup(args), new MVCFunctionDecorator<>(handler));
}
@Override
public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull MVCFunction<M, V, C> handler) {
super.withMVC(mvcType, injectParentGroup(), new MVCFunctionDecorator<>(handler));
}
@Override
public <M extends GriffonModel, V extends GriffonView, C extends GriffonController> void withMVC(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCFunction<M, V, C> handler) {
super.withMVC(mvcType, mvcId, injectParentGroup(), new MVCFunctionDecorator<>(handler));
}
@Override
public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull MVCGroupFunction handler) {
super.withMVCGroup(injectParentGroup(args), mvcType, new MVCGroupFunctionDecorator(handler));
}
@Override
public void withMVCGroup(@Nonnull Map<String, Object> args, @Nonnull String mvcType, @Nonnull String mvcId, @Nonnull MVCGroupFunction handler) {
super.withMVCGroup(injectParentGroup(args), mvcType, mvcId, new MVCGroupFunctionDecorator(handler));
}
@Override
public void withMVCGroup(@Nonnull String mvcType, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
super.withMVCGroup(mvcType, injectParentGroup(args), new MVCGroupFunctionDecorator(handler));
}
@Override
public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull Map<String, Object> args, @Nonnull MVCGroupFunction handler) {
super.withMVCGroup(mvcType, mvcId, injectParentGroup(args), new MVCGroupFunctionDecorator(handler));
}
@Override
public void withMVCGroup(@Nonnull String mvcType, @Nonnull MVCGroupFunction handler) {
super.withMVCGroup(mvcType, injectParentGroup(), new MVCGroupFunctionDecorator(handler));
}
@Override
public void withMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId, @Nonnull final MVCGroupFunction handler) {
super.withMVCGroup(mvcType, mvcId, injectParentGroup(), new MVCGroupFunctionDecorator(handler));
}
@Nonnull
@Override
public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType, @Nonnull String mvcId) {
return manageChildGroup(super.createMVC(mvcType, mvcId, injectParentGroup()));
}
@Nonnull
@Override
public List<? extends GriffonMvcArtifact> createMVC(@Nonnull String mvcType) {
return manageChildGroup(super.createMVC(mvcType, injectParentGroup()));
}
@Nonnull
@Override
public MVCGroup createMVCGroup(@Nonnull String mvcType, @Nonnull String mvcId) {
return manageChildGroup(super.createMVCGroup(mvcType, mvcId, injectParentGroup()));
}
@Nonnull
@Override
public MVCGroup createMVCGroup(@Nonnull String mvcType) {
return manageChildGroup(super.createMVCGroup(mvcType, injectParentGroup()));
}
@Nonnull
private MVCGroup manageChildGroup(@Nonnull MVCGroup group) {
children.put(group.getMvcId(), group);
return group;
}
@Nonnull
private List<? extends GriffonMvcArtifact> manageChildGroup(@Nonnull List<? extends GriffonMvcArtifact> artifacts) {
MVCGroup group = null;
for (GriffonMvcArtifact artifact : artifacts) {
if (artifact != null) {
group = artifact.getMvcGroup();
break;
}
}
if (group != null) {
children.put(group.getMvcId(), group);
}
return artifacts;
}
@Nonnull
@Override
public Map<String, MVCGroup> getChildrenGroups() {
return unmodifiableMap(children);
}
@Nonnull
private Map<String, Object> injectParentGroup() {
return injectParentGroup(new LinkedHashMap<String, Object>());
}
@Nonnull
private Map<String, Object> injectParentGroup(@Nonnull Map<String, Object> args) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("parentGroup", this);
map.putAll(args);
return map;
}
private final class MVCFunctionDecorator<M extends GriffonModel, V extends GriffonView, C extends GriffonController> implements MVCFunction<M, V, C> {
private final MVCFunction<M, V, C> delegate;
private MVCFunctionDecorator(MVCFunction<M, V, C> delegate) {
this.delegate = delegate;
}
@Override
public void apply(@Nullable M model, @Nullable V view, @Nullable C controller) {
MVCGroup group = null;
if (model != null) group = model.getMvcGroup();
if (view != null) group = view.getMvcGroup();
if (controller != null) group = controller.getMvcGroup();
if (group != null) children.put(group.getMvcId(), group);
delegate.apply(model, view, controller);
if (group != null) children.remove(group.getMvcId());
}
}
private final class MVCGroupFunctionDecorator implements MVCGroupFunction {
private final MVCGroupFunction delegate;
private MVCGroupFunctionDecorator(MVCGroupFunction delegate) {
this.delegate = delegate;
}
@Override
public void apply(@Nullable MVCGroup group) {
children.put(group.getMvcId(), group);
delegate.apply(group);
children.remove(group.getMvcId());
}
}
}