|
| 1 | +import * as fs from "fs-extra"; |
| 2 | +import * as jsonConfig from "./annotationConfig"; |
| 3 | +import { ConfigParseError } from "./configParseError"; |
| 4 | +import * as model from "./model"; |
| 5 | +import { lowerInitialChar } from "./utils"; |
| 6 | + |
| 7 | +interface BeanWithName { |
| 8 | + qualifiedClassName: string; |
| 9 | + bean: jsonConfig.Bean; |
| 10 | +} |
| 11 | + |
| 12 | +const beansById = new Map<string, BeanWithName>(); |
| 13 | + |
| 14 | +export async function collectBeansFromConfigFile(filePath: string): Promise<string[]> { |
| 15 | + let json: string; |
| 16 | + try { |
| 17 | + json = await fs.readFile(filePath, "utf8"); |
| 18 | + } catch (err) { |
| 19 | + throw new ConfigParseError(`Can't open input file '${filePath}': ${err.message}`); |
| 20 | + } |
| 21 | + const annotationConfig: jsonConfig.AnnotationConfig = JSON.parse(json); |
| 22 | + for (const qualifiedClassName in annotationConfig.beans) { |
| 23 | + if (!annotationConfig.beans.hasOwnProperty(qualifiedClassName)) |
| 24 | + continue; |
| 25 | + // Create bean ID |
| 26 | + // TODO: Handle name parameter to @Component constructor |
| 27 | + const id = lowerInitialChar(<string>qualifiedClassName.split(".").pop()); |
| 28 | + // Check for duplicate ids from other beans |
| 29 | + if (beansById.has(id)) |
| 30 | + throw new ConfigParseError(`Found multiple beans with id '${id}'`); |
| 31 | + const aliasedBeanId = model.Bean.getAlias(id); |
| 32 | + if (aliasedBeanId !== undefined) |
| 33 | + throw new ConfigParseError(`Found a bean with id same as an alias for '${aliasedBeanId}'`); |
| 34 | + // Create a bean model for the bean |
| 35 | + const bean = { qualifiedClassName: qualifiedClassName, bean: annotationConfig.beans[qualifiedClassName] }; |
| 36 | + beansById.set(id, bean); |
| 37 | + model.Bean.registerIdForClass(bean.qualifiedClassName, id); |
| 38 | + } |
| 39 | + // TODO: Return paths to XML config files specified in code |
| 40 | + return []; |
| 41 | +} |
| 42 | + |
| 43 | +export function beanWithIdExists(id: string): boolean { |
| 44 | + return beansById.has(id); |
| 45 | +} |
| 46 | + |
| 47 | +export function parseAllBeans(): void { |
| 48 | + for (const [ beanId, bean ] of beansById) { |
| 49 | + if (model.Bean.tryGet(beanId) !== undefined) |
| 50 | + continue; |
| 51 | + // We don't store the results of this parsing, it is stored in internal data structures which do enough for us |
| 52 | + parseJsonBean(beanId, bean); |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +export function parseBean(id: string): model.Bean { |
| 57 | + const bean = beansById.get(id); |
| 58 | + if (bean === undefined) |
| 59 | + throw new Error("Called parseBean on a bean that did not exist"); |
| 60 | + return parseJsonBean(id, bean); |
| 61 | +} |
| 62 | + |
| 63 | +function parseJsonBean(id: string | undefined, namedBean: BeanWithName): model.Bean { |
| 64 | + const bean = namedBean.bean; |
| 65 | + const properties: model.BeanProperty[] = []; |
| 66 | + for (const fieldName in bean.fields) { |
| 67 | + if (!bean.fields.hasOwnProperty(fieldName)) |
| 68 | + continue; |
| 69 | + const fieldClass = bean.fields[fieldName].type; |
| 70 | + |
| 71 | + const fieldBeanId = model.Bean.tryGetIdByClass(fieldClass); |
| 72 | + if (fieldBeanId === undefined) |
| 73 | + throw new ConfigParseError( |
| 74 | + `Autowired field '${namedBean.qualifiedClassName}.${fieldName}' depends on a class '${fieldClass}' ` |
| 75 | + + `that has ${model.Bean.hasMultipleBeansForClass(fieldClass) ? "multiple" : "no"} implementations`); |
| 76 | + properties.push({ name: fieldName, value: new model.BeanRefValue(fieldBeanId) }); |
| 77 | + } |
| 78 | + // TODO: Handle constructor arguments |
| 79 | + return new model.Bean(undefined, id, namedBean.qualifiedClassName, model.BeanCreationPolicy.Singleton, [], properties); |
| 80 | +} |
0 commit comments