001package arez.persist.runtime; 002 003import arez.SafeProcedure; 004import arez.persist.StoreTypes; 005import grim.annotations.OmitClinit; 006import grim.annotations.OmitSymbol; 007import javax.annotation.Nonnull; 008import javax.annotation.Nullable; 009import static org.realityforge.braincheck.Guards.*; 010 011/** 012 * Provide an interface to register and access stores and access the root scope as well as global configuration settings. 013 */ 014@OmitClinit 015public final class ArezPersist 016{ 017 private ArezPersist() 018 { 019 } 020 021 /** 022 * Return true if apiInvariants will be checked. 023 * 024 * @return true if apiInvariants will be checked. 025 */ 026 @OmitSymbol 027 public static boolean shouldCheckApiInvariants() 028 { 029 return ArezPersistConfig.shouldCheckApiInvariants(); 030 } 031 032 /** 033 * Return true if the in-memory application store should be registered by the framework. 034 * 035 * @return true if the in-memory application store should be registered by the framework. 036 */ 037 @OmitSymbol 038 public static boolean isApplicationStoreEnabled() 039 { 040 return ArezPersistConfig.isApplicationStoreEnabled(); 041 } 042 043 /** 044 * Return the root scope under which all other scopes are nested. 045 * 046 * @return the root scope under which all other scopes are nested. 047 */ 048 @Nonnull 049 public static Scope getRootScope() 050 { 051 return Registry.getRootScope(); 052 } 053 054 /** 055 * Register a store with specified name and storage service. 056 * It is an error to register multiple stores with the same name. 057 * 058 * <p>As part of the register operation, the store will attempt to restore state from the storage service. 059 * If an error occurs during the restore, then the error will be logged and registration will complete.</p> 060 * 061 * @param name the name of the store. 062 * @param service the associated StorageService. 063 * @return the action to invoke to deregister service. 064 */ 065 @Nonnull 066 public static SafeProcedure registerStore( @Nonnull final String name, @Nonnull final StorageService service ) 067 { 068 return Registry.registerStore( name, service ); 069 } 070 071 /** 072 * Return the store that is registered with the specified name. 073 * It is an error to invoke this method without registering a store under this name. 074 * 075 * @param name the name of the store. 076 * @return the store. 077 */ 078 @Nonnull 079 public static Store getStore( @Nonnull final String name ) 080 { 081 return Registry.getStore( name ); 082 } 083 084 /** 085 * Find the scope with the specified name. 086 * The name can actually consist of name components separated by a "." character. Each name component is 087 * nested within the scope identified by the prior name component. i.e. The name "dashboard.finance.entry" 088 * will look for the scope named "entry" nested in a scope named "finance" nested in a scope named "dashboard". 089 * 090 * @param qualifiedName the qualified scope name. 091 * @return the scope if it exists. 092 */ 093 @Nullable 094 public static Scope findScope( @Nonnull final String qualifiedName ) 095 { 096 Scope scope = getRootScope(); 097 if ( Scope.ROOT_SCOPE_NAME.equals( qualifiedName ) ) 098 { 099 return scope; 100 } 101 int start = 0; 102 int end; 103 while ( -1 != ( end = qualifiedName.indexOf( '.', start ) ) ) 104 { 105 scope = scope.findScope( qualifiedName.substring( start, end ) ); 106 if ( null == scope ) 107 { 108 return null; 109 } 110 else 111 { 112 start = end + 1; 113 } 114 } 115 return scope.findScope( qualifiedName.substring( start ) ); 116 } 117 118 /** 119 * Find the scope with the specified name and if it does not exist then create it. 120 * The name can actually consist of name components separated by a "." character. Each name component is 121 * nested within the scope identified by the prior name component. i.e. The name "dashboard.finance.entry" 122 * will look for the scope named "entry" nested in a scope named "finance" nested in a scope named "dashboard". 123 * 124 * @param qualifiedName the qualified scope name. 125 * @return the scope. 126 */ 127 @Nonnull 128 public static Scope findOrCreateScope( @Nonnull final String qualifiedName ) 129 { 130 Scope scope = getRootScope(); 131 if ( Scope.ROOT_SCOPE_NAME.equals( qualifiedName ) ) 132 { 133 return scope; 134 } 135 int start = 0; 136 int end; 137 while ( -1 != ( end = qualifiedName.indexOf( '.', start ) ) ) 138 { 139 scope = scope.findOrCreateScope( qualifiedName.substring( start, end ) ); 140 start = end + 1; 141 } 142 return scope.findOrCreateScope( qualifiedName.substring( start ) ); 143 } 144 145 /** 146 * Dispose the specified scope. 147 * A dispose operation first performs a {@link #releaseScope(Scope)} on the scope, then attempts to 148 * dispose all nested scopes and finally disposes the specified scope. A disposed scope should no longer be 149 * used to store state. It is an error to attempt to dispose the root scope. 150 * 151 * @param scope the scope to dispose. 152 */ 153 public static void disposeScope( @Nonnull final Scope scope ) 154 { 155 if ( ArezPersist.shouldCheckApiInvariants() ) 156 { 157 apiInvariant( () -> !Scope.ROOT_SCOPE_NAME.equals( scope.getName() ), 158 () -> "disposeScope() invoked with the root scope" ); 159 } 160 Registry.disposeScope( scope ); 161 } 162 163 /** 164 * Release the specified scope. 165 * A release operation removes any state associated with the scope and any nested scope. 166 * 167 * @param scope the scope to release. 168 */ 169 public static void releaseScope( @Nonnull final Scope scope ) 170 { 171 Registry.releaseScope( scope ); 172 } 173 174 /** 175 * Register a converter for a type. 176 * It is an error to register multiple converters with the same name. 177 * 178 * @param type the application type. 179 * @param converter the converter. 180 * @return the action to invoke to deregister converter. 181 * @param <A> the type of the value. 182 */ 183 @Nonnull 184 public static <A> SafeProcedure registerConverter( @Nonnull final Class<A> type, 185 @Nonnull final Converter<A, ?> converter ) 186 { 187 return Registry.registerConverter( type, converter ); 188 } 189 190 /** 191 * Return the converter registered for the specified application type or the identity converter if none are specified. 192 * 193 * @param type the application type. 194 * @return the converter if any. 195 * @param <A> the type of the value. 196 */ 197 @Nonnull 198 public static <A> Converter<A, ?> getConverter( @Nonnull final Class<A> type ) 199 { 200 return Registry.getConverter( type ); 201 } 202 203 static void registerApplicationStoreIfEnabled() 204 { 205 if ( isApplicationStoreEnabled() ) 206 { 207 registerStore( StoreTypes.APPLICATION, new NoopStorageService() ); 208 } 209 } 210}