Add substituting tag, remove some old code
This commit is contained in:
parent
120832f241
commit
cd1171e678
8 changed files with 112 additions and 31 deletions
|
@ -5,6 +5,7 @@ import {Or} from "../../Logic/Tags/Or";
|
||||||
import {And} from "../../Logic/Tags/And";
|
import {And} from "../../Logic/Tags/And";
|
||||||
import {Tag} from "../../Logic/Tags/Tag";
|
import {Tag} from "../../Logic/Tags/Tag";
|
||||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||||
|
import SubstitutingTag from "../../Logic/Tags/SubstitutingTag";
|
||||||
|
|
||||||
export class FromJSON {
|
export class FromJSON {
|
||||||
|
|
||||||
|
@ -12,18 +13,19 @@ export class FromJSON {
|
||||||
const tag = Utils.SplitFirst(json, "=");
|
const tag = Utils.SplitFirst(json, "=");
|
||||||
return new Tag(tag[0], tag[1]);
|
return new Tag(tag[0], tag[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Tag(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter {
|
public static Tag(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter {
|
||||||
try{
|
try {
|
||||||
return this.TagUnsafe(json, context);
|
return this.TagUnsafe(json, context);
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
console.error("Could not parse tag", json,"in context",context,"due to ", e)
|
console.error("Could not parse tag", json, "in context", context, "due to ", e)
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TagUnsafe(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter {
|
private static TagUnsafe(json: AndOrTagConfigJson | string, context: string = ""): TagsFilter {
|
||||||
|
|
||||||
if (json === undefined) {
|
if (json === undefined) {
|
||||||
throw `Error while parsing a tag: 'json' is undefined in ${context}. Make sure all the tags are defined and at least one tag is present in a complex expression`
|
throw `Error while parsing a tag: 'json' is undefined in ${context}. Make sure all the tags are defined and at least one tag is present in a complex expression`
|
||||||
}
|
}
|
||||||
if (typeof (json) == "string") {
|
if (typeof (json) == "string") {
|
||||||
|
@ -49,6 +51,11 @@ export class FromJSON {
|
||||||
new RegExp("^" + split[1] + "$")
|
new RegExp("^" + split[1] + "$")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if(tag.indexOf(":=") >= 0){
|
||||||
|
const split = Utils.SplitFirst(tag, ":=");
|
||||||
|
return new SubstitutingTag(split[0], split[1]);
|
||||||
|
}
|
||||||
|
|
||||||
if (tag.indexOf("!=") >= 0) {
|
if (tag.indexOf("!=") >= 0) {
|
||||||
const split = Utils.SplitFirst(tag, "!=");
|
const split = Utils.SplitFirst(tag, "!=");
|
||||||
if (split[1] === "*") {
|
if (split[1] === "*") {
|
||||||
|
|
|
@ -35,3 +35,32 @@ Regex equals
|
||||||
A tag can also be tested against a regex with `key~regex`. Note that this regex __must match__ the entire value. If the value is allowed to appear anywhere as substring, use `key~.*regex.*`
|
A tag can also be tested against a regex with `key~regex`. Note that this regex __must match__ the entire value. If the value is allowed to appear anywhere as substring, use `key~.*regex.*`
|
||||||
|
|
||||||
Equivalently, `key!~regex` can be used if you _don't_ want to match the regex in order to appear.
|
Equivalently, `key!~regex` can be used if you _don't_ want to match the regex in order to appear.
|
||||||
|
|
||||||
|
|
||||||
|
Using other tags as variables
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
**This is an advanced feature - use with caution**
|
||||||
|
|
||||||
|
Some tags are automatically set or calculated - see [CalculatedTags](CalculatedTags.md) for an entire overview.
|
||||||
|
If one wants to apply such a value as tag, use a substituting-tag such, for example`survey:date:={_date:now}`. Note that the separator between key and value here is `:=`.
|
||||||
|
The text between `{` and `}` is interpreted as a key, and the respective value is substituted into the string.
|
||||||
|
|
||||||
|
One can also append, e.g. `key:={some_key} fixed text {some_other_key}`.
|
||||||
|
|
||||||
|
An assigning tag _cannot_ be used to query OpenStreetMap/Overpass.
|
||||||
|
|
||||||
|
If using a key or variable which might not be defined, add a condition in the mapping to hide the option.
|
||||||
|
This is because, if `some_other_key` is not defined, one might actually upload the literal text `key={some_other_key}` to OSM - which we do not want.
|
||||||
|
|
||||||
|
To mitigate this, use:
|
||||||
|
|
||||||
|
```
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"if":"key:={some_other_key}"
|
||||||
|
"then": "...",
|
||||||
|
"hideInAnswer": "some_other_key="
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
|
@ -1,2 +1,5 @@
|
||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">ShowAndRun</s:String></wpf:ResourceDictionary>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">ShowAndRun</s:String>
|
||||||
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d2f95dca_002Defa2_002D40b6_002D8190_002D724496f13a75/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Session" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
|
<Nothing />
|
||||||
|
</SessionState></s:String></wpf:ResourceDictionary>
|
|
@ -46,14 +46,6 @@ export class And extends TagsFilter {
|
||||||
return allChoices;
|
return allChoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
substituteValues(tags: any): TagsFilter {
|
|
||||||
const newChoices = [];
|
|
||||||
for (const c of this.and) {
|
|
||||||
newChoices.push(c.substituteValues(tags));
|
|
||||||
}
|
|
||||||
return new And(newChoices);
|
|
||||||
}
|
|
||||||
|
|
||||||
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
||||||
return this.and.map(t => t.asHumanString(linkToWiki, shorten)).join("&");
|
return this.and.map(t => t.asHumanString(linkToWiki, shorten)).join("&");
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,6 @@ export class Or extends TagsFilter {
|
||||||
return choices;
|
return choices;
|
||||||
}
|
}
|
||||||
|
|
||||||
substituteValues(tags: any): TagsFilter {
|
|
||||||
const newChoices = [];
|
|
||||||
for (const c of this.or) {
|
|
||||||
newChoices.push(c.substituteValues(tags));
|
|
||||||
}
|
|
||||||
return new Or(newChoices);
|
|
||||||
}
|
|
||||||
|
|
||||||
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
||||||
return this.or.map(t => t.asHumanString(linkToWiki, shorten)).join("|");
|
return this.or.map(t => t.asHumanString(linkToWiki, shorten)).join("|");
|
||||||
}
|
}
|
||||||
|
|
59
Logic/Tags/SubstitutingTag.ts
Normal file
59
Logic/Tags/SubstitutingTag.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import {TagsFilter} from "./TagsFilter";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The substituting-tag uses the tags of a feature a variables and replaces them.
|
||||||
|
*
|
||||||
|
* e.g. key:={other_key}_{ref} will match an object that has at least 'key'.
|
||||||
|
* If {other_key} is _not_ defined, it will not be substituted.
|
||||||
|
*
|
||||||
|
* The 'key' is always fixed and should not contain substitutions.
|
||||||
|
* This cannot be used to query features
|
||||||
|
*/
|
||||||
|
export default class SubstitutingTag implements TagsFilter {
|
||||||
|
private readonly _key: string;
|
||||||
|
private readonly _value: string;
|
||||||
|
|
||||||
|
constructor(key: string, value: string) {
|
||||||
|
this._key = key;
|
||||||
|
this._value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static substituteString(template: string, dict: any) {
|
||||||
|
for (const k in dict) {
|
||||||
|
template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k])
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
||||||
|
return this._key + ":=" + this._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
asOverpass(): string[] {
|
||||||
|
throw "A variable with substitution can not be used to query overpass"
|
||||||
|
}
|
||||||
|
|
||||||
|
isEquivalent(other: TagsFilter): boolean {
|
||||||
|
if (!(other instanceof SubstitutingTag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return other._key === this._key && other._value === this._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
isUsableAsAnswer(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
matchesProperties(properties: any): boolean {
|
||||||
|
const value = properties[this._key];
|
||||||
|
if (value === undefined || value === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const expectedValue = SubstitutingTag.substituteString(this._value, properties);
|
||||||
|
return value === expectedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
usedKeys(): string[] {
|
||||||
|
return [this._key];
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,6 @@ export abstract class TagsFilter {
|
||||||
|
|
||||||
abstract asOverpass(): string[]
|
abstract asOverpass(): string[]
|
||||||
|
|
||||||
abstract substituteValues(tags: any): TagsFilter;
|
|
||||||
|
|
||||||
abstract isUsableAsAnswer(): boolean;
|
abstract isUsableAsAnswer(): boolean;
|
||||||
|
|
||||||
abstract isEquivalent(other: TagsFilter): boolean;
|
abstract isEquivalent(other: TagsFilter): boolean;
|
||||||
|
@ -13,12 +11,5 @@ export abstract class TagsFilter {
|
||||||
abstract asHumanString(linkToWiki: boolean, shorten: boolean);
|
abstract asHumanString(linkToWiki: boolean, shorten: boolean);
|
||||||
|
|
||||||
abstract usedKeys(): string[];
|
abstract usedKeys(): string[];
|
||||||
|
|
||||||
public matches(tags: { k: string, v: string }[]) {
|
|
||||||
const properties = {};
|
|
||||||
for (const kv of tags) {
|
|
||||||
properties[kv.k] = kv.v;
|
|
||||||
}
|
|
||||||
return this.matchesProperties(properties);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -78,6 +78,14 @@ new T("Tags", [
|
||||||
equal(nameStartsWith.matchesProperties({"name": "speelbos Sint-Anna"}), true)
|
equal(nameStartsWith.matchesProperties({"name": "speelbos Sint-Anna"}), true)
|
||||||
equal(nameStartsWith.matchesProperties({"name": "Sint-Anna"}), false)
|
equal(nameStartsWith.matchesProperties({"name": "Sint-Anna"}), false)
|
||||||
equal(nameStartsWith.matchesProperties({"name": ""}), false)
|
equal(nameStartsWith.matchesProperties({"name": ""}), false)
|
||||||
|
|
||||||
|
|
||||||
|
const assign = FromJSON.Tag("survey:date:={_date:now}")
|
||||||
|
equal(assign.matchesProperties({"survey:date":"2021-03-29", "_date:now":"2021-03-29"}), true);
|
||||||
|
equal(assign.matchesProperties({"survey:date":"2021-03-29", "_date:now":"2021-01-01"}), false);
|
||||||
|
equal(assign.matchesProperties({"survey:date":"2021-03-29"}), false);
|
||||||
|
equal(assign.matchesProperties({"_date:now":"2021-03-29"}), false);
|
||||||
|
equal(assign.matchesProperties({"some_key":"2021-03-29"}), false);
|
||||||
|
|
||||||
})],
|
})],
|
||||||
["Is equivalent test", (() => {
|
["Is equivalent test", (() => {
|
||||||
|
|
Loading…
Reference in a new issue