package io.kaif.model.zone; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.regex.Pattern; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.base.Preconditions; import com.google.common.base.Strings; @JsonSerialize(using = ZoneSerializer.class) @JsonDeserialize(using = ZoneDeserializer.class) public class Zone { public static final String ZONE_PATTERN_STR = "^[a-z0-9][a-z0-9\\-]{1,18}[a-z0-9]$"; private static final List<String> RESERVE_ZONES = Collections.singletonList("null"); /** * - must start with az09, end with az09, no dash * - must use dash to separate * - 3~20 chars. * - not allow concat multiple dash (use code to validate, not regex) * <p> * change pattern should review route.dart and ZoneController.java and Emitter.java */ private static final Pattern ZONE_PATTERN = Pattern.compile(ZONE_PATTERN_STR); private static String valueFallback(String rawValue) { if (Strings.isNullOrEmpty(rawValue)) { return null; } return rawValue.toLowerCase().replaceAll("[\\-_]+", "-"); } public static boolean isValid(String rawZone) { return rawZone != null && ZONE_PATTERN.matcher(rawZone).matches() && !rawZone.contains("--") && !RESERVE_ZONES.contains(rawZone); } /** * fallback to valid zone name whenever possible (user is easily typo) * <p> * fallback rule is follow valid zone pattern */ public static Optional<Zone> tryFallback(String rawZone) { return Optional.ofNullable(valueFallback(rawZone)).filter(Zone::isValid).map(Zone::new); } public static Zone valueOf(String validValue) { Preconditions.checkArgument(isValid(validValue), "invalid zone value: %s", validValue); return new Zone(validValue); } private final String value; private Zone(String value) { this.value = value; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Zone zone = (Zone) o; return value.equals(zone.value); } @Override public int hashCode() { return value.hashCode(); } public String value() { return value; } @Override public String toString() { return value; } } class ZoneSerializer extends JsonSerializer<Zone> { @Override public void serialize(Zone zone, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeString(zone.value()); } } class ZoneDeserializer extends JsonDeserializer<Zone> { @Override public Zone deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { String value = jp.readValueAs(String.class); return Zone.valueOf(value); } }