001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.imagery.vectortile.mapbox.style; 003 004import java.util.Arrays; 005import java.util.Objects; 006import java.util.stream.Collectors; 007 008import javax.json.JsonArray; 009import javax.json.JsonObject; 010import javax.json.JsonString; 011import javax.json.JsonValue; 012 013/** 014 * A Mapbox vector style expression (immutable) 015 * @author Taylor Smock 016 * @see <a href="https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/">https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/</a> 017 * @since 17862 018 */ 019public final class Expression { 020 /** An empty expression to use */ 021 public static final Expression EMPTY_EXPRESSION = new Expression(JsonValue.NULL); 022 private static final String EMPTY_STRING = ""; 023 024 private final String mapcssFilterExpression; 025 026 /** 027 * Create a new filter expression. <i>Please note that this currently only supports basic comparators!</i> 028 * @param value The value to parse 029 */ 030 public Expression(JsonValue value) { 031 if (value.getValueType() == JsonValue.ValueType.ARRAY) { 032 final JsonArray array = value.asJsonArray(); 033 if (!array.isEmpty() && array.get(0).getValueType() == JsonValue.ValueType.STRING) { 034 if ("==".equals(array.getString(0))) { 035 // The mapcss equivalent of == is = (for the most part) 036 this.mapcssFilterExpression = convertToString(array.get(1)) + "=" + convertToString(array.get(2)); 037 } else if (Arrays.asList("<=", ">=", ">", "<", "!=").contains(array.getString(0))) { 038 this.mapcssFilterExpression = convertToString(array.get(1)) + array.getString(0) + convertToString(array.get(2)); 039 } else { 040 this.mapcssFilterExpression = EMPTY_STRING; 041 } 042 } else { 043 this.mapcssFilterExpression = EMPTY_STRING; 044 } 045 } else { 046 this.mapcssFilterExpression = EMPTY_STRING; 047 } 048 } 049 050 /** 051 * Convert a value to a string 052 * @param value The value to convert 053 * @return A string 054 */ 055 private static String convertToString(JsonValue value) { 056 switch (value.getValueType()) { 057 case STRING: 058 return ((JsonString) value).getString(); 059 case FALSE: 060 return Boolean.FALSE.toString(); 061 case TRUE: 062 return Boolean.TRUE.toString(); 063 case NUMBER: 064 return value.toString(); 065 case ARRAY: 066 return '[' 067 + ((JsonArray) value).stream().map(Expression::convertToString).collect(Collectors.joining(",")) 068 + ']'; 069 case OBJECT: 070 return '{' 071 + ((JsonObject) value).entrySet().stream() 072 .map(entry -> entry.getKey() + ":" + convertToString(entry.getValue())).collect( 073 Collectors.joining(",")) 074 + '}'; 075 case NULL: 076 default: 077 return EMPTY_STRING; 078 } 079 } 080 081 @Override 082 public String toString() { 083 return !EMPTY_STRING.equals(this.mapcssFilterExpression) ? '[' + this.mapcssFilterExpression + ']' : EMPTY_STRING; 084 } 085 086 @Override 087 public boolean equals(Object other) { 088 if (other instanceof Expression) { 089 Expression o = (Expression) other; 090 return Objects.equals(this.mapcssFilterExpression, o.mapcssFilterExpression); 091 } 092 return false; 093 } 094 095 @Override 096 public int hashCode() { 097 return Objects.hash(this.mapcssFilterExpression); 098 } 099}