/****************************************************************************** * 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 static org.eclipse.sapphire.modeling.util.MiscUtil.equal; import org.eclipse.sapphire.ElementType; import org.eclipse.sapphire.LocalizableText; import org.eclipse.sapphire.PropertyDef; import org.eclipse.sapphire.Text; /** * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public abstract class DataService<T> extends Service { private static final Object INITIAL_DATA = new Object(); @Text( "Reentrant call detected during refresh in {0}" ) private static LocalizableText reentrantRefreshMessage; @Text( "{0} data accessed prior to service initialization" ) private static LocalizableText dataAccessedPriorToInitMessage; @Text( "{0}: {1}.compute() has failed" ) private static LocalizableText computeFailedMessage; static { LocalizableText.init( DataService.class ); } @SuppressWarnings( "unchecked" ) private T data = (T) INITIAL_DATA; private boolean initialized; private boolean refreshing; @Override protected final void init() { synchronized( context().lock() ) { initDataService(); this.initialized = true; } } protected void initDataService() { } public final T data() { synchronized( context().lock() ) { if( ! this.initialized ) { throw new IllegalStateException( dataAccessedPriorToInitMessage.format( getClass().getSimpleName() ) ); } if( this.data == INITIAL_DATA ) { refresh(); } return this.data; } } protected abstract T compute(); protected final void refresh() { boolean broadcast = false; synchronized( context().lock() ) { if( this.refreshing ) { throw new IllegalStateException( reentrantRefreshMessage.format( getClass().getSimpleName() ) ); } this.refreshing = true; try { final T newData; try { newData = compute(); } catch( Exception e ) { final ServiceContext context = context(); final String contextLabel; final PropertyDef property = context.find( PropertyDef.class ); if( property != null ) { contextLabel = property.getModelElementType().getSimpleName() + "." + property.name(); } else { final ElementType type = context.find( ElementType.class ); if( type != null ) { contextLabel = type.getSimpleName(); } else { contextLabel = context.getClass().getSimpleName(); } } throw new RuntimeException( computeFailedMessage.format( contextLabel, getClass().getSimpleName() ), e ); } if( this.data == INITIAL_DATA ) { this.data = newData; } else if( ! equal( this.data, newData ) ) { this.data = newData; broadcast = true; } } finally { this.refreshing = false; } } if( broadcast ) { broadcast(); } } }