/****************************************************************************** * 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.modeling.el; import org.eclipse.sapphire.Element; import org.eclipse.sapphire.ElementHandle; import org.eclipse.sapphire.ElementList; import org.eclipse.sapphire.ElementProperty; import org.eclipse.sapphire.ElementType; import org.eclipse.sapphire.Event; import org.eclipse.sapphire.ImpliedElementProperty; import org.eclipse.sapphire.Listener; import org.eclipse.sapphire.LocalizableText; import org.eclipse.sapphire.PossibleTypesService; import org.eclipse.sapphire.Property; import org.eclipse.sapphire.PropertyContentEvent; import org.eclipse.sapphire.PropertyEvent; import org.eclipse.sapphire.Text; import org.eclipse.sapphire.modeling.localization.LocalizationService; import org.eclipse.sapphire.modeling.localization.SourceLanguageLocalizationService; /** * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public class ModelElementFunctionContext extends FunctionContext { @Text( "Index {0} is outside the bounds of the collection." ) private static LocalizableText indexOutOfBounds; static { LocalizableText.init( ModelElementFunctionContext.class ); } private final Element element; private final LocalizationService localizationService; public ModelElementFunctionContext( final Element element ) { this( element, SourceLanguageLocalizationService.INSTANCE ); } public ModelElementFunctionContext( final Element element, final LocalizationService localizationService ) { if( element == null ) { throw new IllegalArgumentException(); } this.element = element; this.localizationService = localizationService; } public final Element element() { return this.element; } @Override public FunctionResult property( final Object element, final String name ) { if( element == this && name.equalsIgnoreCase( "This" ) ) { final Function f = new Function() { @Override public String name() { return "This"; } @Override public FunctionResult evaluate( final FunctionContext context ) { return new FunctionResult( this, context ) { @Override protected Object evaluate() { return element(); } }; } }; f.init(); return f.evaluate( this ); } else if( element == this || element instanceof Element ) { final Element el = ( element == this ? element() : (Element) element ); final Property property = el.property( name ); if( property != null ) { final Function f = new ReadPropertyFunction( property, name, PropertyContentEvent.class ) { @Override protected Object evaluate() { return this.context; } }; f.init(); return f.evaluate( this ); } } else if( element instanceof ElementHandle ) { final ElementHandle<?> handle = (ElementHandle<?>) element; final ElementProperty elementPropertyDef = handle.definition(); boolean ok = false; if( elementPropertyDef instanceof ImpliedElementProperty ) { ok = ( elementPropertyDef.getType().property( name ) != null ); } else { for( final ElementType possibleChildType : handle.service( PossibleTypesService.class ).types() ) { ok = ( possibleChildType.property( name ) != null ); if( ok ) { break; } } } if( ok ) { final Function f = new ReadPropertyFunction( handle, name, PropertyContentEvent.class ) { @Override protected Object evaluate() { final Element child = ( (ElementHandle<?>) this.context ).content(); if( child != null ) { return child.property( name ); } return null; } }; f.init(); return f.evaluate( this ); } } else if( element instanceof ElementList ) { final ElementList<?> list = (ElementList<?>) element; try { final int index = Integer.parseInt( name ); final Function f = new ReadPropertyFunction( list, name, PropertyContentEvent.class ) { @Override protected Object evaluate() { final ElementList<?> list = (ElementList<?>) this.context; if( index >= 0 && index < list.size() ) { return list.get( index ); } else { throw new FunctionException( indexOutOfBounds.format( index ) ); } } }; f.init(); return f.evaluate( this ); } catch( NumberFormatException e ) { // Ignore. Non-integer property means call isn't trying to index into the list. } } return super.property( element, name ); } @Override public LocalizationService getLocalizationService() { return this.localizationService; } private static abstract class ReadPropertyFunction extends Function { protected final Property context; private final String name; private final Class<? extends PropertyEvent> eventType; public ReadPropertyFunction( final Property context, final String name, final Class<? extends PropertyEvent> eventType ) { this.context = context; this.name = name; this.eventType = eventType; } @Override public final String name() { return this.name; } @Override public final FunctionResult evaluate( final FunctionContext context ) { final Property property = this.context; final Class<? extends PropertyEvent> eventType = this.eventType; return new FunctionResult( this, context ) { private Listener listener; @Override protected void init() { super.init(); this.listener = new Listener() { @Override public void handle( final Event event ) { if( eventType.isInstance( event ) ) { refresh(); } } }; property.attach( this.listener ); } @Override protected Object evaluate() { return ReadPropertyFunction.this.evaluate(); } @Override public void dispose() { super.dispose(); property.detach( this.listener ); } }; } protected abstract Object evaluate(); }; }