/* * 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 org.apache.giraph.compiling; import static org.apache.giraph.compiling.FactoryCodeGenerator.getSimpleName; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import com.google.common.base.Preconditions; import com.google.common.reflect.TypeToken; /** * Conf option which is configured by specifying lambda body, and it returns * compiled instance of wanted functional interface with given body. * See TestLambdaConfOption for example. * * @param <T> Functional interface type */ public class LambdaConfOption<T> extends ObjectFactoryConfOption<T> { private final String[] argumentNames; public LambdaConfOption(String key, Class<T> classType, String codeSnippet, String description, String... argumentNames) { super(key, classType, codeSnippet, description); this.argumentNames = argumentNames; } public LambdaConfOption( String key, TypeToken<T> interfaceTypeToken, String codeSnippet, String description, String... arguments) { super(key, interfaceTypeToken, codeSnippet, description); this.argumentNames = arguments; } @Override protected String createFullCodeSnippet(String codeSnippet) { if (codeSnippet == null || codeSnippet.isEmpty()) { return null; } return createLambdaCode(codeSnippet, getInterfaceType(), argumentNames); } public static String createLambdaCode( String body, Type type, String[] argumentNames) { TypeToken<?> typeToken = TypeToken.of(type); Class<?> rawType = typeToken.getRawType(); if (rawType.isInterface()) { return "return (" + Stream.of(argumentNames).collect(Collectors.joining(", ")) + ") -> " + body + ";"; } else { // Long workaround for abstract classes Method sam = null; for (Method m : rawType.getMethods()) { if (Modifier.isAbstract(m.getModifiers())) { Preconditions.checkState(sam == null); sam = m; } } Preconditions.checkState(sam != null); Type[] params = sam.getGenericParameterTypes(); Preconditions.checkState(params.length == argumentNames.length); String argList = IntStream.range(0, params.length).mapToObj( (i) -> getSimpleName(typeToken.resolveType(params[i])) + " " + argumentNames[i] ).collect(Collectors.joining(", ")); return " return new " + getSimpleName(type) + "() {\n" + " @Override\n" + " public " + getSimpleName(typeToken.resolveType(sam.getGenericReturnType())) + " " + sam.getName() + "(" + argList + ") {\n" + " return " + body + ";\n" + " }\n" + " };"; } } }