Java tutorial
package com.evanzeimet.queryinfo.jpa.result; /* * #%L * queryinfo-jpa * $Id:$ * $HeadURL:$ * %% * Copyright (C) 2015 - 2016 Evan Zeimet * %% * 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 * * 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. * #L% */ import static java.lang.invoke.MethodType.methodType; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.persistence.Tuple; import javax.persistence.TupleElement; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.evanzeimet.queryinfo.QueryInfoException; import com.evanzeimet.queryinfo.jpa.attribute.QueryInfoAttributeUtils; public abstract class AbstractTupleToPojoQueryInfoResultConverter<QueryInfoResultType> implements QueryInfoResultConverter<Tuple, QueryInfoResultType> { private static final Logger logger = LoggerFactory.getLogger(AbstractTupleToPojoQueryInfoResultConverter.class); protected QueryInfoAttributeUtils attributeUtils = new QueryInfoAttributeUtils(); public AbstractTupleToPojoQueryInfoResultConverter() { } public abstract QueryInfoBaseInstanceFactory<QueryInfoResultType> getBaseInstanceFactory(); @Override public List<QueryInfoResultType> convert(List<Tuple> tuples) throws QueryInfoException { List<QueryInfoResultType> result; if (tuples.isEmpty()) { result = Collections.emptyList(); } else { Tuple peek = tuples.get(0); List<TupleElement<?>> tupleElements = peek.getElements(); try { Map<String, MethodHandle> methodHandles = mapElementMethodHandles(tupleElements); result = convertTuples(methodHandles, tuples); } catch (Throwable e) { throw new QueryInfoException(e); } } return result; } protected QueryInfoResultType convertTuple(Map<String, MethodHandle> methodHandles, Tuple tuple) throws Throwable { QueryInfoResultType result = getBaseInstanceFactory().create(); Iterator<Entry<String, MethodHandle>> methodHandlesEntryIterator = methodHandles.entrySet().iterator(); while (methodHandlesEntryIterator.hasNext()) { Entry<String, MethodHandle> methodHandleEntry = methodHandlesEntryIterator.next(); String elementAlias = methodHandleEntry.getKey(); Object value = tuple.get(elementAlias); MethodHandle methodHandle = methodHandleEntry.getValue(); methodHandle.invoke(result, value); } return result; } protected List<QueryInfoResultType> convertTuples(Map<String, MethodHandle> methodHandles, List<Tuple> tuples) throws Throwable { int tupleCount = tuples.size(); List<QueryInfoResultType> result = new ArrayList<>(tupleCount); for (Tuple tuple : tuples) { QueryInfoResultType converted = convertTuple(methodHandles, tuple); result.add(converted); } return result; } protected MethodHandle findFieldPutHandle(String memberName, Class<?> elementJavaType) { Class<QueryInfoResultType> resultClass = getResultClass(); return findFieldPutHandle(resultClass, memberName, elementJavaType); } protected MethodHandle findFieldPutHandle(Class<?> hostClass, String memberName, Class<?> elementJavaType) { MethodHandle result = null; try { Field field = hostClass.getDeclaredField(memberName); field.setAccessible(true); result = MethodHandles.lookup().unreflectSetter(field); } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) { String message = String.format("Could not find field put handle for field [%s]", memberName); logger.debug(message); } if (result == null) { Class<?> hostSuperclass = hostClass.getSuperclass(); if (hostSuperclass != null) { result = findFieldPutHandle(hostSuperclass, memberName, elementJavaType); } } return result; } protected MethodHandle findFieldSetterHandle(String memberName, Class<?> elementJavaType) { MethodHandle result = null; String methodSuffix = StringUtils.capitalize(memberName); String setterName = String.format("set%s", methodSuffix); MethodType methodType = methodType(void.class, elementJavaType); Class<QueryInfoResultType> resultClass = getResultClass(); try { result = MethodHandles.lookup().findVirtual(resultClass, setterName, methodType); } catch (NoSuchMethodException | IllegalAccessException e) { String message = String.format("Could not find field setter handle [%s] for field [%s]", setterName, memberName); logger.debug(message); } return result; } protected MethodHandle getMethodHandleForElement(TupleElement<?> element) throws QueryInfoException { String elementAlias = element.getAlias(); String memberName = attributeUtils.convertAttributeNameToMemberName(elementAlias); Class<?> elementJavaType = element.getJavaType(); MethodHandle result = findFieldSetterHandle(memberName, elementJavaType); if (result == null) { result = findFieldPutHandle(memberName, elementJavaType); } if (result == null) { String message = String.format("Could not find accessible setter or field for attribute [%s]", elementAlias); throw new QueryInfoException(message); } return result; } public abstract Class<QueryInfoResultType> getResultClass(); protected Map<String, MethodHandle> mapElementMethodHandles(List<TupleElement<?>> tupleElements) throws QueryInfoException { int elementCount = tupleElements.size(); Map<String, MethodHandle> result = new HashMap<>(elementCount); Iterator<TupleElement<?>> elementIterator = tupleElements.iterator(); while (elementIterator.hasNext()) { TupleElement<?> element = elementIterator.next(); String elementAlias = element.getAlias(); MethodHandle methodHandle = getMethodHandleForElement(element); result.put(elementAlias, methodHandle); } return result; } }