Refactoring of conversion

This commit is contained in:
Pieter Vander Vennet 2022-02-04 01:05:35 +01:00
parent 5dffb3c5e7
commit 7eeffc2305
12 changed files with 389 additions and 97 deletions

View file

@ -26,14 +26,14 @@ export abstract class Conversion<TIn, TOut> {
return fixed.result;
}
public convertStrict(state: DesugaringContext, json: TIn, context: string): TOut {
const fixed = this.convert(state, json, context)
public convertStrict(json: TIn, context: string): TOut {
const fixed = this.convert(json, context)
return DesugaringStep.strict(fixed)
}
abstract convert(state: DesugaringContext, json: TIn, context: string): { result: TOut, errors?: string[], warnings?: string[] }
abstract convert(json: TIn, context: string): { result: TOut, errors?: string[], warnings?: string[] }
public convertAll(state: DesugaringContext, jsons: TIn[], context: string): { result: TOut[], errors: string[], warnings: string[] } {
public convertAll(jsons: TIn[], context: string): { result: TOut[], errors: string[], warnings: string[] } {
if(jsons === undefined){
throw "convertAll received undefined - don't do this (at "+context+")"
}
@ -42,7 +42,7 @@ export abstract class Conversion<TIn, TOut> {
const warnings = []
for (let i = 0; i < jsons.length; i++) {
const json = jsons[i];
const r = this.convert(state, json, context + "[" + i + "]")
const r = this.convert(json, context + "[" + i + "]")
result.push(r.result)
errors.push(...r.errors ?? [])
warnings.push(...r.warnings ?? [])
@ -69,11 +69,11 @@ export class OnEvery<X, T> extends DesugaringStep<T> {
this.key = key;
}
convert(state: DesugaringContext, json: T, context: string): { result: T; errors?: string[]; warnings?: string[] } {
convert(json: T, context: string): { result: T; errors?: string[]; warnings?: string[] } {
json = {...json}
const step = this.step
const key = this.key;
const r = step.convertAll(state, (<X[]>json[key]), context + "." + key)
const r = step.convertAll((<X[]>json[key]), context + "." + key)
json[key] = r.result
return {
result: json,
@ -94,7 +94,7 @@ export class OnEveryConcat<X, T> extends DesugaringStep<T> {
this.key = key;
}
convert(state: DesugaringContext, json: T, context: string): { result: T; errors: string[]; warnings: string[] } {
convert(json: T, context: string): { result: T; errors: string[]; warnings: string[] } {
json = {...json}
const step = this.step
const key = this.key;
@ -107,7 +107,7 @@ export class OnEveryConcat<X, T> extends DesugaringStep<T> {
warnings: []
}
}
const r = step.convertAll(state, (<X[]>values), context + "." + key)
const r = step.convertAll((<X[]>values), context + "." + key)
const vals: X[][] = r.result
json[key] = [].concat(...vals)
return {
@ -129,12 +129,12 @@ export class Fuse<T> extends DesugaringStep<T> {
this.steps = steps;
}
convert(state: DesugaringContext, json: T, context: string): { result: T; errors: string[]; warnings: string[] } {
convert(json: T, context: string): { result: T; errors: string[]; warnings: string[] } {
const errors = []
const warnings = []
for (let i = 0; i < this.steps.length; i++) {
const step = this.steps[i];
let r = step.convert(state, json, "While running step " +step.name + ": " + context)
let r = step.convert(json, "While running step " +step.name + ": " + context)
errors.push(...r.errors ?? [])
warnings.push(...r.warnings ?? [])
json = r.result
@ -163,7 +163,7 @@ export class SetDefault<T> extends DesugaringStep<T> {
this._overrideEmptyString = overrideEmptyString;
}
convert(state: DesugaringContext, json: T, context: string): { result: T; errors: string[]; warnings: string[] } {
convert(json: T, context: string): { result: T; errors: string[]; warnings: string[] } {
if (json[this.key] === undefined || (json[this.key] === "" && this._overrideEmptyString)) {
json = {...json}
json[this.key] = this.value

View file

@ -1,4 +1,4 @@
import {Conversion, DesugaringContext} from "./Conversion";
import {Conversion} from "./Conversion";
import LayerConfig from "../LayerConfig";
import {LayerConfigJson} from "../Json/LayerConfigJson";
import Translations from "../../../UI/i18n/Translations";
@ -20,7 +20,7 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L
this._includeClosedNotesDays = includeClosedNotesDays;
}
convert(state: DesugaringContext, layerJson: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } {
convert(layerJson: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } {
const errors = []
const warnings = []
const t = Translations.t.importLayer;

View file

@ -11,7 +11,7 @@ export class UpdateLegacyLayer extends DesugaringStep<LayerConfigJson | string |
["overpassTags", "source.osmtags", "tagRenderings[*].id", "mapRendering"]);
}
convert(state: {}, json: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } {
convert(json: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } {
const warnings = []
if (typeof json === "string" || json["builtin"] !== undefined) {
// Reuse of an already existing layer; return as-is
@ -123,7 +123,7 @@ class UpdateLegacyTheme extends DesugaringStep<LayoutConfigJson> {
super("Small fixes in the theme config", ["roamingRenderings"]);
}
convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
const oldThemeConfig = {...json}
if (oldThemeConfig["roamingRenderings"] !== undefined) {

View file

@ -6,21 +6,24 @@ import Translations from "../../../UI/i18n/Translations";
import {Translation} from "../../../UI/i18n/Translation";
class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> {
constructor() {
private readonly _state: DesugaringContext;
constructor(state: DesugaringContext) {
super("Converts a tagRenderingSpec into the full tagRendering", []);
this._state = state;
}
convert(state: DesugaringContext, json: string | TagRenderingConfigJson | { builtin: string | string[]; override: any }, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings: string[] } {
convert(json: string | TagRenderingConfigJson | { builtin: string | string[]; override: any }, context: string): { result: TagRenderingConfigJson[]; errors: string[]; warnings: string[] } {
const errors = []
const warnings = []
return {
result: this.convertUntilStable(state, json, warnings, errors, context),
result: this.convertUntilStable(json, warnings, errors, context),
errors, warnings
};
}
private lookup(state: DesugaringContext, name: string): TagRenderingConfigJson[] {
private lookup(name: string): TagRenderingConfigJson[] {
const state = this._state;
if (state.tagRenderings.has(name)) {
return [state.tagRenderings.get(name)]
}
@ -61,7 +64,8 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
return undefined;
}
private convertOnce(state: DesugaringContext, tr: string | any, warnings: string[], errors: string[], ctx: string): TagRenderingConfigJson[] {
private convertOnce(tr: string | any, warnings: string[], errors: string[], ctx: string): TagRenderingConfigJson[] {
const state = this._state
if (tr === "questions") {
return [{
id: "questions"
@ -70,7 +74,7 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
if (typeof tr === "string") {
const lookup = this.lookup(state, tr);
const lookup = this.lookup(tr);
if (lookup !== undefined) {
return lookup
}
@ -96,7 +100,7 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
const trs: TagRenderingConfigJson[] = []
for (const name of names) {
const lookup = this.lookup(state, name)
const lookup = this.lookup(name)
if (lookup === undefined) {
errors.push(ctx + ": The tagRendering with identifier " + name + " was not found.\n\tDid you mean one of " + Array.from(state.tagRenderings.keys()).join(", ") + "?")
continue
@ -113,13 +117,13 @@ class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | {
return [tr]
}
private convertUntilStable(state: DesugaringContext, spec: string | any, warnings: string[], errors: string[], ctx: string): TagRenderingConfigJson[] {
const trs = this.convertOnce(state, spec, warnings, errors, ctx);
private convertUntilStable(spec: string | any, warnings: string[], errors: string[], ctx: string): TagRenderingConfigJson[] {
const trs = this.convertOnce(spec, warnings, errors, ctx);
const result = []
for (const tr of trs) {
if (tr["builtin"] !== undefined) {
const stable = this.convertUntilStable(state, tr, warnings, errors, ctx + "(RECURSIVE RESOLVE)")
const stable = this.convertUntilStable(tr, warnings, errors, ctx + "(RECURSIVE RESOLVE)")
result.push(...stable)
} else {
result.push(tr)
@ -139,15 +143,16 @@ class ExpandGroupRewrite extends Conversion<{
} | TagRenderingConfigJson, TagRenderingConfigJson[]> {
private static expandSubTagRenderings = new ExpandTagRendering()
private _expandSubTagRenderings;
constructor() {
constructor(state: DesugaringContext) {
super(
"Converts a rewrite config for tagRenderings into the expanded form"
);
this._expandSubTagRenderings = new ExpandTagRendering(state)
}
convert(state: DesugaringContext, json:
convert( json:
{
rewrite:
{ sourceString: string; into: string[] }[]; renderings: (string | { builtin: string; override: any } | TagRenderingConfigJson)[]
@ -186,7 +191,7 @@ class ExpandGroupRewrite extends Conversion<{
}
}
const subRenderingsRes = <{ result: TagRenderingConfigJson[][], errors, warnings }> ExpandGroupRewrite.expandSubTagRenderings.convertAll(state, config.renderings, context);
const subRenderingsRes = <{ result: TagRenderingConfigJson[][], errors, warnings }> this._expandSubTagRenderings.convertAll(config.renderings, context);
const subRenderings: TagRenderingConfigJson[] = [].concat(...subRenderingsRes.result);
const errors = subRenderingsRes.errors;
const warnings = subRenderingsRes.warnings;
@ -279,13 +284,13 @@ class ExpandGroupRewrite extends Conversion<{
export class PrepareLayer extends Fuse<LayerConfigJson> {
constructor() {
constructor(state: DesugaringContext) {
super(
"Fully prepares and expands a layer for the LayerConfig.",
new OnEveryConcat("tagRenderings", new ExpandGroupRewrite()),
new OnEveryConcat("tagRenderings", new ExpandTagRendering()),
new OnEveryConcat("tagRenderings", new ExpandGroupRewrite(state)),
new OnEveryConcat("tagRenderings", new ExpandTagRendering(state)),
new SetDefault("titleIcons", ["defaults"]),
new OnEveryConcat("titleIcons", new ExpandTagRendering())
new OnEveryConcat("titleIcons", new ExpandTagRendering(state))
);
}
}

View file

@ -13,14 +13,17 @@ import DependencyCalculator from "../DependencyCalculator";
import {ValidateThemeAndLayers} from "./Validation";
class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfigJson[]> {
constructor() {
private readonly _state: DesugaringContext;
constructor(
state: DesugaringContext,
) {
super("Converts the identifier of a builtin layer into the actual layer, or converts a 'builtin' syntax with override in the fully expanded form", []);
this._state = state;
}
convert(state: DesugaringContext, json: string | LayerConfigJson, context: string): { result: LayerConfigJson[]; errors: string[]; warnings: string[] } {
convert(json: string | LayerConfigJson, context: string): { result: LayerConfigJson[]; errors: string[] } {
const errors = []
const warnings = []
const state= this._state
function reportNotFound(name: string){
const knownLayers = Array.from(state.sharedLayers.keys())
const withDistance = knownLayers.map(lname => [lname, Utils.levenshteinDistance(name, lname)])
@ -39,12 +42,11 @@ class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfig
return {
result: null,
errors,
warnings
}
}
return {
result: [found],
errors, warnings
errors
}
}
@ -72,28 +74,31 @@ class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfig
}
return {
result: layers,
errors, warnings
errors
}
}
return {
result: [json],
errors, warnings
errors
};
}
}
class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
private _state: DesugaringContext;
constructor() {
constructor(state: DesugaringContext) {
super("Adds the default layers, namely: " + Constants.added_by_default.join(", "), ["layers"]);
this._state = state;
}
convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
const errors = []
const warnings = []
const state = this._state
json.layers = [...json.layers]
const alreadyLoaded = new Set(json.layers.map(l => l["id"]))
@ -141,7 +146,7 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
super("For every layer in the 'layers'-list, create a new layer which'll import notes. (Note that priviliged layers and layers which have a geojson-source set are ignored)", ["layers"]);
}
convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
const errors = []
const warnings = []
@ -176,11 +181,10 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
try {
const importLayerResult = creator.convert(state, layer, context + ".(noteimportlayer)[" + i1 + "]")
const importLayerResult = creator.convert(layer, context + ".(noteimportlayer)[" + i1 + "]")
errors.push(...importLayerResult.errors)
warnings.push(...importLayerResult.warnings)
if (importLayerResult.result !== undefined) {
warnings.push("Added an import layer to theme " + json.id + ", namely " + importLayerResult.result.id)
json.layers.push(importLayerResult.result)
}
} catch (e) {
@ -199,8 +203,10 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
constructor() {
private readonly _state: DesugaringContext;
constructor(state: DesugaringContext, ) {
super("Adds a default 'minimap'-element to the tagrenderings if none of the elements define such a minimap", ["tagRenderings"]);
this._state = state;
}
/**
@ -229,9 +235,9 @@ export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
return false;
}
convert(state: DesugaringContext, layerConfig: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } {
convert(layerConfig: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } {
const state = this._state;
const hasMinimap = layerConfig.tagRenderings?.some(tr => AddMiniMap.hasMinimap(<TagRenderingConfigJson>tr)) ?? true
if (!hasMinimap) {
layerConfig = {...layerConfig}
@ -255,7 +261,7 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
super("Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards", ["overrideAll", "layers"]);
}
convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
const overrideAll = json.overrideAll;
if (overrideAll === undefined) {
@ -280,8 +286,10 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
}
class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
constructor() {
private readonly _state: DesugaringContext;
constructor(state: DesugaringContext, ) {
super("If a layer has a dependency on another layer, these layers are added automatically on the theme. (For example: defibrillator depends on 'walls_and_buildings' to snap onto. This layer is added automatically)", ["layers"]);
this._state = state;
}
private static CalculateDependencies(alreadyLoaded: LayerConfigJson[], allKnownLayers: Map<string, LayerConfigJson>, themeId: string): LayerConfigJson[] {
@ -326,7 +334,8 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
return dependenciesToAdd;
}
convert(state: DesugaringContext, theme: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
convert(theme: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
const state = this._state
const allKnownLayers: Map<string, LayerConfigJson> = state.sharedLayers;
const knownTagRenderings: Map<string, TagRenderingConfigJson> = state.tagRenderings;
const errors = [];
@ -357,18 +366,18 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
export class PrepareTheme extends Fuse<LayoutConfigJson> {
constructor() {
constructor(state: DesugaringContext) {
super(
"Fully prepares and expands a theme",
new OnEveryConcat("layers", new SubstituteLayer()),
new OnEveryConcat("layers", new SubstituteLayer(state)),
new SetDefault("socialImage", "assets/SocialImage.png", true),
new OnEvery("layers", new PrepareLayer()),
new OnEvery("layers", new PrepareLayer(state)),
new ApplyOverrideAll(),
new AddDefaultLayers(),
new AddDependencyLayersToTheme(),
new AddDefaultLayers(state),
new AddDependencyLayersToTheme(state),
new AddImportLayers(),
new OnEvery("layers", new AddMiniMap())
new OnEvery("layers", new AddMiniMap(state))
);
}
}

View file

@ -1,4 +1,4 @@
import {DesugaringContext, DesugaringStep, Fuse, OnEvery} from "./Conversion";
import {DesugaringStep, Fuse, OnEvery} from "./Conversion";
import {LayerConfigJson} from "../Json/LayerConfigJson";
import LayerConfig from "../LayerConfig";
import {Utils} from "../../../Utils";
@ -8,7 +8,6 @@ import {LayoutConfigJson} from "../Json/LayoutConfigJson";
import LayoutConfig from "../LayoutConfig";
import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson";
import {TagUtils} from "../../../Logic/Tags/TagUtils";
import {parseString} from "xml2js";
class ValidateLanguageCompleteness extends DesugaringStep<any> {
@ -19,7 +18,7 @@ class ValidateLanguageCompleteness extends DesugaringStep<any> {
this._languages = languages;
}
convert(state: DesugaringContext, obj: any, context: string): { result: LayerConfig; errors: string[]; warnings: string[] } {
convert(obj: any, context: string): { result: LayerConfig; errors: string[] } {
const errors = []
const translations = Translation.ExtractAllTranslationsFrom(
obj
@ -34,7 +33,7 @@ class ValidateLanguageCompleteness extends DesugaringStep<any> {
return {
result: obj,
warnings: [], errors
errors
};
}
}
@ -55,9 +54,8 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
this._isBuiltin = isBuiltin;
}
convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[] } {
const errors = []
const warnings = []
{
// Legacy format checks
if (this._isBuiltin) {
@ -89,9 +87,8 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
}
if (json["mustHaveLanguage"] !== undefined) {
const checked = new ValidateLanguageCompleteness(...json["mustHaveLanguage"])
.convert(state, theme, theme.id)
.convert(theme, theme.id)
errors.push(...checked.errors)
warnings.push(...checked.warnings)
}
} catch (e) {
@ -100,8 +97,7 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
return {
result: json,
errors,
warnings
errors
};
}
}
@ -122,7 +118,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson>{
super("Checks that an 'overrideAll' does not override a single override");
}
convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } {
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } {
const overrideAll = json.overrideAll;
if(overrideAll === undefined){
@ -144,12 +140,10 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson>{
return {result: json, errors}
}
}
export class PrevalidateTheme extends Fuse<LayoutConfigJson>{
constructor() {
super("Various consistency checks on the raw JSON",
new OverrideShadowingCheck()
@ -157,7 +151,6 @@ export class PrevalidateTheme extends Fuse<LayoutConfigJson>{
}
}
export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJson>{
@ -165,7 +158,7 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
super("Checks that the mappings don't shadow each other");
}
convert(state: DesugaringContext, json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[] } {
convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[] } {
const errors = []
if(json.mappings === undefined || json.mappings.length === 0){
return {result: json}
@ -218,7 +211,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
this._isBuiltin = isBuiltin;
}
convert(state: DesugaringContext, json: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings?: string[] } {
convert(json: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings?: string[] } {
const errors = []
const warnings = []
@ -315,7 +308,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
}
}
if(json.tagRenderings !== undefined){
new DetectShadowedMappings().convertAll(state, <TagRenderingConfigJson[]> json.tagRenderings, context+".tagRenderings")
new DetectShadowedMappings().convertAll(<TagRenderingConfigJson[]> json.tagRenderings, context+".tagRenderings")
}
} catch (e) {