/******************************************************************************
* 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.ui;
import static org.eclipse.sapphire.modeling.util.MiscUtil.normalizeToEmptyString;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.sapphire.Disposable;
import org.eclipse.sapphire.LoggingService;
import org.eclipse.sapphire.Sapphire;
import org.eclipse.sapphire.java.JavaType;
import org.eclipse.sapphire.ui.def.ActionContextRef;
import org.eclipse.sapphire.ui.def.ActionContextsHostDef;
import org.eclipse.sapphire.ui.def.ActionDef;
import org.eclipse.sapphire.ui.def.ActionHandlerDef;
import org.eclipse.sapphire.ui.def.ActionHandlerFactoryDef;
import org.eclipse.sapphire.ui.def.ActionHandlerFilterDef;
import org.eclipse.sapphire.ui.def.ISapphireConditionHostDef;
import org.eclipse.sapphire.ui.def.PartDef;
import org.eclipse.sapphire.ui.util.TopologicalSorter;
/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
public final class SapphireActionGroup implements Disposable
{
private final ISapphirePart part;
private final String context;
private final List<SapphireAction> actions;
public SapphireActionGroup( final ISapphirePart part,
final String context )
{
this.part = part;
this.context = context;
this.actions = new CopyOnWriteArrayList<SapphireAction>();
final PartDef partDef = this.part.definition();
createActions( SapphireExtensionSystem.getActions() );
if( partDef != null )
{
createActions( partDef.getActions() );
}
createActionHandlers( SapphireExtensionSystem.getActionHandlers() );
createActionHandlersFromFactories( SapphireExtensionSystem.getActionHandlerFactories() );
if( partDef != null )
{
createActionHandlers( partDef.getActionHandlers() );
createActionHandlersFromFactories( partDef.getActionHandlerFactories() );
createActionHandlerFilters( partDef.getActionHandlerFilters() );
}
}
public ISapphirePart getPart()
{
return this.part;
}
public String getContext()
{
return this.context;
}
public List<SapphireAction> getActiveActions()
{
final TopologicalSorter<SapphireAction> sorter = new TopologicalSorter<SapphireAction>();
for( SapphireAction action : this.actions )
{
if( action.hasActiveHandlers() )
{
final TopologicalSorter.Entity actionEntity = sorter.entity( action.getId(), action );
for( SapphireActionLocationHint locationHint : action.getLocationHints() )
{
actionEntity.constraint( locationHint.toString() );
}
}
}
return Collections.unmodifiableList( sorter.sort() );
}
public List<SapphireAction> getActions()
{
// Sort actions in two passes. First pass buckets actions by groups.
final Map<String,List<SapphireAction>> buckets = new LinkedHashMap<String,List<SapphireAction>>();
for( SapphireAction action : this.actions )
{
final String group = normalizeToEmptyString( action.getGroup() );
List<SapphireAction> bucket = buckets.get( group );
if( bucket == null )
{
bucket = new ArrayList<SapphireAction>();
buckets.put( group, bucket );
}
bucket.add( action );
}
// Second pass performs a topological sort using before and after location hints.
final TopologicalSorter<SapphireAction> sorter = new TopologicalSorter<SapphireAction>();
for( List<SapphireAction> bucket : buckets.values() )
{
for( SapphireAction action : bucket )
{
final TopologicalSorter.Entity actionEntity = sorter.entity( action.getId(), action );
for( SapphireActionLocationHint locationHint : action.getLocationHints() )
{
actionEntity.constraint( locationHint.toString() );
}
}
}
return Collections.unmodifiableList( sorter.sort() );
}
public void addAction( final SapphireAction action )
{
this.actions.add( action );
}
public void removeAction( final SapphireAction action )
{
this.actions.remove( action );
}
public SapphireAction getAction( final String id )
{
for( SapphireAction action : this.actions )
{
if( id.equalsIgnoreCase( action.getId() ) )
{
return action;
}
}
return null;
}
public boolean isEmpty()
{
for( SapphireAction action : this.actions )
{
if( action.hasActiveHandlers() )
{
return false;
}
}
return true;
}
public void addFilter( final SapphireActionHandlerFilter filter )
{
for( SapphireAction action : this.actions )
{
action.addFilter( filter );
}
}
public void removeFilter( final SapphireActionHandlerFilter filter )
{
for( SapphireAction action : this.actions )
{
action.removeFilter( filter );
}
}
@Override
public void dispose()
{
for( SapphireAction action : this.actions )
{
action.dispose();
}
}
private void createActions( final List<ActionDef> defs )
{
for( ActionDef def : defs )
{
if( isForContext( def ) && checkCondition( def ) )
{
final SapphireAction action = new SapphireAction();
action.init( this, def );
addAction( action );
}
}
}
private void createActionHandlers( final List<ActionHandlerDef> defs )
{
for( ActionHandlerDef def : defs )
{
final SapphireAction action = getAction( def.getAction().content() );
if( action != null && isForContext( def ) && checkCondition( def ) )
{
try
{
final JavaType implType = def.getImplClass().target();
if( implType != null )
{
final Class<?> implClass = (Class<?>) implType.artifact();
if( implClass != null )
{
final SapphireActionHandler handler = (SapphireActionHandler) implClass.newInstance();
handler.init( action, def );
action.addHandler( handler );
}
}
}
catch( Exception e )
{
Sapphire.service( LoggingService.class ).log( e );
}
}
}
}
private void createActionHandlersFromFactories( final List<ActionHandlerFactoryDef> defs )
{
for( ActionHandlerFactoryDef def : defs )
{
final SapphireAction action = getAction( def.getAction().content() );
if( action != null && isForContext( def ) && checkCondition( def ) )
{
try
{
final JavaType implType = def.getImplClass().target();
if( implType != null )
{
final Class<?> implClass = (Class<?>) implType.artifact();
if( implClass != null )
{
final SapphireActionHandlerFactory factory = (SapphireActionHandlerFactory) implClass.newInstance();
factory.init( action, def );
action.addHandlerFactory( factory );
}
}
}
catch( Exception e )
{
Sapphire.service( LoggingService.class ).log( e );
}
}
}
}
private void createActionHandlerFilters( final List<ActionHandlerFilterDef> defs )
{
for( ActionHandlerFilterDef def : defs )
{
if( isForContext( def ) )
{
try
{
final JavaType implClass = def.getImplClass().target();
if( implClass != null )
{
final SapphireActionHandlerFilter filter = (SapphireActionHandlerFilter) ( (Class<?>) implClass.artifact() ).newInstance();
addFilter( filter );
}
}
catch( Exception e )
{
Sapphire.service( LoggingService.class ).log( e );
}
}
}
}
private boolean checkCondition( final ISapphireConditionHostDef def )
{
try
{
final JavaType conditionType = def.getConditionClass().target();
if( conditionType != null )
{
final Class<?> conditionClass = (Class<?>) conditionType.artifact();
if( conditionClass != null )
{
try( SapphireCondition condition = SapphireCondition.create( this.part, conditionClass, null ) )
{
if( condition != null )
{
return condition.getConditionState();
}
}
}
}
return true;
}
catch( Exception e )
{
Sapphire.service( LoggingService.class ).log( e );
return false;
}
}
private boolean isForContext( final ActionContextsHostDef def )
{
if( ! def.getContexts().isEmpty() )
{
for( ActionContextRef ctxt : def.getContexts() )
{
if( this.context.equalsIgnoreCase( ctxt.getContext().content() ) )
{
return true;
}
}
return false;
}
return true;
}
}