/** ----------------------------------------------------------------- * Copyright ( c) 2014 BestSolution.at EDV Systemhaus GmbH * All Rights Reserved . * * BestSolution.at MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE , EITHER EXPRESS OR IMPLIED , INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE , OR NON - INFRINGEMENT. * * BestSolution.at SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS * SOFTWARE OR ITS DERIVATIVES. * * This software is released under the terms of the * * "Eclipse Public License, Version 1.0 * or any later version " * * and may only be distributed and used under the terms of the * mentioned license. You should have received a copy of the license * along with this software product, if not you can download it from * http://www.eclipse.org/legal/epl-v10.html * ---------------------------------------------------------------- */ package at.bestsolution.emf.navi.databinding; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.databinding.observable.Diffs; import org.eclipse.core.databinding.observable.Realm; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.list.ListDiff; import org.eclipse.core.databinding.observable.list.ListDiffEntry; import org.eclipse.core.databinding.property.INativePropertyListener; import org.eclipse.core.databinding.property.ISimplePropertyListener; import org.eclipse.core.databinding.property.SimplePropertyEvent; import org.eclipse.core.databinding.property.list.IListProperty; import org.eclipse.core.databinding.property.list.SimpleListProperty; import org.eclipse.core.internal.databinding.property.list.SimplePropertyObservableList; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import at.bestsolution.emf.navi.FeaturePath; import at.bestsolution.emf.navi.FeaturePathCallback; import at.bestsolution.emf.navi.FeaturePathSegment; import at.bestsolution.emf.navi.FeaturePathUtil; /** * * @author Martin Platter * */ public class FeaturePathDatabindingUtil { private static final class NavigableListProperty extends SimpleListProperty { private FeaturePath path; public NavigableListProperty( FeaturePath path ) { this.path = path; } private FeaturePathSegment[] getSegments() { return path.getSegments().toArray( new FeaturePathSegment[ 0 ] ); } @Override public Object getElementType() { FeaturePathSegment[] segs = getSegments(); return segs[ segs.length - 1 ].feature.getEType(); } @SuppressWarnings( "rawtypes" ) @Override protected List doGetList( Object source ) { return FeaturePathUtil.filter( (EObject) source, path ); } @SuppressWarnings( "rawtypes" ) @Override protected void doSetList( Object source, List list, ListDiff diff ) { List<?> currentList = doGetList( source ); diff.applyTo( currentList ); } @SuppressWarnings( "restriction" ) public IObservableList observe( Realm realm, Object source ) { return new SimplePropertyObservableList( realm, source, this ); } @Override public INativePropertyListener adaptListener( final ISimplePropertyListener listener ) { return new FeaturePathListListener( listener, this ); } private static class FeaturePathNodeListener extends AdapterImpl { private EObject source; private EStructuralFeature feature; private List<Object> children = new ArrayList<Object>(); private ISimplePropertyListener listener; private NavigableListProperty owner; private boolean root; public FeaturePathNodeListener( FeaturePathNodeListener parent, EObject node, EStructuralFeature feature, ISimplePropertyListener listener, NavigableListProperty owner ) { this.source = node; this.feature = feature; this.listener = listener; this.owner = owner; this.root = parent == null; if ( !root ) { parent.registerChild( this ); } } private void registerChild( Object leaf ) { children.add( leaf ); } @Override public void notifyChanged( Notification msg ) { if ( msg.getFeature() == feature && !msg.isTouch() ) { List<EObject> oldvalues = new ArrayList<EObject>(); fillOldValues( oldvalues ); List<EObject> newvalues = new ArrayList<EObject>(); fillNewValues( newvalues ); ListDiff diff = Diffs.computeListDiff( oldvalues, newvalues ); for ( ListDiffEntry de : diff.getDifferences() ) { if ( !de.isAddition() ) { cleanup( (EObject) de.getElement() ); } } listener.handleEvent( ( new SimplePropertyEvent( SimplePropertyEvent.CHANGE, msg.getNotifier(), owner, diff ) ) ); } } private Boolean cleanup( EObject element ) { if ( source == element ) { return source.eAdapters().remove( this ); } else { Iterator<Object> it = children.iterator(); while ( it.hasNext() ) { Object child = it.next(); if ( child == element ) { it.remove(); return null; } if ( child instanceof FeaturePathNodeListener ) { Boolean result = ( (FeaturePathNodeListener) child ).cleanup( element ); if ( result == null ) { return null; } if ( result ) { children.remove( child ); return null; } } } return false; } } private int getFeaturePathSegmentIndex( FeaturePathSegment[] segs, EStructuralFeature feature ) { for ( int i = 0; i < segs.length; i++ ) { if ( segs[ i ].feature == feature ) { return i; } } return -1; } private void fillNewValues( List<EObject> newvalues ) { FeaturePathSegment[] segs = owner.getSegments(); List<Object> result = new ArrayList<Object>(); FeaturePathUtil.filter( source, segs, getFeaturePathSegmentIndex( segs, feature ), result, new FeaturePathRunnableRegistrationCallback( listener, owner ), this ); for ( Object o : result ) { newvalues.add( (EObject) o ); } } private void fillOldValues( List<EObject> oldvalues ) { for ( Object child : children ) { if ( child instanceof FeaturePathNodeListener ) { ( (FeaturePathNodeListener) child ).fillOldValues( oldvalues ); } else { oldvalues.add( (EObject) child ); } } } } private static class FeaturePathRunnableRegistrationCallback implements FeaturePathCallback { private ISimplePropertyListener listener; private NavigableListProperty owner; private FeaturePathNodeListener root; public FeaturePathRunnableRegistrationCallback( ISimplePropertyListener listener, NavigableListProperty owner ) { this.listener = listener; this.owner = owner; } @Override public Object runOnLeaf( Object memento, EObject leaf, FeaturePathSegment segment ) { ( (FeaturePathNodeListener) memento ).registerChild( leaf ); return memento; } @Override public void runOnLeaf(Object memento, Object leaf) { ( (FeaturePathNodeListener) memento ).registerChild( leaf ); } @Override public Object runOnNode( Object memento, EObject node, FeaturePathSegment segment ) { EList<Adapter> eAdapters = node.eAdapters(); // TODO [PM] try to avoid being inclined to register a adapter several times for ( Adapter adapter : eAdapters ) { if ( adapter instanceof FeaturePathNodeListener && ( (FeaturePathNodeListener) adapter ).listener == listener ) { return adapter; } } FeaturePathNodeListener fpnlistener = new FeaturePathNodeListener( (FeaturePathNodeListener) memento, node, segment.feature, listener, owner ); if ( memento == null ) { root = fpnlistener; } eAdapters.add( fpnlistener ); return fpnlistener; } public void cleanup( EObject source ) { root.cleanup( source ); } public FeaturePathSegment[] getSegments() { return owner.getSegments(); } } private static class FeaturePathListListener implements INativePropertyListener { private FeaturePathRunnableRegistrationCallback cb; public FeaturePathListListener( ISimplePropertyListener listener, NavigableListProperty owner ) { cb = new FeaturePathRunnableRegistrationCallback( listener, owner ); } @Override public void removeFrom( Object source ) { if ( source != null ) { cb.cleanup( (EObject) source ); } } @Override public void addTo( Object source ) { if ( source != null ) { FeaturePathUtil.filter( (EObject) source, cb.getSegments(), cb ); } } } } public static IListProperty list( FeaturePath path ) { return new NavigableListProperty( path ); } }