/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.foundation.ie.cl;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InvalidNameException;
import org.openflexo.foundation.AttributeDataModification;
import org.openflexo.foundation.FlexoObserver;
import org.openflexo.foundation.bindings.Bindable;
import org.openflexo.foundation.bindings.BindingDefinition;
import org.openflexo.foundation.bindings.BindingModel;
import org.openflexo.foundation.bindings.BindingVariable;
import org.openflexo.foundation.bindings.ComponentBindingDefinition;
import org.openflexo.foundation.dm.ComponentDMEntity;
import org.openflexo.foundation.dm.ComponentRepository;
import org.openflexo.foundation.dm.DMCardinality;
import org.openflexo.foundation.dm.DMEntity;
import org.openflexo.foundation.dm.DMModel;
import org.openflexo.foundation.dm.DMProperty;
import org.openflexo.foundation.dm.DMPropertyImplementationType;
import org.openflexo.foundation.dm.DMType;
import org.openflexo.foundation.dm.DMType.KindOfType;
import org.openflexo.foundation.dm.DuplicateClassNameException;
import org.openflexo.foundation.ie.ComponentInstance;
import org.openflexo.foundation.ie.DummyComponentInstance;
import org.openflexo.foundation.ie.IEObject;
import org.openflexo.foundation.ie.IERegExp;
import org.openflexo.foundation.ie.IEWOComponent;
import org.openflexo.foundation.ie.IObject;
import org.openflexo.foundation.ie.dm.ComponentDeleteRequest;
import org.openflexo.foundation.ie.dm.ComponentDeleted;
import org.openflexo.foundation.ie.dm.ComponentLoaded;
import org.openflexo.foundation.ie.dm.ComponentNameChanged;
import org.openflexo.foundation.ie.dm.ComponentNameChanged2;
import org.openflexo.foundation.rm.DuplicateResourceException;
import org.openflexo.foundation.rm.FlexoComponentResource;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.foundation.utils.FlexoIndexManager;
import org.openflexo.foundation.utils.FlexoProgress;
import org.openflexo.foundation.utils.Sortable;
import org.openflexo.foundation.validation.CompoundIssue;
import org.openflexo.foundation.validation.FixProposal;
import org.openflexo.foundation.validation.Validable;
import org.openflexo.foundation.validation.ValidationError;
import org.openflexo.foundation.validation.ValidationIssue;
import org.openflexo.foundation.validation.ValidationModel;
import org.openflexo.foundation.validation.ValidationReport;
import org.openflexo.foundation.validation.ValidationRule;
import org.openflexo.foundation.wkf.dm.ChildrenOrderChanged;
import org.openflexo.foundation.wkf.node.OperationNode;
import org.openflexo.foundation.xml.FlexoComponentLibraryBuilder;
import org.openflexo.inspector.InspectableObject;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.toolbox.ReservedKeyword;
/**
* Abstract class representing a component, but only the definition, not the component itself (no need to load the component to handle a
* ComponentDefinition)
*
* @author sguerin
*
*/
public abstract class ComponentDefinition extends IECLObject implements InspectableObject, Serializable, FlexoObserver, Bindable, Sortable {
public static final ComponentComparator COMPARATOR = new ComponentComparator();
public static class ComponentComparator implements Comparator<ComponentDefinition> {
protected ComponentComparator() {
}
/**
* Overrides compare
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(ComponentDefinition o1, ComponentDefinition o2) {
return o1.getName().compareTo(o2.getName());
}
}
private static final Logger logger = Logger.getLogger(ComponentDefinition.class.getPackage().getName());
private Vector<ComponentInstance> componentInstances = new Vector<ComponentInstance>();
private DummyComponentInstance _dummyComponentInstance;
private ComponentDMEntity _componentDMEntity = null;
protected FlexoComponentFolder _folder;
protected String _componentName;
private String _input;
private String _behavior;
private boolean hasTabContainer = false;
private int index = -1;
protected ComponentDefinition(FlexoComponentLibrary componentLibrary) {
super(componentLibrary);
}
protected ComponentDefinition(String aComponentName, FlexoComponentLibrary componentLibrary, FlexoComponentFolder aFolder,
FlexoProject prj) throws DuplicateResourceException {
this(componentLibrary);
_componentName = aComponentName;
createsComponentDMEntityIfRequired();
if (aFolder != null) {
aFolder.addToComponents(this);
}
componentLibrary.handleNewComponentCreated(this);
}
public ComponentDefinition(FlexoComponentLibraryBuilder builder) {
this(builder.componentLibrary);
}
@SuppressWarnings("unchecked")
public DummyComponentInstance getDummyComponentInstance() {
if (_dummyComponentInstance == null) {
return _dummyComponentInstance = new DummyComponentInstance(this);
}
return _dummyComponentInstance;
}
@Override
public String getFullyQualifiedName() {
return "COMPONENT." + getName();
}
@Override
public String getName() {
return _componentName;
}
@Override
public void setName(String aName) throws DuplicateResourceException, DuplicateClassNameException, InvalidNameException {
setComponentName(aName);
}
@Override
public IEObject getParent() {
return getFolder();
}
public FlexoComponentFolder getFolder() {
return _folder;
}
public void setFolder(FlexoComponentFolder aFolder) {
_folder = aFolder;
if (!isDeserializing()) {
if (_folder == null) {
this.index = -1;
} else {
this.index = _folder.getComponents().size();
}
}
}
public String getComponentPrefix() {
return _folder.getComponentPrefix();
}
public IEWOComponent getWOComponent() {
return getWOComponent(null);
}
public IEWOComponent getWOComponent(FlexoProgress progress) {
return getComponentResource().getResourceData(progress);
}
public String getComponentName() {
return _componentName;
}
public void setComponentName(String name) throws DuplicateResourceException, DuplicateClassNameException, InvalidNameException {
if (_componentName != null && !_componentName.equals(name) && name != null && !isDeserializing()) {
if (!name.matches(IERegExp.JAVA_CLASS_NAME_REGEXP)) {
throw new InvalidNameException();
}
if (ReservedKeyword.contains(name)) {
throw new InvalidNameException();
}
if (getProject() != null) {
ComponentDefinition cd = getComponentLibrary().getComponentNamed(name);
if (cd != null && cd != this) {
throw new DuplicateResourceException(getComponentResource());
}
DMEntity e = getProject().getDataModel().getEntityNamed(name);
if (e != null && e != getComponentDMEntity()) {
throw new DuplicateClassNameException(name);
}
FlexoComponentResource resource = getComponentResource();
if (resource != null) {
if (logger.isLoggable(Level.INFO)) {
logger.info("Renaming component resource !");
}
try {
getProject().renameResource(resource, name);
} catch (DuplicateResourceException e1) {
throw e1;
}
}
if (getComponentDMEntity() != null) {
getComponentDMEntity().setEntityClassName(name);
getComponentDMEntity().setName(name);
}
String oldComponentName = _componentName;
_componentName = name;
createsComponentDMEntityIfRequired();
setChanged();
notifyObservers(new ComponentNameChanged("name", this, oldComponentName, name));
// After that, launch this notification for the CG resources
setChanged();
notifyObservers(new ComponentNameChanged2("name", this, oldComponentName, name));
}
} else {
_componentName = name;
createsComponentDMEntityIfRequired();
}
}
protected ComponentDMEntity createsComponentDMEntityIfRequired() {
return getComponentDMEntity();
}
public ComponentDMEntity getComponentDMEntity() {
if (_componentDMEntity == null || _componentDMEntity.isDeleted()) {
if (getProject() != null) {
DMModel dmModel = getProject().getDataModel();
ComponentRepository componentRepository = dmModel.getComponentRepository();
_componentDMEntity = componentRepository.getComponentDMEntity(this);
if (_componentDMEntity == null && _componentName != null && !_componentName.trim().equals("")) {
if (logger.isLoggable(Level.INFO)) {
logger.info("Creates entry in ComponentRepository");
}
_componentDMEntity = new ComponentDMEntity(getProject().getDataModel(), this);
getProject().getDataModel().getComponentRepository().registerEntity(_componentDMEntity);
}
if (_componentDMEntity != null) {
_componentDMEntity.setComponentDefinition(this);
}
}
}
return _componentDMEntity;
}
public String requestDeletion() {
setChanged();
ComponentDeleteRequest request = new ComponentDeleteRequest(this);
setChanged();
notifyObservers(request);
int count = 0;
if (request.hasWarnings()) {
StringBuffer buffer = new StringBuffer();
Enumeration en = request.warnings().elements();
while (en.hasMoreElements()) {
buffer.append((String) en.nextElement()).append("\n");
count++;
if (count > 9 && en.hasMoreElements()) {
buffer.append("and " + (request.warnings().size() - 10) + " more...\n");
break;
}
}
return buffer.toString();
} else {
return null;
}
}
@Override
public void delete() {
if (getFolder() != null) {
getFolder().removeFromComponents(this);
}
if (logger.isLoggable(Level.INFO)) {
logger.info("Removing component !");
}
if (hasComponentResource() && getWOComponent() != null) {
getWOComponent().delete(false);
}
if (_componentDMEntity != null) {
_componentDMEntity.delete();
}
setChanged();
notifyObservers(new ComponentDeleted(this));
super.delete();
deleteObservers();
}
public abstract IEWOComponent createNewComponent();
public boolean hasComponentResource() {
return getComponentResource(false) != null;
}
/**
* This method is final because if you want to override the default behaviour, you should instead override the method
* {@link #getComponentResource(boolean)}
*
* @return
*/
public final FlexoComponentResource getComponentResource() {
return getComponentResource(true);
}
public abstract FlexoComponentResource getComponentResource(boolean createIfNotExists);
public void notifyWOComponentHasBeenLoaded() {
setChanged(false);
if (logger.isLoggable(Level.FINE)) {
logger.fine("Notify observers that WO has been loaded");
}
notifyObservers(new ComponentLoaded(this));
}
public boolean isLoaded() {
return hasComponentResource() && getComponentResource().isLoaded();
}
/**
* Return a Vector of embedded IEObjects at this level. NOTE that this is NOT a recursive method
*
* @return a Vector of IEObject instances
*/
@Override
public Vector<IObject> getEmbeddedIEObjects() {
Vector<IObject> answer = new Vector<IObject>();
answer.add(this);
return answer;
}
@Override
public Vector<Validable> getAllEmbeddedValidableObjects() {
Vector<Validable> v = super.getAllEmbeddedValidableObjects();
v.addAll(getWOComponent().getAllEmbeddedValidableObjects());
return v;
}
@Override
public ValidationModel getDefaultValidationModel() {
if (getProject() != null) {
return getProject().getIEValidationModel();
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not access to project !");
}
}
return null;
}
/**
* Returns a flag indicating if this object is valid according to default validation model
*
* @return boolean
*/
@Override
public boolean isValid() {
return isValid(getDefaultValidationModel());
}
/**
* Returns a flag indicating if this object is valid according to specified validation model
*
* @return boolean
*/
@Override
public boolean isValid(ValidationModel validationModel) {
return validationModel.isValid(this);
}
/**
* Validates this object by building new ValidationReport object Default validation model is used to perform this validation.
*/
@Override
public ValidationReport validate() {
return validate(getDefaultValidationModel());
}
/**
* Validates this object by building new ValidationReport object Supplied validation model is used to perform this validation.
*/
@Override
public ValidationReport validate(ValidationModel validationModel) {
return validationModel.validate(this);
}
/**
* Validates this object by appending eventual issues to supplied ValidationReport. Default validation model is used to perform this
* validation.
*
* @param report
* , a ValidationReport object on which found issues are appened
*/
@Override
public void validate(ValidationReport report) {
validate(report, getDefaultValidationModel());
}
/**
* Validates this object by appending eventual issues to supplied ValidationReport. Supplied validation model is used to perform this
* validation.
*
* @param report
* , a ValidationReport object on which found issues are appened
*/
@Override
public void validate(ValidationReport report, ValidationModel validationModel) {
validationModel.validate(this, report);
}
/**
* Returns all the binding definitions for this component
*
* @return a vector of ComponentBindingDefinition
*/
public Vector<ComponentBindingDefinition> getBindingDefinitions() {
if (getComponentDMEntity() != null) {
return getComponentDMEntity().getBindingDefinitions();
}
return null;
}
private Vector<ComponentBindingDefinition> getMandatoryBindingDefinitions() {
Enumeration<ComponentBindingDefinition> en = getBindingDefinitions().elements();
Vector<ComponentBindingDefinition> answer = new Vector<ComponentBindingDefinition>();
while (en.hasMoreElements()) {
ComponentBindingDefinition compDef = en.nextElement();
if (compDef.getIsMandatory()) {
answer.add(compDef);
}
}
return answer;
}
public void notifyComponentDefinitionsHasChanged() {
for (ComponentInstance ci : getComponentInstances()) {
ci.setChanged();
}
}
public boolean isAssignableFromDA() {
Vector mandatoryBD = getMandatoryBindingDefinitions();
if (mandatoryBD.size() == 0) {
return true;
}
Enumeration en = mandatoryBD.elements();
while (en.hasMoreElements()) {
ComponentBindingDefinition cbd = (ComponentBindingDefinition) en.nextElement();
if (cbd.getType() == null || !cbd.getType().isEOEntity() || !daEntitiesContainsType(getProject(), cbd.getType())) {
return false;
}
}
return true;
}
public boolean isOperation() {
return this instanceof OperationComponentDefinition;
}
public boolean isPopup() {
return this instanceof PopupComponentDefinition;
}
public boolean isTab() {
return this instanceof TabComponentDefinition;
}
public boolean isPage() {
return true;
}
private static boolean daEntitiesContainsType(FlexoProject project, DMType type) {
return type.getBaseEntity() == project.getDataModel().getEntityNamed(String.class.getName())
|| type.getBaseEntity() == project.getDataModel().getEntityNamed(Integer.class.getName());
}
public ComponentBindingDefinition bindingDefinitionNamed(String aName) {
if (getComponentDMEntity() != null) {
return getComponentDMEntity().bindingDefinitionNamed(aName);
}
return null;
}
/*
* Commented because bindings are no more stored on the ComponentDefinition public void notifyBindingDefinitionAdded(BindingDefinition
* bindingDefinition) { setChanged(); notifyObservers(new BindingAdded(bindingDefinition)); }
*
* public void notifyBindingDefinitionRemoved(BindingDefinition bindingDefinition) { setChanged(); notifyObservers(new
* BindingRemoved(bindingDefinition)); }
*/
private BindingModel _bindingModel = null;
@Override
public BindingModel getBindingModel() {
if (_bindingModel == null) {
if (getComponentDMEntity() != null) {
_bindingModel = new ComponentDefinitionBindingModel();
}
}
return _bindingModel;
}
/**
*
* Binding model for a component definition
*
* @author sguerin
*/
public class ComponentDefinitionBindingModel extends BindingModel {
public static final String COMPONENT_BINDING_VARIABLE_NAME = "component";
private BindingVariable _componentVariable;
public ComponentDefinitionBindingModel() {
super();
_componentVariable = new BindingVariable(ComponentDefinition.this, getProject().getDataModel(),
FlexoLocalization.localizedForKey("access_to_the_current_component"));
_componentVariable.setVariableName(COMPONENT_BINDING_VARIABLE_NAME);
_componentVariable.setType(DMType.makeResolvedDMType(getComponentDMEntity()));
}
@Override
public int getBindingVariablesCount() {
return 1;
}
@Override
public BindingVariable getBindingVariableAt(int index) {
return getComponentBindingVariable();
}
public BindingVariable getComponentBindingVariable() {
return _componentVariable;
}
@Override
public boolean allowsNewBindingVariableCreation() {
return false;
}
}
/**
*
*/
public ComponentBindingDefinition createNewBinding() {
if (getComponentDMEntity() != null) {
String newPropertyName = getProject().getDataModel().getNextDefautPropertyName(getComponentDMEntity());
DMProperty newProperty = new DMProperty(getProject().getDataModel(), /* getComponentDMEntity(), */newPropertyName, null,
DMCardinality.SINGLE, false, true, DMPropertyImplementationType.PUBLIC_ACCESSORS_PRIVATE_FIELD);
boolean success = getComponentDMEntity().registerProperty(newProperty, true);
ComponentBindingDefinition reply = getComponentDMEntity().bindingDefinitionForProperty(newProperty);
if (success && reply != null) {
getComponentDMEntity().addToMandatoryBindingProperties(newProperty);
}
return reply;
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not create binding: cannot access ComponentDMEntity !");
}
return null;
}
}
public void deleteBinding(ComponentBindingDefinition bd) {
if (bd != null && bd.getProperty() != null) {
bd.getProperty().delete();
}
}
public boolean isBindingDefinitionDeletable(ComponentBindingDefinition bd) {
return true;
}
public String getBehavior() {
return _behavior;
}
public void setBehavior(String behavior) {
_behavior = behavior;
}
public String getInput() {
return _input;
}
public void setInput(String input) {
_input = input;
}
public String getNameRegexp() {
return IERegExp.JAVA_CLASS_NAME_REGEXP;
}
@Override
public String toString() {
return getComponentName();
}
public boolean getHasTabContainer() {
return hasTabContainer;
}
public void setHasTabContainer(boolean hasTabContainer) {
this.hasTabContainer = hasTabContainer;
}
public ComponentBindingDefinition getBindingDefinitionTyped(DMEntity type) {
return getBindingDefinitionTyped(DMType.makeResolvedDMType(type));
}
public ComponentBindingDefinition getBindingDefinitionTyped(DMType type) {
Enumeration<ComponentBindingDefinition> en = getBindingDefinitions().elements();
while (en.hasMoreElements()) {
ComponentBindingDefinition bd = en.nextElement();
if (bd.getIsMandatory() && bd.getType() != null && bd.getType().equals(type)) {
return bd;
}
}
return null;
}
public boolean isIndexed() {
return this.index > -1;
}
@Override
public int getIndex() {
if (isBeingCloned()) {
return -1;
}
if (index == -1 && getCollection() != null) {
index = getCollection().length;
FlexoIndexManager.reIndexObjectOfArray(getCollection());
}
return index;
}
@Override
public void setIndex(int index) {
if (isDeserializing() || isCreatedByCloning()) {
setIndexValue(index);
return;
}
FlexoIndexManager.switchIndexForKey(this.index, index, this);
if (getIndex() != index) {
setChanged();
AttributeDataModification dm = new AttributeDataModification("index", null, getIndex());
dm.setReentrant(true);
notifyObservers(dm);
}
}
@Override
public int getIndexValue() {
return getIndex();
}
@Override
public void setIndexValue(int index) {
if (this.index == index) {
return;
}
int old = this.index;
this.index = index;
setChanged();
notifyModification("index", old, index);
if (!isDeserializing() && !isCreatedByCloning() && getFolder() != null) {
getFolder().setChanged();
getFolder().notifyObservers(new ChildrenOrderChanged());
}
}
/**
* Overrides getCollection
*
* @see org.openflexo.foundation.utils.Sortable#getCollection()
*/
@Override
public ComponentDefinition[] getCollection() {
if (getFolder() == null) {
return null;
}
return getFolder().getComponents().toArray(new ComponentDefinition[0]);
}
public Vector<ComponentInstance> getComponentInstances() {
return componentInstances;
}
public void addToComponentInstances(ComponentInstance instance) {
addToComponentInstances(instance, true);
}
public void addToComponentInstances(ComponentInstance instance, boolean notify) {
if (!componentInstances.contains(instance) && !(instance instanceof DummyComponentInstance)) {
if (!instance.getXMLResourceData().getFlexoXMLFileResource().isConverting()) {
componentInstances.add(instance);
if (notify) {
setChanged();
}
} else if (logger.isLoggable(Level.WARNING)) {
logger.warning("Found resource converting, ignoring component instance");
}
}
}
public void removeFromComponentInstances(ComponentInstance instance) {
if (componentInstances.remove(instance)) {
setChanged();
}
}
abstract public List<OperationNode> getAllOperationNodesLinkedToThisComponent();
// =============================================================
// ======================== Validation =========================
// =============================================================
public static class BindingsMustDefineType<CD extends ComponentDefinition, CI extends ComponentInstance> extends
ValidationRule<BindingsMustDefineType<CD, CI>, CD> {
/**
* @param objectType
* @param ruleName
*/
public BindingsMustDefineType() {
super(ComponentDefinition.class, "binding_must_define_a_type");
}
/**
* Overrides applyValidation
*
* @see org.openflexo.foundation.validation.ValidationRule#applyValidation(org.openflexo.foundation.validation.Validable)
*/
@Override
public ValidationIssue<BindingsMustDefineType<CD, CI>, CD> applyValidation(CD cd) {
CompoundIssue<BindingsMustDefineType<CD, CI>, CD> issues = null;
for (ComponentBindingDefinition cbd : cd.getBindingDefinitions()) {
DMProperty p = cbd.getProperty();
if (p.getType() == null || p.getType().getKindOfType() == KindOfType.UNRESOLVED
&& (p.getType().getName() == null || p.getType().getName().equals("null"))) {
Vector<FixProposal<BindingsMustDefineType<CD, CI>, CD>> fixes = new Vector<FixProposal<BindingsMustDefineType<CD, CI>, CD>>();
fixes.add(new SetType<BindingsMustDefineType<CD, CI>, CD, CI>(cbd, DMType.makeResolvedDMType(p.getDMModel()
.getDMEntity(String.class))));
fixes.add(new SetType<BindingsMustDefineType<CD, CI>, CD, CI>(cbd, DMType.makeResolvedDMType(p.getDMModel()
.getDMEntity(Boolean.class))));
fixes.add(new SetType<BindingsMustDefineType<CD, CI>, CD, CI>(cbd, DMType.makeResolvedDMType(p.getDMModel()
.getDMEntity(Object.class))));
fixes.add(new DeleteBinding<BindingsMustDefineType<CD, CI>, CD, CI>(cbd));
if (issues == null) {
issues = new CompoundIssue<BindingsMustDefineType<CD, CI>, CD>(cd);
}
issues.addToContainedIssues(new ValidationError<BindingsMustDefineType<CD, CI>, CD>(this, cd,
"binding_must_define_a_type", fixes));
}
}
return issues;
}
}
public static class DeleteBinding<V extends ValidationRule<V, CD>, CD extends ComponentDefinition, CI extends ComponentInstance>
extends FixProposal<V, CD> {
private ComponentBindingDefinition binding;
public DeleteBinding(ComponentBindingDefinition def) {
super("delete_binding_($binding.variableName)");
this.binding = def;
}
@Override
protected void fixAction() {
((ComponentDefinition) getObject()).deleteBinding(binding);
}
public BindingDefinition getBinding() {
return binding;
}
}
/**
* @author gpolet
*
*/
public static class SetType<V extends ValidationRule<V, CD>, CD extends ComponentDefinition, CI extends ComponentInstance> extends
FixProposal<V, CD> {
private DMType type;
private ComponentBindingDefinition binding;
/**
* @param aMessage
*/
public SetType(ComponentBindingDefinition cbd, DMType type) {
super("set_type_to_($type)");
this.type = type;
this.binding = cbd;
}
/**
* Overrides fixAction
*
* @see org.openflexo.foundation.validation.FixProposal#fixAction()
*/
@Override
protected void fixAction() {
binding.getProperty().setType(type);
}
public DMType getType() {
return type;
}
}
}