Package org.eclipse.sapphire.modeling.el

Source Code of org.eclipse.sapphire.modeling.el.FunctionResult

/******************************************************************************
* Copyright (c) 2014 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 java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.sapphire.Disposable;
import org.eclipse.sapphire.Element;
import org.eclipse.sapphire.Event;
import org.eclipse.sapphire.Listener;
import org.eclipse.sapphire.ListenerContext;
import org.eclipse.sapphire.LocalizableText;
import org.eclipse.sapphire.MasterConversionService;
import org.eclipse.sapphire.Property;
import org.eclipse.sapphire.Sapphire;
import org.eclipse.sapphire.Text;
import org.eclipse.sapphire.Value;
import org.eclipse.sapphire.modeling.Status;

/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/

public abstract class FunctionResult implements Disposable
{
    @Text( "Cannot convert {0} to {1}." )
    private static LocalizableText cannotCastMessage;
   
    @Text( "Function {0} missing operand {1}." )
    private static LocalizableText missingOperandMessage;
   
    @Text( "Function {0} does not accept nulls in position {1}." )
    private static LocalizableText operandNullMessage;
   
    @Text( "Function {0} expects {2} in position {1}, but {3} was found. A conversion was not possible." )
    private static LocalizableText operandWrongTypeMessage;
   
    static
    {
        LocalizableText.init( FunctionResult.class );
    }

    private final Function function;
    private final FunctionContext context;
    private final List<FunctionResult> operands;
    private Set<Property> properties;
    private final ListenerContext listeners;
    private final Listener listener;
    private Object value;
    private Status status;
   
    public FunctionResult( final Function function,
                           final FunctionContext context )
    {
        this.function = function;
        this.context = context;
        this.listeners = new ListenerContext();
        this.operands = Collections.unmodifiableList( initOperands() );

        if( this.operands.isEmpty() )
        {
            this.listener = null;
        }
        else
        {
            this.listener = new Listener()
            {
                @Override
                public void handle( final Event event )
                {
                    refresh();
                }
            };
        }
       
        init();
        refresh( false );
    }
   
    protected List<FunctionResult> initOperands()
    {
        final List<Function> operands = function().operands();
       
        if( operands.size() == 0 )
        {
            return Collections.emptyList();
        }
        else if( operands.size() == 1 )
        {
            return Collections.singletonList( operands.get( 0 ).evaluate( this.context ) );
        }
        else
        {
            final List<FunctionResult> result = new ArrayList<FunctionResult>();
           
            for( Function operand : operands )
            {
                result.add( operand.evaluate( this.context ) );
            }
           
            return result;
        }
    }

    protected void init()
    {
    }
   
    public final Function function()
    {
        return this.function;
    }
   
    public final FunctionContext context()
    {
        return this.context;
    }
   
    public final List<FunctionResult> operands()
    {
        for( FunctionResult operand : this.operands )
        {
            listenToOperand( operand );
        }
       
        return this.operands;
    }
   
    public final Object operand( final int position )
    {
        if( position < this.operands.size() )
        {
            final FunctionResult operand = this.operands.get( position );
           
            listenToOperand( operand );
           
            return operand.value();
        }
        else
        {
            throw new FunctionException( missingOperandMessage.format( getClass().getName(), String.valueOf( position ) ) );
        }
    }
   
    public final <T> T operand( final int position, final Class<T> type, final boolean nullable )
    {
        final Object operand = operand( position );
        final T operandTyped;
       
        try
        {
            operandTyped = cast( operand, type );
        }
        catch( FunctionException e )
        {
            final String msg = operandWrongTypeMessage.format( function().name(), position, type.getName(), operand.getClass().getName() );
            throw new FunctionException( msg );
        }
       
        if( operandTyped == null && ! nullable )
        {
            final String msg = operandNullMessage.format( function().name(), position );
            throw new FunctionException( msg );
        }
       
        return operandTyped;
    }
   
    private void listenToOperand( final FunctionResult operand )
    {
        operand.attach( this.listener );
       
        Object obj = null;
       
        try
        {
            obj = operand.value();
        }
        catch( FunctionException e )
        {
            // Safe to ignore. When the function implementation accesses the value,
            // the exception will be thrown again.
        }
       
        if( obj instanceof Property )
        {
            final Property property = (Property) obj;
           
            property.attach( this.listener );
           
            if( this.properties == null )
            {
                this.properties = new HashSet<Property>( 1 );
            }
           
            this.properties.add( property );
        }          
    }
   
    protected abstract Object evaluate() throws FunctionException;
   
    /**
     * Returns the value computed by the function.
     *
     * @return the value computed by the function
     * @throws FunctionException if function evaluation failed with an error; to avoid exception, check
     *   status first
     */
   
    public final Object value() throws FunctionException
    {
        if( this.status.severity() == Status.Severity.ERROR )
        {
            throw new FunctionException( this.status );
        }
       
        return this.value;
    }
   
    /**
     * Returns the status of function execution. This will show if function executed without any issues or if it
     * encountered an error condition.
     *
     * @return the status of function execution
     */
   
    public final Status status()
    {
        return this.status;
    }

    protected final void refresh()
    {
        refresh( true );
    }
   
    private final void refresh( final boolean broadcastIfNecessary )
    {
        Object newValue = null;
        Status newStatus = Status.createOkStatus();
       
        if( this.properties != null )
        {
            for( Property property : this.properties )
            {
                property.detach( this.listener );
            }
           
            this.properties.clear();
        }
       
        try
        {
            newValue = evaluate();
        }
        catch( FunctionException e )
        {
            newStatus = e.status();
        }
        catch( Exception e )
        {
            newStatus = Status.createErrorStatus( e );
        }
       
        if( newValue instanceof Function )
        {
            throw new IllegalStateException();
        }
       
        if( ! equal( this.value, newValue ) || ! equal( this.status, newStatus ))
        {
            this.value = newValue;
            this.status = newStatus;
           
            if( broadcastIfNecessary )
            {
                this.listeners.broadcast( new Event() );
            }
        }
    }
   
    public final boolean attach( final Listener listener )
    {
        return this.listeners.attach( listener );
    }
   
    public final boolean detach( final Listener listener )
    {
        return this.listeners.detach( listener );
    }
   
    @Override
    public void dispose()
    {
        for( FunctionResult operand : this.operands )
        {
            operand.dispose();
        }
       
        if( this.properties != null )
        {
            for( Property property : this.properties )
            {
                property.detach( this.listener );
            }
           
            this.properties.clear();
        }
    }
   
    protected final <X> X cast( Object obj,
                                final Class<X> type )
    {
        if( obj instanceof FunctionResult )
        {
            throw new IllegalArgumentException();
        }
       
        if( obj != null && type.isAssignableFrom( obj.getClass() ) )
        {
            return type.cast( obj );
        }
       
        try
        {
            if( type == String.class )
            {
                if( obj instanceof String )
                {
                    return type.cast( obj );
                }
                else if( obj == null )
                {
                    return type.cast( "" );
                }
                else if( obj instanceof Value )
                {
                    String res = ( (Value<?>) obj ).text();
                    res = ( res == null ? "" : res );
                    return type.cast( res );
                }
                else if( obj instanceof List || obj instanceof Set )
                {
                    final StringBuilder res = new StringBuilder();
                    boolean first = true;
                   
                    for( Object entry : ( (Collection<?>) obj ) )
                    {
                        if( first )
                        {
                            first = false;
                        }
                        else
                        {
                            res.append( ',' );
                        }
                       
                        final String str = cast( entry, String.class );
                       
                        if( str != null )
                        {
                            res.append( str );
                        }
                    }
                   
                    return type.cast( res.toString() );
                }
                else if( obj.getClass().isArray() )
                {
                    final StringBuilder res = new StringBuilder();
                   
                    for( int i = 0, n = Array.getLength( obj ); i < n; i++ )
                    {
                        if( i > 0 )
                        {
                            res.append( ',' );
                        }
                       
                        final String str = cast( Array.get( obj, i ), String.class );
                       
                        if( str != null )
                        {
                            res.append( str );
                        }
                    }
                   
                    return type.cast( res.toString() );
                }
            }
           
            if( Number.class.isAssignableFrom( type ) )
            {
                if( obj instanceof Value )
                {
                    obj = ( (Value<?>) obj ).content();
                }
               
                if( obj == null || ( obj instanceof String && ( (String) obj ).length() == 0 ) )
                {
                    obj = (short) 0;
                }
                else if( obj instanceof Character )
                {
                    obj = (short) ( (Character) obj ).charValue();
                }
                else if( obj instanceof Boolean )
                {
                    throw new FunctionException( cannotCastMessage.format( obj.getClass().getName(), type.getName() ) );
                }
               
                if( obj.getClass() == type )
                {
                    return type.cast( obj );
                }
                else if( obj instanceof Number )
                {
                    if( type == BigInteger.class )
                    {
                        if( obj instanceof BigDecimal )
                        {
                            return type.cast( ( (BigDecimal) obj ).toBigInteger() );
                        }
                        else
                        {
                            return type.cast( BigInteger.valueOf( ( (Number) obj ).longValue() ) );
                        }
                    }
                    else if( type == BigDecimal.class )
                    {
                        if( obj instanceof BigInteger )
                        {
                            return type.cast( new BigDecimal( (BigInteger) obj ) );
                        }
                        else
                        {
                            return type.cast( new BigDecimal( ( (Number) obj ).doubleValue() ) );
                        }
                    }
                    else if( type == Byte.class )
                    {
                        return type.cast( Byte.valueOf( ( (Number) obj ).byteValue() ) );
                    }
                    else if( type == Short.class )
                    {
                        return type.cast( Short.valueOf( ( (Number) obj ).shortValue() ) );
                    }
                    else if( type == Integer.class )
                    {
                        return type.cast( Integer.valueOf( ( (Number) obj ).intValue() ) );
                    }
                    else if( type == Long.class )
                    {
                        return type.cast( Long.valueOf( ( (Number) obj ).longValue() ) );
                    }
                    else if( type == Float.class )
                    {
                        return type.cast( Float.valueOf( ( (Number) obj ).floatValue() ) );
                    }
                    else if( type == Double.class )
                    {
                        return type.cast( Double.valueOf( ( (Number) obj ).doubleValue() ) );
                    }
                }
                else if( obj instanceof String )
                {
                    if( type == BigDecimal.class )
                    {
                        return type.cast( new BigDecimal( (String) obj ) );
                    }
                    else if( type == BigInteger.class )
                    {
                        return type.cast( new BigInteger( (String) obj ) );
                    }
                    else if( type == Byte.class )
                    {
                        return type.cast( Byte.valueOf( (String) obj ) );
                    }
                    else if( type == Short.class )
                    {
                        return type.cast( Short.valueOf( (String) obj ) );
                    }
                    else if( type == Integer.class )
                    {
                        return type.cast( Integer.valueOf( (String) obj ) );
                    }
                    else if( type == Long.class )
                    {
                        return type.cast( Long.valueOf( (String) obj ) );
                    }
                    else if( type == Float.class )
                    {
                        return type.cast( Float.valueOf( (String) obj ) );
                    }
                    else if( type == Double.class )
                    {
                        return type.cast( Double.valueOf( (String) obj ) );
                    }
                }
   
                throw new FunctionException( cannotCastMessage.format( obj.getClass().getName(), type.getName() ) );
            }
            else if( type == Character.class )
            {
                if( obj instanceof Value )
                {
                    obj = ( (Value<?>) obj ).content();
                }
               
                if( obj == null || ( obj instanceof String && ( (String) obj ).length() == 0 ) )
                {
                    return type.cast( (char) 0 );
                }
                else if( obj instanceof Character )
                {
                    return type.cast( obj );
                }
                else if( obj instanceof Boolean )
                {
                    throw new FunctionException( cannotCastMessage.format( obj.getClass().getName(), type.getName() ) );
                }
                else if( obj instanceof Number )
                {
                    return type.cast( (char) (short) cast( obj, Short.class ) );
                }
                else if( obj instanceof String )
                {
                    return type.cast( ( (String) obj ).charAt( 0 ) );
                }
   
                throw new FunctionException( cannotCastMessage.format( obj.getClass().getName(), type.getName() ) );
            }
            else if( type == Boolean.class )
            {
                if( obj instanceof Value )
                {
                    obj = ( (Value<?>) obj ).content();
                }
               
                if( obj == null || ( obj instanceof String && ( (String) obj ).length() == 0 ) )
                {
                    return type.cast( Boolean.FALSE );
                }
                else if( obj instanceof Boolean )
                {
                    return type.cast( obj );
                }
                else if( obj instanceof String )
                {
                    return type.cast( Boolean.valueOf( (String) obj ) );
                }
   
                throw new FunctionException( cannotCastMessage.format( obj.getClass().getName(), type.getName() ) );
            }
            else if( List.class.isAssignableFrom( type ) )
            {
                if( obj instanceof Value )
                {
                    obj = ( (Value<?>) obj ).content();
                }
               
                if( obj == null )
                {
                    return null;
                }
                else if( obj instanceof List )
                {
                    return type.cast( obj );
                }
                else if( obj instanceof Collection )
                {
                    return type.cast( new ArrayList<Object>( (Collection<?>) obj ) );
                }
                else if( obj.getClass().isArray() )
                {
                    final List<Object> list = new ArrayList<Object>();
                   
                    for( int i = 0, n = Array.getLength( obj ); i < n; i++ )
                    {
                        list.add( Array.get( obj, i ) );
                    }
                   
                    return type.cast( list );
                }
                else if( obj instanceof String )
                {
                    final String str = (String) obj;
                   
                    if( str.length() == 0 )
                    {
                        return type.cast( Collections.emptyList() );
                    }
                    else
                    {
                        return type.cast( Arrays.asList( ( (String) obj ).split( "\\," ) ) );
                    }
                }
                else
                {
                    return type.cast( Collections.singletonList( obj ) );
                }
            }
            else
            {
                if( obj instanceof Value )
                {
                    obj = ( (Value<?>) obj ).content();
                }
               
                if( obj == null )
                {
                    return null;
                }
                else if( type.isInstance( obj ) )
                {
                    return type.cast( obj );
                }
                else if( obj instanceof String && ( (String) obj ).length() == 0 )
                {
                    return null;
                }
                else
                {
                    final MasterConversionService masterConversionService;
                    final Object origin = this.function.origin();
                   
                    if( origin instanceof Element )
                    {
                        masterConversionService = ( (Element) origin ).service( MasterConversionService.class );
                    }
                    else
                    {
                        masterConversionService = Sapphire.service( MasterConversionService.class );
                    }

                    final X result = type.cast( masterConversionService.convert( obj, type ) );
                   
                    if( result != null )
                    {
                        return result;
                    }
                }
               
                throw new FunctionException( cannotCastMessage.format( obj.getClass().getName(), type.getName() ) );
            }
        }
        catch( FunctionException e )
        {
            if( ! ( obj instanceof String ) )
            {
                try
                {
                    return cast( cast( obj, String.class ), type );
                }
                catch( FunctionException ex )
                {
                    // Ignore the composite cast failure, since we want the original exception.
                }
            }
           
            throw e;
        }
    }
   
    protected final boolean equal( Object a, Object b )
    {
        if( a instanceof Value<?> )
        {
            final Value<?> value = (Value<?>) a;
           
            if( ! value.disposed() )
            {
                a = value.text();
            }
        }
       
        if( b instanceof Value<?> )
        {
            final Value<?> value = (Value<?>) b;
           
            if( ! value.disposed() )
            {
                b = value.text();
            }
        }
       
        if( a == b )
        {
            return true;
        }
        else if( a == null || b == null )
        {
            return false;
        }
        else if( a instanceof BigDecimal || b instanceof BigDecimal )
        {
            final BigDecimal x = cast( a, BigDecimal.class );
            final BigDecimal y = cast( b, BigDecimal.class );
            return x.equals( y );
        }
        else if( a instanceof Float || a instanceof Double || b instanceof Float || b instanceof Double )
        {
            final Double x = cast( a, Double.class );
            final Double y = cast( b, Double.class );
            return x.equals( y );
        }
        else if( a instanceof BigInteger || b instanceof BigInteger )
        {
            final BigInteger x = cast( a, BigInteger.class );
            final BigInteger y = cast( b, BigInteger.class );
            return x.equals( y );
        }
        else if( a instanceof Byte || a instanceof Short || a instanceof Character || a instanceof Integer || a instanceof Long ||
                 b instanceof Byte || b instanceof Short || b instanceof Character || b instanceof Integer || b instanceof Long )
        {
            final Long x = cast( a, Long.class );
            final Long y = cast( b, Long.class );
            return x.equals( y );
        }
        else if( a instanceof Boolean || b instanceof Boolean )
        {
            final Boolean x = cast( a, Boolean.class );
            final Boolean y = cast( b, Boolean.class );
            return x.equals( y );
        }
        else if( a instanceof Enum )
        {
            return ( a == cast( b, a.getClass() ) );
        }
        else if( b instanceof Enum )
        {
            return ( cast( a, b.getClass() ) == b );
        }
        else if( a instanceof String || b instanceof String )
        {
            final String x = cast( a, String.class );
            final String y = cast( b, String.class );
            return ( x.compareTo( y ) == 0 );
        }
        else
        {
            return a.equals( b );
        }
    }
   
}
TOP

Related Classes of org.eclipse.sapphire.modeling.el.FunctionResult

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.