/* * (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.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.bindings.BindingDefinition.BindingDefinitionType; import org.openflexo.foundation.dm.DMEntity; import org.openflexo.foundation.dm.DMMethod; import org.openflexo.foundation.dm.DMTranstyper; import org.openflexo.foundation.dm.DMTranstyper.DMTranstyperEntry; import org.openflexo.foundation.dm.DMType; import org.openflexo.foundation.rm.XMLStorageResourceData; import org.openflexo.inspector.InspectableObject; public class TranstypedBinding extends AbstractBinding { static final Logger logger = Logger.getLogger(TranstypedBinding.class.getPackage().getName()); private DMTranstyper transtyper; private Vector<TranstypedBindingValue> values; public class TranstypedBindingValue extends FlexoModelObject implements InspectableObject { protected DMTranstyperEntry entry; private BindingDefinition bindingDefinition; private AbstractBinding bindingValue; protected TranstypedBindingValue(DMTranstyperEntry anEntry) { super(); entry = anEntry; bindingValue = null; bindingDefinition = new BindingDefinition(entry.getName(), entry.getType(), TranstypedBinding.this, BindingDefinitionType.GET, true) { @Override public String getVariableName() { return entry.getName(); } @Override public DMType getType() { return entry.getType(); } }; } public BindingDefinition getBindingDefinition() { return bindingDefinition; } public AbstractBinding getBindingValue() { return bindingValue; } public void setBindingValue(AbstractBinding aValue) { bindingValue = aValue; if (bindingValue != null) { bindingValue.setBindingDefinition(getBindingDefinition()); bindingValue.setOwner(TranstypedBinding.this); } } public DMTranstyperEntry getEntry() { return entry; } @Override public String getInspectorName() { // never inspected alone return null; } @Override public String getFullyQualifiedName() { return TranstypedBinding.this.getFullyQualifiedName() + "." + (getEntry() != null ? entry.getName() : null); } @Override public XMLStorageResourceData getXMLResourceData() { return TranstypedBinding.this.getOwner().getXMLResourceData(); } @Override public String getClassNameKey() { return "transtyped_binding_value"; } @Override public boolean equals(Object object) { if (object == null) { return false; } if (object instanceof TranstypedBindingValue) { TranstypedBindingValue o = (TranstypedBindingValue) object; return o.getEntry() == getEntry() && o.getBindingValue() == getBindingValue(); } else { return false; } } @Override public int hashCode() { return (getEntry() == null ? 0 : getEntry().hashCode()) + (getBindingValue() == null ? 0 : getBindingValue().hashCode()); } } public TranstypedBinding() { super(); values = new Vector<TranstypedBindingValue>(); } public TranstypedBinding(BindingDefinition bindingDefinition, FlexoModelObject owner) { super(bindingDefinition, owner); values = new Vector<TranstypedBindingValue>(); } public DMTranstyper getTranstyper() { return transtyper; } public void setTranstyper(DMTranstyper aTranstyper) { if (aTranstyper != transtyper) { transtyper = aTranstyper; values.clear(); if (transtyper != null) { for (DMTranstyperEntry entry : transtyper.getEntries()) { values.add(new TranstypedBindingValue(entry)); } } } } public Vector<TranstypedBindingValue> getValues() { return values; } @Override public String getCodeStringRepresentation() { return getStringRepresentation(); } @Override public String getClassNameKey() { return "transtyped_binding"; } @Override public String getFullyQualifiedName() { return "TRANSTYPED_BINDING=" + getStringRepresentation(); } @Override protected void _applyNewBindingDefinition() { // TODO Auto-generated method stub } // ========================================================== // ================= Serialization stuff ==================== // ========================================================== public static TranstypedBinding makeTranstypedBinding(String value, FlexoModelObject owner) { if (owner != null && owner.getProject() != null && owner instanceof Bindable) { TranstypedBindingStringConverter converter = owner.getProject().getTranstypedBindingStringConverter(); TranstypedBinding returned = converter.convertFromString(value); returned.setOwner(owner); return returned; } return null; } public TranstypedBinding getStaticBindingFromString(String aValue) { return getConverter().convertFromString(aValue); } public static class TranstypedBindingStringConverter extends AbstractBindingStringConverter<TranstypedBinding> { private Bindable _bindable; private AbstractBindingStringConverter _abstractBindingStringConverter; public TranstypedBindingStringConverter(AbstractBindingStringConverter abstractBindingStringConverter) { super(TranstypedBinding.class); _abstractBindingStringConverter = abstractBindingStringConverter; } public Bindable getBindable() { return _bindable; } @Override public void setBindable(Bindable bindable) { _bindable = bindable; } 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().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; } @Override public TranstypedBinding convertFromString(String aValue) { String fullyQualifiedTranstyperName; Vector<String> paramsAsString; try { ExpressionParser parser = new DefaultExpressionParser(); Expression parsedExpression = parser.parse(aValue, null); if (parsedExpression instanceof Function) { fullyQualifiedTranstyperName = ((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 TranstypedBinding : trying to find transtyper syntax with '" + aValue + " : wrong syntax"); } return null; } } catch (ParseException e) { if (logger.isLoggable(Level.WARNING) && warnOnFailure) { logger.warning("Could not decode TranstypedBinding : parse error while trying to decode as transtyped binding '" + aValue); } return null; } String fullyQualifiedEntityName = fullyQualifiedTranstyperName.substring(0, fullyQualifiedTranstyperName.lastIndexOf(".")); String transtyperName = fullyQualifiedTranstyperName.substring(fullyQualifiedTranstyperName.lastIndexOf(".") + 1); if (_bindable == null) { if (logger.isLoggable(Level.WARNING) && warnOnFailure) { logger.warning("Could not decode TranstypedBinding '" + aValue + "' : no declared bindable !"); } return null; } if (_bindable.getBindingModel() == null) { if (logger.isLoggable(Level.WARNING) && warnOnFailure) { logger.warning("Could not decode TranstypedBinding '" + aValue + "' : declared bindable has a null binding model !"); } return null; } DMEntity declaringEntity = ((FlexoModelObject) _bindable).getProject().getDataModel().getEntityNamed(fullyQualifiedEntityName); if (declaringEntity == null) { if (logger.isLoggable(Level.WARNING) && warnOnFailure) { logger.warning("Could not decode TranstypedBinding '" + aValue + "' : no declaring entity !"); } return null; } DMTranstyper transtyper = declaringEntity.getDMTranstyper(transtyperName); if (transtyper == null) { if (logger.isLoggable(Level.WARNING) && warnOnFailure) { logger.warning("Could not decode TranstypedBinding '" + aValue + "' : could not find transtyper " + transtyperName + " for " + declaringEntity.getFullyQualifiedName()); } return null; } TranstypedBinding returned = new TranstypedBinding(); returned.setTranstyper(transtyper); logger.info("Built new TranstypedBinding: " + returned); if (returned.getValues().size() != paramsAsString.size()) { if (logger.isLoggable(Level.WARNING) && warnOnFailure) { logger.warning("Could not decode TranstypedBinding '" + aValue + "' : parameters size does not match"); } return null; } _abstractBindingStringConverter.setBindable(_bindable); for (int i = 0; i < paramsAsString.size(); i++) { String bindingAsString = paramsAsString.elementAt(i); if (logger.isLoggable(Level.FINE)) { logger.fine("Attempt to parse: " + bindingAsString); } AbstractBinding paramBindingValue = _abstractBindingStringConverter.convertFromString(bindingAsString); if (paramBindingValue != null) { returned.getValues().elementAt(i).setBindingValue(paramBindingValue); } else { if (logger.isLoggable(Level.WARNING) && warnOnFailure) { logger.warning("Could not decode TranstypedBinding '" + aValue + "' : failed to decode '" + bindingAsString + "'"); } return null; } } return returned; } @Override public String convertToString(TranstypedBinding value) { return value.getStringRepresentation(); } } @Override public TranstypedBindingStringConverter getConverter() { if (getProject() != null) { return getProject().getTranstypedBindingStringConverter(); } return null; } @Override public void update(FlexoObservable observable, DataModification dataModification) { // TODO to be implemented } @Override public void setsWith(AbstractBinding aValue) { super.setsWith(aValue); if (aValue != null) { if (aValue instanceof TranstypedBinding) { TranstypedBinding otherBinding = (TranstypedBinding) aValue; setTranstyper(otherBinding.getTranstyper()); if (transtyper != null) { for (int i = 0; i < transtyper.getEntries().size(); i++) { values.elementAt(i).setBindingValue(otherBinding.getValues().elementAt(i).getBindingValue()); } } } else { logger.warning("setsWith called with mismatched type " + aValue.getClass().getSimpleName() + ", expected TranstypedBinding"); } } } @Override public boolean equals(Object object) { if (object == null) { return false; } if (object instanceof TranstypedBinding) { TranstypedBinding tb = (TranstypedBinding) object; if (getBindingDefinition() == null) { if (tb.getBindingDefinition() != null) { return false; } } else { if (!getBindingDefinition().equals(tb.getBindingDefinition())) { return false; } } return _owner == tb._owner && getTranstyper() == tb.getTranstyper() && getValues().equals(tb.getValues()); } else { return super.equals(object); } } /** * Return accessed type * * @return */ @Override public DMType getAccessedType() { if (transtyper != null) { return transtyper.getReturnedType(); } return null; } @Override public boolean isBindingValid() { if (logger.isLoggable(Level.FINE)) { logger.fine("Is StaticBinding " + 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 binding definition is null"); } return false; } else if (getBindingDefinition().getIsSettable()) { if (logger.isLoggable(Level.FINE)) { logger.fine("Invalid binding because binding definition is declared as settable"); } return false; } else if (getBindingDefinition().getBindingDefinitionType() == BindingDefinitionType.EXECUTE) { if (logger.isLoggable(Level.FINE)) { logger.fine("Invalid binding because binding definition is declared as executable"); } return false; } if (getTranstyper() == null) { if (logger.isLoggable(Level.FINE)) { logger.fine("Invalid binding because transtyper is null"); } return false; } for (TranstypedBindingValue v : getValues()) { if (v.getEntry() == null) { if (logger.isLoggable(Level.FINE)) { logger.fine("Invalid binding because value entry is null"); } return false; } if (v.getBindingValue() == null) { if (logger.isLoggable(Level.FINE)) { logger.fine("Invalid binding because value of " + v.getEntry().getName() + " is null"); } return false; } else if (!v.getBindingValue().isBindingValid()) { if (logger.isLoggable(Level.FINE)) { logger.fine("Invalid binding because value of " + v.getEntry().getName() + " is not valid"); } 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; } protected boolean _areTypesMatching() { return getBindingDefinition().getType() == null || getBindingDefinition().getType().isAssignableFrom(getAccessedType(), true); } @Override public boolean isStaticValue() { return false; } @Override public TranstypedBinding clone() { TranstypedBinding clone = new TranstypedBinding(getBindingDefinition(), getOwner()); clone.setsWith(this); return clone; } @Override public String getJavaCodeStringRepresentation() { return getStringRepresentation(); } @Override public String getStringRepresentation() { if (getTranstyper() == null) { return "null"; } StringBuffer sb = new StringBuffer(); sb.append(getTranstyper().getDeclaringEntity().getFullyQualifiedName()); sb.append("." + getTranstyper().getJavaMethodName()); sb.append("("); boolean isFirst = true; for (TranstypedBindingValue entry : getValues()) { sb.append((isFirst ? "" : ",") + (entry.getBindingValue() != null ? entry.getBindingValue().getStringRepresentation() : "null")); isFirst = false; } sb.append(")"); return sb.toString(); } @Override public String getWodStringRepresentation() { logger.severe("transtyper in wod files isn't supported yet"); return "\"transtyper in wod files isn't supported yet\""; } public boolean isBasicTranstyping() { return getTranstyper() != null && getTranstyper().getEntries().size() == 1; } }