21 private static final String UNSUPPORTED_TUNABLE_TYPE =
"Unsupported tunable type: ";
28 public static class TunableStruct<T>
extends Tunable<T> {
32 m_value = initialValue;
36 public void set(T value) {
48 return m_struct.getTypeClass();
69 public static class TunableStructArray<T>
extends Tunable<T[]> {
74 m_value = initialValue ==
null ? null : initialValue.clone();
78 public void set(T[] value) {
79 m_value = value ==
null ? null : value.clone();
85 return m_value ==
null ? null : m_value.clone();
89 @SuppressWarnings(
"PMD.MethodReturnsInternalArray")
110 private final Class<T[]> m_cls;
119 public static class TunableProtobuf<T>
extends Tunable<T> {
123 m_value = initialValue;
127 public void set(T value) {
139 return m_proto.getTypeClass();
155 private abstract static class BasicTunable<T>
extends Tunable<T> {
166 private final Class<T> m_cls;
180 @SuppressWarnings(
"unchecked")
181 private static <T> Class<T> getValueClass(T value) {
182 return (Class<T>) value.getClass();
185 private static boolean isSupportedBasicType(Class<?> cls) {
186 return cls == Boolean.class
187 || cls == Integer.class
189 || cls == Float.class
190 || cls == Double.class
191 || cls == String.class
192 || cls ==
byte[].class
193 || cls ==
boolean[].class
194 || cls ==
int[].class
195 || cls ==
long[].class
196 || cls ==
float[].class
197 || cls ==
double[].class
198 || cls == String[].class;
201 private static String getTypeName(Class<?> cls) {
202 return cls ==
null ?
"<null>" : cls.getName();
205 private static void warnIfUnsupportedBasicType(Class<?> cls) {
206 if (!isSupportedBasicType(cls)) {
207 TunableRegistry.reportWarning(
208 UNSUPPORTED_TUNABLE_TYPE + getTypeName(cls) +
"; it may not be published by backends");
212 private static void warnIfUnpublishableTunable(
TunableBase tunable) {
213 if (tunable ==
null) {
217 warnIfUnpublishableTunable(custom.getInnerTunable());
218 }
else if (tunable instanceof TunableBoolean
219 || tunable instanceof TunableInt
220 || tunable instanceof TunableLong
221 || tunable instanceof TunableFloat
222 || tunable instanceof TunableDouble
223 || tunable instanceof TunableStruct<?>
224 || tunable instanceof TunableStructArray<?>
225 || tunable instanceof TunableProtobuf<?>) {
227 }
else if (tunable instanceof
Tunable<?> generic) {
230 TunableRegistry.reportWarning(UNSUPPORTED_TUNABLE_TYPE + tunable.getClass().getName());
234 private static <T>
Tunable<T> createBasic(T initialValue, TunableConfig config) {
235 return createBasic(initialValue, getValueClass(initialValue), config);
238 private static <T>
Tunable<T> createBasic(T initialValue, Class<T> cls, TunableConfig config) {
239 warnIfUnsupportedBasicType(cls);
240 return new BasicTunable<T>(cls, config) {
242 public void set(T value) {
252 private T m_value = initialValue;
256 private static <T> Struct<T> getStruct(Class<T> cls) {
257 Struct<?>
struct = getStructObject(cls);
258 if (struct ==
null) {
261 @SuppressWarnings(
"unchecked")
262 Struct<T> typedStruct = (Struct<T>) struct;
266 private static Struct<?> getStructObject(Class<?> cls) {
270 obj = cls.getField(
"struct").get(
null);
271 }
catch (NoSuchFieldException e) {
272 TunableRegistry.reportWarning(
"could not get struct field for " + cls.getName());
274 }
catch (IllegalAccessException e) {
275 TunableRegistry.reportWarning(
"could not access struct field for " + cls.getName());
279 case Struct<?> s when s.getTypeClass().equals(cls) -> {
283 TunableRegistry.reportWarning(
284 "type mismatch, expected '"
285 + s.getTypeClass().getName()
290 TunableRegistry.reportWarning(
291 "struct field for " + cls.getName() +
" is not of Struct<?> type");
296 @SuppressWarnings(
"unchecked")
297 private static <T> Tunable<T> createStructArrayTunable(
298 T initialValue, Class<T> cls, Struct<?> struct, TunableConfig config) {
300 new TunableStructArray<>(
301 (Object[]) initialValue, (Struct<Object>) struct, (Class<Object[]>) cls, config);
304 @SuppressWarnings(
"unchecked")
305 private static <T> Tunable<T> createGetterSetterStructArrayTunable(
310 TunableConfig config) {
312 new TunableStructArray<>(
null, (Struct<Object>) struct, (Class<Object[]>) cls, config) {
314 public void set(Object[] value) {
315 if (setter !=
null) {
316 setter.accept((T) value);
322 public Object[]
get() {
323 return (Object[]) getter.get();
327 public Object[] mutate() {
334 private static <T> Protobuf<T, ?> getProtobuf(Class<T> cls) {
338 obj = cls.getField(
"proto").get(
null);
339 }
catch (NoSuchFieldException e) {
340 TunableRegistry.reportWarning(
"could not get proto field for " + cls.getName());
342 }
catch (IllegalAccessException e) {
343 TunableRegistry.reportWarning(
"could not access proto field for " + cls.getName());
347 case Protobuf<?, ?> s when s.getTypeClass().equals(cls) -> {
348 @SuppressWarnings(
"unchecked")
349 Protobuf<T, ?> proto = (Protobuf<T, ?>) s;
352 case Protobuf<?, ?> s ->
353 TunableRegistry.reportWarning(
354 "type mismatch, expected '"
355 + s.getTypeClass().getName()
360 TunableRegistry.reportWarning(
361 "proto field for " + cls.getName() +
" is not of Protobuf<?, ?> type");
366 private static <T> Tunable<T> createGetterSetterTunable(
367 Supplier<T> getter, Consumer<T> setter, Struct<T> struct, TunableConfig config) {
368 return new TunableStruct<T>(
null, struct, config) {
370 public void set(T value) {
371 if (setter !=
null) {
372 setter.accept(value);
384 private static <T> Tunable<T> createGetterSetterTunable(
385 Supplier<T> getter, Consumer<T> setter, Protobuf<T, ?> proto, TunableConfig config) {
386 return new TunableProtobuf<T>(
null, proto, config) {
388 public void set(T value) {
389 if (setter !=
null) {
390 setter.accept(value);
422 return createBasic(
null, cls, config);
445 Objects.requireNonNull(struct);
458 Objects.requireNonNull(proto);
473 public static <T>
Tunable<T> create(Supplier<T> getter, Consumer<T> setter, Class<T> cls) {
486 Objects.requireNonNull(initialValue);
487 Class<T> cls = getValueClass(initialValue);
490 if (struct !=
null) {
493 }
else if (cls.isArray() &&
StructSerializable.class.isAssignableFrom(cls.getComponentType())) {
494 Struct<?> struct = getStructObject(cls.getComponentType());
495 if (struct !=
null) {
496 return createStructArrayTunable(initialValue, cls, struct, config);
506 if (handler !=
null) {
507 Tunable<T> tunable = handler.createTunable(initialValue, config);
508 if (tunable !=
null) {
509 warnIfUnpublishableTunable(tunable);
514 return createBasic(initialValue, config);
528 return new TunableStruct<T>(initialValue, struct, config);
542 return new TunableProtobuf<T>(initialValue, proto, config);
558 Supplier<T> getter, Consumer<T> setter, Class<T> cls,
TunableConfig config) {
559 if (config ==
null) {
566 if (struct !=
null) {
567 return createGetterSetterTunable(getter, setter, struct, config);
569 }
else if (cls !=
null
572 Struct<?> struct = getStructObject(cls.getComponentType());
573 if (struct !=
null) {
574 return createGetterSetterStructArrayTunable(getter, setter, cls, struct, config);
579 return createGetterSetterTunable(getter, setter, proto, config);
582 warnIfUnsupportedBasicType(cls);
583 return new BasicTunable<T>(cls, config) {
585 public void set(T value) {
586 if (setter !=
null) {
587 setter.accept(value);
625 public abstract void set(T value);
635 public abstract T
get();
653 public void accept(T value) {