blob: 13c8fc8e14f53541fb6d1d93f3f939f14019758d [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.calcite.plan;
import org.apache.calcite.config.CalciteConnectionConfig;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Utilities for {@link Context}.
*/
public class Contexts {
public static final EmptyContext EMPTY_CONTEXT = new EmptyContext();
private Contexts() {}
/** Returns a context that contains a
* {@link org.apache.calcite.config.CalciteConnectionConfig}.
*
* @deprecated Use {@link #of}
*/
@Deprecated // to be removed before 2.0
public static Context withConfig(CalciteConnectionConfig config) {
return of(config);
}
/** Returns a context that returns null for all inquiries. */
public static Context empty() {
return EMPTY_CONTEXT;
}
/** Returns a context that wraps an object.
*
* <p>A call to {@code unwrap(C)} will return {@code target} if it is an
* instance of {@code C}.
*/
public static Context of(Object o) {
return new WrapContext(o);
}
/** Returns a context that wraps an array of objects, ignoring any nulls. */
public static Context of(@Nullable Object... os) {
final List<Context> contexts = new ArrayList<>();
for (Object o : os) {
if (o != null) {
contexts.add(of(o));
}
}
return chain(contexts);
}
/** Returns a context that wraps a list of contexts.
*
* <p>A call to {@code unwrap(C)} will return the first object that is an
* instance of {@code C}.
*
* <p>If any of the contexts is a {@link Context}, recursively looks in that
* object. Thus this method can be used to chain contexts.
*/
public static Context chain(Context... contexts) {
return chain(ImmutableList.copyOf(contexts));
}
private static Context chain(Iterable<? extends Context> contexts) {
// Flatten any chain contexts in the list, and remove duplicates
final List<Context> list = new ArrayList<>();
for (Context context : contexts) {
build(list, context);
}
switch (list.size()) {
case 0:
return empty();
case 1:
return list.get(0);
default:
return new ChainContext(ImmutableList.copyOf(list));
}
}
/** Recursively populates a list of contexts. */
private static void build(List<Context> list, Context context) {
if (context == EMPTY_CONTEXT || list.contains(context)) {
return;
}
if (context instanceof ChainContext) {
ChainContext chainContext = (ChainContext) context;
for (Context child : chainContext.contexts) {
build(list, child);
}
} else {
list.add(context);
}
}
/** Context that wraps an object. */
private static class WrapContext implements Context {
final Object target;
WrapContext(Object target) {
this.target = Objects.requireNonNull(target, "target");
}
@Override public <T extends Object> @Nullable T unwrap(Class<T> clazz) {
if (clazz.isInstance(target)) {
return clazz.cast(target);
}
return null;
}
}
/** Empty context. */
static class EmptyContext implements Context {
@Override public <T extends Object> @Nullable T unwrap(Class<T> clazz) {
return null;
}
}
/** Context that wraps a chain of contexts. */
private static final class ChainContext implements Context {
final ImmutableList<Context> contexts;
ChainContext(ImmutableList<Context> contexts) {
this.contexts = Objects.requireNonNull(contexts, "contexts");
for (Context context : contexts) {
assert !(context instanceof ChainContext) : "must be flat";
}
}
@Override public <T extends Object> @Nullable T unwrap(Class<T> clazz) {
for (Context context : contexts) {
final T t = context.unwrap(clazz);
if (t != null) {
return t;
}
}
return null;
}
}
}