/****************************************************************************** * 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.services; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.sapphire.Disposable; import org.eclipse.sapphire.EventDeliveryJob; import org.eclipse.sapphire.JobQueue; import org.eclipse.sapphire.modeling.internal.SapphireModelingExtensionSystem; import org.eclipse.sapphire.modeling.internal.SapphireModelingExtensionSystem.ServiceExtension; import org.eclipse.sapphire.modeling.util.DependencySorter; import org.eclipse.sapphire.util.ListFactory; /** * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public class ServiceContext implements Disposable { public static final String ID_ROOT = "Sapphire"; public static final String ID_ELEMENT_INSTANCE = "Sapphire.Element.Instance"; public static final String ID_ELEMENT_METAMODEL = "Sapphire.Element.MetaModel"; public static final String ID_PROPERTY_INSTANCE = "Sapphire.Property.Instance"; public static final String ID_PROPERTY_METAMODEL = "Sapphire.Property.MetaModel"; private final String type; private final ServiceContext parent; private List<ServiceProxy> services; /** * Cache of previous service lookups. */ private Map<Class<?>,List<? extends Service>> cache; private final JobQueue<EventDeliveryJob> queue; /** * The object that should be used for synchronization by all of this context's services. */ private final Object lock; private boolean disposed = false; public ServiceContext( final String type ) { this( type, null, null, null ); } public ServiceContext( final String type, final ServiceContext parent ) { this( type, parent, null, null ); } public ServiceContext( final String type, final ServiceContext parent, final Object lock, final JobQueue<EventDeliveryJob> queue ) { this.type = type; this.parent = parent; this.lock = ( lock == null ? this : lock ); this.queue = ( queue == null ? new JobQueue<EventDeliveryJob>() : queue ); } public final String type() { return this.type; } public final ServiceContext parent() { return this.parent; } public <T> T find( final Class<T> type ) { return null; } public final <S extends Service> S service( final Class<S> type ) { if( type == null ) { throw new IllegalArgumentException(); } if( this.disposed ) { throw new IllegalStateException(); } final List<S> services = services( type ); return ( services.isEmpty() ? null : services.get( 0 ) ); } @SuppressWarnings( "unchecked" ) public final <S extends Service> List<S> services( final Class<S> type ) { synchronized( this.lock ) { if( type == null ) { throw new IllegalArgumentException(); } if( this.disposed ) { throw new IllegalStateException(); } if( this.cache != null ) { final List<? extends Service> services = this.cache.get( type ); if( services != null ) { return (List<S>) services; } } if( this.services == null ) { final ListFactory<ServiceProxy> services = ListFactory.start(); services.add( local() ); for( final ServiceExtension extension : SapphireModelingExtensionSystem.services() ) { if( extension.contexts().contains( this.type ) ) { services.add ( new ServiceProxy ( this, extension.id(), extension.implementation(), extension.condition(), extension.overrides(), null ) ); } } this.services = new CopyOnWriteArrayList<ServiceProxy>( services.result() ); } final DependencySorter<String,S> sorter = new DependencySorter<String,S>(); final ListFactory<ServiceProxy> failed = ListFactory.start(); for( final ServiceProxy proxy : this.services ) { if( type.isAssignableFrom( proxy.type() ) ) { if( sorter.contains( proxy.id() ) ) { failed.add( proxy ); } else { final S service = type.cast( proxy.service() ); if( service == null ) { failed.add( proxy ); } else { sorter.add( service.id(), service ); for( final String override : service.overrides() ) { sorter.dependency( override, service.id() ); } } } } } this.services.removeAll( failed.result() ); if( this.parent != null ) { for( final S service : this.parent.services( type ) ) { if( ! sorter.contains( service.id() ) ) { sorter.add( service.id(), service ); for( final String override : service.overrides() ) { sorter.dependency( override, service.id() ); } } } } final List<S> services = sorter.sort(); for( final Service service : services ) { service.initIfNecessary(); } if( this.cache == null ) { this.cache = new IdentityHashMap<Class<?>,List<? extends Service>>(); } this.cache.put( type, services ); return services; } } /** * Returns the object that should be used for synchronization by all of this context's services. * * @return the object that should be used for synchronization by all of this context's services */ public final Object lock() { return this.lock; } final JobQueue<EventDeliveryJob> queue() { return this.queue; } protected List<ServiceProxy> local() { return Collections.emptyList(); } @Override public final void dispose() { this.disposed = true; for( final ServiceProxy service : this.services ) { service.dispose(); } this.services = null; } }