| /* |
| * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // 10/01/2018: Will Dazey |
| // - #253: Add support for embedded constructor results with CriteriaBuilder |
| package org.eclipse.persistence.queries; |
| |
| import java.lang.reflect.Constructor; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.exceptions.QueryException; |
| import org.eclipse.persistence.expressions.Expression; |
| import org.eclipse.persistence.internal.expressions.ConstantExpression; |
| import org.eclipse.persistence.internal.expressions.MapEntryExpression; |
| import org.eclipse.persistence.internal.helper.ClassConstants; |
| import org.eclipse.persistence.internal.helper.StringHelper; |
| import org.eclipse.persistence.internal.queries.ReportItem; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedGetConstructorFor; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| |
| |
| /** |
| * <b>Purpose</b>: An item specifying a class constructor method to be used in a ReportQuery's returned results. |
| * <p>Example: |
| * <blockquote><pre> |
| * ConstructorReportItem item = new ConstructorReportItem("Employee"); |
| * item.setResultType(Employee.class); |
| * item.addAttribute("firstName", employees.get("firstName")); |
| * query.addConstructorReportItem(item); |
| * </pre></blockquote> |
| * <p> |
| * When executed will return a collection of ReportQueryResults that contain Employee objects created using |
| * the new Employee(firstname) constructor. |
| * |
| * @author Chris Delahunt |
| * @since TopLink Essentials 1.0 |
| */ |
| public class ConstructorReportItem extends ReportItem { |
| |
| /** |
| * String prefix of {@link #toString()} output. |
| */ |
| private static final String TO_STR_PREFIX = "ConstructorReportItem("; |
| |
| /** |
| * String to separate name and items array of {@link #toString()} output. |
| */ |
| private static final String TO_STR_ARRAY = " -> ["; |
| |
| /** |
| * String suffix of {@link #toString()} output. |
| */ |
| private static final String TO_STR_SUFFIX = "])"; |
| |
| protected Class[] constructorArgTypes; |
| protected List<DatabaseMapping> constructorMappings; |
| protected List<ReportItem> reportItems; |
| protected Constructor constructor; |
| |
| /** |
| * Create a new constructor item. |
| */ |
| public ConstructorReportItem() { |
| } |
| |
| /** |
| * Create a new constructor item. |
| * @param name string used to look up this result in the ReportQueryResult. |
| */ |
| public ConstructorReportItem(String name) { |
| super(name, null); |
| } |
| |
| /** |
| * Method to add an expression to be used to return the parameter that is then passed into the constructor method. |
| * Similar to ReportQuery's addAttribute method, but name is not needed. |
| */ |
| public void addAttribute(Expression attributeExpression) { |
| ReportItem item = new ReportItem(getName()+getReportItems().size(), attributeExpression); |
| getReportItems().add(item); |
| } |
| |
| /** |
| * Add the attribute with joining. |
| */ |
| public void addAttribute(String attributeName, Expression attributeExpression, List joinedExpressions) { |
| ReportItem item = new ReportItem(attributeName, attributeExpression); |
| item.getJoinedAttributeManager().setJoinedAttributeExpressions_(joinedExpressions); |
| getReportItems().add(item); |
| } |
| |
| public void addItem(ReportItem item) { |
| getReportItems().add(item); |
| } |
| |
| public Class[] getConstructorArgTypes(){ |
| return constructorArgTypes; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the mappings for the items. |
| */ |
| public List<DatabaseMapping> getConstructorMappings(){ |
| return constructorMappings; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the constructor. |
| */ |
| public Constructor getConstructor(){ |
| return constructor; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the constructor. |
| */ |
| public void setConstructor(Constructor constructor){ |
| this.constructor = constructor; |
| } |
| |
| public List<ReportItem> getReportItems(){ |
| if (reportItems == null) { |
| reportItems = new ArrayList<>(); |
| } |
| return reportItems; |
| } |
| |
| /** |
| * INTERNAL: |
| * Looks up mapping for attribute during preExecute of ReportQuery |
| */ |
| @Override |
| public void initialize(ReportQuery query) throws QueryException { |
| int size= getReportItems().size(); |
| List<DatabaseMapping> mappings = new ArrayList<>(); |
| for (int index = 0; index < size; index++) { |
| ReportItem item = reportItems.get(index); |
| item.initialize(query); |
| mappings.add(item.getMapping()); |
| } |
| setConstructorMappings(mappings); |
| |
| int numberOfItems = getReportItems().size(); |
| // Arguments may be initialized depending on how the query was constructed, so types may be undefined though. |
| if (getConstructorArgTypes() == null) { |
| setConstructorArgTypes(new Class[numberOfItems]); |
| } |
| Class[] constructorArgTypes = getConstructorArgTypes(); |
| for (int index = 0; index < numberOfItems; index++) { |
| if (constructorArgTypes[index] == null) { |
| ReportItem argumentItem = getReportItems().get(index); |
| if (mappings.get(index) != null) { |
| DatabaseMapping mapping = constructorMappings.get(index); |
| if (argumentItem.getAttributeExpression() != null && argumentItem.getAttributeExpression().isMapEntryExpression()){ |
| if (((MapEntryExpression)argumentItem.getAttributeExpression()).shouldReturnMapEntry()){ |
| constructorArgTypes[index] = Map.Entry.class; |
| } else { |
| constructorArgTypes[index] = (Class) mapping.getContainerPolicy().getKeyType(); |
| } |
| } else { |
| constructorArgTypes[index] = mapping.getAttributeClassification(); |
| } |
| } else if (argumentItem.getResultType() != null) { |
| constructorArgTypes[index] = argumentItem.getResultType(); |
| } else if (argumentItem.getDescriptor() != null) { |
| constructorArgTypes[index] = argumentItem.getDescriptor().getJavaClass(); |
| } else if (argumentItem.getAttributeExpression() != null && argumentItem.getAttributeExpression().isConstantExpression()){ |
| constructorArgTypes[index] = ((ConstantExpression)argumentItem.getAttributeExpression()).getValue().getClass(); |
| } else { |
| // Use Object.class by default. |
| constructorArgTypes[index] = ClassConstants.OBJECT; |
| } |
| } |
| } |
| if (getConstructor() == null) { |
| try { |
| Constructor constructor = null; |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| constructor = AccessController.doPrivileged(new PrivilegedGetConstructorFor(getResultType(), constructorArgTypes, true)); |
| } catch (PrivilegedActionException exception) { |
| throw QueryException.exceptionWhileUsingConstructorExpression(exception.getException(), query); |
| } |
| } else { |
| constructor = PrivilegedAccessHelper.getConstructorFor(getResultType(), constructorArgTypes, true); |
| } |
| setConstructor(constructor); |
| } catch (NoSuchMethodException exception) { |
| throw QueryException.exceptionWhileUsingConstructorExpression(exception, query); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isConstructorItem(){ |
| return true; |
| } |
| |
| public void setConstructorArgTypes(Class[] constructorArgTypes){ |
| this.constructorArgTypes = constructorArgTypes; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the mappings for the items. |
| */ |
| public void setConstructorMappings(List<DatabaseMapping> constructorMappings){ |
| this.constructorMappings = constructorMappings; |
| } |
| |
| public void setReportItems(List<ReportItem> reportItems){ |
| this.reportItems = reportItems; |
| } |
| |
| @Override |
| public String toString() { |
| String name = StringHelper.nonNullString(getName()); |
| // Calculate string length |
| int length = TO_STR_PREFIX.length() + name.length() |
| + TO_STR_ARRAY.length() + TO_STR_SUFFIX.length(); |
| int size = reportItems != null ? reportItems.size() : 0; |
| String[] items = new String[size]; |
| for (int i=0; i < size; i++) { |
| items[i] = StringHelper.nonNullString(reportItems.get(i).toString()); |
| length += items[i].length(); |
| } |
| // Build string |
| StringBuilder str = new StringBuilder(length); |
| str.append(TO_STR_PREFIX).append(name).append(TO_STR_ARRAY); |
| for (int i=0; i < size; i++) { |
| str.append(items[i]); |
| } |
| str.append(TO_STR_SUFFIX); |
| return str.toString(); |
| } |
| |
| } |