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