/* * Copyright 2014 - 2017 Blazebit. * * 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. */ package com.blazebit.persistence.impl.eclipselink.function; import com.blazebit.persistence.spi.JpqlFunction; import org.eclipse.persistence.expressions.Expression; import org.eclipse.persistence.expressions.ExpressionOperator; import org.eclipse.persistence.internal.expressions.ClassTypeExpression; import org.eclipse.persistence.internal.expressions.CompoundExpression; import org.eclipse.persistence.internal.expressions.ConstantExpression; import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter; import org.eclipse.persistence.internal.expressions.FieldExpression; import org.eclipse.persistence.internal.expressions.FromSubSelectExpression; import org.eclipse.persistence.internal.expressions.FunctionExpression; import org.eclipse.persistence.internal.expressions.ObjectExpression; import org.eclipse.persistence.internal.expressions.ParameterExpression; import org.eclipse.persistence.internal.expressions.QueryKeyExpression; import org.eclipse.persistence.internal.expressions.SubSelectExpression; import org.eclipse.persistence.internal.helper.DatabaseTable; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.mappings.DatabaseMapping; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Vector; /** * * @author Christian Beikov * @since 1.0 */ public class JpqlFunctionExpressionOperator extends ExpressionOperator { private static final long serialVersionUID = 1L; private final JpqlFunction function; private final AbstractSession session; private final Map<Class, String> classTypes; public JpqlFunctionExpressionOperator(JpqlFunction function, AbstractSession session, Map<Class, String> classTypes) { this.function = function; this.session = session; this.classTypes = classTypes; } public JpqlFunction unwrap() { return function; } @Override public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) { prepare(Arrays.asList(first, second)); super.printDuo(first, second, printer); } @Override @SuppressWarnings({ "rawtypes", "unchecked" }) public void printCollection(Vector items, ExpressionSQLPrinter printer) { prepare((List<Expression>) items); // Certain functions don't allow binding on some platforms. if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) { printer.getCall().setUsesBinding(false); } int dbStringIndex = 0; try { if (isPrefix()) { printer.getWriter().write(getDatabaseStrings()[0]); dbStringIndex = 1; } else { dbStringIndex = 0; } } catch (IOException e) { e.printStackTrace(); } if (argumentIndices == null) { argumentIndices = new int[items.size()]; for (int i = 0; i < argumentIndices.length; i++) { argumentIndices[i] = i; } } for (final int index : argumentIndices) { Expression item = (Expression)items.elementAt(index); if ((this.selector == Ref) || ((this.selector == Deref) && (item.isObjectExpression()))) { DatabaseTable alias = ((ObjectExpression)item).aliasForTable(((ObjectExpression)item).getDescriptor().getTables().firstElement()); printer.printString(alias.getNameDelimited(printer.getPlatform())); } else if ((this.selector == Count) && (item.isExpressionBuilder())) { printer.printString("*"); } else { item.printSQL(printer); } if (dbStringIndex < getDatabaseStrings().length) { printer.printString(getDatabaseStrings()[dbStringIndex++]); } } for (;dbStringIndex < getDatabaseStrings().length; dbStringIndex++) { printer.printString(getDatabaseStrings()[dbStringIndex]); } } @SuppressWarnings({ "unchecked", "rawtypes" }) private void prepare(List<Expression> items) { EclipseLinkFunctionRenderContext context; // for eclipselink, we need to append one dummy argument for functions without any arguments // to make this transparent for the JpqlFunction implementation, we need to remove this dummy argument at this point if (function.hasArguments()) { context = new EclipseLinkFunctionRenderContext(items, session); } else { if (items.size() > 1) { throw new IllegalStateException("Expected only one dummy argument for function [" + function.getClass() + "] but found " + items.size() + " arguments."); } context = new EclipseLinkFunctionRenderContext(Collections.<Expression>emptyList(), session); } function.render(context); setArgumentIndices(context.getArgumentIndices()); if (context.isChunkFirst()) { bePrefix(); } else { bePostfix(); } // add cast if necessary final Class<?> firstArgumentType; if (items.isEmpty()) { firstArgumentType = null; } else { firstArgumentType = getExpressionType(items.get(0)); } final String returnTypeName = classTypes.get(function.getReturnType(firstArgumentType)); if (returnTypeName != null) { final String[] castStrings = cast().getDatabaseStrings(); final List<String> chunks = context.getChunks(); if (chunks.isEmpty()) { context.addChunk(castStrings[0]); bePrefix(); } else if (context.isChunkFirst()) { chunks.set(0, new StringBuilder(castStrings[0]).append(chunks.get(0)).toString()); } else { chunks.add(0, castStrings[0]); bePrefix(); } context.addChunk(castStrings[1]); context.addChunk(returnTypeName); context.addChunk(castStrings[2]); } printsAs(new Vector(context.getChunks())); } private Class<?> getExpressionType(Expression expression) { if (expression instanceof SubSelectExpression) { return getReturnTypeFromSubSelectExpression((SubSelectExpression) expression); } else if (expression instanceof ParameterExpression) { return (Class<?>) ((ParameterExpression) expression).getType(); } else if (expression instanceof FunctionExpression) { return ((FunctionExpression) expression).getResultType(); } else if (expression instanceof FieldExpression) { return ((FieldExpression) expression).getField().getType(); } else if (expression instanceof FromSubSelectExpression) { return getExpressionType(((FromSubSelectExpression) expression).getSubSelect()); } else if (expression instanceof QueryKeyExpression) { final DatabaseMapping mapping = ((QueryKeyExpression) expression).getMapping(); return mapping == null ? null : mapping.getAttributeClassification(); } else if (expression instanceof ClassTypeExpression) { return ((ClassTypeExpression) expression).getField().getType(); } else if (expression instanceof ConstantExpression) { return ((ConstantExpression) expression).getValue().getClass(); } else if (expression instanceof CompoundExpression) { return Boolean.class; } else { return null; } } private Class<?> getReturnTypeFromSubSelectExpression(SubSelectExpression subSelectExpression) { try { return (Class<?>) SubSelectExpression.class.getField("returnType").get(subSelectExpression); } catch (Exception e) { throw new RuntimeException(e); } } }