/* * 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.openjpa.persistence.criteria; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.persistence.TupleElement; import javax.persistence.criteria.CompoundSelection; import javax.persistence.criteria.Selection; import org.apache.openjpa.kernel.FillStrategy; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.persistence.TupleFactory; import org.apache.openjpa.persistence.TupleImpl; /** * Implements selection terms that are composed of other selection terms. * * @author Pinaki Poddar * * @since 2.0.0 * */ class CompoundSelections { private static Localizer _loc = Localizer.forPackage(CompoundSelections.class); /** * Gets the strategy to fill a given compound selection. * */ static <X> FillStrategy<X> getFillStrategy(Selection<X> s) { if (s instanceof CompoundSelectionImpl) { return ((CompoundSelectionImpl<X>)s).getFillStrategy(); } else { return new FillStrategy.Assign<X>(); } } /** * Abstract implementation of a selection term composed of multiple selection terms. * */ private abstract static class CompoundSelectionImpl<X> extends SelectionImpl<X> implements CompoundSelection<X> { private final List<Selection<?>> _args; public CompoundSelectionImpl(Class<X> cls, Selection<?>...args) { super(cls); // assertNoCompoundSelection(args); _args = args == null ? (List<Selection<?>>)Collections.EMPTY_LIST : Arrays.asList(args); } public final boolean isCompoundSelection() { return true; } /** * Return selection items composing a compound selection * @return list of selection items * @throws IllegalStateException if selection is not a compound * selection */ public final List<Selection<?>> getCompoundSelectionItems() { return Expressions.returnCopy(_args); } void assertNoCompoundSelection(Selection<?>...args) { if (args == null) return; for (Selection<?> s : args) { if (s.isCompoundSelection() && !(s.getClass() == NewInstance.class)) { throw new IllegalArgumentException("compound selection " + s + " can not be nested in " + this); } } } abstract FillStrategy<X> getFillStrategy(); @Override public StringBuilder asValue(AliasContext q) { StringBuilder buffer = new StringBuilder(); for (int i = 0; i < _args.size(); i++) { buffer.append((((CriteriaExpression)_args.get(i)).asValue(q))); if (i+1 != _args.size()) buffer.append(", "); } return buffer; } @Override public StringBuilder asProjection(AliasContext q) { StringBuilder buffer = new StringBuilder(); for (int i = 0; i < _args.size(); i++) { buffer.append((((CriteriaExpression)_args.get(i)).asProjection(q))); if (i+1 != _args.size()) buffer.append(", "); } return buffer; } } /** * A compound selection which is an array of its component terms. * * @param <X> type must be an array */ static class Array<X> extends CompoundSelectionImpl<X> { public Array(Class<X> cls, Selection<?>... terms) { super(cls, terms); if (!cls.isArray()) { throw new IllegalArgumentException(cls + " is not an array. " + this + " needs an array"); } } public FillStrategy<X> getFillStrategy() { return new FillStrategy.Array<X>(getJavaType()); } } /** * A compound selection which is an instance constructed of its component terms. * * @param <X> type of the constructed instance */ static class NewInstance<X> extends CompoundSelectionImpl<X> { private FillStrategy.NewInstance<X> strategy; public NewInstance(Class<X> cls, Selection<?>... selections) { super(cls, selections); strategy = new FillStrategy.NewInstance<X>(findConstructor(cls, selections)); } public FillStrategy<X> getFillStrategy() { return strategy; } private Constructor<X> findConstructor(Class<X> cls, Selection<?>... selections) { Class<?>[] types = selections == null ? null : new Class[selections.length]; if (selections != null) { for (int i = 0; i < selections.length; i++) { types[i] = selections[i].getJavaType(); } } try { return cls.getConstructor(types); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(_loc.get("select-no-ctor", cls, types == null ? "[]" : Arrays.toString(types)).getMessage()); } } @Override public StringBuilder asValue(AliasContext q) { return new StringBuilder("NEW ").append(getJavaType().getName()).append("(") .append(super.asValue(q)).append(")"); } } /** * A compound selection which is a Tuple composed of its component terms. * */ static class Tuple extends CompoundSelectionImpl<javax.persistence.Tuple> { public Tuple(final Selection<?>[] selections) { super(javax.persistence.Tuple.class, selections); } public FillStrategy<javax.persistence.Tuple> getFillStrategy() { List<Selection<?>> terms = getCompoundSelectionItems(); TupleFactory factory = new TupleFactory(terms.toArray(new TupleElement[terms.size()])); return new FillStrategy.Factory<javax.persistence.Tuple>(factory, TupleImpl.PUT); } } /** * A selection of terms that interprets its arguments based on target result type. * * @param <T> the target result type. */ static class MultiSelection<T> extends CompoundSelectionImpl<T> { public MultiSelection(Class<T> result, final Selection<?>[] selections) { super(result, selections); } public FillStrategy<T> getFillStrategy() { Class<?> resultClass = getJavaType(); List<Selection<?>> terms = getCompoundSelectionItems(); FillStrategy<?> strategy = null; if (javax.persistence.Tuple.class.isAssignableFrom(resultClass)) { TupleFactory factory = new TupleFactory(terms.toArray(new TupleElement[terms.size()])); strategy = new FillStrategy.Factory<javax.persistence.Tuple>(factory, TupleImpl.PUT); } else if (resultClass == Object.class) { if (terms.size() > 1) { resultClass = Object[].class; strategy = new FillStrategy.Array<Object[]>(Object[].class); } else { strategy = new FillStrategy.Assign(); } } else { strategy = resultClass.isArray() ? new FillStrategy.Array(resultClass) : new FillStrategy.NewInstance(resultClass); } return (FillStrategy<T>)strategy; } } }