mirror of
https://github.com/elastic/logstash.git
synced 2025-04-24 22:57:16 -04:00
MINOR: Cleanup Valuefier to have the same efficient approach that is already used by BiValues
Fixes #8103
This commit is contained in:
parent
61982bcc5b
commit
44c61eaa12
3 changed files with 126 additions and 74 deletions
|
@ -1,9 +1,9 @@
|
||||||
package org.logstash;
|
package org.logstash;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.jruby.RubyArray;
|
import org.jruby.RubyArray;
|
||||||
import org.jruby.RubyBoolean;
|
import org.jruby.RubyBoolean;
|
||||||
|
@ -22,85 +22,132 @@ import org.logstash.bivalues.BiValues;
|
||||||
import org.logstash.ext.JrubyTimestampExtLibrary;
|
import org.logstash.ext.JrubyTimestampExtLibrary;
|
||||||
|
|
||||||
public final class Valuefier {
|
public final class Valuefier {
|
||||||
private static final String PROXY_ERR_TEMPLATE = "Missing Valuefier handling for full class name=%s, simple name=%s, wrapped object=%s";
|
|
||||||
private static final String ERR_TEMPLATE = "Missing Valuefier handling for full class name=%s, simple name=%s";
|
|
||||||
|
|
||||||
private Valuefier(){}
|
private static final Valuefier.Converter IDENTITY = input -> input;
|
||||||
|
|
||||||
private static Object convertJavaProxy(final JavaProxy jp) {
|
private static final Valuefier.Converter FLOAT_CONVERTER =
|
||||||
final Object obj = JavaUtil.unwrapJavaObject(jp);
|
input -> RubyUtil.RUBY.newFloat(((Number) input).doubleValue());
|
||||||
if (obj instanceof IRubyObject[]) {
|
|
||||||
return ConvertedList.newFromRubyArray((IRubyObject[]) obj);
|
|
||||||
}
|
|
||||||
if (obj instanceof List) {
|
|
||||||
return ConvertedList.newFromList((Collection<?>) obj);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return BiValues.newBiValue(jp);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
final Class<?> cls = obj.getClass();
|
|
||||||
throw new IllegalArgumentException(String.format(PROXY_ERR_TEMPLATE, cls.getName(), cls.getSimpleName(), obj.getClass().getName()), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object convertNonCollection(Object o) {
|
private static final Valuefier.Converter JAVAPROXY_CONVERTER =
|
||||||
try {
|
input -> {
|
||||||
return BiValues.newBiValue(o);
|
final Object obj = JavaUtil.unwrapJavaObject((JavaProxy) input);
|
||||||
} catch (IllegalArgumentException e) {
|
if (obj instanceof IRubyObject[]) {
|
||||||
final Class<?> cls = o.getClass();
|
return ConvertedList.newFromRubyArray((IRubyObject[]) obj);
|
||||||
throw new IllegalArgumentException(String.format(ERR_TEMPLATE, cls.getName(), cls.getSimpleName()), e);
|
}
|
||||||
}
|
if (obj instanceof List) {
|
||||||
|
return ConvertedList.newFromList((Collection<?>) obj);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return BiValues.newBiValue(input);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
final Class<?> cls = obj.getClass();
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"Missing Valuefier handling for full class name=%s, simple name=%s, wrapped object=%s",
|
||||||
|
cls.getName(), cls.getSimpleName(), obj.getClass().getName()
|
||||||
|
), e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Valuefier.Converter BIVALUES_CONVERTER = BiValues::newBiValue;
|
||||||
|
|
||||||
|
private static final Map<Class<?>, Valuefier.Converter> CONVERTER_MAP = initConverters();
|
||||||
|
|
||||||
|
private Valuefier() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object convert(final Object o) {
|
public static Object convert(final Object o) {
|
||||||
if (o instanceof RubyString || o instanceof RubyFloat
|
if (o == null) {
|
||||||
|| o instanceof JrubyTimestampExtLibrary.RubyTimestamp
|
return BiValues.NULL_BI_VALUE;
|
||||||
|| o instanceof ConvertedMap || o instanceof ConvertedList
|
|
||||||
|| o instanceof BiValue || o instanceof RubyBoolean) {
|
|
||||||
return o;
|
|
||||||
}
|
}
|
||||||
if (o instanceof String) {
|
final Class<?> cls = o.getClass();
|
||||||
return RubyUtil.RUBY.newString((String) o);
|
final Valuefier.Converter converter = CONVERTER_MAP.get(cls);
|
||||||
|
if (converter != null) {
|
||||||
|
return converter.convert(o);
|
||||||
}
|
}
|
||||||
if (o instanceof Float || o instanceof Double) {
|
return fallbackConvert(o, cls);
|
||||||
return RubyUtil.RUBY.newFloat(((Number) o).doubleValue());
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback for types not covered by {@link Valuefier#convert(Object)} as a result of no
|
||||||
|
* {@link Valuefier.Converter} having been cached for the given class. Uses the fact that
|
||||||
|
* the only subclasses of the keys in {@link Valuefier#CONVERTER_MAP} as set up by
|
||||||
|
* {@link Valuefier#initConverters()} can be converted here and hence find the appropriate
|
||||||
|
* super class for unknown types by checking each entry in {@link Valuefier#CONVERTER_MAP} for
|
||||||
|
* being a supertype of the given class. If this fails {@link Valuefier#BIVALUES_CONVERTER}
|
||||||
|
* will be cached and used.
|
||||||
|
* @param o Object to convert
|
||||||
|
* @param cls Class of given object {@code o}
|
||||||
|
* @return Conversion result equivalent to what {@link Valuefier#convert(Object)} would return
|
||||||
|
*/
|
||||||
|
private static Object fallbackConvert(final Object o, final Class<?> cls) {
|
||||||
|
for (final Map.Entry<Class<?>, Valuefier.Converter> entry : CONVERTER_MAP.entrySet()) {
|
||||||
|
if (entry.getKey().isAssignableFrom(cls)) {
|
||||||
|
final Valuefier.Converter found = entry.getValue();
|
||||||
|
CONVERTER_MAP.put(cls, found);
|
||||||
|
return found.convert(o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (o instanceof Boolean) {
|
CONVERTER_MAP.put(cls, BIVALUES_CONVERTER);
|
||||||
return RubyUtil.RUBY.newBoolean((Boolean) o);
|
return BIVALUES_CONVERTER.convert(o);
|
||||||
}
|
}
|
||||||
if (o instanceof Timestamp) {
|
|
||||||
return JrubyTimestampExtLibrary.RubyTimestamp.newRubyTimestamp(
|
private static Map<Class<?>, Valuefier.Converter> initConverters() {
|
||||||
RubyUtil.RUBY, (Timestamp) o
|
final Map<Class<?>, Valuefier.Converter> converters =
|
||||||
);
|
new ConcurrentHashMap<>(50, 0.2F, 1);
|
||||||
}
|
converters.put(RubyString.class, IDENTITY);
|
||||||
if (o instanceof RubyTime) {
|
converters.put(JrubyTimestampExtLibrary.RubyTimestamp.class, IDENTITY);
|
||||||
return JrubyTimestampExtLibrary.RubyTimestamp.newRubyTimestamp(
|
converters.put(RubyFloat.class, IDENTITY);
|
||||||
RubyUtil.RUBY, new Timestamp(((RubyTime) o).getDateTime())
|
converters.put(ConvertedMap.class, IDENTITY);
|
||||||
);
|
converters.put(ConvertedList.class, IDENTITY);
|
||||||
}
|
converters.put(RubyBoolean.class, IDENTITY);
|
||||||
if (o instanceof DateTime) {
|
converters.put(BiValue.class, IDENTITY);
|
||||||
return JrubyTimestampExtLibrary.RubyTimestamp.newRubyTimestamp(
|
converters.put(String.class, input -> RubyUtil.RUBY.newString((String) input));
|
||||||
RubyUtil.RUBY, new Timestamp((DateTime) o)
|
converters.put(Float.class, FLOAT_CONVERTER);
|
||||||
);
|
converters.put(Double.class, FLOAT_CONVERTER);
|
||||||
}
|
converters.put(Boolean.class, input -> RubyUtil.RUBY.newBoolean((Boolean) input));
|
||||||
if (o instanceof RubyHash) {
|
converters.put(
|
||||||
return ConvertedMap.newFromRubyHash((RubyHash) o);
|
Timestamp.class,
|
||||||
}
|
input -> JrubyTimestampExtLibrary.RubyTimestamp.newRubyTimestamp(
|
||||||
if (o instanceof RubyArray) {
|
RubyUtil.RUBY, (Timestamp) input
|
||||||
return ConvertedList.newFromRubyArray((RubyArray) o);
|
)
|
||||||
}
|
);
|
||||||
if (o instanceof Map) {
|
converters.put(
|
||||||
return ConvertedMap.newFromMap((Map<Serializable, Object>) o);
|
RubyTime.class, input -> JrubyTimestampExtLibrary.RubyTimestamp.newRubyTimestamp(
|
||||||
}
|
RubyUtil.RUBY, new Timestamp(((RubyTime) input).getDateTime())
|
||||||
if (o instanceof List) {
|
)
|
||||||
return ConvertedList.newFromList((List<Object>) o);
|
);
|
||||||
}
|
converters.put(
|
||||||
if (o instanceof MapJavaProxy){
|
DateTime.class, input -> JrubyTimestampExtLibrary.RubyTimestamp.newRubyTimestamp(
|
||||||
return ConvertedMap.newFromMap((Map)((MapJavaProxy) o).getObject());
|
RubyUtil.RUBY, new Timestamp((DateTime) input)
|
||||||
}
|
)
|
||||||
if (o instanceof ArrayJavaProxy || o instanceof ConcreteJavaProxy){
|
);
|
||||||
return convertJavaProxy((JavaProxy) o);
|
converters.put(RubyHash.class, input -> ConvertedMap.newFromRubyHash((RubyHash) input));
|
||||||
}
|
converters.put(Map.class, input -> ConvertedMap.newFromMap((Map) input));
|
||||||
return o == null ? BiValues.NULL_BI_VALUE : convertNonCollection(o);
|
converters.put(List.class, input -> ConvertedList.newFromList((List) input));
|
||||||
|
converters.put(ArrayJavaProxy.class, JAVAPROXY_CONVERTER);
|
||||||
|
converters.put(ConcreteJavaProxy.class, JAVAPROXY_CONVERTER);
|
||||||
|
converters.put(
|
||||||
|
MapJavaProxy.class,
|
||||||
|
input -> ConvertedMap.newFromMap((Map) ((MapJavaProxy) input).getObject())
|
||||||
|
);
|
||||||
|
converters.put(
|
||||||
|
RubyArray.class, input -> ConvertedList.newFromRubyArray((RubyArray) input)
|
||||||
|
);
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converter from either a Java or a Ruby type to a type that both {@link Javafier} and
|
||||||
|
* {@link Rubyfier} are able to convert back their respective types efficiently.
|
||||||
|
*/
|
||||||
|
private interface Converter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a Java or a Ruby typed object to an object that can be efficiently converted
|
||||||
|
* back to Java as well as Ruby.
|
||||||
|
* @param input Either a Java or Ruby type object
|
||||||
|
* @return Object that can be converted back to Java as well as Ruby efficiently
|
||||||
|
*/
|
||||||
|
Object convert(Object input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,12 @@ public final class BiValues {
|
||||||
final Class<?> cls = o.getClass();
|
final Class<?> cls = o.getClass();
|
||||||
final BiValues.BiValueType type = CONVERTER_CACHE.get(cls);
|
final BiValues.BiValueType type = CONVERTER_CACHE.get(cls);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
throw new IllegalArgumentException("Unsupported class " + cls);
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"Missing Converter handling for full class name=%s, simple name=%s",
|
||||||
|
cls.getName(), cls.getSimpleName()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return type.build(o);
|
return type.build(o);
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ public class ValuefierTest extends TestBase {
|
||||||
public void testUnhandledObject() {
|
public void testUnhandledObject() {
|
||||||
RubyMatchData md = new RubyMatchData(ruby);
|
RubyMatchData md = new RubyMatchData(ruby);
|
||||||
exception.expect(IllegalArgumentException.class);
|
exception.expect(IllegalArgumentException.class);
|
||||||
exception.expectMessage("Missing Valuefier handling for full class name=org.jruby.RubyMatchData, simple name=RubyMatchData");
|
exception.expectMessage("Missing Converter handling for full class name=org.jruby.RubyMatchData, simple name=RubyMatchData");
|
||||||
Valuefier.convert(md);
|
Valuefier.convert(md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue