/****************************************************************************** * Copyright (c) 2016 Oracle * 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: * Konstantin Komissarchik - initial implementation and ongoing maintenance ******************************************************************************/ package org.eclipse.sapphire; import static org.eclipse.sapphire.modeling.util.MiscUtil.equal; import org.eclipse.sapphire.services.ReferenceService; import org.eclipse.sapphire.services.ServiceEvent; /** * A common implementation base for {@link ReferenceService} implementations that resolve to an {@link Element}. * * <p>In many situations, the reference semantics can be specified using the @{@link ElementReference} annotation rather than through * a custom {@link ElementReferenceService} implementation.</p> * * <pre><code><font color="#888888"> {@literal @}Reference( target = Table.class )</font> * {@literal @}ElementReference( list = "/Tables", key = "Name" ) * * <font color="#888888">ValueProperty PROP_TABLE = new ValueProperty( TYPE, "Table" ); * * ReferenceValue<String,Table> getTable(); * void setTable( String value );</font></code></pre> * * <p>When more control is necessary, a custom implementation of {@link ElementReferenceService} can be provided. This is * necessary, for example, when the referenced elements are located in a different model or when the list and key are variable.</p> * * <pre><code> <font color="#888888">{@literal @}Reference( target = Table.class )</font> * {@literal @}Service( impl = ExampleElementReferenceService.class ) * * <font color="#888888">ValueProperty PROP_TABLE = new ValueProperty( TYPE, "Table" ); * * ReferenceValue<String,Table> getTable(); * void setTable( String value );</font></code></pre> * * <p>A PossibleValuesService implementation is automatically provided when this service is implemented.</p> * * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public abstract class ElementReferenceService extends ReferenceService<Element> { private ElementList<?> list; private String key; private Listener listener; /** * Returns the list containing elements being referenced. If the list returned by this method changes to another list, * a ListEvent must be broadcasted. */ public abstract ElementList<?> list(); /** * Returns the path through the model from an element in the list to the value property used by the reference. If the * path returned by this method changes, a KeyEvent must be broadcasted. */ public abstract String key(); @Override protected void initReferenceService() { this.listener = new FilteredListener<PropertyContentEvent>() { @Override protected void handleTypedEvent( final PropertyContentEvent event ) { if( event instanceof ValuePropertyContentEvent ) { final ValuePropertyContentEvent evt = (ValuePropertyContentEvent) event; final Element element = evt.property().element(); if( element == target() && evt.refactor() ) { context( Value.class ).write( evt.after() ); } } refresh(); } }; attach ( new FilteredListener<SourceEvent>() { @Override protected void handleTypedEvent( final SourceEvent event ) { refresh(); } } ); } @Override protected final Element compute() { final Value<?> reference = context( Value.class ); final String text = reference.text(); final ElementList<?> list = list(); final String key = key(); if( this.list != list || ! equal( this.key, key ) ) { if( this.list != null ) { if( ! this.list.disposed() ) { this.list.detach( this.listener, this.key ); } this.list = null; } this.list = list; this.key = key; if( list != null ) { this.list.attach( this.listener, this.key ); } } if( list != null && text != null ) { for( final Element element : list ) { final String n = reference( element ); if( n != null && n.equals( text ) ) { return element; } } } return null; } @Override public final String reference( final Element element ) { if( ! list().contains( element ) ) { throw new IllegalArgumentException(); } return ( (Value<?>) element.property( key() ) ).text(); } @Override public void dispose() { if( this.list != null ) { if( ! this.list.disposed() ) { this.list.detach( this.listener, this.key ); } this.list = null; } this.key = null; this.listener = null; super.dispose(); } /** * An event that's broadcast when the list changes. */ public final class ListEvent extends SourceEvent { } /** * An event that's broadcast when the key changes. */ public final class KeyEvent extends SourceEvent { } /** * The common base class for {@link ListEvent} and {@link KeyEvent}. */ public abstract class SourceEvent extends ServiceEvent { public SourceEvent() { super( ElementReferenceService.this ); } } }