Package org.eclipse.sapphire.ui.forms

Source Code of org.eclipse.sapphire.ui.forms.PropertyEditorPart

/******************************************************************************
* 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
*    Greg Amerson - [342656] read.only rendering hint is not parsed as Boolean
******************************************************************************/

package org.eclipse.sapphire.ui.forms;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.FilteredListener;
import org.eclipse.sapphire.ImpliedElementProperty;
import org.eclipse.sapphire.ListProperty;
import org.eclipse.sapphire.Listener;
import org.eclipse.sapphire.LocalizableText;
import org.eclipse.sapphire.LoggingService;
import org.eclipse.sapphire.Property;
import org.eclipse.sapphire.PropertyContentEvent;
import org.eclipse.sapphire.PropertyDef;
import org.eclipse.sapphire.PropertyEnablementEvent;
import org.eclipse.sapphire.PropertyEvent;
import org.eclipse.sapphire.PropertyValidationEvent;
import org.eclipse.sapphire.Sapphire;
import org.eclipse.sapphire.Text;
import org.eclipse.sapphire.Value;
import org.eclipse.sapphire.ValueProperty;
import org.eclipse.sapphire.modeling.CapitalizationType;
import org.eclipse.sapphire.modeling.ElementDisposeEvent;
import org.eclipse.sapphire.modeling.ModelPath;
import org.eclipse.sapphire.modeling.Status;
import org.eclipse.sapphire.modeling.el.AndFunction;
import org.eclipse.sapphire.modeling.el.Function;
import org.eclipse.sapphire.modeling.el.FunctionResult;
import org.eclipse.sapphire.modeling.el.Literal;
import org.eclipse.sapphire.modeling.localization.LabelTransformer;
import org.eclipse.sapphire.ui.PartValidationEvent;
import org.eclipse.sapphire.ui.SapphireActionSystem;
import org.eclipse.sapphire.ui.SapphirePart;
import org.eclipse.sapphire.ui.def.ISapphireHint;
import org.eclipse.sapphire.ui.def.ISapphireUiDef;
import org.eclipse.sapphire.ui.forms.swt.CheckBoxListPropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.FormComponentPresentation;
import org.eclipse.sapphire.ui.forms.swt.PropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.PropertyEditorPresentationFactory;
import org.eclipse.sapphire.ui.forms.swt.SlushBucketPropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.SwtPresentation;
import org.eclipse.sapphire.ui.forms.swt.TablePropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.TextFieldPropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.internal.CheckBoxGroupPropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.internal.CheckBoxPropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.internal.EnumPropertyEditorPresentationFactory;
import org.eclipse.sapphire.ui.forms.swt.internal.NamedValuesPropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.internal.PopUpListFieldPropertyEditorPresentation;
import org.eclipse.sapphire.ui.forms.swt.internal.RadioButtonGroupPropertyEditorPresentation;
import org.eclipse.sapphire.util.ListFactory;
import org.eclipse.swt.widgets.Composite;

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

public final class PropertyEditorPart extends FormComponentPart
{
    public static final String RELATED_CONTROLS = "related-controls";
    public static final String BROWSE_BUTTON = "browse-button";
    public static final String DATA_BINDING = "binding";
   
    private static final List<PropertyEditorPresentationFactory> FACTORIES = new ArrayList<PropertyEditorPresentationFactory>();
   
    static
    {
        FACTORIES.add( new CheckBoxPropertyEditorPresentation.Factory() );
        FACTORIES.add( new RadioButtonGroupPropertyEditorPresentation.Factory() );
        FACTORIES.add( new PopUpListFieldPropertyEditorPresentation.Factory() );
        FACTORIES.add( new EnumPropertyEditorPresentationFactory() );
        FACTORIES.add( new NamedValuesPropertyEditorPresentation.Factory() );
        FACTORIES.add( new TextFieldPropertyEditorPresentation.Factory() );
        FACTORIES.add( new CheckBoxGroupPropertyEditorPresentation.HorizontalFactory() );
        FACTORIES.add( new CheckBoxGroupPropertyEditorPresentation.VerticalFactory() );
        FACTORIES.add( new CheckBoxListPropertyEditorPresentation.EnumFactory() );
        FACTORIES.add( new SlushBucketPropertyEditorPresentation.Factory() );
        FACTORIES.add( new TablePropertyEditorPresentation.Factory() );
    }
   
    @Text( "Property editor''s property reference path \"{0}\" is invalid." )
    private static LocalizableText invalidPath;
   
    @Text( "Child property path \"{1}\" is invalid for \"{0}\"." )
    private static LocalizableText invalidChildPropertyPath;
   
    static
    {
        LocalizableText.init( PropertyEditorPart.class );
    }

    private Property property;
    private List<ModelPath> childPropertyPaths;
    private Map<Element,Map<ModelPath,PropertyEditorPart>> childPropertyEditors;
    private Map<String,Object> hints;
    private List<FormComponentPart> relatedContentParts;
    private Listener propertyValidationListener;
    private FunctionResult labelFunctionResult;
   
    @Override
    protected void init()
    {
        super.init();
       
        final ISapphireUiDef rootdef = this.definition.nearest( ISapphireUiDef.class );
        final PropertyEditorDef propertyEditorPartDef = (PropertyEditorDef) this.definition;
       
        final String propertyEditorPath = substituteParams( propertyEditorPartDef.getProperty().text() );
        this.property = getModelElement().property( new ModelPath( propertyEditorPath ) );
       
        if( this.property == null )
        {
            throw new RuntimeException( invalidPath.format( propertyEditorPath ) );
        }
       
        // Read the property to ensure that initial events are broadcast and avoid being surprised
        // by them later.
       
        this.property.empty();
       
        // Child properties.       
       
        final ListFactory<ModelPath> childPropertiesListFactory = ListFactory.start();
        final ElementType type = this.property.definition().getType();
       
        if( type != null )
        {
            if( propertyEditorPartDef.getChildProperties().isEmpty() )
            {
                for( PropertyDef childProperty : type.properties() )
                {
                    if( childProperty instanceof ValueProperty )
                    {
                        childPropertiesListFactory.add( new ModelPath( childProperty.name() ) );
                    }
                }
            }
            else
            {
                for( PropertyEditorDef childPropertyEditor : propertyEditorPartDef.getChildProperties() )
                {
                    final ModelPath childPropertyPath = new ModelPath( childPropertyEditor.getProperty().content() );
                    boolean invalid = false;
                   
                    if( childPropertyPath.length() == 0 )
                    {
                        invalid = true;
                    }
                    else
                    {
                        ElementType t = type;
                       
                        for( int i = 0, n = childPropertyPath.length(); i < n && ! invalid; i++ )
                        {
                            final ModelPath.Segment segment = childPropertyPath.segment( i );
                           
                            if( segment instanceof ModelPath.PropertySegment )
                            {
                                final PropertyDef p = t.property( ( (ModelPath.PropertySegment) segment ).getPropertyName() );
                               
                                if( p instanceof ValueProperty )
                                {
                                    if( i + 1 != n )
                                    {
                                        invalid = true;
                                    }
                                }
                                else if( p instanceof ImpliedElementProperty )
                                {
                                    if( i + 1 == n )
                                    {
                                        invalid = true;
                                    }
                                    else
                                    {
                                        t = p.getType();
                                    }
                                }
                                else
                                {
                                    invalid = true;
                                }
                            }
                            else
                            {
                                invalid = true;
                            }
                        }
                    }
                   
                    if( invalid )
                    {
                        final String msg = invalidChildPropertyPath.format( this.property.name(), childPropertyPath.toString() );
                        Sapphire.service( LoggingService.class ).logError( msg );
                    }
                    else
                    {
                        childPropertiesListFactory.add( childPropertyPath );
                    }
                }
            }
        }
       
        this.childPropertyPaths = childPropertiesListFactory.result();
        this.childPropertyEditors = new HashMap<Element,Map<ModelPath,PropertyEditorPart>>();
       
        // Listen for PropertyValidationEvent and update property editor's validation.
       
        if( this.property instanceof ElementList || this.property instanceof ElementHandle )
        {
            this.propertyValidationListener = new FilteredListener<PropertyEvent>()
            {
                @Override
                protected void handleTypedEvent( final PropertyEvent event )
                {
                    if( ! ( event instanceof PropertyContentEvent && event.property() instanceof Value ) )
                    {
                        refreshValidation();
                    }
                }
            };
           
            this.property.attach( this.propertyValidationListener, "*" );
        }
        else
        {
            this.propertyValidationListener = new FilteredListener<PropertyEvent>()
            {
                @Override
                protected void handleTypedEvent( final PropertyEvent event )
                {
                    if( event instanceof PropertyValidationEvent || event instanceof PropertyEnablementEvent )
                    {
                        refreshValidation();
                    }
                }
            };
           
            this.property.attach( this.propertyValidationListener );
        }

        // Hints

        this.hints = new HashMap<String,Object>();
       
        for( ISapphireHint hint : propertyEditorPartDef.getHints() )
        {
            final String name = hint.getName().text();
            final String valueString = hint.getValue().text();
            Object parsedValue = valueString;
           
            if( name.equals( PropertyEditorDef.HINT_SHOW_HEADER ) ||
                name.equals( PropertyEditorDef.HINT_BORDER ) ||
                name.equals( PropertyEditorDef.HINT_BROWSE_ONLY ) ||
                name.equals( PropertyEditorDef.HINT_READ_ONLY ) )
            {
                parsedValue = Boolean.parseBoolean( valueString );
            }
            else if( name.startsWith( PropertyEditorDef.HINT_FACTORY ) ||
                     name.startsWith( PropertyEditorDef.HINT_AUX_TEXT_PROVIDER ) )
            {
                parsedValue = rootdef.resolveClass( valueString );
            }
            else if( name.equals( PropertyEditorDef.HINT_LISTENERS ) )
            {
                final List<Class<?>> contributors = new ArrayList<Class<?>>();
               
                for( String segment : valueString.split( "," ) )
                {
                    final Class<?> cl = rootdef.resolveClass( segment.trim() );
                   
                    if( cl != null )
                    {
                        contributors.add( cl );
                    }
                }
               
                parsedValue = contributors;
            }
           
            this.hints.put( name, parsedValue );
        }
       
        final ListFactory<FormComponentPart> relatedContentPartsListFactory = ListFactory.start();
       
        final Listener relatedContentPartListener = new FilteredListener<PartValidationEvent>()
        {
            @Override
            protected void handleTypedEvent( PartValidationEvent event )
            {
                refreshValidation();
            }
        };

        for( final FormComponentDef relatedContentPartDef : propertyEditorPartDef.getRelatedContent() )
        {
            final FormComponentPart relatedContentPart = (FormComponentPart) create( this, this.property.element(), relatedContentPartDef, this.params );
            relatedContentPart.attach( relatedContentPartListener );
            relatedContentPartsListFactory.add( relatedContentPart );
        }
       
        this.relatedContentParts = relatedContentPartsListFactory.result();
    }

    @Override
    protected Function initVisibleWhenFunction()
    {
        return AndFunction.create
        (
            super.initVisibleWhenFunction(),
            createVersionCompatibleFunction( property() )
        );
    }
   
    @Override
    public PropertyEditorDef definition()
    {
        return (PropertyEditorDef) super.definition();
    }
   
    @Override
    public Element getLocalModelElement()
    {
        return this.property.element();
    }
   
    public Property property()
    {
        return this.property;
    }
   
    public List<ModelPath> getChildProperties()
    {
        return this.childPropertyPaths;
    }
   
    public PropertyEditorPart getChildPropertyEditor( final Element element,
                                                      final PropertyDef property )
    {
        return getChildPropertyEditor( element, new ModelPath( property.name() ) );
    }
   
    public PropertyEditorPart getChildPropertyEditor( final Element element,
                                                      final ModelPath property )
    {
        Map<ModelPath,PropertyEditorPart> propertyEditorsForElement = this.childPropertyEditors.get( element );
       
        if( propertyEditorsForElement == null )
        {
            propertyEditorsForElement = new HashMap<ModelPath,PropertyEditorPart>();
            this.childPropertyEditors.put( element, propertyEditorsForElement );
           
            final Map<ModelPath,PropertyEditorPart> finalPropertyEditorsForElement = propertyEditorsForElement;
           
            element.attach
            (
                new FilteredListener<ElementDisposeEvent>()
                {
                    @Override
                    protected void handleTypedEvent( final ElementDisposeEvent event )
                    {
                        for( PropertyEditorPart propertyEditor : finalPropertyEditorsForElement.values() )
                        {
                            propertyEditor.dispose();
                        }
                       
                        PropertyEditorPart.this.childPropertyEditors.remove( element );
                    }
                }
            );
        }
       
        PropertyEditorPart childPropertyEditorPart = propertyEditorsForElement.get( property );
       
        if( childPropertyEditorPart == null )
        {
            PropertyEditorDef childPropertyEditorDef = ( (PropertyEditorDef) this.definition ).getChildPropertyEditor( property );
           
            if( childPropertyEditorDef == null )
            {
                childPropertyEditorDef = PropertyEditorDef.TYPE.instantiate();
                childPropertyEditorDef.setProperty( property.toString() );
            }
           
            childPropertyEditorPart = new PropertyEditorPart();
            childPropertyEditorPart.init( this, element, childPropertyEditorDef, this.params );
            childPropertyEditorPart.initialize();
           
            propertyEditorsForElement.put( property, childPropertyEditorPart );
        }
       
        return childPropertyEditorPart;
    }
   
    public String label()
    {
        return label( CapitalizationType.NO_CAPS, true );
    }
   
    public String label( final CapitalizationType capitalizationType, final boolean includeMnemonic )
    {
        final PropertyEditorDef def = definition();
       
        if( def.getShowLabel().content() )
        {
            if( this.labelFunctionResult == null )
            {
                this.labelFunctionResult = initExpression
                (
                    def.getLabel().content(),
                    String.class,
                    Literal.create( this.property.definition().getLabel( false, CapitalizationType.NO_CAPS, true ) ),
                    new Runnable()
                    {
                        public void run()
                        {
                            broadcast( new LabelChangedEvent( PropertyEditorPart.this ) );
                        }
                    }
                );
            }
           
            final String label = (String) this.labelFunctionResult.value();
            return LabelTransformer.transform( label, capitalizationType, includeMnemonic );
        }
       
        return null;
    }
   
    public boolean getSpanBothColumns()
    {
        return definition().getSpanBothColumns().content();
    }
   
    public int getWidth( final int defaultValue )
    {
        final Integer width = definition().getWidth().content();
        return ( width == null || width < 1 ? defaultValue : width );
    }
   
    public int getHeight( final int defaultValue )
    {
        final Integer height = definition().getHeight().content();
        return ( height == null || height < 1 ? defaultValue : height );
    }
   
    public int getMarginLeft()
    {
        int marginLeft = definition().getMarginLeft().content();
       
        if( marginLeft < 0 )
        {
            marginLeft = 0;
        }
       
        return marginLeft;
    }

    @SuppressWarnings( "unchecked" )
   
    public <T> T getRenderingHint( final String name,
                                   final T defaultValue )
    {
        final Object hintValue = this.hints == null ? null : this.hints.get( name );
        return hintValue == null ? defaultValue : (T) hintValue;
    }

    public boolean getRenderingHint( final String name,
                                     final boolean defaultValue )
    {
        final Object hintValue = this.hints == null ? null : this.hints.get( name );
        return hintValue == null ? defaultValue : (Boolean) hintValue;
    }
   
    public List<FormComponentPart> getRelatedContent()
    {
        return this.relatedContentParts;
    }
   
    public int getRelatedContentWidth()
    {
        final Value<Integer> relatedContentWidth = definition().getRelatedContentWidth();
       
        if( relatedContentWidth.validation().ok() )
        {
            return relatedContentWidth.content();
        }
        else
        {
            return relatedContentWidth.getDefaultContent();
        }
    }
   
    @Override
    public FormComponentPresentation createPresentation( final SwtPresentation parent, final Composite composite )
    {
        PropertyEditorPresentation presentation = null;
       
        try
        {
            final Class<PropertyEditorPresentationFactory> factoryClass
                = getRenderingHint( PropertyEditorDef.HINT_FACTORY, (Class<PropertyEditorPresentationFactory>) null );
           
            if( factoryClass != null )
            {
                final PropertyEditorPresentationFactory factory = factoryClass.newInstance();
                presentation = factory.create( this, parent, composite );
            }
        }
        catch( Exception e )
        {
            Sapphire.service( LoggingService.class ).log( e );
        }

        if( presentation == null )
        {
            for( final PropertyEditorPresentationFactory f : FACTORIES )
            {
                presentation = f.create( this, parent, composite );
               
                if( presentation != null )
                {
                    break;
                }
            }
        }
       
        if( presentation == null )
        {
            throw new IllegalStateException( this.property.toString() );
        }
       
        return presentation;
    }

    @Override
    protected Status computeValidation()
    {
        final Status.CompositeStatusFactory factory = Status.factoryForComposite();
       
        if( property().enabled() )
        {
            factory.merge( this.property.validation() );
           
            if( this.property instanceof ElementList )
            {
                for( final Element child : (ElementList<?>) this.property )
                {
                    factory.merge( child.validation() );
                }
            }
            else if( this.property instanceof ElementHandle )
            {
                final Element child = ( (ElementHandle<?>) this.property ).content();
               
                if( child != null )
                {
                    factory.merge( child.validation() );
                }
            }
        }
       
        for( SapphirePart relatedContentPart : this.relatedContentParts )
        {
            factory.merge( relatedContentPart.validation() );
        }
       
        return factory.create();
    }
   
    @Override
   
    public boolean setFocus()
    {
        if( property().enabled() )
        {
            broadcast( new FocusReceivedEvent( this ) );
            return true;
        }
       
        return false;
    }

    @Override
   
    public boolean setFocus( final ModelPath path )
    {
        final ModelPath.Segment head = path.head();
       
        if( head instanceof ModelPath.PropertySegment )
        {
            final String propertyName = ( (ModelPath.PropertySegment) head ).getPropertyName();
           
            if( propertyName.equals( this.property.name() ) )
            {
                return setFocus();
            }
        }
       
        return false;
    }

    public String getActionContext()
    {
        final String context;
       
        if( this.property.definition() instanceof ValueProperty )
        {
            context = SapphireActionSystem.CONTEXT_VALUE_PROPERTY_EDITOR;
        }
        else if( this.property.definition() instanceof ElementProperty )
        {
            context = SapphireActionSystem.CONTEXT_ELEMENT_PROPERTY_EDITOR;
        }
        else if( this.property.definition() instanceof ListProperty )
        {
            context = SapphireActionSystem.CONTEXT_LIST_PROPERTY_EDITOR;
        }
        else
        {
            throw new IllegalStateException();
        }
       
        return context;
    }

    @Override
    public Set<String> getActionContexts()
    {
        return Collections.singleton( getActionContext() );
    }
   
    public boolean isReadOnly()
    {
        return ( this.property.definition().isReadOnly() || getRenderingHint( PropertyEditorDef.HINT_READ_ONLY, false ) );       
    }

    @Override
    public void dispose()
    {
        if( this.propertyValidationListener != null && ! this.property.disposed() )
        {
            if( this.property instanceof ElementList )
            {
                this.property.detach( this.propertyValidationListener, "*" );
            }
            else
            {
                this.property.detach( this.propertyValidationListener );
            }
        }
       
        if( this.labelFunctionResult != null )
        {
            this.labelFunctionResult.dispose();
        }
       
        for( Map<ModelPath,PropertyEditorPart> propertyEditorsForElement : this.childPropertyEditors.values() )
        {
            for( PropertyEditorPart propertyEditor : propertyEditorsForElement.values() )
            {
                propertyEditor.dispose();
            }
        }
       
        super.dispose();
    }
   
}
TOP

Related Classes of org.eclipse.sapphire.ui.forms.PropertyEditorPart

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.