| /* |
| * Copyright (c) 2008, 2009 Borland Software Corp. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Artem Tikhomirov (Borland) - initial API and implementation |
| */ |
| import Context; |
| |
| modeltype widget_ uses "http://www.eclipse.org/gmf/2008/Widget"; |
| modeltype context uses "http://www.eclipse.org/gmf/2008/Context"; |
| modeltype binding uses "http://www.eclipse.org/gmf/2008/Binding"; |
| modeltype ecore uses "http://www.eclipse.org/emf/2002/Ecore"; |
| |
| library Forms; |
| |
| helper context::Context::isBoolean() : Boolean { |
| return false |
| } |
| |
| helper context::FeatureContext::isBoolean() : Boolean { |
| return self.selector.eType.name = 'EBoolean' or self.selector.eType.name = 'EBooleanObject' or self.selector.eType.name = 'Boolean' /* UML. Though not 100% sure it's right place */or |
| // just in case, if user defined own primitive type with Java's implementation of a boolean kind |
| (self.selector.eType.instanceClassName = 'java.lang.Boolean' or self.selector.eType.instanceClassName = 'boolean') |
| } |
| |
| helper hasReferenceBasedCheckBoxes(s : binding::Section, c : context::Condition) : Boolean { |
| return bindingsToRefresh(s, c)->exists(b | b.widget.oclIsKindOf(widget_::CheckBox) and not b.selector.isBoolean()) |
| } |
| |
| helper referenceBasedCheckBoxBindings(s : binding::Section, c : context::Condition) : Sequence(binding::Binding) { |
| return bindingsToRefresh(s, c)[widget.oclIsKindOf(widget_::CheckBox) and not selector.isBoolean()] |
| } |
| |
| helper getConditionAccessors(e : binding::Section) : Sequence(context::FeatureContext) { |
| return (e.bindings->asSequence()->refreshCondition/*[accessor != null]*/.accessor->asOrderedSet()->asSequence())[context::FeatureContext] |
| } |
| |
| helper needsCast(inputType : ecore::EClass, x : context::FeatureContext) : Boolean { |
| return (if x.chain = null then not x.selector.eContainingClass.isSuperTypeOf(inputType) else not x.selector.eContainingClass.isSuperTypeOf(x.chain.oclAsType(context::FeatureContext).selector.oclAsType(ecore::EReference).eReferenceType) endif) |
| } |
| |
| // look for conditions that share model accessor with the binding of the widget |
| // - assume when binding's widget got changed, there's a need to trigger UI enablement actions |
| helper dependantActions(s : binding::Section, w : widget_::Widget) : Sequence(binding::Action) { |
| return triggeredConditions(s, associatedBinding(s, w))->collect(c | triggeredActions(s, c)) |
| // FIXME replace with smth like: s.actions.select(a | !a.trigger.intersect(triggeredConditions).isEmpty()) |
| } |
| |
| helper associatedBinding(s : binding::Section, w : widget_::Widget) : binding::Binding { |
| return s.bindings[widget = w]->first() |
| } |
| |
| helper triggeredActions(s : binding::Section, c : context::Condition) : Sequence(binding::Action) { |
| return (s.actions->asSequence())[trigger->includes(c)] |
| } |
| |
| // |
| // XXX replace with EReference [0..*] from Binding to Condition |
| // for now, assume triggered condition share model accessor with binding |
| helper triggeredConditions(s : binding::Section, b : binding::Binding) : Sequence(context::Condition) { |
| return (s.conditions->asSequence())[accessor = b.selector] |
| } |
| |
| // |
| // next two are analogous to triggeredCondition |
| // but for radio buttons case, where few conditions may share the same accessor, |
| // so, need to tell actions that became activated from those that became deactivated |
| // NOTE, use of commitCondition is nothing but an attempt to figure out which |
| // condition is associated with the activated binding, perhaps, need to check |
| // refreshCondition also/besides. |
| helper triggeredActions_activated(s : binding::Section, b : binding::Binding) : Sequence(binding::Action) { |
| //s.conditions.select(c | c.accessor == b.selector && b.commitCondition == c).actions |
| return (s.actions->asSequence())[trigger->exists(c | c.accessor = b.selector and b.commitCondition = c)] |
| } |
| |
| helper triggeredActions_deactivated(s : binding::Section, b : binding::Binding) : Sequence(binding::Action) { |
| //s.conditions.select(c | c.accessor == b.selector && b.commitCondition != c).actions |
| return (s.actions->asSequence())[trigger->exists(c | c.accessor = b.selector and b.commitCondition <> c)] |
| } |
| |
| helper triggeredBindingToRefreshBesidesTheOne(s : binding::Section, b : binding::Binding) : Sequence(binding::Binding) { |
| return let triggeredAndActivatedConditions = s.conditions->select(c | c.accessor = b.selector and b.commitCondition = c) in s.bindings->asSequence()->select(v | v <> b and triggeredAndActivatedConditions->includes(v.refreshCondition)) |
| } |
| |
| // XXX since we use this extension to perform refresh, do we really need to |
| // take commitCondition into account? |
| helper dependantBindings(s : binding::Section, w : widget_::Widget) : Sequence(binding::Binding) { |
| return let activeBinding = associatedBinding(s, w) in let triggered = triggeredConditions(s, activeBinding) in s.bindings->asSequence()->select(b | b <> activeBinding and (triggered->includes(b.refreshCondition) or triggered->includes(b.commitCondition))) |
| } |
| |
| helper bindingsToRefresh(s : binding::Section, c : context::Condition) : Sequence(binding::Binding) { |
| return (s.bindings->asSequence())[refreshCondition = c] |
| } |
| |
| // RadioButton in SWT doesn't deselect another active radiobutton |
| // in the composite on *programmatic* setSelection, hence |
| // need to manually deselect any leftover from previous input |
| helper needsRadioButtonWorkaround(s : binding::Section, c : context::Condition) : Boolean { |
| return bindingsToRefresh(s, c)->exists(b | b.widget.oclIsKindOf(widget_::RadioButton)) |
| } |
| |
| helper radioButtonToWorkaround(s : binding::Section, c : context::Condition) : Sequence(binding::Binding) { |
| return bindingsToRefresh(s, c)[widget.oclIsKindOf(widget_::RadioButton)] |
| } |
| |
| // looks for contexts that are being used by bindings and may be null. |
| // NOTE: context directly used by bindings as their accessors are EXCLUDED |
| // from the return list even if they may be null (they are assumed to get created |
| // on appropriate ViewToModel call (which makes success of the view-2-model assignment dependent |
| // on order of binding processing)). Consider the example of radiobutton associated with a EClass: |
| // if (myFlowLayoutRadio.isSelected()) { |
| // /*begin: code that should not get generated*/ |
| // if (getInput().getLayout() == null) {getInput().setLayout(new Layout());} |
| // /*end*/ |
| // getInput().setLayout(new FlowLayout()); |
| // } |
| helper needsCreation(bindings : Sequence(binding::Binding)) : Sequence(context::Context) { |
| // TODO bindings.select(b | needsNullCheck(b.selector)).selector.chain.purgeDups().typeSelect(Context).wholeChain().flatten().toSet().without(bindings.selector).typeSelect(Context).select(x | mayBeNull(x)) |
| var res = bindings->selector[needsNullCheck()]->chain->asOrderedSet()->asSequence()->collect(ctx | wholeChain(ctx))[mayBeNull()]; |
| bindings.selector->forEach(i) { |
| res := res->excluding(i); |
| }; |
| return res->asOrderedSet()->asSequence(); |
| } |
| |
| helper deduceInputType(s : binding::Section) : ecore::EClass { |
| return deduceCommon(s.input[context::FeatureContext].selector.eContainingClass) |
| } |
| |
| // XXX actually, the code below is not tested and |
| // apparently is not really working as intended, and may |
| // easily get to situations with no answer though common superclass exists |
| // Just didn't get enough time to implement [Head|Tail] logic to implement |
| // pair-by-pair comparison |
| helper deduceCommon(classes : Sequence(ecore::EClass)) : ecore::EClass { |
| return if classes->size() = 1 |
| then classes->first() |
| else let supertypesOfTheRest = classes->select(c | classes->forAll(cc | c.isSuperTypeOf(cc))) |
| in if supertypesOfTheRest->isEmpty() |
| then let supertypesOfNone = classes->select(c | classes->forAll(cc | cc <> c and not c.isSuperTypeOf(cc))) /* no cycles in supertypes => this collection will not be empty*/ |
| in if supertypesOfNone->isEmpty() |
| then deduceCommon(classes.eSuperTypes) /* [AS] Should never happen ?*/ |
| else deduceCommon((supertypesOfNone.eSuperTypes->union(classes->asOrderedSet()->-(supertypesOfNone->asSet())->asSequence())->asOrderedSet()->asSequence())[ecore::EClass]) |
| endif |
| else mostSpecific(supertypesOfTheRest) /* [AS] this collection contains only one EClass or several instances of same EClass (no cycles in supertypes) ?*/ |
| endif |
| endif |
| } |
| |
| helper mostSpecific(classes : Sequence(ecore::EClass)) : ecore::EClass { |
| return if classes->size() = 1 |
| then classes->first() |
| else classes->reject(c | classes->exists(cc | cc <> c and c.isSuperTypeOf(cc)))->first() /* looks strange - should be "->select" instead of "->reject" */ |
| endif |
| } |