| // See file LICENSE for more information. |
| |
| import 'package:pointycastle/api.dart'; |
| import 'package:pointycastle/src/registry/registration.dart'; |
| |
| final FactoryRegistry registry = _RegistryImpl(); |
| |
| abstract class FactoryRegistry { |
| T create<T>(String registrableName); |
| |
| void register<T>(FactoryConfig config); |
| } |
| |
| typedef /*Registrable*/ RegistrableConstructor = Function(); |
| typedef DynamicConstructorFactory = RegistrableConstructor Function( |
| String registrableName, Match match); |
| |
| abstract class FactoryConfig { |
| final Type type; |
| |
| FactoryConfig(this.type); |
| } |
| |
| class StaticFactoryConfig extends FactoryConfig { |
| final String algorithmName; |
| final RegistrableConstructor factory; |
| |
| StaticFactoryConfig(super.type, this.algorithmName, this.factory); |
| } |
| |
| // From the PatternCharacter rule here: |
| // http://ecma-international.org/ecma-262/5.1/#sec-15.10 |
| final _specialRegExpChars = RegExp(r'([\\\^\$\.\|\+\[\]\(\)\{\}])'); |
| |
| /// Escapes special regular expression characters in [str] so that it can be |
| /// used as a literal match inside of a [RegExp]. |
| /// |
| /// The special characters are: \ ^ $ . | + [ ] ( ) { } |
| /// as defined here: http://ecma-international.org/ecma-262/5.1/#sec-15.10 |
| String _escapeRegExp(String str) => str.splitMapJoin(_specialRegExpChars, |
| onMatch: (Match m) => '\\${m.group(0)}', onNonMatch: (s) => s); |
| |
| class DynamicFactoryConfig extends FactoryConfig { |
| final RegExp regExp; |
| final DynamicConstructorFactory factory; |
| |
| DynamicFactoryConfig(super.type, this.regExp, this.factory); |
| |
| DynamicFactoryConfig.regex( |
| Type type, String regexString, DynamicConstructorFactory factory) |
| : this(type, RegExp(regexString), factory); |
| |
| /// A dynamic registry that matches by prefix. |
| /// The part after the prefix will be in `match.group(1)`. |
| DynamicFactoryConfig.prefix( |
| Type type, String prefix, DynamicConstructorFactory factory) |
| : this.regex(type, '^${_escapeRegExp(prefix)}(.+)\$', factory); |
| |
| /// A dynamic registry that matches by suffix. |
| /// The part before the suffix will be in `match.group(1)`. |
| DynamicFactoryConfig.suffix( |
| Type type, String suffix, DynamicConstructorFactory factory) |
| : this.regex(type, '^(.+)${_escapeRegExp(suffix)}\$', factory); |
| |
| /// Invokes the factory when it matches. Else returns null. |
| RegistrableConstructor? tryFactory(String algorithmName) { |
| Match? match = regExp.firstMatch(algorithmName); |
| if (match == null) { |
| return null; |
| } |
| return factory(algorithmName, match); |
| } |
| } |
| |
| class _RegistryImpl implements FactoryRegistry { |
| static const int _CONSTRUCTOR_CACHE_SIZE = 25; |
| |
| final Map<Type, Map<String, RegistrableConstructor>> _staticFactories; |
| final Map<Type, Set<DynamicFactoryConfig>> _dynamicFactories; |
| |
| final Map<String, RegistrableConstructor> _constructorCache = |
| <String, RegistrableConstructor>{}; |
| |
| bool _initialized = false; |
| |
| _RegistryImpl() |
| : _staticFactories = <Type, Map<String, RegistrableConstructor>>{}, |
| _dynamicFactories = <Type, Set<DynamicFactoryConfig>>{}; |
| |
| @override |
| T create<T>(String registrableName) { |
| var type = T; |
| var constructor = getConstructor(type, registrableName); |
| var result = constructor() as T; |
| return result; |
| } |
| |
| RegistrableConstructor getConstructor(Type type, String registrableName) { |
| var constructor = _constructorCache['$type.$registrableName']; |
| |
| if (constructor == null) { |
| constructor = _createConstructor(type, registrableName); |
| if (_constructorCache.length > _CONSTRUCTOR_CACHE_SIZE) { |
| _constructorCache.clear(); |
| } |
| _constructorCache['$type.$registrableName'] = constructor!; |
| } |
| return constructor; |
| } |
| |
| RegistrableConstructor? _createConstructor( |
| Type type, String registrableName) { |
| // Init lazily |
| _checkInit(); |
| |
| if (_staticFactories.containsKey(type) && |
| _staticFactories[type]!.containsKey(registrableName)) { |
| return _staticFactories[type]![registrableName]; |
| } |
| |
| if (_dynamicFactories.containsKey(type)) { |
| for (var factory in _dynamicFactories[type]!) { |
| var constructor = factory.tryFactory(registrableName); |
| if (constructor != null) { |
| return constructor; |
| } |
| } |
| } |
| |
| // No factory found |
| throw RegistryFactoryException.unknown(registrableName, type); |
| } |
| |
| void _checkInit() { |
| if (!_initialized) { |
| _initialize(); |
| } |
| } |
| |
| @override |
| void register<T>(FactoryConfig config) { |
| if (config is StaticFactoryConfig) { |
| _addStaticFactoryConfig(config); |
| } else if (config is DynamicFactoryConfig) { |
| _addDynamicFactoryConfig(config); |
| } |
| } |
| |
| void _addStaticFactoryConfig(StaticFactoryConfig config) { |
| Map factories = _staticFactories.putIfAbsent( |
| config.type, () => <String, RegistrableConstructor>{}); |
| factories[config.algorithmName] = config.factory; |
| } |
| |
| void _addDynamicFactoryConfig(DynamicFactoryConfig config) { |
| Set factories = _dynamicFactories.putIfAbsent( |
| config.type, () => <DynamicFactoryConfig>{}); |
| factories.add(config); |
| } |
| |
| void _initialize() { |
| registerFactories(this); |
| _initialized = true; |
| } |
| } |