org.apache.openjpa.persistence.criteria.CompoundSelections Maven / Gradle / Ivy
/*
* 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 FillStrategy getFillStrategy(Selection s) {
if (s instanceof CompoundSelectionImpl) {
return ((CompoundSelectionImpl)s).getFillStrategy();
} else {
return new FillStrategy.Assign<>();
}
}
/**
* Abstract implementation of a selection term composed of multiple selection terms.
*
*/
private abstract static class CompoundSelectionImpl extends SelectionImpl implements CompoundSelection {
private final List> _args;
public CompoundSelectionImpl(Class cls, Selection>...args) {
super(cls);
// assertNoCompoundSelection(args);
_args = args == null ? (List>)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> 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 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 type must be an array
*/
static class Array extends CompoundSelectionImpl {
public Array(Class cls, Selection>... terms) {
super(cls, terms);
if (!cls.isArray()) {
throw new IllegalArgumentException(cls + " is not an array. " + this + " needs an array");
}
}
@Override
public FillStrategy getFillStrategy() {
return new FillStrategy.Array<>(getJavaType());
}
}
/**
* A compound selection which is an instance constructed of its component terms.
*
* @param type of the constructed instance
*/
static class NewInstance extends CompoundSelectionImpl {
private FillStrategy.NewInstance strategy;
public NewInstance(Class cls, Selection>... selections) {
super(cls, selections);
strategy = new FillStrategy.NewInstance<>(findConstructor(cls, selections));
}
@Override
public FillStrategy getFillStrategy() {
return strategy;
}
private Constructor findConstructor(Class 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 {
public Tuple(final Selection>[] selections) {
super(javax.persistence.Tuple.class, selections);
}
@Override
public FillStrategy getFillStrategy() {
List> 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 the target result type.
*/
static class MultiSelection extends CompoundSelectionImpl {
public MultiSelection(Class result, final Selection>[] selections) {
super(result, selections);
}
@Override
public FillStrategy getFillStrategy() {
Class> resultClass = getJavaType();
List> 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)strategy;
}
}
}