Stringable.java
/*
* Copyright (c) 2020, Stein Eldar Johnsen
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.morimekta.strings;
import net.morimekta.strings.internal.DecimalFormatUtil;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import static net.morimekta.strings.Displayable.displayableDateTime;
import static net.morimekta.strings.Displayable.displayableDuration;
import static net.morimekta.strings.Displayable.displayableInstant;
import static net.morimekta.strings.EscapeUtil.javaEscape;
/**
* A class that can be made into a string. This is meant to be
* a concise and meaningful, but not necessarily human understandable
* string value, rather e.g. to make a portable non-platform bound
* string value of an object.
*/
public interface Stringable {
/**
* Make a string representation of the instance value.
*
* @return The string representation.
*/
String asString();
/**
* Make a printable string from a collection using the tools here.
*
* @param collection The collection to stringify.
* @return The collection string value.
*/
static String asString(Collection<?> collection) {
if (collection == null) {
return NULL;
}
StringBuilder builder = new StringBuilder();
builder.append(collection instanceof Set ? '{' : '[');
boolean first = true;
for (Object item : collection) {
if (first) {
first = false;
} else {
builder.append(',');
}
builder.append(asString(item));
}
builder.append(collection instanceof Set ? '}' : ']');
return builder.toString();
}
/**
* Make a minimal printable string value from a typed map.
*
* @param map The map to stringify.
* @return The resulting string.
*/
static String asString(Map<?, ?> map) {
if (map == null) {
return NULL;
}
StringBuilder builder = new StringBuilder();
builder.append('{');
boolean first = true;
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (first) {
first = false;
} else {
builder.append(',');
}
builder.append(asString(entry.getKey()))
.append(':')
.append(asString(entry.getValue()));
}
builder.append('}');
return builder.toString();
}
/**
* Stringify a duration.
*
* @param duration The duration to stringify.
* @return The duration string.
*/
static String asString(Duration duration) {
return "Duration{" + displayableDuration(duration) + "}";
}
/**
* Stringify a local date time.
*
* @param localDateTime The date-time to stringify.
* @return The LocalDateTime string.
*/
static String asString(LocalDateTime localDateTime) {
return "LocalDateTime{" + displayableDateTime(localDateTime) + "}";
}
/**
* Stringify a Zoned date time.
* @param zonedDateTime The date-time to stringify.
* @return The ZonedDateTime string.
*/
static String asString(ZonedDateTime zonedDateTime) {
return "ZonedDateTime{" + displayableDateTime(zonedDateTime) + "}";
}
/**
* Stringify an Offset date time.
*
* @param offsetDateTime The date-time to stringify.
* @return The OffsetDateTime string.
*/
static String asString(OffsetDateTime offsetDateTime) {
return "OffsetDateTime{" + displayableDateTime(offsetDateTime) + "}";
}
/**
* Stringify an instant.
*
* @param instant The instant to stringify.
* @return The Instant string.
*/
static String asString(Instant instant) {
return "Instant{" + displayableInstant(instant) + "}";
}
/**
* Make an object into a string using the typed tools here.
*
* @param o The object to stringify.
* @return The resulting string.
*/
static String asString(Object o) {
if (o == null) {
return NULL;
} else if (o instanceof Stringable) {
return ((Stringable) o).asString();
} else if (o instanceof CharSequence) {
return String.format("\"%s\"", javaEscape((CharSequence) o));
} else if (o instanceof Double) {
return asString(((Number) o).doubleValue());
} else if (o instanceof Float) {
return asString(((Number) o).floatValue());
} else if (o instanceof Collection) {
return asString((Collection<?>) o);
} else if (o instanceof Map) {
return asString((Map<?, ?>) o);
} else if (o instanceof Duration) {
return asString((Duration) o);
} else if (o instanceof LocalDateTime) {
return asString((LocalDateTime) o);
} else if (o instanceof OffsetDateTime) {
return asString((OffsetDateTime) o);
} else if (o instanceof ZonedDateTime) {
return asString((ZonedDateTime) o);
} else if (o instanceof Instant) {
return asString((Instant) o);
} else {
return o.toString();
}
}
/**
* Make a minimal printable string from a double value. This method does
* not necessarily generate a string that when parsed generates the identical
* number as given in. But ut should consistently generate the same string
* (locale independent) for the same number with reasonable accuracy.
* <p>
* The float variant is pretty complex as we have to choose a printing format
* that rounds just the right number of decimals to remove the float-rounding
* error.
*
* @param f The float value.
* @return The string value.
*/
static String asString(float f) {
return DecimalFormatUtil.formatFloat(f);
}
/**
* Make a minimal printable string from a double value. This method does
* not necessarily generate a string that when parsed generates the identical
* number as given in. But ut should consistently generate the same string
* (locale independent) for the same number with reasonable accuracy.
* <p>
* The float variant is pretty complex as we have to choose a printing format
* that rounds just the right number of decimals to remove the double-rounding
* error when using extreme precisions.
*
* @param d The double value.
* @return The string value.
*/
static String asString(double d) {
return DecimalFormatUtil.formatDouble(d);
}
/**
* The 'null' string.
*/
String NULL = "null";
}