Refactor type building
We now always construct a "unique" type before reconstituting the original type's children. That way we can't get into direct loops anymore that we need to avoid with obscure machinery.
This commit is contained in:
Родитель
50471d767a
Коммит
18b17ad01b
|
@ -50,7 +50,7 @@ function lookupKey(accessors: AccessorNames, key: string, language: string): [st
|
|||
|
||||
export function objectPropertyNames(o: ObjectType, language: string): Map<string, [string, boolean] | undefined> {
|
||||
const accessors = accessorNamesTypeAttributeKind.tryGetInAttributes(o.getAttributes());
|
||||
const map = o.properties;
|
||||
const map = o.getProperties();
|
||||
if (accessors === undefined) return map.map(_ => undefined);
|
||||
return map.map((_cp, n) => lookupKey(accessors, n, language));
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ function typeSetsCanBeCombined(s1: OrderedSet<Type>, s2: OrderedSet<Type>): bool
|
|||
}
|
||||
|
||||
function canBeCombined(c1: ClassType, c2: ClassType): boolean {
|
||||
const p1 = c1.properties;
|
||||
const p2 = c2.properties;
|
||||
const p1 = c1.getProperties();
|
||||
const p2 = c2.getProperties();
|
||||
if (p1.size < p2.size * REQUIRED_OVERLAP || p2.size < p1.size * REQUIRED_OVERLAP) {
|
||||
return false;
|
||||
}
|
||||
|
@ -127,7 +127,8 @@ export function combineClasses(
|
|||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
alphabetizeProperties: boolean,
|
||||
conflateNumbers: boolean
|
||||
conflateNumbers: boolean,
|
||||
debugPrintReconstitution: boolean
|
||||
): TypeGraph {
|
||||
const cliques = findSimilarityCliques(graph, false);
|
||||
|
||||
|
@ -148,5 +149,12 @@ export function combineClasses(
|
|||
);
|
||||
}
|
||||
|
||||
return graph.rewrite("combine classes", stringTypeMapping, alphabetizeProperties, cliques, makeCliqueClass);
|
||||
return graph.rewrite(
|
||||
"combine classes",
|
||||
stringTypeMapping,
|
||||
alphabetizeProperties,
|
||||
cliques,
|
||||
debugPrintReconstitution,
|
||||
makeCliqueClass
|
||||
);
|
||||
}
|
||||
|
|
|
@ -347,7 +347,8 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
let ns: Namespace | undefined;
|
||||
|
||||
const accessorNames = objectPropertyNames(o, this.targetLanguage.name);
|
||||
const names = o.sortedProperties
|
||||
const names = o
|
||||
.getSortedProperties()
|
||||
.map((p, jsonName) => {
|
||||
const [assignedName, isFixed] = getAccessorName(accessorNames, jsonName);
|
||||
let name: Name | undefined;
|
||||
|
@ -452,7 +453,8 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
const names = this.names;
|
||||
if (t instanceof ClassType) {
|
||||
const propertyNameds = defined(this._propertyNamesStoreView).get(t);
|
||||
const sortedMap = t.properties
|
||||
const sortedMap = t
|
||||
.getProperties()
|
||||
.filter((_, n) => propertyNameds.get(n) !== undefined)
|
||||
.map(p => p.type)
|
||||
.sortBy((_, n) => defined(names.get(defined(propertyNameds.get(n)))));
|
||||
|
@ -602,11 +604,11 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
if (this._alphabetizeProperties) {
|
||||
const alphabetizedPropertyNames = propertyNames.sortBy(n => this.names.get(n)).toOrderedMap();
|
||||
this.forEachWithBlankLines(alphabetizedPropertyNames, blankLocations, (name, jsonName) => {
|
||||
const p = defined(o.properties.get(jsonName));
|
||||
const p = defined(o.getProperties().get(jsonName));
|
||||
f(name, jsonName, p);
|
||||
});
|
||||
} else {
|
||||
this.forEachWithBlankLines(o.properties, blankLocations, (p, jsonName) => {
|
||||
this.forEachWithBlankLines(o.getProperties(), blankLocations, (p, jsonName) => {
|
||||
const name = defined(propertyNames.get(jsonName));
|
||||
f(name, jsonName, p);
|
||||
});
|
||||
|
@ -806,7 +808,7 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
this._haveMaps = types.some(t => t instanceof MapType);
|
||||
this._haveOptionalProperties = types
|
||||
.filter(t => t instanceof ClassType)
|
||||
.some(c => (c as ClassType).properties.some(p => p.isOptional));
|
||||
.some(c => (c as ClassType).getProperties().some(p => p.isOptional));
|
||||
this._namedTypes = this._declarationIR.declarations.filter(d => d.kind === "define").map(d => d.type);
|
||||
const { objects, enums, unions } = separateNamedTypes(this._namedTypes);
|
||||
this._namedObjects = objects;
|
||||
|
|
|
@ -13,7 +13,8 @@ export function flattenUnions(
|
|||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
conflateNumbers: boolean,
|
||||
makeObjectTypes: boolean
|
||||
makeObjectTypes: boolean,
|
||||
debugPrintReconstitution: boolean
|
||||
): [TypeGraph, boolean] {
|
||||
let needsRepeat = false;
|
||||
|
||||
|
@ -47,7 +48,7 @@ export function flattenUnions(
|
|||
foundIntersection = true;
|
||||
return false;
|
||||
});
|
||||
graph = graph.rewrite("flatten unions", stringTypeMapping, false, groups, replace);
|
||||
graph = graph.rewrite("flatten unions", stringTypeMapping, false, groups, debugPrintReconstitution, replace);
|
||||
|
||||
// console.log(`flattened ${nonCanonicalUnions.size} of ${unions.size} unions`);
|
||||
return [graph, !needsRepeat && !foundIntersection];
|
||||
|
|
|
@ -21,12 +21,12 @@ export function gatherNames(graph: TypeGraph): void {
|
|||
let processed: Set<List<any>> = Set();
|
||||
|
||||
function processObjectType(o: ObjectType, names: OrderedSet<string>, parentNames: OrderedSet<string> | undefined) {
|
||||
const properties = o.properties.sortBy((_, n) => n);
|
||||
const properties = o.getProperties().sortBy((_, n) => n);
|
||||
properties.forEach((property, propertyName) => {
|
||||
processType(property.type, OrderedSet([propertyName]), names);
|
||||
});
|
||||
|
||||
const values = o.additionalProperties;
|
||||
const values = o.getAdditionalProperties();
|
||||
if (values !== undefined) {
|
||||
processType(values, names.map(pluralize.singular), parentNames, "value");
|
||||
}
|
||||
|
|
|
@ -68,20 +68,35 @@ function replaceUnion(group: Set<UnionType>, builder: GraphRewriteBuilder<UnionT
|
|||
return builder.getUnionType(u.getAttributes(), OrderedSet(types), forwardingRef);
|
||||
}
|
||||
|
||||
export function inferEnums(graph: TypeGraph, stringTypeMapping: StringTypeMapping): TypeGraph {
|
||||
export function inferEnums(
|
||||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
debugPrintReconstitution: boolean
|
||||
): TypeGraph {
|
||||
const allStrings = graph
|
||||
.allTypesUnordered()
|
||||
.filter(t => t instanceof StringType)
|
||||
.map(t => [t])
|
||||
.toArray() as StringType[][];
|
||||
return graph.rewrite("infer enums", stringTypeMapping, false, allStrings, replaceString);
|
||||
return graph.rewrite("infer enums", stringTypeMapping, false, allStrings, debugPrintReconstitution, replaceString);
|
||||
}
|
||||
|
||||
export function flattenStrings(graph: TypeGraph, stringTypeMapping: StringTypeMapping): TypeGraph {
|
||||
export function flattenStrings(
|
||||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
debugPrintReconstitution: boolean
|
||||
): TypeGraph {
|
||||
const allUnions = graph.allNamedTypesSeparated().unions;
|
||||
const unionsToReplace = allUnions
|
||||
.filter(unionNeedsReplacing)
|
||||
.map(t => [t])
|
||||
.toArray();
|
||||
return graph.rewrite("flatten strings", stringTypeMapping, false, unionsToReplace, replaceUnion);
|
||||
return graph.rewrite(
|
||||
"flatten strings",
|
||||
stringTypeMapping,
|
||||
false,
|
||||
unionsToReplace,
|
||||
debugPrintReconstitution,
|
||||
replaceUnion
|
||||
);
|
||||
}
|
||||
|
|
|
@ -86,14 +86,19 @@ function shouldBeMap(properties: Map<string, ClassProperty>): Set<Type> | undefi
|
|||
return allCases;
|
||||
}
|
||||
|
||||
export function inferMaps(graph: TypeGraph, stringTypeMapping: StringTypeMapping, conflateNumbers: boolean): TypeGraph {
|
||||
export function inferMaps(
|
||||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
conflateNumbers: boolean,
|
||||
debugPrintReconstitution: boolean
|
||||
): TypeGraph {
|
||||
function replaceClass(
|
||||
setOfOneClass: Set<ClassType>,
|
||||
builder: GraphRewriteBuilder<ClassType>,
|
||||
forwardingRef: TypeRef
|
||||
): TypeRef {
|
||||
const c = defined(setOfOneClass.first());
|
||||
const properties = c.properties;
|
||||
const properties = c.getProperties();
|
||||
|
||||
const shouldBe = shouldBeMap(properties);
|
||||
if (shouldBe === undefined) {
|
||||
|
@ -123,6 +128,15 @@ export function inferMaps(graph: TypeGraph, stringTypeMapping: StringTypeMapping
|
|||
const allClasses = graph.allNamedTypesSeparated().objects.filter(o => o instanceof ClassType) as OrderedSet<
|
||||
ClassType
|
||||
>;
|
||||
const classesToReplace = allClasses.filter(c => !c.isFixed && shouldBeMap(c.properties) !== undefined).toArray();
|
||||
return graph.rewrite("infer maps", stringTypeMapping, false, classesToReplace.map(c => [c]), replaceClass);
|
||||
const classesToReplace = allClasses
|
||||
.filter(c => !c.isFixed && shouldBeMap(c.getProperties()) !== undefined)
|
||||
.toArray();
|
||||
return graph.rewrite(
|
||||
"infer maps",
|
||||
stringTypeMapping,
|
||||
false,
|
||||
classesToReplace.map(c => [c]),
|
||||
debugPrintReconstitution,
|
||||
replaceClass
|
||||
);
|
||||
}
|
||||
|
|
|
@ -331,7 +331,7 @@ export class CSharpRenderer extends ConvenienceRenderer {
|
|||
className,
|
||||
this.superclassForType(c),
|
||||
() => {
|
||||
if (c.properties.isEmpty()) return;
|
||||
if (c.getProperties().isEmpty()) return;
|
||||
const blankLines = this.blankLinesBetweenAttributes() ? "interposing" : "none";
|
||||
let columns: Sourcelike[][] = [];
|
||||
let isFirstProperty = true;
|
||||
|
|
|
@ -136,13 +136,13 @@ export class JSONSchemaRenderer extends ConvenienceRenderer {
|
|||
private definitionForObject(o: ObjectType, title: string | undefined): Schema {
|
||||
let properties: Schema | undefined;
|
||||
let required: string[] | undefined;
|
||||
if (o.properties.isEmpty()) {
|
||||
if (o.getProperties().isEmpty()) {
|
||||
properties = undefined;
|
||||
required = undefined;
|
||||
} else {
|
||||
const props: Schema = {};
|
||||
const req: string[] = [];
|
||||
o.properties.forEach((p, name) => {
|
||||
o.getProperties().forEach((p, name) => {
|
||||
props[name] = this.schemaForType(p.type);
|
||||
if (!p.isOptional) {
|
||||
req.push(name);
|
||||
|
@ -151,8 +151,8 @@ export class JSONSchemaRenderer extends ConvenienceRenderer {
|
|||
properties = props;
|
||||
required = req.sort();
|
||||
}
|
||||
const additionalProperties =
|
||||
o.additionalProperties !== undefined ? this.schemaForType(o.additionalProperties) : false;
|
||||
const additional = o.getAdditionalProperties();
|
||||
const additionalProperties = additional !== undefined ? this.schemaForType(additional) : false;
|
||||
return {
|
||||
type: "object",
|
||||
additionalProperties,
|
||||
|
|
|
@ -356,7 +356,7 @@ export class JavaRenderer extends ConvenienceRenderer {
|
|||
}
|
||||
|
||||
protected emitClassAttributes(c: ClassType, _className: Name): void {
|
||||
if (c.properties.isEmpty() && !this._justTypes) {
|
||||
if (c.getProperties().isEmpty() && !this._justTypes) {
|
||||
this.emitLine("@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.NONE)");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,8 +187,9 @@ export class JavaScriptRenderer extends ConvenienceRenderer {
|
|||
|
||||
this.emitBlock(`const typeMap${anyAnnotation} = `, ";", () => {
|
||||
this.forEachObject("none", (t: ObjectType, name: Name) => {
|
||||
const additionalProperties = t.getAdditionalProperties();
|
||||
const additional =
|
||||
t.additionalProperties !== undefined ? this.typeMapTypeFor(t.additionalProperties) : "false";
|
||||
additionalProperties !== undefined ? this.typeMapTypeFor(additionalProperties) : "false";
|
||||
this.emitBlock(['"', name, '": o('], [", ", additional, "),"], () => {
|
||||
this.forEachClassProperty(t, "none", (propName, _propJsonName, property) => {
|
||||
this.emitLine(propName, ": ", this.typeMapTypeForProperty(property), ",");
|
||||
|
|
|
@ -668,7 +668,7 @@ export class ObjectiveCRenderer extends ConvenienceRenderer {
|
|||
});
|
||||
|
||||
if (!this._justTypes && isTopLevel) {
|
||||
if (t.properties.count() > 0) this.ensureBlankLine();
|
||||
if (!t.getProperties().isEmpty()) this.ensureBlankLine();
|
||||
|
||||
this.emitLine(
|
||||
"+ (_Nullable instancetype)fromJSON:(NSString *)json encoding:(NSStringEncoding)encoding error:(NSError *_Nullable *)error;"
|
||||
|
@ -1048,7 +1048,7 @@ export class ObjectiveCRenderer extends ConvenienceRenderer {
|
|||
return (
|
||||
t instanceof MapType ||
|
||||
t instanceof ArrayType ||
|
||||
(t instanceof ClassType && t.properties.some(p => needsMap(p.type)))
|
||||
(t instanceof ClassType && t.getProperties().some(p => needsMap(p.type)))
|
||||
);
|
||||
}
|
||||
return this.typeGraph.allTypesUnordered().some(needsMap);
|
||||
|
|
|
@ -401,7 +401,7 @@ export class RubyRenderer extends ConvenienceRenderer {
|
|||
this.emitDescription(this.descriptionForType(c));
|
||||
this.emitBlock(["class ", className, " < Dry::Struct"], () => {
|
||||
let table: Sourcelike[][] = [];
|
||||
let count = c.properties.count();
|
||||
let count = c.getProperties().size;
|
||||
this.forEachClassProperty(c, "none", (name, jsonName, p) => {
|
||||
const last = --count === 0;
|
||||
const description = this.descriptionForClassProperty(c, jsonName);
|
||||
|
|
|
@ -534,7 +534,7 @@ export class SwiftRenderer extends ConvenienceRenderer {
|
|||
const allPropertiesRedundant = groups.every(group => {
|
||||
return group.every(p => p.label === undefined);
|
||||
});
|
||||
if (!allPropertiesRedundant && !c.properties.isEmpty()) {
|
||||
if (!allPropertiesRedundant && !c.getProperties().isEmpty()) {
|
||||
this.ensureBlankLine();
|
||||
this.emitBlock("enum CodingKeys: String, CodingKey", () => {
|
||||
for (const group of groups) {
|
||||
|
|
|
@ -12,7 +12,8 @@ export function replaceObjectType(
|
|||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
_conflateNumbers: boolean,
|
||||
leaveFullObjects: boolean
|
||||
leaveFullObjects: boolean,
|
||||
debugPrintReconstitution: boolean
|
||||
): TypeGraph {
|
||||
function replace(
|
||||
setOfOneType: Set<ObjectType>,
|
||||
|
@ -21,8 +22,8 @@ export function replaceObjectType(
|
|||
): TypeRef {
|
||||
const o = defined(setOfOneType.first());
|
||||
const attributes = o.getAttributes();
|
||||
const properties = o.properties;
|
||||
const additionalProperties = o.additionalProperties;
|
||||
const properties = o.getProperties();
|
||||
const additionalProperties = o.getAdditionalProperties();
|
||||
|
||||
function reconstituteProperties(): OrderedMap<string, ClassProperty> {
|
||||
return properties.map(cp => new ClassProperty(builder.reconstituteTypeRef(cp.typeRef), cp.isOptional));
|
||||
|
@ -82,8 +83,8 @@ export function replaceObjectType(
|
|||
|
||||
const allObjectTypes = graph.allTypesUnordered().filter(t => t.kind === "object") as Set<ObjectType>;
|
||||
const objectTypesToReplace = leaveFullObjects
|
||||
? allObjectTypes.filter(o => o.properties.isEmpty() || o.additionalProperties === undefined)
|
||||
? allObjectTypes.filter(o => o.getProperties().isEmpty() || o.getAdditionalProperties() === undefined)
|
||||
: allObjectTypes;
|
||||
const groups = objectTypesToReplace.toArray().map(t => [t]);
|
||||
return graph.rewrite("replace object type", stringTypeMapping, false, groups, replace);
|
||||
return graph.rewrite("replace object type", stringTypeMapping, false, groups, debugPrintReconstitution, replace);
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@ class IntersectionAccumulator
|
|||
}
|
||||
|
||||
this._objectAttributes = combineTypeAttributes(this._objectAttributes, maybeObject.getAttributes());
|
||||
const objectAdditionalProperties = maybeObject.getAdditionalProperties();
|
||||
|
||||
if (this._objectProperties === undefined) {
|
||||
assert(this._additionalPropertyTypes === undefined);
|
||||
|
@ -177,10 +178,10 @@ class IntersectionAccumulator
|
|||
const allPropertyNames = this._objectProperties
|
||||
.keySeq()
|
||||
.toOrderedSet()
|
||||
.union(maybeObject.properties.keySeq());
|
||||
.union(maybeObject.getProperties().keySeq());
|
||||
allPropertyNames.forEach(name => {
|
||||
const existing = defined(this._objectProperties).get(name);
|
||||
const newProperty = maybeObject.properties.get(name);
|
||||
const newProperty = maybeObject.getProperties().get(name);
|
||||
|
||||
if (existing !== undefined && newProperty !== undefined) {
|
||||
const cp = new GenericClassProperty(
|
||||
|
@ -188,9 +189,9 @@ class IntersectionAccumulator
|
|||
existing.isOptional && newProperty.isOptional
|
||||
);
|
||||
this._objectProperties = defined(this._objectProperties).set(name, cp);
|
||||
} else if (existing !== undefined && maybeObject.additionalProperties !== undefined) {
|
||||
} else if (existing !== undefined && objectAdditionalProperties !== undefined) {
|
||||
const cp = new GenericClassProperty(
|
||||
existing.typeData.add(maybeObject.additionalProperties),
|
||||
existing.typeData.add(objectAdditionalProperties),
|
||||
existing.isOptional
|
||||
);
|
||||
this._objectProperties = defined(this._objectProperties).set(name, cp);
|
||||
|
@ -209,9 +210,9 @@ class IntersectionAccumulator
|
|||
}
|
||||
});
|
||||
|
||||
if (this._additionalPropertyTypes !== undefined && maybeObject.additionalProperties !== undefined) {
|
||||
this._additionalPropertyTypes = this._additionalPropertyTypes.add(maybeObject.additionalProperties);
|
||||
} else if (this._additionalPropertyTypes !== undefined || maybeObject.additionalProperties !== undefined) {
|
||||
if (this._additionalPropertyTypes !== undefined && objectAdditionalProperties !== undefined) {
|
||||
this._additionalPropertyTypes = this._additionalPropertyTypes.add(objectAdditionalProperties);
|
||||
} else if (this._additionalPropertyTypes !== undefined || objectAdditionalProperties !== undefined) {
|
||||
this._additionalPropertyTypes = undefined;
|
||||
this._lostTypeAttributes = true;
|
||||
}
|
||||
|
@ -404,7 +405,11 @@ class IntersectionUnionBuilder extends UnionBuilder<
|
|||
}
|
||||
}
|
||||
|
||||
export function resolveIntersections(graph: TypeGraph, stringTypeMapping: StringTypeMapping): [TypeGraph, boolean] {
|
||||
export function resolveIntersections(
|
||||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
debugPrintReconstitution: boolean
|
||||
): [TypeGraph, boolean] {
|
||||
let needsRepeat = false;
|
||||
|
||||
function replace(types: Set<Type>, builder: GraphRewriteBuilder<Type>, forwardingRef: TypeRef): TypeRef {
|
||||
|
@ -441,7 +446,7 @@ export function resolveIntersections(graph: TypeGraph, stringTypeMapping: String
|
|||
>;
|
||||
const resolvableIntersections = allIntersections.filter(canResolve);
|
||||
const groups = makeGroupsToFlatten(resolvableIntersections, undefined);
|
||||
graph = graph.rewrite("resolve intersections", stringTypeMapping, false, groups, replace);
|
||||
graph = graph.rewrite("resolve intersections", stringTypeMapping, false, groups, debugPrintReconstitution, replace);
|
||||
|
||||
// console.log(`resolved ${resolvableIntersections.size} of ${intersections.size} intersections`);
|
||||
return [graph, !needsRepeat && allIntersections.size === resolvableIntersections.size];
|
||||
|
|
193
src/Type.ts
193
src/Type.ts
|
@ -62,7 +62,7 @@ export abstract class Type {
|
|||
|
||||
abstract get isNullable(): boolean;
|
||||
abstract isPrimitive(): this is PrimitiveType;
|
||||
abstract map(builder: TypeReconstituter, f: (tref: TypeRef) => TypeRef): TypeRef;
|
||||
abstract reconstitute<T extends Type>(builder: TypeReconstituter<T>): void;
|
||||
|
||||
equals(other: any): boolean {
|
||||
if (!Object.prototype.hasOwnProperty.call(other, "typeRef")) {
|
||||
|
@ -182,9 +182,8 @@ export class PrimitiveType extends Type {
|
|||
return true;
|
||||
}
|
||||
|
||||
map(builder: TypeReconstituter, _: (tref: TypeRef) => TypeRef): TypeRef {
|
||||
// console.log(`${mapIndentation()}mapping ${this.kind}`);
|
||||
return builder.getPrimitiveType(this.kind);
|
||||
reconstitute<T extends Type>(builder: TypeReconstituter<T>): void {
|
||||
builder.getPrimitiveType(this.kind);
|
||||
}
|
||||
|
||||
protected structuralEqualityStep(_other: Type, _queue: (a: Type, b: Type) => boolean): boolean {
|
||||
|
@ -201,9 +200,8 @@ export class StringType extends PrimitiveType {
|
|||
super(typeRef, "string", false);
|
||||
}
|
||||
|
||||
map(builder: TypeReconstituter, _: (tref: TypeRef) => TypeRef): TypeRef {
|
||||
// console.log(`${mapIndentation()}mapping ${this.kind}`);
|
||||
return builder.getStringType(this.enumCases);
|
||||
reconstitute<T extends Type>(builder: TypeReconstituter<T>): void {
|
||||
builder.getStringType(this.enumCases);
|
||||
}
|
||||
|
||||
protected structuralEqualityStep(_other: Type, _queue: (a: Type, b: Type) => boolean): boolean {
|
||||
|
@ -249,12 +247,15 @@ export class ArrayType extends Type {
|
|||
return false;
|
||||
}
|
||||
|
||||
map(builder: TypeReconstituter, f: (tref: TypeRef) => TypeRef): TypeRef {
|
||||
// console.log(`${mapIndentation()}mapping ${this.kind}`);
|
||||
// mapPath.push("[]");
|
||||
const result = builder.getArrayType(f(this.getItemsRef()));
|
||||
// mapPath.pop();
|
||||
return result;
|
||||
reconstitute<T extends Type>(builder: TypeReconstituter<T>): void {
|
||||
const itemsRef = this.getItemsRef();
|
||||
const maybeItems = builder.lookup(itemsRef);
|
||||
if (maybeItems === undefined) {
|
||||
builder.getUniqueArrayType();
|
||||
builder.setArrayItems(builder.reconstitute(this.getItemsRef()));
|
||||
} else {
|
||||
builder.getArrayType(maybeItems);
|
||||
}
|
||||
}
|
||||
|
||||
protected structuralEqualityStep(other: ArrayType, queue: (a: Type, b: Type) => boolean): boolean {
|
||||
|
@ -296,14 +297,16 @@ export class ObjectType extends Type {
|
|||
typeRef: TypeRef,
|
||||
kind: TypeKind,
|
||||
readonly isFixed: boolean,
|
||||
readonly properties: OrderedMap<string, ClassProperty>,
|
||||
private _properties: OrderedMap<string, ClassProperty> | undefined,
|
||||
private _additionalPropertiesRef: TypeRef | undefined
|
||||
) {
|
||||
super(typeRef, kind);
|
||||
|
||||
assert(kind === "object" || kind === "map" || kind === "class");
|
||||
if (kind === "map") {
|
||||
assert(properties.isEmpty());
|
||||
if (_properties !== undefined) {
|
||||
assert(_properties.isEmpty());
|
||||
}
|
||||
assert(!isFixed);
|
||||
} else if (kind === "class") {
|
||||
assert(_additionalPropertiesRef === undefined);
|
||||
|
@ -312,21 +315,43 @@ export class ObjectType extends Type {
|
|||
}
|
||||
}
|
||||
|
||||
get sortedProperties(): OrderedMap<string, ClassProperty> {
|
||||
const properties = this.properties;
|
||||
setProperties(properties: OrderedMap<string, ClassProperty>, additionalPropertiesRef: TypeRef | undefined) {
|
||||
if (this instanceof MapType) {
|
||||
assert(properties.isEmpty(), "Cannot set properties on map type");
|
||||
} else if (this._properties !== undefined) {
|
||||
return panic("Tried to set object properties again");
|
||||
}
|
||||
|
||||
if (this instanceof ClassType) {
|
||||
assert(additionalPropertiesRef === undefined, "Cannot set additional properties of class type");
|
||||
}
|
||||
|
||||
this._properties = properties;
|
||||
this._additionalPropertiesRef = additionalPropertiesRef;
|
||||
}
|
||||
|
||||
getProperties(): OrderedMap<string, ClassProperty> {
|
||||
return defined(this._properties);
|
||||
}
|
||||
|
||||
getSortedProperties(): OrderedMap<string, ClassProperty> {
|
||||
const properties = this.getProperties();
|
||||
const sortedKeys = properties.keySeq().sort();
|
||||
const props = sortedKeys.map((k: string): [string, ClassProperty] => [k, defined(properties.get(k))]);
|
||||
return OrderedMap(props);
|
||||
}
|
||||
|
||||
get additionalProperties(): Type | undefined {
|
||||
getAdditionalProperties(): Type | undefined {
|
||||
assert(this._properties !== undefined, "Properties are not set yet");
|
||||
if (this._additionalPropertiesRef === undefined) return undefined;
|
||||
return this._additionalPropertiesRef.deref()[0];
|
||||
}
|
||||
|
||||
get children(): OrderedSet<Type> {
|
||||
const children = this.sortedProperties.map(p => p.type).toOrderedSet();
|
||||
const additionalProperties = this.additionalProperties;
|
||||
const children = this.getSortedProperties()
|
||||
.map(p => p.type)
|
||||
.toOrderedSet();
|
||||
const additionalProperties = this.getAdditionalProperties();
|
||||
if (additionalProperties === undefined) {
|
||||
return children;
|
||||
}
|
||||
|
@ -341,38 +366,63 @@ export class ObjectType extends Type {
|
|||
return false;
|
||||
}
|
||||
|
||||
map(builder: TypeReconstituter, f: (tref: TypeRef) => TypeRef): TypeRef {
|
||||
// const indent = " ".repeat(mapPath.length);
|
||||
// const path = mapPath.join(".");
|
||||
// console.log(`${indent} mapping class ${this.getCombinedName()} at ${path}`);
|
||||
const properties = this.properties.map((p, _n) => {
|
||||
// console.log(`${indent} property ${path}.${n}`);
|
||||
// mapPath.push(n);
|
||||
const result = new ClassProperty(f(p.typeRef), p.isOptional);
|
||||
// mapPath.pop();
|
||||
return result;
|
||||
});
|
||||
const additionalProperties = mapOptional(f, this._additionalPropertiesRef);
|
||||
switch (this.kind) {
|
||||
case "object":
|
||||
assert(this.isFixed);
|
||||
return builder.getUniqueObjectType(properties, additionalProperties);
|
||||
case "map":
|
||||
return builder.getMapType(defined(additionalProperties));
|
||||
case "class":
|
||||
if (this.isFixed) {
|
||||
return builder.getUniqueClassType(true, properties);
|
||||
} else {
|
||||
return builder.getClassType(properties);
|
||||
}
|
||||
default:
|
||||
return panic(`Invalid object type kind ${this.kind}`);
|
||||
reconstitute<T extends Type>(builder: TypeReconstituter<T>): void {
|
||||
const maybePropertyTypes = builder.lookup(this.getProperties().map(cp => cp.typeRef));
|
||||
const maybeAdditionalProperties = mapOptional(r => builder.lookup(r), this._additionalPropertiesRef);
|
||||
|
||||
if (
|
||||
maybePropertyTypes !== undefined &&
|
||||
(maybeAdditionalProperties !== undefined || this._additionalPropertiesRef === undefined)
|
||||
) {
|
||||
const properties = this.getProperties().map(
|
||||
(cp, n) => new ClassProperty(defined(maybePropertyTypes.get(n)), cp.isOptional)
|
||||
);
|
||||
|
||||
switch (this.kind) {
|
||||
case "object":
|
||||
assert(this.isFixed);
|
||||
builder.getObjectType(properties, maybeAdditionalProperties);
|
||||
break;
|
||||
case "map":
|
||||
builder.getMapType(defined(maybeAdditionalProperties));
|
||||
break;
|
||||
case "class":
|
||||
if (this.isFixed) {
|
||||
builder.getUniqueClassType(true, properties);
|
||||
} else {
|
||||
builder.getClassType(properties);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return panic(`Invalid object type kind ${this.kind}`);
|
||||
}
|
||||
} else {
|
||||
switch (this.kind) {
|
||||
case "object":
|
||||
assert(this.isFixed);
|
||||
builder.getUniqueObjectType(undefined, undefined);
|
||||
break;
|
||||
case "map":
|
||||
builder.getUniqueMapType();
|
||||
break;
|
||||
case "class":
|
||||
builder.getUniqueClassType(this.isFixed, undefined);
|
||||
break;
|
||||
default:
|
||||
return panic(`Invalid object type kind ${this.kind}`);
|
||||
}
|
||||
|
||||
const properties = this.getProperties().map(
|
||||
cp => new ClassProperty(builder.reconstitute(cp.typeRef), cp.isOptional)
|
||||
);
|
||||
const additionalProperties = mapOptional(r => builder.reconstitute(r), this._additionalPropertiesRef);
|
||||
builder.setObjectProperties(properties, additionalProperties);
|
||||
}
|
||||
}
|
||||
|
||||
protected structuralEqualityStep(other: ObjectType, queue: (a: Type, b: Type) => boolean): boolean {
|
||||
const pa = this.properties;
|
||||
const pb = other.properties;
|
||||
const pa = this.getProperties();
|
||||
const pb = other.getProperties();
|
||||
if (pa.size !== pb.size) return false;
|
||||
let failed = false;
|
||||
pa.forEach((cpa, name) => {
|
||||
|
@ -384,9 +434,11 @@ export class ObjectType extends Type {
|
|||
});
|
||||
if (failed) return false;
|
||||
|
||||
if ((this.additionalProperties === undefined) !== (other.additionalProperties === undefined)) return false;
|
||||
if (this.additionalProperties === undefined || other.additionalProperties === undefined) return true;
|
||||
return queue(this.additionalProperties, other.additionalProperties);
|
||||
const thisAdditionalProperties = this.getAdditionalProperties();
|
||||
const otherAdditionalProperties = other.getAdditionalProperties();
|
||||
if ((thisAdditionalProperties === undefined) !== (otherAdditionalProperties === undefined)) return false;
|
||||
if (thisAdditionalProperties === undefined || otherAdditionalProperties === undefined) return true;
|
||||
return queue(thisAdditionalProperties, otherAdditionalProperties);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,7 +446,7 @@ export class ClassType extends ObjectType {
|
|||
// @ts-ignore: This is initialized in the Type constructor
|
||||
kind: "class";
|
||||
|
||||
constructor(typeRef: TypeRef, readonly isFixed: boolean, readonly properties: OrderedMap<string, ClassProperty>) {
|
||||
constructor(typeRef: TypeRef, isFixed: boolean, properties: OrderedMap<string, ClassProperty> | undefined) {
|
||||
super(typeRef, "class", isFixed, properties, undefined);
|
||||
}
|
||||
}
|
||||
|
@ -403,12 +455,13 @@ export class MapType extends ObjectType {
|
|||
// @ts-ignore: This is initialized in the Type constructor
|
||||
readonly kind: "map";
|
||||
|
||||
constructor(typeRef: TypeRef, valuesRef: TypeRef) {
|
||||
constructor(typeRef: TypeRef, valuesRef: TypeRef | undefined) {
|
||||
super(typeRef, "map", false, OrderedMap(), valuesRef);
|
||||
}
|
||||
|
||||
// FIXME: Remove and use `getAdditionalProperties()` instead.
|
||||
get values(): Type {
|
||||
return defined(this.additionalProperties);
|
||||
return defined(this.getAdditionalProperties());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,9 +499,8 @@ export class EnumType extends Type {
|
|||
return false;
|
||||
}
|
||||
|
||||
map(builder: TypeReconstituter, _: (tref: TypeRef) => TypeRef): TypeRef {
|
||||
// console.log(`${mapIndentation()}mapping ${this.kind}`);
|
||||
return builder.getEnumType(this.cases);
|
||||
reconstitute<T extends Type>(builder: TypeReconstituter<T>): void {
|
||||
builder.getEnumType(this.cases);
|
||||
}
|
||||
|
||||
protected structuralEqualityStep(other: EnumType, _queue: (a: Type, b: Type) => void): boolean {
|
||||
|
@ -492,7 +544,7 @@ export abstract class SetOperationType extends Type {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected structuralEqualityStep(other: UnionType, queue: (a: Type, b: Type) => boolean): boolean {
|
||||
protected structuralEqualityStep(other: SetOperationType, queue: (a: Type, b: Type) => boolean): boolean {
|
||||
return setOperationCasesEqual(this.members, other.members, queue);
|
||||
}
|
||||
}
|
||||
|
@ -509,10 +561,15 @@ export class IntersectionType extends SetOperationType {
|
|||
return panic("isNullable not implemented for IntersectionType");
|
||||
}
|
||||
|
||||
map(builder: TypeReconstituter, f: (tref: TypeRef) => TypeRef): TypeRef {
|
||||
const members = this.getMemberRefs().map(f);
|
||||
// FIXME: Eventually switch to non-unique
|
||||
return builder.getUniqueIntersectionType(members);
|
||||
reconstitute<T extends Type>(builder: TypeReconstituter<T>): void {
|
||||
const memberRefs = this.getMemberRefs();
|
||||
const maybeMembers = builder.lookup(memberRefs);
|
||||
if (maybeMembers === undefined) {
|
||||
builder.getUniqueIntersectionType();
|
||||
builder.setSetOperationMembers(builder.reconstitute(memberRefs));
|
||||
} else {
|
||||
builder.getIntersectionType(maybeMembers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -562,9 +619,15 @@ export class UnionType extends SetOperationType {
|
|||
return true;
|
||||
}
|
||||
|
||||
map(builder: TypeReconstituter, f: (tref: TypeRef) => TypeRef): TypeRef {
|
||||
const members = this.getMemberRefs().map(f);
|
||||
return builder.getUnionType(members);
|
||||
reconstitute<T extends Type>(builder: TypeReconstituter<T>): void {
|
||||
const memberRefs = this.getMemberRefs();
|
||||
const maybeMembers = builder.lookup(memberRefs);
|
||||
if (maybeMembers === undefined) {
|
||||
builder.getUniqueUnionType();
|
||||
builder.setSetOperationMembers(builder.reconstitute(memberRefs));
|
||||
} else {
|
||||
builder.getUnionType(maybeMembers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
import { Map, OrderedMap, OrderedSet, Set } from "immutable";
|
||||
import { Map, OrderedMap, OrderedSet, Set, Collection, isCollection } from "immutable";
|
||||
|
||||
import {
|
||||
PrimitiveTypeKind,
|
||||
|
@ -28,7 +28,7 @@ import {
|
|||
emptyTypeAttributes,
|
||||
makeTypeAttributesInferred
|
||||
} from "./TypeAttributes";
|
||||
import { defined, assert, panic, assertNever, setUnion } from "./Support";
|
||||
import { defined, assert, panic, assertNever, setUnion, mapOptional } from "./Support";
|
||||
|
||||
export type TypeRefCallback = (index: number) => void;
|
||||
|
||||
|
@ -129,6 +129,8 @@ function provenanceToString(p: Set<TypeRef>): string {
|
|||
.join(",");
|
||||
}
|
||||
|
||||
// FIXME: Don't infer provenance. All original types should be present in
|
||||
// non-inferred form in the final graph.
|
||||
export const provenanceTypeAttributeKind = new TypeAttributeKind<Set<TypeRef>>(
|
||||
"provenance",
|
||||
setUnion,
|
||||
|
@ -148,6 +150,15 @@ export const NoStringTypeMapping: StringTypeMapping = {
|
|||
dateTime: "date-time"
|
||||
};
|
||||
|
||||
function forwardIfNecessary(forwardingRef: TypeRef | undefined, maybeRef: TypeRef): TypeRef;
|
||||
function forwardIfNecessary(forwardingRef: TypeRef | undefined, maybeRef: TypeRef | undefined): TypeRef | undefined;
|
||||
function forwardIfNecessary(forwardingRef: TypeRef | undefined, maybeRef: TypeRef | undefined): TypeRef | undefined {
|
||||
if (forwardingRef !== undefined && maybeRef !== undefined) {
|
||||
forwardingRef.resolve(maybeRef);
|
||||
}
|
||||
return maybeRef;
|
||||
}
|
||||
|
||||
export class TypeBuilder {
|
||||
readonly typeGraph: TypeGraph;
|
||||
|
||||
|
@ -264,6 +275,7 @@ export class TypeBuilder {
|
|||
private _enumTypes: Map<Set<string>, TypeRef> = Map();
|
||||
private _classTypes: Map<Map<string, ClassProperty>, TypeRef> = Map();
|
||||
private _unionTypes: Map<Set<TypeRef>, TypeRef> = Map();
|
||||
private _intersectionTypes: Map<Set<TypeRef>, TypeRef> = Map();
|
||||
|
||||
getPrimitiveType(kind: PrimitiveTypeKind, forwardingRef?: TypeRef): TypeRef {
|
||||
assert(kind !== "string", "Use getStringType to create strings");
|
||||
|
@ -273,7 +285,7 @@ export class TypeBuilder {
|
|||
if (kind === "string") {
|
||||
return this.getStringType(undefined, undefined, forwardingRef);
|
||||
}
|
||||
let tref = this._primitiveTypes.get(kind);
|
||||
let tref = forwardIfNecessary(forwardingRef, this._primitiveTypes.get(kind));
|
||||
if (tref === undefined) {
|
||||
tref = this.addType(forwardingRef, tr => new PrimitiveType(tr, kind), undefined);
|
||||
this._primitiveTypes = this._primitiveTypes.set(kind, tref);
|
||||
|
@ -301,14 +313,14 @@ export class TypeBuilder {
|
|||
this._noEnumStringType = this.addType(forwardingRef, tr => new StringType(tr, undefined), undefined);
|
||||
}
|
||||
this.addAttributes(this._noEnumStringType, attributes);
|
||||
return this._noEnumStringType;
|
||||
return forwardIfNecessary(forwardingRef, this._noEnumStringType);
|
||||
}
|
||||
return this.addType(forwardingRef, tr => new StringType(tr, cases), attributes);
|
||||
}
|
||||
|
||||
getEnumType(attributes: TypeAttributes, cases: OrderedSet<string>, forwardingRef?: TypeRef): TypeRef {
|
||||
const unorderedCases = cases.toSet();
|
||||
let tref = this._enumTypes.get(unorderedCases);
|
||||
let tref = forwardIfNecessary(forwardingRef, this._enumTypes.get(unorderedCases));
|
||||
if (tref === undefined) {
|
||||
tref = this.addType(forwardingRef, tr => new EnumType(tr, cases), attributes);
|
||||
this._enumTypes = this._enumTypes.set(unorderedCases, tref);
|
||||
|
@ -320,11 +332,11 @@ export class TypeBuilder {
|
|||
|
||||
getUniqueObjectType(
|
||||
attributes: TypeAttributes,
|
||||
properties: OrderedMap<string, ClassProperty>,
|
||||
properties: OrderedMap<string, ClassProperty> | undefined,
|
||||
additionalProperties: TypeRef | undefined,
|
||||
forwardingRef?: TypeRef
|
||||
): TypeRef {
|
||||
properties = this.modifyPropertiesIfNecessary(properties);
|
||||
properties = mapOptional(p => this.modifyPropertiesIfNecessary(p), properties);
|
||||
return this.addType(
|
||||
forwardingRef,
|
||||
tref => new ObjectType(tref, "object", true, properties, additionalProperties),
|
||||
|
@ -332,8 +344,12 @@ export class TypeBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
getUniqueMapType(forwardingRef?: TypeRef): TypeRef {
|
||||
return this.addType(forwardingRef, tr => new MapType(tr, undefined), undefined);
|
||||
}
|
||||
|
||||
getMapType(values: TypeRef, forwardingRef?: TypeRef): TypeRef {
|
||||
let tref = this._mapTypes.get(values);
|
||||
let tref = forwardIfNecessary(forwardingRef, this._mapTypes.get(values));
|
||||
if (tref === undefined) {
|
||||
tref = this.addType(forwardingRef, tr => new MapType(tr, values), undefined);
|
||||
this._mapTypes = this._mapTypes.set(values, tref);
|
||||
|
@ -341,8 +357,24 @@ export class TypeBuilder {
|
|||
return tref;
|
||||
}
|
||||
|
||||
setObjectProperties(
|
||||
ref: TypeRef,
|
||||
properties: OrderedMap<string, ClassProperty>,
|
||||
additionalProperties: TypeRef | undefined
|
||||
): void {
|
||||
const type = ref.deref()[0];
|
||||
if (!(type instanceof ObjectType)) {
|
||||
return panic("Tried to set properties of non-object type");
|
||||
}
|
||||
type.setProperties(this.modifyPropertiesIfNecessary(properties), additionalProperties);
|
||||
}
|
||||
|
||||
getUniqueArrayType(forwardingRef?: TypeRef): TypeRef {
|
||||
return this.addType(forwardingRef, tr => new ArrayType(tr, undefined), undefined);
|
||||
}
|
||||
|
||||
getArrayType(items: TypeRef, forwardingRef?: TypeRef): TypeRef {
|
||||
let tref = this._arrayTypes.get(items);
|
||||
let tref = forwardIfNecessary(forwardingRef, this._arrayTypes.get(items));
|
||||
if (tref === undefined) {
|
||||
tref = this.addType(forwardingRef, tr => new ArrayType(tr, items), undefined);
|
||||
this._arrayTypes = this._arrayTypes.set(items, tref);
|
||||
|
@ -350,6 +382,14 @@ export class TypeBuilder {
|
|||
return tref;
|
||||
}
|
||||
|
||||
setArrayItems(ref: TypeRef, items: TypeRef): void {
|
||||
const type = ref.deref()[0];
|
||||
if (!(type instanceof ArrayType)) {
|
||||
return panic("Tried to set items of non-array type");
|
||||
}
|
||||
type.setItems(items);
|
||||
}
|
||||
|
||||
modifyPropertiesIfNecessary(properties: OrderedMap<string, ClassProperty>): OrderedMap<string, ClassProperty> {
|
||||
if (this.alphabetizeProperties) {
|
||||
properties = properties.sortBy((_, n) => n);
|
||||
|
@ -366,11 +406,8 @@ export class TypeBuilder {
|
|||
forwardingRef?: TypeRef
|
||||
): TypeRef {
|
||||
properties = this.modifyPropertiesIfNecessary(properties);
|
||||
let tref = this._classTypes.get(properties.toMap());
|
||||
// FIXME: It's not clear to me that the `forwardingRef` condition here
|
||||
// might actually ever be true. And if it can, shouldn't we also have
|
||||
// it in all the other `getXXX` methods here?
|
||||
if ((forwardingRef !== undefined && forwardingRef.maybeIndex !== undefined) || tref === undefined) {
|
||||
let tref = forwardIfNecessary(forwardingRef, this._classTypes.get(properties.toMap()));
|
||||
if (tref === undefined) {
|
||||
tref = this.addType(forwardingRef, tr => new ClassType(tr, false, properties), attributes);
|
||||
this._classTypes = this._classTypes.set(properties.toMap(), tref);
|
||||
} else {
|
||||
|
@ -384,16 +421,16 @@ export class TypeBuilder {
|
|||
getUniqueClassType(
|
||||
attributes: TypeAttributes,
|
||||
isFixed: boolean,
|
||||
properties: OrderedMap<string, ClassProperty>,
|
||||
properties: OrderedMap<string, ClassProperty> | undefined,
|
||||
forwardingRef?: TypeRef
|
||||
): TypeRef {
|
||||
properties = this.modifyPropertiesIfNecessary(properties);
|
||||
properties = mapOptional(p => this.modifyPropertiesIfNecessary(p), properties);
|
||||
return this.addType(forwardingRef, tref => new ClassType(tref, isFixed, properties), attributes);
|
||||
}
|
||||
|
||||
getUnionType(attributes: TypeAttributes, members: OrderedSet<TypeRef>, forwardingRef?: TypeRef): TypeRef {
|
||||
const unorderedMembers = members.toSet();
|
||||
let tref = this._unionTypes.get(unorderedMembers);
|
||||
let tref = forwardIfNecessary(forwardingRef, this._unionTypes.get(unorderedMembers));
|
||||
if (tref === undefined) {
|
||||
tref = this.addType(forwardingRef, tr => new UnionType(tr, members), attributes);
|
||||
this._unionTypes = this._unionTypes.set(unorderedMembers, tref);
|
||||
|
@ -411,6 +448,18 @@ export class TypeBuilder {
|
|||
return this.addType(forwardingRef, tref => new UnionType(tref, members), attributes);
|
||||
}
|
||||
|
||||
getIntersectionType(attributes: TypeAttributes, members: OrderedSet<TypeRef>, forwardingRef?: TypeRef): TypeRef {
|
||||
const unorderedMembers = members.toSet();
|
||||
let tref = forwardIfNecessary(forwardingRef, this._intersectionTypes.get(unorderedMembers));
|
||||
if (tref === undefined) {
|
||||
tref = this.addType(forwardingRef, tr => new IntersectionType(tr, members), attributes);
|
||||
this._intersectionTypes = this._intersectionTypes.set(unorderedMembers, tref);
|
||||
} else {
|
||||
this.addAttributes(tref, attributes);
|
||||
}
|
||||
return tref;
|
||||
}
|
||||
|
||||
getUniqueIntersectionType(
|
||||
attributes: TypeAttributes,
|
||||
members: OrderedSet<TypeRef> | undefined,
|
||||
|
@ -438,78 +487,170 @@ export interface TypeLookerUp {
|
|||
registerUnion(typeRefs: TypeRef[], reconstituted: TypeRef): void;
|
||||
}
|
||||
|
||||
export class TypeReconstituter {
|
||||
export class TypeReconstituter<T extends Type> {
|
||||
private _wasUsed: boolean = false;
|
||||
private _typeRef: TypeRef | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _typeBuilder: TypeBuilder,
|
||||
private readonly _typeBuilder: GraphRewriteBuilder<T>,
|
||||
private readonly _makeClassUnique: boolean,
|
||||
private readonly _typeAttributes: TypeAttributes,
|
||||
private readonly _forwardingRef: TypeRef
|
||||
private readonly _forwardingRef: TypeRef,
|
||||
private readonly _register: (tref: TypeRef) => void
|
||||
) {}
|
||||
|
||||
private useBuilder(): TypeBuilder {
|
||||
private builderForNewType(): GraphRewriteBuilder<T> {
|
||||
assert(!this._wasUsed, "TypeReconstituter used more than once");
|
||||
this._wasUsed = true;
|
||||
return this._typeBuilder;
|
||||
}
|
||||
|
||||
private addAttributes(tref: TypeRef): TypeRef {
|
||||
private builderForSetting(): GraphRewriteBuilder<T> {
|
||||
assert(this._wasUsed && this._typeRef !== undefined, "Can't set type members before constructing a type");
|
||||
return this._typeBuilder;
|
||||
}
|
||||
|
||||
getResult(): TypeRef {
|
||||
if (this._typeRef === undefined) {
|
||||
return panic("Type was not reconstituted");
|
||||
}
|
||||
return this._typeRef;
|
||||
}
|
||||
|
||||
// FIXME: Do registration automatically.
|
||||
private register(tref: TypeRef): void {
|
||||
assert(this._typeRef === undefined, "Cannot register a type twice");
|
||||
this._typeRef = tref;
|
||||
this._register(tref);
|
||||
}
|
||||
|
||||
private registerAndAddAttributes(tref: TypeRef): void {
|
||||
this._typeBuilder.addAttributes(tref, this._typeAttributes);
|
||||
return tref;
|
||||
this.register(tref);
|
||||
}
|
||||
|
||||
getPrimitiveType(kind: PrimitiveTypeKind): TypeRef {
|
||||
return this.addAttributes(this.useBuilder().getPrimitiveType(kind, this._forwardingRef));
|
||||
lookup(tref: TypeRef): TypeRef | undefined;
|
||||
lookup<C extends Collection<any, TypeRef>>(trefs: C): C | undefined;
|
||||
lookup<C extends Collection<any, TypeRef>>(trefs: TypeRef | C): TypeRef | C | undefined {
|
||||
assert(!this._wasUsed, "Cannot lookup constituents after building type");
|
||||
if (isCollection(trefs)) {
|
||||
const maybeRefs = trefs.map(tref => this._typeBuilder.lookupTypeRefs([tref], undefined, false));
|
||||
if (maybeRefs.some(tref => tref === undefined)) return undefined;
|
||||
return maybeRefs as C;
|
||||
}
|
||||
return this._typeBuilder.lookupTypeRefs([trefs], undefined, false);
|
||||
}
|
||||
|
||||
getStringType(enumCases: OrderedMap<string, number> | undefined): TypeRef {
|
||||
return this.useBuilder().getStringType(this._typeAttributes, enumCases, this._forwardingRef);
|
||||
reconstitute(tref: TypeRef): TypeRef;
|
||||
reconstitute<C extends Collection<any, TypeRef>>(trefs: C): C;
|
||||
reconstitute<C extends Collection<any, TypeRef>>(trefs: TypeRef | C): TypeRef | C {
|
||||
assert(this._wasUsed, "Cannot reconstitute constituents before building type");
|
||||
if (isCollection(trefs)) {
|
||||
return trefs.map(tref => this._typeBuilder.reconstituteTypeRef(tref)) as C;
|
||||
}
|
||||
return this._typeBuilder.reconstituteTypeRef(trefs);
|
||||
}
|
||||
|
||||
getEnumType(cases: OrderedSet<string>): TypeRef {
|
||||
return this.useBuilder().getEnumType(defined(this._typeAttributes), cases, this._forwardingRef);
|
||||
getPrimitiveType(kind: PrimitiveTypeKind): void {
|
||||
this.registerAndAddAttributes(this.builderForNewType().getPrimitiveType(kind, this._forwardingRef));
|
||||
}
|
||||
|
||||
getMapType(values: TypeRef): TypeRef {
|
||||
return this.addAttributes(this.useBuilder().getMapType(values, this._forwardingRef));
|
||||
getStringType(enumCases: OrderedMap<string, number> | undefined): void {
|
||||
this.register(this.builderForNewType().getStringType(this._typeAttributes, enumCases, this._forwardingRef));
|
||||
}
|
||||
|
||||
getArrayType(items: TypeRef): TypeRef {
|
||||
return this.addAttributes(this.useBuilder().getArrayType(items, this._forwardingRef));
|
||||
getEnumType(cases: OrderedSet<string>): void {
|
||||
this.register(this.builderForNewType().getEnumType(this._typeAttributes, cases, this._forwardingRef));
|
||||
}
|
||||
|
||||
getUniqueMapType(): void {
|
||||
this.registerAndAddAttributes(this.builderForNewType().getUniqueMapType(this._forwardingRef));
|
||||
}
|
||||
|
||||
getMapType(values: TypeRef): void {
|
||||
this.registerAndAddAttributes(this.builderForNewType().getMapType(values, this._forwardingRef));
|
||||
}
|
||||
|
||||
getUniqueArrayType(): void {
|
||||
this.registerAndAddAttributes(this.builderForNewType().getUniqueArrayType(this._forwardingRef));
|
||||
}
|
||||
|
||||
getArrayType(items: TypeRef): void {
|
||||
this.registerAndAddAttributes(this.builderForNewType().getArrayType(items, this._forwardingRef));
|
||||
}
|
||||
|
||||
setArrayItems(items: TypeRef): void {
|
||||
this.builderForSetting().setArrayItems(this.getResult(), items);
|
||||
}
|
||||
|
||||
getObjectType(properties: OrderedMap<string, ClassProperty>, additionalProperties: TypeRef | undefined): void {
|
||||
this.register(
|
||||
this.builderForNewType().getUniqueObjectType(
|
||||
this._typeAttributes,
|
||||
properties,
|
||||
additionalProperties,
|
||||
this._forwardingRef
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getUniqueObjectType(
|
||||
properties: OrderedMap<string, ClassProperty> | undefined,
|
||||
additionalProperties: TypeRef | undefined
|
||||
): void {
|
||||
this.register(
|
||||
this.builderForNewType().getUniqueObjectType(
|
||||
this._typeAttributes,
|
||||
properties,
|
||||
additionalProperties,
|
||||
this._forwardingRef
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getClassType(properties: OrderedMap<string, ClassProperty>): void {
|
||||
if (this._makeClassUnique) {
|
||||
this.getUniqueClassType(false, properties);
|
||||
return;
|
||||
}
|
||||
this.register(this.builderForNewType().getClassType(this._typeAttributes, properties, this._forwardingRef));
|
||||
}
|
||||
|
||||
getUniqueClassType(isFixed: boolean, properties: OrderedMap<string, ClassProperty> | undefined): void {
|
||||
this.register(
|
||||
this.builderForNewType().getUniqueClassType(this._typeAttributes, isFixed, properties, this._forwardingRef)
|
||||
);
|
||||
}
|
||||
|
||||
setObjectProperties(
|
||||
properties: OrderedMap<string, ClassProperty>,
|
||||
additionalProperties: TypeRef | undefined
|
||||
): TypeRef {
|
||||
return this.addAttributes(
|
||||
this.useBuilder().getUniqueObjectType(defined(this._typeAttributes), properties, additionalProperties)
|
||||
): void {
|
||||
this.builderForSetting().setObjectProperties(this.getResult(), properties, additionalProperties);
|
||||
}
|
||||
|
||||
getUnionType(members: OrderedSet<TypeRef>): void {
|
||||
this.register(this.builderForNewType().getUnionType(this._typeAttributes, members, this._forwardingRef));
|
||||
}
|
||||
|
||||
getUniqueUnionType(): void {
|
||||
this.register(
|
||||
this.builderForNewType().getUniqueUnionType(this._typeAttributes, undefined, this._forwardingRef)
|
||||
);
|
||||
}
|
||||
|
||||
getClassType(properties: OrderedMap<string, ClassProperty>): TypeRef {
|
||||
if (this._makeClassUnique) {
|
||||
return this.getUniqueClassType(false, properties);
|
||||
}
|
||||
return this.useBuilder().getClassType(defined(this._typeAttributes), properties, this._forwardingRef);
|
||||
getIntersectionType(members: OrderedSet<TypeRef>): void {
|
||||
this.register(this.builderForNewType().getIntersectionType(this._typeAttributes, members, this._forwardingRef));
|
||||
}
|
||||
|
||||
getUniqueClassType(isFixed: boolean, properties: OrderedMap<string, ClassProperty>): TypeRef {
|
||||
return this.useBuilder().getUniqueClassType(
|
||||
defined(this._typeAttributes),
|
||||
isFixed,
|
||||
properties,
|
||||
this._forwardingRef
|
||||
getUniqueIntersectionType(members?: OrderedSet<TypeRef>): void {
|
||||
this.register(
|
||||
this.builderForNewType().getUniqueIntersectionType(this._typeAttributes, members, this._forwardingRef)
|
||||
);
|
||||
}
|
||||
|
||||
getUnionType(members: OrderedSet<TypeRef>): TypeRef {
|
||||
return this.useBuilder().getUnionType(defined(this._typeAttributes), members, this._forwardingRef);
|
||||
}
|
||||
|
||||
getUniqueIntersectionType(members: OrderedSet<TypeRef>): TypeRef {
|
||||
return this.useBuilder().getUniqueIntersectionType(defined(this._typeAttributes), members, this._forwardingRef);
|
||||
setSetOperationMembers(members: OrderedSet<TypeRef>): void {
|
||||
this.builderForSetting().setSetOperationMembers(this.getResult(), members);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,12 +661,15 @@ export class GraphRewriteBuilder<T extends Type> extends TypeBuilder implements
|
|||
|
||||
private _lostTypeAttributes: boolean = false;
|
||||
|
||||
private _printIndent = 0;
|
||||
|
||||
constructor(
|
||||
private readonly _originalGraph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
alphabetizeProperties: boolean,
|
||||
graphHasProvenanceAttributes: boolean,
|
||||
setsToReplace: T[][],
|
||||
private readonly _debugPrintReconstitution: boolean,
|
||||
private readonly _replacer: (
|
||||
typesToReplace: Set<T>,
|
||||
builder: GraphRewriteBuilder<T>,
|
||||
|
@ -578,12 +722,36 @@ export class GraphRewriteBuilder<T extends Type> extends TypeBuilder implements
|
|||
|
||||
private forceReconstituteTypeRef(originalRef: TypeRef, maybeForwardingRef?: TypeRef): TypeRef {
|
||||
return this.withForwardingRef(maybeForwardingRef, (forwardingRef: TypeRef): TypeRef => {
|
||||
this._reconstitutedTypes = this._reconstitutedTypes.set(originalRef.getIndex(), forwardingRef);
|
||||
const [originalType, originalNames] = originalRef.deref();
|
||||
return originalType.map(
|
||||
new TypeReconstituter(this, this.alphabetizeProperties, originalNames, forwardingRef),
|
||||
(ref: TypeRef) => this.reconstituteTypeRef(ref)
|
||||
const [originalType, originalAttributes] = originalRef.deref();
|
||||
const index = originalRef.getIndex();
|
||||
|
||||
if (this._debugPrintReconstitution) {
|
||||
console.log(`${" ".repeat(this._printIndent)}reconstituting ${index}`);
|
||||
this._printIndent += 1;
|
||||
}
|
||||
|
||||
const reconstituter = new TypeReconstituter(
|
||||
this,
|
||||
this.alphabetizeProperties,
|
||||
originalAttributes,
|
||||
forwardingRef,
|
||||
tref => {
|
||||
if (this._debugPrintReconstitution) {
|
||||
this._printIndent -= 1;
|
||||
console.log(`${" ".repeat(this._printIndent)}reconstituted ${index} as ${tref.getIndex()}`);
|
||||
}
|
||||
|
||||
assert(tref.equals(forwardingRef), "We didn't pass the forwarding ref");
|
||||
const alreadyReconstitutedType = this._reconstitutedTypes.get(index);
|
||||
if (alreadyReconstitutedType === undefined) {
|
||||
this._reconstitutedTypes = this._reconstitutedTypes.set(index, tref);
|
||||
} else {
|
||||
assert(tref.equals(alreadyReconstitutedType), "We reconstituted a type twice differently");
|
||||
}
|
||||
}
|
||||
);
|
||||
originalType.reconstitute(reconstituter);
|
||||
return reconstituter.getResult();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -610,7 +778,7 @@ export class GraphRewriteBuilder<T extends Type> extends TypeBuilder implements
|
|||
|
||||
// If the union of these type refs have been, or are supposed to be, reconstituted to
|
||||
// one target type, return it. Otherwise return undefined.
|
||||
lookupTypeRefs(typeRefs: TypeRef[], forwardingRef?: TypeRef): TypeRef | undefined {
|
||||
lookupTypeRefs(typeRefs: TypeRef[], forwardingRef?: TypeRef, replaceSet: boolean = true): TypeRef | undefined {
|
||||
this.assertTypeRefsToReconstitute(typeRefs, forwardingRef);
|
||||
|
||||
// Check whether we have already reconstituted them. That means ensuring
|
||||
|
@ -652,6 +820,7 @@ export class GraphRewriteBuilder<T extends Type> extends TypeBuilder implements
|
|||
}
|
||||
}
|
||||
// Yes, this set is requested to be replaced, so do it.
|
||||
if (!replaceSet) return undefined;
|
||||
return this.replaceSet(maybeSet, forwardingRef);
|
||||
}
|
||||
|
||||
|
|
|
@ -242,6 +242,7 @@ export class TypeGraph {
|
|||
stringTypeMapping: StringTypeMapping,
|
||||
alphabetizeProperties: boolean,
|
||||
replacementGroups: T[][],
|
||||
debugPrintReconstitution: boolean,
|
||||
replacer: (typesToReplace: Set<T>, builder: GraphRewriteBuilder<T>, forwardingRef: TypeRef) => TypeRef,
|
||||
force: boolean = false
|
||||
): TypeGraph {
|
||||
|
@ -257,6 +258,7 @@ export class TypeGraph {
|
|||
alphabetizeProperties,
|
||||
this._haveProvenanceAttributes,
|
||||
replacementGroups,
|
||||
debugPrintReconstitution,
|
||||
replacer
|
||||
);
|
||||
const newGraph = builder.finish();
|
||||
|
@ -285,6 +287,7 @@ export class TypeGraph {
|
|||
NoStringTypeMapping,
|
||||
alphabetizeProperties,
|
||||
[],
|
||||
false,
|
||||
(_t, _b) => mustNotBeCalled(),
|
||||
true
|
||||
);
|
||||
|
@ -337,7 +340,11 @@ export class TypeGraph {
|
|||
}
|
||||
}
|
||||
|
||||
export function noneToAny(graph: TypeGraph, stringTypeMapping: StringTypeMapping): TypeGraph {
|
||||
export function noneToAny(
|
||||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
debugPrintReconstitution: boolean
|
||||
): TypeGraph {
|
||||
const noneTypes = graph.allTypesUnordered().filter(t => t.kind === "none");
|
||||
if (noneTypes.size === 0) {
|
||||
return graph;
|
||||
|
@ -348,6 +355,7 @@ export function noneToAny(graph: TypeGraph, stringTypeMapping: StringTypeMapping
|
|||
stringTypeMapping,
|
||||
false,
|
||||
[noneTypes.toArray()],
|
||||
debugPrintReconstitution,
|
||||
(types, builder, forwardingRef) => {
|
||||
const tref = builder.getPrimitiveType("any", forwardingRef);
|
||||
const attributes = combineTypeAttributesOfTypes(types);
|
||||
|
@ -357,9 +365,13 @@ export function noneToAny(graph: TypeGraph, stringTypeMapping: StringTypeMapping
|
|||
);
|
||||
}
|
||||
|
||||
export function optionalToNullable(graph: TypeGraph, stringTypeMapping: StringTypeMapping): TypeGraph {
|
||||
export function optionalToNullable(
|
||||
graph: TypeGraph,
|
||||
stringTypeMapping: StringTypeMapping,
|
||||
debugPrintReconstitution: boolean
|
||||
): TypeGraph {
|
||||
function rewriteClass(c: ClassType, builder: GraphRewriteBuilder<ClassType>, forwardingRef: TypeRef): TypeRef {
|
||||
const properties = c.properties.map((p, name) => {
|
||||
const properties = c.getProperties().map((p, name) => {
|
||||
const t = p.type;
|
||||
let ref: TypeRef;
|
||||
if (!p.isOptional || t.isNullable) {
|
||||
|
@ -389,7 +401,7 @@ export function optionalToNullable(graph: TypeGraph, stringTypeMapping: StringTy
|
|||
|
||||
const classesWithOptional = graph
|
||||
.allTypesUnordered()
|
||||
.filter(t => t instanceof ClassType && t.properties.some(p => p.isOptional));
|
||||
.filter(t => t instanceof ClassType && t.getProperties().some(p => p.isOptional));
|
||||
const replacementGroups = classesWithOptional.map(c => [c as ClassType]).toArray();
|
||||
if (classesWithOptional.size === 0) {
|
||||
return graph;
|
||||
|
@ -399,6 +411,7 @@ export function optionalToNullable(graph: TypeGraph, stringTypeMapping: StringTy
|
|||
stringTypeMapping,
|
||||
false,
|
||||
replacementGroups,
|
||||
debugPrintReconstitution,
|
||||
(setOfClass, builder, forwardingRef) => {
|
||||
assert(setOfClass.size === 1);
|
||||
const c = defined(setOfClass.first());
|
||||
|
|
|
@ -22,7 +22,7 @@ function getCliqueProperties(
|
|||
let lostTypeAttributes = false;
|
||||
let propertyNames = OrderedSet<string>();
|
||||
for (const o of clique) {
|
||||
propertyNames = propertyNames.union(o.properties.keySeq());
|
||||
propertyNames = propertyNames.union(o.getProperties().keySeq());
|
||||
}
|
||||
|
||||
let properties = propertyNames
|
||||
|
@ -30,7 +30,7 @@ function getCliqueProperties(
|
|||
.map(name => [name, OrderedSet(), false] as [string, OrderedSet<Type>, boolean]);
|
||||
let additionalProperties: OrderedSet<Type> | undefined = undefined;
|
||||
for (const o of clique) {
|
||||
let additional = o.additionalProperties;
|
||||
let additional = o.getAdditionalProperties();
|
||||
if (additional !== undefined) {
|
||||
if (additionalProperties === undefined) {
|
||||
additionalProperties = OrderedSet();
|
||||
|
@ -42,7 +42,7 @@ function getCliqueProperties(
|
|||
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
let [name, types, isOptional] = properties[i];
|
||||
const maybeProperty = o.properties.get(name);
|
||||
const maybeProperty = o.getProperties().get(name);
|
||||
if (maybeProperty === undefined) {
|
||||
isOptional = true;
|
||||
if (additional !== undefined && additional.kind !== "any") {
|
||||
|
@ -85,10 +85,10 @@ function countProperties(
|
|||
let hasAdditionalProperties = false;
|
||||
let hasNonAnyAdditionalProperties = false;
|
||||
for (const o of clique) {
|
||||
if (!o.properties.isEmpty()) {
|
||||
if (!o.getProperties().isEmpty()) {
|
||||
hasProperties = true;
|
||||
}
|
||||
const additional = o.additionalProperties;
|
||||
const additional = o.getAdditionalProperties();
|
||||
if (additional !== undefined) {
|
||||
hasAdditionalProperties = true;
|
||||
if (additional.kind !== "any") {
|
||||
|
@ -143,11 +143,18 @@ export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp,
|
|||
const { hasProperties, hasAdditionalProperties, hasNonAnyAdditionalProperties } = countProperties(objectTypes);
|
||||
|
||||
if (!this._makeObjectTypes && (hasNonAnyAdditionalProperties || (!hasProperties && hasAdditionalProperties))) {
|
||||
const propertyTypes = unionOfSets(objectTypes.map(o => o.properties.map(cp => cp.typeRef).toOrderedSet()));
|
||||
const propertyTypes = unionOfSets(
|
||||
objectTypes.map(o =>
|
||||
o
|
||||
.getProperties()
|
||||
.map(cp => cp.typeRef)
|
||||
.toOrderedSet()
|
||||
)
|
||||
);
|
||||
const additionalPropertyTypes = OrderedSet(
|
||||
objectTypes
|
||||
.filter(o => o.additionalProperties !== undefined)
|
||||
.map(o => defined(o.additionalProperties).typeRef)
|
||||
.filter(o => o.getAdditionalProperties() !== undefined)
|
||||
.map(o => defined(o.getAdditionalProperties()).typeRef)
|
||||
);
|
||||
const allPropertyTypes = propertyTypes.union(additionalPropertyTypes).toArray();
|
||||
const tref = this.typeBuilder.getMapType(this._unifyTypes(allPropertyTypes, emptyTypeAttributes));
|
||||
|
|
|
@ -425,7 +425,7 @@ function makeOptionDefinitions(targetLanguages: TargetLanguage[]): OptionDefinit
|
|||
name: "debug",
|
||||
type: String,
|
||||
typeLabel: "OPTIONS",
|
||||
description: "Comma separated debug options: print-graph,provenance"
|
||||
description: "Comma separated debug options: print-graph, provenance, print-reconstitution"
|
||||
},
|
||||
{
|
||||
name: "telemetry",
|
||||
|
@ -741,12 +741,15 @@ export async function makeQuicktypeOptions(
|
|||
|
||||
let debugPrintGraph = false;
|
||||
let checkProvenance = false;
|
||||
let debugPrintReconstitution = false;
|
||||
if (options.debug !== undefined) {
|
||||
const components = options.debug.split(",");
|
||||
for (let component of components) {
|
||||
component = component.trim();
|
||||
if (component === "print-graph") {
|
||||
debugPrintGraph = true;
|
||||
} else if (component === "print-reconstitution") {
|
||||
debugPrintReconstitution = true;
|
||||
} else if (component === "provenance") {
|
||||
checkProvenance = true;
|
||||
} else {
|
||||
|
@ -778,7 +781,8 @@ export async function makeQuicktypeOptions(
|
|||
outputFilename: mapOptional(path.basename, options.out),
|
||||
schemaStore: new FetchingJSONSchemaStore(),
|
||||
debugPrintGraph,
|
||||
checkProvenance
|
||||
checkProvenance,
|
||||
debugPrintReconstitution
|
||||
};
|
||||
}
|
||||
|
||||
|
|
54
src/index.ts
54
src/index.ts
|
@ -63,6 +63,7 @@ export interface Options {
|
|||
schemaStore: JSONSchemaStore | undefined;
|
||||
debugPrintGraph: boolean;
|
||||
checkProvenance: boolean;
|
||||
debugPrintReconstitution: boolean;
|
||||
}
|
||||
|
||||
const defaultOptions: Options = {
|
||||
|
@ -84,7 +85,8 @@ const defaultOptions: Options = {
|
|||
outputFilename: "stdout",
|
||||
schemaStore: undefined,
|
||||
debugPrintGraph: false,
|
||||
checkProvenance: false
|
||||
checkProvenance: false,
|
||||
debugPrintReconstitution: false
|
||||
};
|
||||
|
||||
export class Run {
|
||||
|
@ -155,16 +157,28 @@ export class Run {
|
|||
graph.printGraph();
|
||||
}
|
||||
|
||||
const debugPrintReconstitution = this._options.debugPrintReconstitution === true;
|
||||
|
||||
let unionsDone = false;
|
||||
if (!schemaInputs.isEmpty()) {
|
||||
let intersectionsDone = false;
|
||||
do {
|
||||
const graphBeforeRewrites = graph;
|
||||
if (!intersectionsDone) {
|
||||
[graph, intersectionsDone] = resolveIntersections(graph, stringTypeMapping);
|
||||
[graph, intersectionsDone] = resolveIntersections(
|
||||
graph,
|
||||
stringTypeMapping,
|
||||
debugPrintReconstitution
|
||||
);
|
||||
}
|
||||
if (!unionsDone) {
|
||||
[graph, unionsDone] = flattenUnions(graph, stringTypeMapping, conflateNumbers, true);
|
||||
[graph, unionsDone] = flattenUnions(
|
||||
graph,
|
||||
stringTypeMapping,
|
||||
conflateNumbers,
|
||||
true,
|
||||
debugPrintReconstitution
|
||||
);
|
||||
}
|
||||
|
||||
if (graph === graphBeforeRewrites) {
|
||||
|
@ -173,9 +187,21 @@ export class Run {
|
|||
} while (!intersectionsDone || !unionsDone);
|
||||
}
|
||||
|
||||
graph = replaceObjectType(graph, stringTypeMapping, conflateNumbers, targetLanguage.supportsFullObjectType);
|
||||
graph = replaceObjectType(
|
||||
graph,
|
||||
stringTypeMapping,
|
||||
conflateNumbers,
|
||||
targetLanguage.supportsFullObjectType,
|
||||
debugPrintReconstitution
|
||||
);
|
||||
do {
|
||||
[graph, unionsDone] = flattenUnions(graph, stringTypeMapping, conflateNumbers, false);
|
||||
[graph, unionsDone] = flattenUnions(
|
||||
graph,
|
||||
stringTypeMapping,
|
||||
conflateNumbers,
|
||||
false,
|
||||
debugPrintReconstitution
|
||||
);
|
||||
} while (!unionsDone);
|
||||
|
||||
if (this._options.findSimilarClassesSchemaURI !== undefined) {
|
||||
|
@ -183,18 +209,24 @@ export class Run {
|
|||
}
|
||||
|
||||
if (this._options.combineClasses) {
|
||||
graph = combineClasses(graph, stringTypeMapping, this._options.alphabetizeProperties, conflateNumbers);
|
||||
graph = combineClasses(
|
||||
graph,
|
||||
stringTypeMapping,
|
||||
this._options.alphabetizeProperties,
|
||||
conflateNumbers,
|
||||
debugPrintReconstitution
|
||||
);
|
||||
}
|
||||
if (doInferEnums) {
|
||||
graph = inferEnums(graph, stringTypeMapping);
|
||||
graph = inferEnums(graph, stringTypeMapping, debugPrintReconstitution);
|
||||
}
|
||||
graph = flattenStrings(graph, stringTypeMapping);
|
||||
graph = flattenStrings(graph, stringTypeMapping, debugPrintReconstitution);
|
||||
if (this._options.inferMaps) {
|
||||
graph = inferMaps(graph, stringTypeMapping, conflateNumbers);
|
||||
graph = inferMaps(graph, stringTypeMapping, conflateNumbers, debugPrintReconstitution);
|
||||
}
|
||||
graph = noneToAny(graph, stringTypeMapping);
|
||||
graph = noneToAny(graph, stringTypeMapping, debugPrintReconstitution);
|
||||
if (!targetLanguage.supportsOptionalClassProperties) {
|
||||
graph = optionalToNullable(graph, stringTypeMapping);
|
||||
graph = optionalToNullable(graph, stringTypeMapping, debugPrintReconstitution);
|
||||
}
|
||||
// Sometimes we combine classes in ways that will the order come out
|
||||
// differently compared to what it would be from the equivalent schema,
|
||||
|
|
Загрузка…
Ссылка в новой задаче