blob: 948f813abd32992595ce578e1adf2cd28cb4d437 [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.sis.filter;
import java.util.Arrays;
import java.util.Collection;
import org.opengis.util.ScopedName;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.util.ObjectConverters;
import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.feature.builder.FeatureTypeBuilder;
import org.apache.sis.feature.builder.PropertyTypeBuilder;
import org.apache.sis.feature.builder.AttributeTypeBuilder;
import org.apache.sis.internal.feature.FeatureExpression;
import org.apache.sis.util.resources.Errors;
// Branch-dependent imports
import org.apache.sis.feature.DefaultFeatureType;
/**
* Expression whose results are converted to a different type.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.2
*
* @param <R> the type of resources (e.g. {@code Feature}) used as inputs.
* @param <S> the type of value computed by the wrapped exception. This is the type to convert.
* @param <V> the type of value computed by this expression. This is the type after conversion.
*
* @see org.apache.sis.internal.filter.GeometryConverter
*
* @since 1.1
* @module
*/
final class ConvertFunction<R,S,V> extends UnaryFunction<R,S>
implements FeatureExpression<R,V>, Optimization.OnExpression<R,V>
{
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 4686604324414717316L;
/**
* Name of this expression.
*/
private static final ScopedName NAME = createName("Convert");
/**
* The converter to use.
*/
private final ObjectConverter<? super S, ? extends V> converter;
/**
* Creates a new converted expression.
*
* @param expression the expression providing source values.
* @param source the type of value produced by given expression
* @param target the desired type for the expression result.
* @throws UnconvertibleObjectException if no converter is found.
*/
ConvertFunction(final Expression<? super R, ? extends S> expression, final Class<S> source, final Class<V> target) {
super(expression);
converter = ObjectConverters.find(source, target);
}
/**
* Creates a new converted expression after optimization.
*
* @param expression the expression providing source values.
* @throws UnconvertibleObjectException if no converter is found.
*/
private ConvertFunction(final ConvertFunction<R,S,V> original, final Expression<? super R, ? extends S> expression) {
super(expression);
converter = original.converter;
}
/**
* Creates a new expression of the same type than this expression, but with optimized parameters.
*/
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Expression<R,V> recreate(Expression<? super R, ?>[] effective) {
final Expression<? super R, ?> e = effective[0];
if (e instanceof FeatureExpression<?,?>) {
final Class<? extends V> target = getValueClass(); // This is <V>.
final Class<?> source = ((FeatureExpression<?,?>) e).getValueClass(); // May become <S>.
if (target.isAssignableFrom(source)) {
return (Expression<R,V>) e;
}
if (source != Object.class) {
return new ConvertFunction(e, source, target);
}
}
final Class<? super S> source = converter.getSourceClass();
return new ConvertFunction(this, e.toValueType(source));
}
/**
* Returns an identification of this operation.
*/
@Override
public ScopedName getFunctionName() {
return NAME;
}
/**
* Returns the singleton expression tested by this operator
* together with the source and target classes.
*/
@Override
protected Collection<?> getChildren() {
return Arrays.asList(expression, converter.getSourceClass(), converter.getTargetClass());
}
/**
* Evaluates the expression for producing a result of the given type.
* If this method can not produce a value of the given type, then it returns {@code null}.
* This implementation evaluates the expression {@linkplain Expression#apply(Object) in the default way},
* then tries to convert the result to the target type.
*
* @param feature the value or feature to evaluate with this expression.
* @return the result, or {@code null} if it can not be of the specified type.
*/
@Override
public V apply(final R feature) {
final S value = expression.apply(feature);
try {
return converter.apply(value);
} catch (UnconvertibleObjectException e) {
warning(e, false);
return null;
}
}
/**
* Returns the type of values computed by this expression.
*/
@Override
public Class<? extends V> getValueClass() {
return converter.getTargetClass();
}
/**
* Provides the type of values produced by this expression when a feature of the given type is evaluated.
* May return {@code null} if the type can not be determined.
*/
@Override
public PropertyTypeBuilder expectedType(final DefaultFeatureType valueType, final FeatureTypeBuilder addTo) {
final FeatureExpression<?,?> fex = FeatureExpression.castOrCopy(expression);
if (fex == null) {
return null;
}
final PropertyTypeBuilder p = fex.expectedType(valueType, addTo);
if (p instanceof AttributeTypeBuilder<?>) {
return ((AttributeTypeBuilder<?>) p).setValueClass(getValueClass());
}
return p;
}
/**
* Returns an expression doing the same evaluation than this method, but returning results as values
* of the specified type. The result may be {@code this}.
*
* @param <N> compile-time value of {@code type}.
* @param target desired type of expression results.
* @return expression doing the same operation this this expression but with results of the specified type.
* @throws ClassCastException if the specified type is not a target type supported by implementation.
*/
@Override
@SuppressWarnings("unchecked")
public <N> Expression<R,N> toValueType(final Class<N> target) {
if (target.isAssignableFrom(getValueClass())) {
return (Expression<R,N>) this;
}
final Class<? super S> source = converter.getSourceClass();
if (target.isAssignableFrom(source)) {
return (Expression<R,N>) expression;
} else try {
return new ConvertFunction<>(expression, source, target);
} catch (UnconvertibleObjectException e) {
throw (ClassCastException) new ClassCastException(Errors.format(
Errors.Keys.CanNotConvertValue_2, expression.getFunctionName(), target)).initCause(e);
}
}
}