1// Copyright (C) 2018 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15export interface HasKind { 16 kind: string; 17} 18 19export class RegistryError extends Error { 20 constructor(message?: string) { 21 super(message); 22 this.name = this.constructor.name; 23 } 24} 25 26export class Registry<T> { 27 private key: (t: T) => string; 28 protected registry: Map<string, T>; 29 30 static kindRegistry<T extends HasKind>(): Registry<T> { 31 return new Registry<T>((t) => t.kind); 32 } 33 34 constructor(key: (t: T) => string) { 35 this.registry = new Map<string, T>(); 36 this.key = key; 37 } 38 39 register(registrant: T): Disposable { 40 const kind = this.key(registrant); 41 if (this.registry.has(kind)) { 42 throw new RegistryError( 43 `Registrant ${kind} already exists in the registry`, 44 ); 45 } 46 this.registry.set(kind, registrant); 47 48 return { 49 [Symbol.dispose]: () => this.registry.delete(kind), 50 }; 51 } 52 53 has(kind: string): boolean { 54 return this.registry.has(kind); 55 } 56 57 get(kind: string): T { 58 const registrant = this.registry.get(kind); 59 if (registrant === undefined) { 60 throw new RegistryError(`${kind} has not been registered.`); 61 } 62 return registrant; 63 } 64 65 tryGet(kind: string): T | undefined { 66 return this.registry.get(kind); 67 } 68 69 // Support iteration: for (const foo of fooRegistry.values()) { ... } 70 *values() { 71 yield* this.registry.values(); 72 } 73 74 valuesAsArray(): ReadonlyArray<T> { 75 return Array.from(this.values()); 76 } 77 78 unregisterAllForTesting(): void { 79 this.registry.clear(); 80 } 81} 82