org.pkl.config.java.mapper.PObjectToDataObject Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pkl-tools Show documentation
Show all versions of pkl-tools Show documentation
Fat Jar containing pkl-cli, pkl-codegen-java, pkl-codegen-kotlin, pkl-config-java, pkl-core, pkl-doc, and their shaded third-party dependencies.
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed 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
*
* https://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 org.pkl.config.java.mapper;
import java.beans.ConstructorProperties;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.*;
import java.util.*;
import org.pkl.core.Composite;
import org.pkl.core.PClassInfo;
import org.pkl.core.PObject;
import org.pkl.core.util.Nullable;
public class PObjectToDataObject implements ConverterFactory {
private static final Lookup lookup = MethodHandles.lookup();
@SuppressWarnings("unchecked")
private static final @Nullable Class extends Annotation> javaxInjectNamedClass =
(Class extends Annotation>) Reflection.tryLoadClass("javax.inject.Named");
private static final @Nullable Method javaxInjectNamedValueMethod;
static {
try {
javaxInjectNamedValueMethod =
javaxInjectNamedClass == null ? null : javaxInjectNamedClass.getMethod("value");
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
}
protected PObjectToDataObject() {}
@Override
public final Optional> create(PClassInfo> sourceType, Type targetType) {
if (!(sourceType == PClassInfo.Module || sourceType.getJavaClass() == PObject.class)) {
return Optional.empty();
}
return selectConstructor(Reflection.toRawType(targetType))
.flatMap(
constructor ->
getParameters(constructor, targetType)
.map(
parameters -> {
try {
return new ConverterImpl<>(
targetType, lookup.unreflectConstructor(constructor), parameters);
} catch (IllegalAccessException e) {
throw new ConversionException(
String.format("Error accessing constructor `%s`.", constructor), e);
}
}));
}
protected Optional> selectConstructor(Class> clazz) {
return Arrays.stream(clazz.getDeclaredConstructors())
.max(Comparator.comparingInt(Constructor::getParameterCount));
}
protected Optional> getParameterNames(Constructor> constructor) {
var paramNames = new ArrayList(constructor.getParameterCount());
var properties = getAnnotation(constructor, ConstructorProperties.class);
if (properties != null) {
return Optional.of(Arrays.asList(properties.value()));
}
for (Parameter parameter : constructor.getParameters()) {
var name = getParameterName(parameter);
if (name == null) return Optional.empty();
paramNames.add(name);
}
return Optional.of(paramNames);
}
private Optional>> getParameters(
Constructor> constructor, Type targetType) {
return getParameterNames(constructor)
.map(
paramNames -> {
var paramTypes = Reflection.getExactParameterTypes(constructor, targetType);
var parameters = new ArrayList>(paramNames.size());
for (int i = 0; i < paramNames.size(); i++) {
var name = paramNames.get(i);
parameters.add(Tuple2.of(name, paramTypes[i]));
}
return parameters;
});
}
private static @Nullable String getParameterName(Parameter parameter) {
if (parameter.isNamePresent()) {
return parameter.getName();
}
Named named = getAnnotation(parameter, Named.class);
if (named != null) {
return named.value();
}
if (javaxInjectNamedClass != null) {
assert javaxInjectNamedValueMethod != null;
var ann = getAnnotation(parameter, javaxInjectNamedClass);
if (ann != null) {
try {
return (String) javaxInjectNamedValueMethod.invoke(ann);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new ConversionException("Failed to invoke `javax.inject.Named.value()`.", e);
}
}
}
return null;
}
private static @Nullable T getAnnotation(
Constructor> constructor, Class annotationClass) {
try {
return constructor.getAnnotation(annotationClass);
} catch (IndexOutOfBoundsException e) {
// workaround for https://bugs.openjdk.java.net/browse/JDK-8025806
return null;
}
}
private static @Nullable T getAnnotation(
Parameter parameter, Class annotationClass) {
try {
return parameter.getAnnotation(annotationClass);
} catch (
IndexOutOfBoundsException
e) { // workaround for https://bugs.openjdk.java.net/browse/JDK-8025806
return null;
}
}
private static class ConverterImpl implements Converter {
private final Type targetType;
private final MethodHandle constructorHandle;
private final Collection> parameters;
private final PClassInfo
© 2015 - 2024 Weber Informatics LLC | Privacy Policy