/*
* (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.bindings;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.antar.expr.DefaultExpressionParser;
import org.openflexo.antar.expr.Expression;
import org.openflexo.antar.expr.Function;
import org.openflexo.antar.expr.oldparser.ExpressionParser;
import org.openflexo.antar.expr.oldparser.ParseException;
import org.openflexo.foundation.DataModification;
import org.openflexo.foundation.FlexoModelObject;
import org.openflexo.foundation.FlexoObservable;
import org.openflexo.foundation.FlexoObserver;
import org.openflexo.foundation.bindings.MethodCall.MethodCallArgument;
import org.openflexo.foundation.dm.DMEntity;
import org.openflexo.foundation.dm.DMMethod;
import org.openflexo.foundation.dm.DMProperty;
import org.openflexo.foundation.dm.DMType;
import org.openflexo.foundation.dm.dm.DMPropertyNameChanged;
import org.openflexo.foundation.dm.dm.EntityDeleted;
import org.openflexo.foundation.dm.dm.PropertyDeleted;
import org.openflexo.foundation.ie.IEObject;
import org.openflexo.foundation.ie.cl.ComponentDefinition;
import org.openflexo.foundation.ie.cl.ComponentDefinition.ComponentDefinitionBindingModel;
import org.openflexo.foundation.ie.dm.IEDataModification;
import org.openflexo.foundation.wkf.WKFObject;
/**
* Represents a binding value. A binding value MUST refer to a BindingDefinition object, and an owner which is the Bindable objet used to
* get the BindingModel. The root object defining the binding value is the BindingVariable object, while properties path defined as a Vector
* defines all the properties leading to accessed value.
*
* @author sguerin
*
*/
public class BindingValue extends AbstractBinding {
static final Logger logger = Logger.getLogger(BindingValue.class.getPackage().getName());
BindingVariable _bindingVariable;
protected BindingPath _bindingPath;
protected boolean _isConnected;
public boolean isCompoundBinding() {
for (BindingPathElement e : _bindingPath) {
if (e instanceof MethodCall) {
return true;
}
}
return false;
}
public BindingValue() {
super();
init();
}
public BindingValue(BindingDefinition bindingDefinition, FlexoModelObject owner) {
super(bindingDefinition, owner);
init();
if (owner != null && !(owner instanceof Bindable)) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Declared non-null owner is not a Bindable !");
}
}
}
private void init() {
_bindingVariable = null;
_bindingPath = new BindingPath();
_isConnected = false;
}
@Override
public BindingValue clone() {
BindingValue clone = new BindingValue(getBindingDefinition(), getOwner());
clone.setsWith(this);
return clone;
}
public boolean isProperty(BindingPathElement p) {
return getBindingPath() != null && getBindingPath().size() == 1 && getBindingPathElementAtIndex(0).equals(p);
}
@Override
public void setsWith(AbstractBinding aValue) {
super.setsWith(aValue);
if (aValue != null) {
if (aValue instanceof BindingValue) {
BindingValue bv = (BindingValue) aValue;
setBindingVariable(bv.getBindingVariable());
if (getBindingVariable() != null && getBindingVariable().getType() != null
&& getBindingVariable().getType().getBaseEntity() != null) {
getBindingVariable().getType().getBaseEntity().addObserver(this);
}
BindingPath bindingPath = new BindingPath();
for (BindingPathElement e : bv.getBindingPath()) {
bindingPath.add(e);
}
// bindingPath.addAll(aValue.getBindingPath());
setBindingPath(bindingPath);
for (BindingPathElement element : bindingPath) {
element.addObserver(this);
}
} else {
logger.warning("setsWith called with mismatched type " + aValue.getClass().getSimpleName() + ", expected BindingValue");
}
}
}
public boolean isConnected() {
return _isConnected;
}
public void connect() {
unparsableValue = null;
if (isBindingValid()) {
_isConnected = true;
setChanged();
if (getOwner() instanceof WKFObject) {
getOwner().setChanged();
((WKFObject) getOwner()).notifyAttributeModification(getBindingDefinition().getVariableName(), null, this);
} else if (getOwner() instanceof IEObject) {
getOwner().setChanged();
((IEObject) getOwner()).notifyObservers(new IEDataModification(getBindingDefinition().getVariableName(), null, this));
}
}
}
@Override
public boolean isStaticValue() {
return false;
}
@Override
public boolean isBindingValid() {
// logger.setLevel(Level.FINE);
if (logger.isLoggable(Level.FINE)) {
logger.fine("Is BindingValue " + this + " valid ?");
}
if (getAccessedType() == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Invalid binding because accessed type is null");
}
return false;
}
if (getBindingDefinition() == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Invalid binding because _bindingDefinition is null");
}
return false;
}
if (_bindingVariable == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Invalid binding because _bindingVariable is null");
}
return false;
}
if (!_checkBindingPathValid()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Invalid binding because binding path not valid");
}
return false;
}
if (getBindingDefinition() != null && getBindingDefinition().getIsSettable()) {
if (getBindingPathLastElement() == null || !(getBindingPathLastElement() instanceof DMProperty)
|| !((DMProperty) getBindingPathLastElement()).getIsSettable()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Invalid binding because binding definition declared as settable and definition cannot satisfy it");
}
return false;
}
}
if (logger.isLoggable(Level.FINEST)) {
logger.finest("_bindingDefinition.getType()=" + getBindingDefinition().getType());
logger.finest("_bindingDefinition.getType().getBaseEntity()=" + getBindingDefinition().getType().getBaseEntity());
logger.finest("getAccessedType()=" + getAccessedType());
logger.finest("_bindingPath.size()=" + _bindingPath.size());
logger.finest("_bindingDefinition.getType().isAssignableFrom(getAccessedType())="
+ getBindingDefinition().getType().isAssignableFrom(getAccessedType(), true));
}
if (getBindingDefinition().getType() != null && getBindingDefinition().getType().isAssignableFrom(getAccessedType(), true)) {
if (getOwner() instanceof IEObject) {
if (ComponentDefinitionBindingModel.COMPONENT_BINDING_VARIABLE_NAME.equals(getStringRepresentation())) {
return false;
}
}
}
if (getBindingDefinition().getType() == null || getBindingDefinition().getType().isAssignableFrom(getAccessedType(), true)) {
return true;
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("Invalid binding because types are not matching searched " + getBindingDefinition().getType() + " having "
+ getAccessedType());
}
return false;
}
/**
* Return accessed type
*
* @return
*/
@Override
public DMType getAccessedType() {
if (_bindingPath.size() == 0) {
if (_bindingVariable != null) {
return _bindingVariable.getType();
}
return null;
}
return _bindingPath.getResultingTypeAtIndex(_bindingPath.size() - 1);
}
protected boolean isBindingValidWithoutBindingDefinition() {
return _checkBindingPathValid();
}
private boolean _checkBindingPathValid() {
if (_bindingVariable == null) {
return false;
}
DMType currentType = _bindingVariable.getType();
if (currentType == null) {
return false;
}
for (int i = 0; i < _bindingPath.size(); i++) {
BindingPathElement element = _bindingPath.elementAt(i);
if (element.getEntity() == null || currentType.getBaseEntity() == null
|| !element.getEntity().isAncestorOf(currentType.getBaseEntity())) {
return false;
}
if (!element.isBindingValid()) {
return false;
}
currentType = _bindingPath.getResultingTypeAtIndex(i);
if (currentType == null) {
return false;
}
}
return true;
}
@Override
protected void _applyNewBindingDefinition() {
if (!isDeserializing() && isConnected() && !isBindingValid()) {
_isConnected = false;
}
}
public BindingVariable getBindingVariable() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Binding Variable of bindingValue for Entry:"
+ (getBindingDefinition() != null ? getBindingDefinition().getVariableName() : null) + " var:"
+ (_bindingVariable != null ? _bindingVariable.getVariableName() : null) + " " + _bindingVariable);
}
return _bindingVariable;
}
public void setBindingVariable(BindingVariable bindingVariable) {
unparsableValue = null;
if (logger.isLoggable(Level.FINE)) {
logger.fine("Set binding variable to " + bindingVariable);
}
/*if (bindingVariable != null)
_isStaticValue = false;*/
if (bindingVariable != _bindingVariable) {
BindingVariable oldBindingVariable = _bindingVariable;
if (oldBindingVariable != null && oldBindingVariable.getType() != null) {
oldBindingVariable.getType().getBaseEntity().deleteObserver(this);
}
_bindingVariable = bindingVariable;
if (_bindingVariable != null && _bindingVariable.getType() != null) {
if (_bindingVariable.getType().getBaseEntity() != null) {
_bindingVariable.getType().getBaseEntity().addObserver(this);
}
}
_bindingPath.removeAllElements();
_isConnected = false;
}
}
public BindingPath getBindingPath() {
return _bindingPath;
}
public void setBindingPath(BindingPath bindingPath) {
unparsableValue = null;
_bindingPath = bindingPath;
}
/**
* @param element
* @param i
*/
public DMType addBindingPathElement(BindingPathElement element) {
unparsableValue = null;
int index = _bindingPath.size();
setBindingPathElementAtIndex(element, index);
return _bindingPath.getResultingTypeAtIndex(index);
}
/**
* @param element
* @param i
*/
public void setBindingPathElementAtIndex(BindingPathElement element, int i) {
unparsableValue = null;
if (i < _bindingPath.size() && _bindingPath.elementAt(i) == element) {
return;
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("Set property " + element + " at index " + i);
}
if (i < _bindingPath.size()) {
_bindingPath.set(i, element);
int size = _bindingPath.size();
for (int j = i + 1; j < size; j++) {
_bindingPath.removeElementAt(i + 1);
}
} else if (i == _bindingPath.size()) {
_bindingPath.add(element);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not set property at index " + i);
}
}
_isConnected = false;
}
public void disconnect() {
unparsableValue = null;
_isConnected = false;
}
public BindingPathElement getBindingPathElementAtIndex(int i) {
if (i < _bindingPath.size()) {
return _bindingPath.elementAt(i);
}
return null;
}
public int getBindingPathElementCount() {
return _bindingPath.size();
}
public void removeBindingPathElementAfter(BindingPathElement requestedLast) {
unparsableValue = null;
if (_bindingPath != null && _bindingPath.lastElement() != null && _bindingPath.lastElement().equals(requestedLast)) {
return;
} else if (_bindingPath != null && _bindingPath.lastElement() != null) {
_bindingPath.removeElement(_bindingPath.lastElement());
removeBindingPathElementAfter(requestedLast);
}
}
public boolean isLastBindingPathElement(BindingPathElement element, int index) {
if (_bindingPath.size() < 1) {
return false;
}
return _bindingPath.lastElement() == element && index == _bindingPath.size() - 1;
}
public DMType getBindingPathLastElementType() {
if (getBindingPath() != null && getBindingPath().size() > 0 && getBindingPath().lastElement() != null) {
return getBindingPath().lastElement().getType();
}
return null;
}
public BindingPathElement getBindingPathLastElement() {
if (getBindingPath() != null && getBindingPath().size() > 0 && getBindingPath().lastElement() != null) {
return getBindingPath().lastElement();
}
return null;
}
@Override
public void setOwner(FlexoModelObject owner) {
super.setOwner(owner);
for (BindingPathElement e : _bindingPath) {
if (e instanceof MethodCall) {
MethodCall mc = (MethodCall) e;
for (MethodCallArgument arg : mc.getArgs()) {
if (arg.getBinding() != null) {
arg.getBinding().setOwner(owner);
}
}
}
}
}
public String getStringRepresentationWithoutNfirstElements(int n) {
String s = getStringRepresentation();
for (int k = n; k > 0; k--) {
try {
s = s.substring(s.indexOf(".") + 1);
} catch (Exception e) {
return null;
}
}
return s;
}
public String getStringRepresentationWithoutNlastElements(int n) {
if (_bindingVariable != null) {
StringBuilder sb = new StringBuilder(_bindingVariable.getVariableName());
int i = 0;
for (BindingPathElement element : _bindingPath) {
if (i < _bindingPath.size() - n) {
if (element != null) {
sb.append(".").append(element.getSerializationRepresentation());
} else {
sb.append(".null");
}
}
i++;
}
return sb.toString();
} else {
return "";
}
}
@Override
public String getStringRepresentation() {
if (getUnparsableValue() != null) {
return getUnparsableValue();
}
if (_bindingVariable != null) {
StringBuilder sb = new StringBuilder(_bindingVariable.getVariableName());
for (BindingPathElement element : _bindingPath) {
if (element != null) {
sb.append(".").append(element.getSerializationRepresentation());
} else {
sb.append(".null");
}
}
return sb.toString();
} else {
return "";
}
}
public boolean isPointingToSessionVariable() {
if (_bindingPath.size() != 2) {
return false;
}
DMEntity _bdPath0Entity = _bindingPath.get(0).getResultingType().getBaseEntity();
DMEntity _sessionType = getProject().getDataModel().getWORepository().getCustomSessionEntity();
return _bdPath0Entity.equals(_sessionType);
}
public boolean isPointingToComponentVariable() {
return _bindingPath.size() == 1;
}
public boolean isPointingToSessionOrComponentVariable() {
return isBindingValid() && (isPointingToSessionVariable() || isPointingToComponentVariable());
}
@Override
public String getWodStringRepresentation() {
return getCodeStringRepresentation().replaceAll("\\(\\s*\\)", "");
}
@Override
public String getCodeStringRepresentation() {
String answer = getStringRepresentation();
// TODO: we must find a way to handle properly those issues
// by supplying a parameter to that method for example
if (answer.startsWith("component.")) {
return answer.substring(10);
}
return answer;
}
@Override
public String getJavaCodeStringRepresentation() {
if (_bindingVariable == null) {
return "???";
}
StringBuilder sb = new StringBuilder();
// TODO: we must find a way to handle properly those issues
// by supplying a parameter to that method for example
if (!ComponentDefinition.ComponentDefinitionBindingModel.COMPONENT_BINDING_VARIABLE_NAME.equals(_bindingVariable.getVariableName())) {
sb.append(_bindingVariable.getJavaAccess());
}
DMType currentType = _bindingVariable.getType();
for (BindingPathElement element : _bindingPath) {
if (element.getEntity() == null || !element.getEntity().isAncestorOf(currentType.getBaseEntity())) {
return getCodeStringRepresentation();
}
if (!element.isBindingValid()) {
return getCodeStringRepresentation();
}
if (sb.length() > 0) {
sb.append(".");
}
if (element instanceof DMProperty) {
if (!((DMProperty) element).hasAccessors()) {
sb.append(((DMProperty) element).getName());
} else {
sb.append(((DMProperty) element).getGetterName());
sb.append("()");
}
} else if (element instanceof MethodCall) {
sb.append(((MethodCall) element).getJavaCodeStringRepresentation());
} else {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("BindingPathElement is neither a MethodCall nor a property!!!");
}
}
currentType = element.getType();
}
String reply = sb.toString();
// TODO: we must find a way to handle properly those issues
// by supplying a parameter to that method for example
if (reply.startsWith("session().")) {
return "getSession()." + reply.substring(10);
}
return reply;
}
public String getSetterJavaCodeStringRepresentation(AbstractBinding value) {
return getSetterJavaCodeStringRepresentation(value.getJavaCodeStringRepresentation());
}
public String getSetterJavaCodeStringRepresentation(String value) {
StringBuilder sb = new StringBuilder();
// TODO: we must find a way to handle properly those issues
// by supplying a parameter to that method for example
if (!ComponentDefinition.ComponentDefinitionBindingModel.COMPONENT_BINDING_VARIABLE_NAME.equals(_bindingVariable.getVariableName())) {
sb.append(_bindingVariable.getJavaAccess());
}
DMType currentType = _bindingVariable.getType();
for (BindingPathElement element : _bindingPath) {
if (element.getEntity() == null || !element.getEntity().isAncestorOf(currentType.getBaseEntity())) {
return getCodeStringRepresentation();
}
if (!element.isBindingValid()) {
return getCodeStringRepresentation();
}
if (sb.length() > 0) {
sb.append(".");
}
if (element == getBindingPathLastElement()) {
if (element instanceof DMProperty && ((DMProperty) element).getIsSettable()) {
if (!((DMProperty) element).hasAccessors()) {
sb.append(((DMProperty) element).getName());
sb.append(" = ");
sb.append(value);
} else {
sb.append(((DMProperty) element).getSetterName());
sb.append("(");
sb.append(value);
sb.append(")");
}
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Last BindingPathElement for a BindingValue considered as settable is not a property or not settable !!!");
}
}
} else {
if (element instanceof DMProperty) {
if (!((DMProperty) element).hasAccessors()) {
sb.append(((DMProperty) element).getName());
} else {
sb.append(((DMProperty) element).getGetterName());
sb.append("()");
}
} else if (element instanceof MethodCall) {
sb.append(((MethodCall) element).getJavaCodeStringRepresentation());
} else {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("BindingPathElement is neither a MethodCall nor a property!!!");
}
}
}
currentType = element.getType();
}
String reply = sb.toString();
// TODO: we must find a way to handle properly those issues
// by supplying a parameter to that method for example
if (reply.startsWith("session().")) {
return "getSession()." + reply.substring(10);
}
return reply;
}
@Override
public String getFullyQualifiedName() {
if (getBindingDefinition() != null) {
return new StringBuffer("BINDING_VALUE.").append(getBindingDefinition().getVariableName()).append("=")
.append(getCodeStringRepresentation()).toString();
} else {
return "BINDING_VALUE.<null>=" + getCodeStringRepresentation();
}
}
// ==========================================================
// ================= Serialization stuff ====================
// ==========================================================
public static class BindingValueStringConverter extends AbstractBindingStringConverter<BindingValue> {
private Bindable _bindable;
private DecodingPreProcessor _preProcessor = null;
private AbstractBindingStringConverter _abstractBindingStringConverter;
public BindingValueStringConverter(AbstractBindingStringConverter abstractBindingStringConverter) {
super(BindingValue.class);
_abstractBindingStringConverter = abstractBindingStringConverter;
}
private MethodCall tryToDecodeAsMethodCall(BindingValue owner, DMType currentType, String aValue) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("tryToDecodeAsMethodCall " + aValue);
}
String methodName;
Vector<String> paramsAsString;
try {
ExpressionParser parser = new DefaultExpressionParser();
Expression parsedExpression = parser.parse(aValue, null);
if (parsedExpression instanceof Function) {
methodName = ((Function) parsedExpression).getName();
paramsAsString = new Vector<String>();
for (Expression e : ((Function) parsedExpression).getArgs()) {
paramsAsString.add(e.toString());
}
} else {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue : trying to find method call matching '" + aValue
+ " this is not a function call");
}
return null;
}
} catch (ParseException e) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue : parse error while trying to find method call matching '" + aValue);
}
return null;
}
Vector<DMMethod> allMethods = currentType.getBaseEntity().getAccessibleMethods();
if (logger.isLoggable(Level.FINE)) {
logger.fine("allMethods=" + allMethods);
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("paramsAsString=" + paramsAsString);
}
Vector<DMMethod> possiblyMatchingMethods = new Vector<DMMethod>();
for (DMMethod method : allMethods) {
if (method.getName() != null && method.getName().equals(methodName)
&& method.getParameters().size() == paramsAsString.size()) {
possiblyMatchingMethods.add(method);
}
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("possiblyMatchingMethods=" + possiblyMatchingMethods);
}
Vector<MethodCall> results = new Vector<MethodCall>();
for (DMMethod method : possiblyMatchingMethods) {
boolean successfull = true;
MethodCall methodCall = new MethodCall(owner, method);
for (int i = 0; i < method.getParameters().size(); i++) {
DMMethod.DMMethodParameter param = method.getParameters().elementAt(i);
String bindingAsString = paramsAsString.elementAt(i);
DMType type = param.getType();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Attempt to parse: " + bindingAsString);
}
AbstractBinding paramBindingValue = _abstractBindingStringConverter.convertFromString(bindingAsString);
if (paramBindingValue != null) {
paramBindingValue.setOwner((FlexoModelObject) _bindable);
if (logger.isLoggable(Level.FINE)) {
logger.fine("paramBindingValue=" + paramBindingValue + " of " + paramBindingValue.getAccessedType());
}
if (paramBindingValue.isStaticValue()) {
paramBindingValue.setBindingDefinition(methodCall.new MethodCallParamBindingDefinition(param));
}
if (type != null && paramBindingValue.getAccessedType() != null
&& type.isAssignableFrom(paramBindingValue.getAccessedType(), true)) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Lookup on " + type.getName() + " succeded: " + paramBindingValue.getStringRepresentation());
}
methodCall.setBindingValueForParam(paramBindingValue, param);
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Lookup on type " + type + " failed (wrong type): "
+ paramBindingValue.getStringRepresentation() + "types: " + "looking " + type + " found "
+ paramBindingValue.getAccessedType());
}
successfull = false;
}
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Lookup on " + type.getName() + " failed (cannot analysing): " + bindingAsString);
}
successfull = false;
}
}
if (successfull) {
results.add(methodCall);
}
}
if (results.size() == 1) {
return results.firstElement();
} else if (results.size() > 1) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("While decoding BindingValue '" + aValue + "' : found ambigous methods " + methodName);
}
return results.firstElement();
}
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue : cannot find method call matching '" + aValue);
}
return null;
}
protected class PathTokenizer {
private Vector<String> _tokens;
private Enumeration<String> enumeration;
protected PathTokenizer(String value) {
super();
_tokens = new Vector<String>();
StringTokenizer st = new StringTokenizer(value, ".()", true);
String current = "";
int level = 0;
while (st.hasMoreElements()) {
String next = st.nextToken();
if (next.equals(".") && current.trim().length() > 0 && level == 0) {
_tokens.add(current);
current = "";
} else if (next.equals("(")) {
current += next;
level++;
} else if (next.equals(")")) {
current += next;
level--;
} else {
current += next;
}
}
if (current.trim().length() > 0 && level == 0) {
_tokens.add(current);
current = "";
}
enumeration = _tokens.elements();
}
public boolean hasMoreTokens() {
return enumeration.hasMoreElements();
}
public String nextToken() {
String returned = enumeration.nextElement();
if (logger.isLoggable(Level.FINE)) {
logger.fine("> " + returned);
}
return returned;
}
}
@Override
public BindingValue convertFromString(String aValue) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Decode " + aValue);
}
if (_bindable == null) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + aValue + "' : bindable not set !");
}
return null;
} else {
String value;
if (_preProcessor != null) {
value = _preProcessor.preProcessString(aValue);
} else {
value = aValue;
}
if ("null".equals(value)) {
return null;
}
BindingValue returned = new BindingValue();
PathTokenizer st = new PathTokenizer(value);
if (st.hasMoreTokens()) {
String bindingVariableName = st.nextToken();
if (_bindable == null) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value + "' : no declared bindable !");
}
return null;
}
if (_bindable.getBindingModel() == null) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value + "' : declared bindable has a null binding model !");
}
return null;
}
BindingVariable bv = _bindable.getBindingModel().bindingVariableNamed(bindingVariableName);
if (bv == null) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value + "' : variable " + bindingVariableName
+ " not found in binding model !");
}
return null;
} else {
DMType currentType = bv.getType();
if (currentType == null) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value + "' : variable " + bindingVariableName
+ " doesn't implement any type !");
}
return null;
}
returned.setBindingVariable(bv);
while (st.hasMoreTokens()) {
String nextTokenName = st.nextToken();
BindingPathElement nextElement;
if (currentType.getBaseEntity() == null) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value + "' : cannot find base entity for type '"
+ currentType);
}
return null;
}
nextElement = currentType.getBaseEntity().getProperty(nextTokenName);
if (nextElement == null) {
// OK, may be this is a MethodCall
nextElement = tryToDecodeAsMethodCall(returned, currentType, nextTokenName);
}
if (nextElement == null) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value
+ "' : cannot find property nor method matching '" + nextTokenName + "' for type "
+ currentType + " !");
}
return null;
} else {
currentType = returned.addBindingPathElement(nextElement);
// returned.addBindingPathElement(nextElement);
// currentType = nextElement.getType();
if (currentType == null) {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value + "' : token " + nextTokenName
+ " doesn't implement any type !");
}
return null;
}
}
}
// Before to receive its owner, we set to knonwn bindable, in order to check validity
returned.setOwner((FlexoModelObject) _bindable);
if (returned.isBindingValidWithoutBindingDefinition()) {
returned._isConnected = true;
return returned;
} else {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value + "' : invalid binding !");
}
return null;
}
}
} else {
if (logger.isLoggable(Level.WARNING) && warnOnFailure) {
logger.warning("Could not decode BindingValue '" + value + "' : variable not set !");
}
return null;
}
}
}
@Override
public String convertToString(BindingValue value) {
return value.getStringRepresentation();
}
public Bindable getBindable() {
return _bindable;
}
@Override
public void setBindable(Bindable bindable) {
_bindable = bindable;
}
public DecodingPreProcessor getPreProcessor() {
return _preProcessor;
}
public void setPreProcessor(DecodingPreProcessor preProcessor) {
_preProcessor = preProcessor;
}
}
public static abstract class DecodingPreProcessor {
public abstract String preProcessString(String aString);
}
@Override
public BindingValueStringConverter getConverter() {
if (getProject() != null) {
return getProject().getBindingValueConverter();
}
return null;
}
@Override
public void update(FlexoObservable observable, DataModification dataModification) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("update in BindingValue " + getStringRepresentation() + " for " + dataModification.getClass().getName()
+ " owner is " + getOwner());
}
if (dataModification instanceof EntityDeleted && getBindingVariable() != null
&& ((EntityDeleted) dataModification).oldValue() == getBindingVariable().getType()) {
setBindingVariable(null);
setChanged();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Handled entity deleted");
}
} else if (dataModification instanceof PropertyDeleted) {
PropertyDeleted propertyDeleted = (PropertyDeleted) dataModification;
if (getBindingPath().contains(propertyDeleted.oldValue())) {
setBindingVariable(null);
// For me it is not consistant to keep a part of the binding if one previously selected part has been deleted, thus I prefer
// to delete the entire binding
// int size = getBindingPath().size();
// for (int j = getBindingPath().indexOf(propertyDeleted.oldValue()); j < size; j++) {
// getBindingPath().removeElementAt(getBindingPath().size() - 1);
// }
_isConnected = false;
setChanged();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Handled property deleted");
}
}
} else if (dataModification instanceof DMPropertyNameChanged) {
if (getBindingPath().contains(((DMPropertyNameChanged) dataModification).getDMProperty())) {
setChanged();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Handled property renamed");
}
}
}
}
@Override
protected void finalize() throws Throwable {
if (logger.isLoggable(Level.FINE)) {
logger.fine("finalize called for BindingValue " + getStringRepresentation());
}
setBindingVariable(null);
super.finalize();
}
// GPO: Please leave the extends with the full qualified name of the interface to allow ant to properly compile this class
public static interface BindingPathElement extends org.openflexo.foundation.dm.Typed {
public DMEntity getEntity();
@Override
public DMType getType();
public DMType getResultingType();
public void addObserver(FlexoObserver o);
public void deleteObserver(FlexoObserver o);
public String getSerializationRepresentation();
public boolean isBindingValid();
}
/**
* Internal class extending Vector to provide observing management for BindingPathElement Additionnaly, provides generics type resolving
* by maintaining a resulting type vector
*
* @author sguerin
*
*/
public class BindingPath extends Vector<BindingPathElement> {
private Vector<DMType> _resultingTypes;
BindingPath() {
super();
_resultingTypes = new Vector<DMType>();
}
@Override
public synchronized void removeAllElements() {
for (BindingPathElement element : this) {
element.deleteObserver(BindingValue.this);
}
super.removeAllElements();
_resultingTypes.clear();
}
@Override
public synchronized BindingPathElement set(int index, BindingPathElement element) {
if (get(index) != null) {
get(index).deleteObserver(BindingValue.this);
}
element.addObserver(BindingValue.this);
BindingPathElement returned = super.set(index, element);
setResultingTypeAtIndex(index);
return returned;
}
@Override
public synchronized void removeElementAt(int index) {
if (get(index) != null) {
get(index).deleteObserver(BindingValue.this);
}
super.removeElementAt(index);
}
@Override
public synchronized boolean add(BindingPathElement o) {
o.addObserver(BindingValue.this);
boolean returned = super.add(o);
setResultingTypeAtIndex(size() - 1);
return returned;
}
private void setResultingTypeAtIndex(int index) {
if (_bindingVariable != null) {
BindingPathElement bpe = elementAt(index);
if (bpe == null) {
logger.warning("Unexpected null element at index " + index);
return;
}
DMType type = bpe.getResultingType();
if (type == null) {
logger.warning("Cannot find resuling type for bindingPathElement " + bpe.getSerializationRepresentation()
+ " in path :" + getStringRepresentation());
return;
}
DMType parentType = null;
// TODO java.lang.ArrayIndexOutOfBoundsException may happen here, fix this
// BMA : java.lang.ArrayIndexOutOfBoundsException won't append anymore...
if (index > 0 && _resultingTypes.size() >= index) {
parentType = _resultingTypes.elementAt(index - 1);
} else {
parentType = _bindingVariable.getType();
}
if (type.isGeneric()) {
type = DMType.makeInstantiatedDMType(type, parentType);
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("Set resulting type at index " + index + " with " + type + " parent=" + parentType);
}
if (_resultingTypes.size() > index) {
_resultingTypes.setElementAt(type, index);
} else if (_resultingTypes.size() == index) {
_resultingTypes.add(type);
} else {
logger.warning("Unexpected index " + index + " for _resultingTypes.size()=" + _resultingTypes.size());
}
}
}
public DMType getResultingTypeAtIndex(int index) {
if (index < _resultingTypes.size()) {
return _resultingTypes.elementAt(index);
}
return null;
}
}
/**
* Overrides getClassNameKey
*
* @see org.openflexo.foundation.FlexoModelObject#getClassNameKey()
*/
@Override
public String getClassNameKey() {
return "binding_value";
}
public BindingPathElement findFirstBindingPathElementOfType(DMEntity entity) {
Enumeration<BindingPathElement> en = getBindingPath().elements();
BindingPathElement reply = null;
while (en.hasMoreElements()) {
reply = en.nextElement();
if (reply.getType().getBaseEntity().equals(entity)) {
return reply;
}
}
return null;
}
public void addBindingPathElementAfterFirstPathElementOfType(DMEntity entity, DMProperty newProperty) {
BindingPathElement beforeLast = findFirstBindingPathElementOfType(entity);
if (beforeLast != null) {
removeBindingPathElementAfter(beforeLast);
} else {
while (getBindingPathLastElement() != null) {
getBindingPath().removeAllElements();
}
}
addBindingPathElement(newProperty);
}
public DMEntity getLastEditableEntity() {
DMEntity reply = getBindingVariable().getContainer() instanceof DMEntity ? (DMEntity) getBindingVariable().getContainer() : null;
Enumeration<BindingPathElement> en = getBindingPath().elements();
DMEntity temp = null;
while (en.hasMoreElements()) {
temp = en.nextElement().getType().getBaseEntity();
if (!temp.getIsReadOnly()) {
reply = temp;
}
}
return reply;
}
public BindingPathElement getLastBindingPathElementOfType(DMEntity newEntryEntity) {
BindingPathElement reply = null;
Enumeration<BindingPathElement> en = getBindingPath().elements();
BindingPathElement temp = null;
while (en.hasMoreElements()) {
temp = en.nextElement();
if (!temp.getType().getBaseEntity().equals(newEntryEntity)) {
reply = temp;
}
}
return reply;
}
}