/*
* Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved.
*
* This file is part of the Jspresso framework.
*
* Jspresso is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Jspresso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Jspresso. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jspresso.framework.application.model;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Collections;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.jspresso.framework.application.backend.IBackendController;
import org.jspresso.framework.application.model.descriptor.BeanModuleDescriptor;
import org.jspresso.framework.model.descriptor.IComponentDescriptor;
import org.jspresso.framework.model.descriptor.IComponentDescriptorProvider;
import org.jspresso.framework.util.bean.IPropertyChangeCapable;
import org.jspresso.framework.util.lang.ObjectUtils;
import org.jspresso.framework.view.descriptor.EBorderType;
import org.jspresso.framework.view.descriptor.IViewDescriptor;
import org.jspresso.framework.view.descriptor.basic.BasicBorderViewDescriptor;
import org.jspresso.framework.view.descriptor.basic.BasicComponentViewDescriptor;
import org.jspresso.framework.view.descriptor.basic.BasicViewDescriptor;
/**
* This type of module keeps a reference on a single bean. There is no
* assumption made on whether this bean is actually a persistent entity or any
* other type of java bean.
* <p>
* Bean modules must have their referenced bean initialized somehow. So it's
* rather common to have the module content initialized through a startup action
* depending on the session state or dynamically constructed by a standard
* action like {@code AddBeanAsSubModuleAction}.
* <p>
* This type of module is definitely the one that offers maximum flexibility to
* handle arbitrary models.
*
* @author Vincent Vandenschrick
*/
public class BeanModule extends Module implements PropertyChangeListener {
/**
* {@code MODULE_OBJECT} is "moduleObject".
*/
public static final String MODULE_OBJECT = "moduleObject";
private IComponentDescriptor<Object> componentDescriptor;
private Object moduleObject;
/**
* Equality based on projected object.
* <p>
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BeanModule)) {
return false;
}
if (this == obj) {
return true;
}
BeanModule rhs = (BeanModule) obj;
EqualsBuilder equalsBuilder = new EqualsBuilder().appendSuper(super.equals(obj));
if (moduleObject != null) {
equalsBuilder.append(moduleObject, rhs.moduleObject);
}
return equalsBuilder.isEquals();
}
/**
* Gets the componentDescriptor.
*
* @return the componentDescriptor.
*/
@SuppressWarnings("unchecked")
public IComponentDescriptor<?> getComponentDescriptor() {
if (componentDescriptor == null) {
if (getProjectedViewDescriptor() != null
&& getProjectedViewDescriptor().getModelDescriptor() instanceof IComponentDescriptorProvider<?>) {
return ((IComponentDescriptorProvider<Object>) getProjectedViewDescriptor()
.getModelDescriptor()).getComponentDescriptor();
}
}
return componentDescriptor;
}
/**
* {@inheritDoc}
*/
@Override
public Collection<String> getGrantedRoles() {
Collection<String> grantedRoles = super.getGrantedRoles();
if (grantedRoles == null && componentDescriptor != null) {
grantedRoles = componentDescriptor.getGrantedRoles();
}
return grantedRoles;
}
/**
* Gets the module's projected object.
*
* @return the projected object.
*/
public Object getModuleObject() {
return moduleObject;
}
/**
* {@inheritDoc}
*/
@Override
public IViewDescriptor getProjectedViewDescriptor() {
IViewDescriptor projectedViewDescriptor = super
.getProjectedViewDescriptor();
if (componentDescriptor != null) {
if (projectedViewDescriptor == null) {
projectedViewDescriptor = createDefaultProjectedViewDescriptor();
setProjectedViewDescriptor(projectedViewDescriptor);
}
if (projectedViewDescriptor.getModelDescriptor() == null
&& projectedViewDescriptor instanceof BasicViewDescriptor) {
((BasicViewDescriptor) projectedViewDescriptor)
.setModelDescriptor(componentDescriptor);
}
}
return projectedViewDescriptor;
}
/**
* Create default projected view descriptor.
*
* @return the view descriptor
*/
protected IViewDescriptor createDefaultProjectedViewDescriptor() {
BasicComponentViewDescriptor defaultProjectedViewDescriptor = new BasicComponentViewDescriptor();
defaultProjectedViewDescriptor.setModelDescriptor(componentDescriptor);
defaultProjectedViewDescriptor.setBorderType(EBorderType.TITLED);
defaultProjectedViewDescriptor.setName(componentDescriptor.getName());
defaultProjectedViewDescriptor.setColumnCount(3);
return defaultProjectedViewDescriptor;
}
/**
* Returns the projectedViewDescriptor nested in a "moduleObject" property
* view.
* <p>
* {@inheritDoc}
*/
@Override
public IViewDescriptor getViewDescriptor() {
if (getProjectedViewDescriptor() != null) {
BeanModuleDescriptor beanModuleDescriptor = getDescriptor();
BasicBorderViewDescriptor nestingViewDescriptor = new BasicBorderViewDescriptor();
nestingViewDescriptor
.setCenterViewDescriptor(getProjectedViewDescriptor());
nestingViewDescriptor.setModelDescriptor(beanModuleDescriptor
.getPropertyDescriptor(MODULE_OBJECT));
BasicBorderViewDescriptor viewDescriptor = new BasicBorderViewDescriptor();
viewDescriptor.setModelDescriptor(beanModuleDescriptor);
viewDescriptor.setCenterViewDescriptor(nestingViewDescriptor);
return viewDescriptor;
}
return null;
}
/**
* Hash code based on projected object.
* <p>
* {@inheritDoc}
*/
@Override
public int hashCode() {
return new HashCodeBuilder(23, 53).append(getModuleObject()).toHashCode();
}
/**
* {@inheritDoc}
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
String oldI18nName = getI18nName();
setI18nName(String.valueOf(this.moduleObject));
firePropertyChange(I18N_NAME, oldI18nName, getI18nName());
}
/**
* Configures the type of bean this module manages. A bunch of default values
* are inferred from this component descriptor. For instance, icon image URL
* or even granted roles can be inferred from the configured component
* descriptor. The latter means that bean modules based on forbidden entities
* will automatically be excluded from the workspace of the logged-in user.
* <p>
* However, when not set, the component descriptor it self can be inferred from
* the configured projected view descriptor model.
*
* @param componentDescriptor
* the componentDescriptor to set.
*/
public void setComponentDescriptor(
IComponentDescriptor<Object> componentDescriptor) {
this.componentDescriptor = componentDescriptor;
}
/**
* Assigns the bean this module manages. The projected view will automatically
* reflect this change since a "moduleObject" property change will
* be fired.
*
* @param moduleObject
* the projected object.
*/
public void setModuleObject(Object moduleObject) {
if (ObjectUtils.equals(this.moduleObject, moduleObject)) {
return;
}
String toStringProperty = getComponentDescriptor().getToStringProperty();
if (getName() == null
&& this.moduleObject instanceof IPropertyChangeCapable) {
if (toStringProperty != null) {
((IPropertyChangeCapable) this.moduleObject)
.removePropertyChangeListener(toStringProperty, this);
} else {
((IPropertyChangeCapable) this.moduleObject)
.removePropertyChangeListener(this);
}
}
Object oldValue = getModuleObject();
this.moduleObject = moduleObject;
if (getName() == null
&& this.moduleObject instanceof IPropertyChangeCapable) {
if (toStringProperty != null) {
((IPropertyChangeCapable) this.moduleObject)
.addPropertyChangeListener(toStringProperty, this);
} else {
((IPropertyChangeCapable) this.moduleObject)
.addPropertyChangeListener(this);
}
}
firePropertyChange(MODULE_OBJECT, oldValue, getModuleObject());
}
/**
* Gets the module descriptor.
*
* @return the module descriptor.
*/
protected BeanModuleDescriptor getDescriptor() {
return new BeanModuleDescriptor(getComponentDescriptor());
}
/**
* {@inheritDoc}
*/
@Override
public BeanModule clone() {
BeanModule clone = (BeanModule) super.clone();
clone.moduleObject = null;
return clone;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isLocallyDirty(IBackendController backendController) {
boolean locallyDirty = backendController
.isAnyDirtyInDepth(Collections.singleton(getModuleObject()));
return locallyDirty;
}
}