/*
* (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.fib.model;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Observable;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.antar.binding.Bindable;
import org.openflexo.antar.binding.BindingDefinition;
import org.openflexo.antar.binding.BindingFactory;
import org.openflexo.antar.binding.BindingModel;
import org.openflexo.fib.FIBLibrary;
import org.openflexo.fib.model.validation.FixProposal;
import org.openflexo.fib.model.validation.ProblemIssue;
import org.openflexo.fib.model.validation.ValidationError;
import org.openflexo.fib.model.validation.ValidationIssue;
import org.openflexo.fib.model.validation.ValidationReport;
import org.openflexo.fib.model.validation.ValidationRule;
import org.openflexo.fib.model.validation.ValidationWarning;
import org.openflexo.fib.utils.LocalizedDelegateGUIImpl;
import org.openflexo.toolbox.FileResource;
import org.openflexo.toolbox.StringUtils;
import org.openflexo.xmlcode.KeyValueDecoder;
import org.openflexo.xmlcode.XMLSerializable;
public abstract class FIBModelObject extends Observable implements Bindable, XMLSerializable {
private static final Logger logger = Logger.getLogger(FIBModelObject.class.getPackage().getName());
// Instanciate a new localizer in directory src/dev/resources/FIBLocalizer
// Little hack to be removed: linked to parent localizer (which is Openflexo main localizer)
public static LocalizedDelegateGUIImpl LOCALIZATION = new LocalizedDelegateGUIImpl(new FileResource("FIBLocalized"),
new LocalizedDelegateGUIImpl(new FileResource("Localized"), null, false), true);
public static interface FIBModelAttribute {
public String name();
}
public static enum Parameters implements FIBModelAttribute {
name, description, parameters
}
private String name;
private String description;
private Vector<FIBParameter> parameters = new Vector<FIBParameter>();
public FIBModelObject() {
super();
}
public void delete() {
}
public String getName() {
return name;
}
public void setName(String name) {
FIBAttributeNotification<String> notification = requireChange(Parameters.name, name);
if (notification != null) {
this.name = name;
hasChanged(notification);
}
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
FIBAttributeNotification<String> notification = requireChange(Parameters.description, description);
if (notification != null) {
this.description = description;
hasChanged(notification);
}
}
public String getParameter(String parameterName) {
for (FIBParameter p : parameters) {
if (parameterName.equals(p.getName())) {
return p.getValue();
}
}
return null;
}
public Vector<FIBParameter> getParameters() {
return parameters;
}
public void setParameters(Vector<FIBParameter> parameters) {
FIBAttributeNotification<Vector<FIBParameter>> notification = requireChange(Parameters.parameters, parameters);
if (notification != null) {
this.parameters = parameters;
hasChanged(notification);
}
}
public void addToParameters(FIBParameter p) {
parameters.add(p);
setChanged();
notifyObservers(new FIBAddingNotification<FIBParameter>(Parameters.parameters, p));
}
public void removeFromParameters(FIBParameter p) {
parameters.remove(p);
setChanged();
notifyObservers(new FIBRemovingNotification<FIBParameter>(Parameters.parameters, p));
}
public FIBParameter createNewParameter() {
FIBParameter returned = new FIBParameter("param", "value");
addToParameters(returned);
System.out.println("getParameters()=" + getParameters());
return returned;
}
public void deleteParameter(FIBParameter p) {
removeFromParameters(p);
}
public boolean isParameterAddable() {
return true;
}
public boolean isParameterDeletable(FIBParameter p) {
return true;
}
public abstract FIBComponent getRootComponent();
@Override
public BindingModel getBindingModel() {
if (getRootComponent() != null && getRootComponent() != this) {
return getRootComponent().getBindingModel();
}
return null;
}
@Override
public BindingFactory getBindingFactory() {
return FIBLibrary.instance().getBindingFactory();
}
public void initializeDeserialization() {
}
public void finalizeDeserialization() {
}
// *******************************************************************************
// * Utils *
// *******************************************************************************
protected <T extends Object> void notifyChange(FIBModelAttribute parameterKey, T oldValue, T newValue) {
// Never notify unchanged values
if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
return;
}
setChanged();
notifyObservers(new FIBAttributeNotification<T>(parameterKey, oldValue, newValue));
}
protected void notifyChange(FIBModelAttribute parameterKey) {
setChanged();
notifyObservers(new FIBAttributeNotification(parameterKey, null, null));
}
protected <T extends Object> void notifyChange(String parameterName, T oldValue, T newValue) {
setChanged();
notifyObservers(new FIBModelNotification<T>(parameterName, oldValue, newValue));
}
protected <T extends Object> FIBAttributeNotification<T> requireChange(FIBModelAttribute parameterKey, T value) {
T oldValue = (T) KeyValueDecoder.objectForKey(this, ((Enum<?>) parameterKey).name());
if (oldValue == null) {
if (value == null) {
return null; // No change
} else {
return new FIBAttributeNotification<T>(parameterKey, oldValue, value);
}
} else {
if (oldValue.equals(value)) {
return null; // No change
} else {
return new FIBAttributeNotification<T>(parameterKey, oldValue, value);
}
}
}
public void notify(FIBModelNotification notification) {
hasChanged(notification);
}
protected void hasChanged(FIBModelNotification notification) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Change attribute " + notification.getAttributeName() + " for object " + this + " was: " + notification.oldValue()
+ " is now: " + notification.newValue());
}
setChanged();
notifyObservers(notification);
}
public void notifyBindingChanged(DataBinding binding) {
}
public static boolean equals(Object o1, Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null) {
return o2 == null;
} else {
return o1.equals(o2);
}
}
public static boolean notEquals(Object o1, Object o2) {
return !equals(o1, o2);
}
public final boolean isValid() {
ValidationReport report = validate();
return report.getErrorNb() == 0;
}
public final ValidationReport validate() {
ValidationReport returned = new ValidationReport(this);
validate(returned);
return returned;
}
protected final void validate(ValidationReport report) {
applyValidation(report);
if (getEmbeddedObjects() != null) {
for (FIBModelObject o : getEmbeddedObjects()) {
o.validate(report);
}
}
}
protected void applyValidation(ValidationReport report) {
performValidation(FIBModelObjectShouldHaveAUniqueName.class, report);
}
private static final Hashtable<Class<?>, ValidationRule<?, ?>> rules = new Hashtable<Class<?>, ValidationRule<?, ?>>();
private static <R extends ValidationRule<R, C>, C extends FIBModelObject> R getRule(Class<R> validationRuleClass) {
R returned = (R) rules.get(validationRuleClass);
if (returned == null) {
Constructor<R> c = (Constructor<R>) validationRuleClass.getConstructors()[0];
try {
returned = c.newInstance(null);
rules.put(validationRuleClass, returned);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return returned;
}
/*protected final <R extends ValidationRule<R, C>, C extends FIBModelObject> void performValidation(Class<R> validationRuleClass,
ValidationReport report) {
R rule = getRule(validationRuleClass);
ValidationIssue<R, C> issue = rule.applyValidation((C) this);
if (issue != null) {
report.addToValidationIssues(issue);
}
}*/
protected final <R extends ValidationRule> void performValidation(Class<R> validationRuleClass, ValidationReport report) {
R rule = (R) getRule(validationRuleClass);
ValidationIssue issue = rule.applyValidation(this);
if (issue != null) {
report.addToValidationIssues(issue);
}
}
protected String generateUniqueName(String baseName) {
String currentName = baseName;
int i = 2;
while (isNameUsedInHierarchy(currentName)) {
currentName = baseName + i;
i++;
}
return currentName;
}
protected String getBaseName() {
return null;
}
public boolean isNameUsedInHierarchy(String aName) {
return isNameUsedInHierarchy(aName, getRootComponent());
}
private static boolean isNameUsedInHierarchy(String aName, FIBModelObject object) {
if (object.getName() != null && object.getName().equals(aName)) {
return true;
}
if (object.getEmbeddedObjects() != null) {
for (FIBModelObject o : object.getEmbeddedObjects()) {
if (isNameUsedInHierarchy(aName, o)) {
return true;
}
}
}
return false;
}
public List<FIBModelObject> getObjectsWithName(String aName) {
return retrieveObjectsWithName(aName, getRootComponent(), new ArrayList<FIBModelObject>());
}
private static List<FIBModelObject> retrieveObjectsWithName(String aName, FIBModelObject object, List<FIBModelObject> list) {
if (object.getName() != null && object.getName().equals(aName)) {
list.add(object);
}
if (object.getEmbeddedObjects() != null) {
for (FIBModelObject o : object.getEmbeddedObjects()) {
retrieveObjectsWithName(aName, o, list);
}
}
return list;
}
public abstract List<? extends FIBModelObject> getEmbeddedObjects();
public static class FIBModelObjectShouldHaveAUniqueName extends ValidationRule<FIBModelObjectShouldHaveAUniqueName, FIBModelObject> {
public FIBModelObjectShouldHaveAUniqueName() {
super(FIBModelObject.class, "object_should_not_have_duplicated_name");
}
@Override
public ValidationIssue<FIBModelObjectShouldHaveAUniqueName, FIBModelObject> applyValidation(FIBModelObject object) {
if (StringUtils.isNotEmpty(object.getName())) {
List<FIBModelObject> allObjectsWithThatName = object.getObjectsWithName(object.getName());
if (allObjectsWithThatName.size() > 1) {
allObjectsWithThatName.remove(object);
GenerateUniqueName fixProposal = new GenerateUniqueName();
ProblemIssue<FIBModelObjectShouldHaveAUniqueName, FIBModelObject> returned;
if (object instanceof FIBWidget && ((FIBWidget) object).getManageDynamicModel()) {
returned = new ValidationError<FIBModelObjectShouldHaveAUniqueName, FIBModelObject>(this, object,
"object_($object.toString)_has_duplicated_name", fixProposal);
} else {
returned = new ValidationWarning<FIBModelObjectShouldHaveAUniqueName, FIBModelObject>(this, object,
"object_($object.toString)_has_duplicated_name", fixProposal);
}
returned.addToRelatedValidableObjects(allObjectsWithThatName);
return returned;
}
}
return null;
}
protected static class GenerateUniqueName extends FixProposal<FIBModelObjectShouldHaveAUniqueName, FIBModelObject> {
public GenerateUniqueName() {
super("generate_unique_name_:_($uniqueName)");
}
@Override
protected void fixAction() {
getObject().setName(getUniqueName());
}
public String getUniqueName() {
return getObject().generateUniqueName(getObject().getBaseName());
}
}
}
public static abstract class BindingMustBeValid<C extends FIBModelObject> extends ValidationRule<BindingMustBeValid<C>, C> {
public BindingMustBeValid(String ruleName, Class<C> clazz) {
super(clazz, ruleName);
}
public abstract DataBinding getBinding(C object);
public abstract BindingDefinition getBindingDefinition(C object);
@Override
public ValidationIssue<BindingMustBeValid<C>, C> applyValidation(C object) {
if (getBinding(object) != null && getBinding(object).isSet()) {
if (!getBinding(object).isValid()) {
DeleteBinding<C> deleteBinding = new DeleteBinding<C>(this);
return new ValidationError<BindingMustBeValid<C>, C>(this, object, BindingMustBeValid.this.getNameKey() + " '"
+ getBinding(object) + "'", deleteBinding);
}
}
return null;
}
protected static class DeleteBinding<C extends FIBModelObject> extends FixProposal<BindingMustBeValid<C>, C> {
private BindingMustBeValid rule;
public DeleteBinding(BindingMustBeValid rule) {
super("delete_this_binding");
this.rule = rule;
}
@Override
protected void fixAction() {
rule.getBinding(getObject()).setBinding(null);
}
}
}
}