blob: 441f34c4e04d66d8b7bf683fe3e0a5940bc80015 [file] [log] [blame]
/*
* 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<>();
}
}
/**
* 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);
}
@Override
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
*/
@Override
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");
}
}
@Override
public FillStrategy<X> getFillStrategy() {
return new FillStrategy.Array<>(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<>(findConstructor(cls, selections));
}
@Override
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);
}
@Override
public FillStrategy<javax.persistence.Tuple> getFillStrategy() {
List<Selection<?>> terms = getCompoundSelectionItems();
TupleFactory factory = new TupleFactory(terms.toArray(new TupleElement[terms.size()]));
return new FillStrategy.Factory<>(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);
}
@Override
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<>(factory, TupleImpl.PUT);
} else if (resultClass == Object.class) {
if (terms.size() > 1) {
resultClass = Object[].class;
strategy = new FillStrategy.Array<>(Object[].class);
} else {
strategy = new FillStrategy.Assign();
}
} else {
strategy = resultClass.isArray()
? new FillStrategy.Array(resultClass)
: new FillStrategy.NewInstance(resultClass);
}
return (FillStrategy<T>)strategy;
}
}
}