org.apache.solr.handler.clustering.FlatKeysAttrVisitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of solr-clustering Show documentation
Show all versions of solr-clustering Show documentation
Apache Solr (module: clustering)
/*
* 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.solr.handler.clustering;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import org.carrot2.attrs.AcceptingVisitor;
import org.carrot2.attrs.AliasMapper;
import org.carrot2.attrs.AttrBoolean;
import org.carrot2.attrs.AttrDouble;
import org.carrot2.attrs.AttrEnum;
import org.carrot2.attrs.AttrInteger;
import org.carrot2.attrs.AttrObject;
import org.carrot2.attrs.AttrObjectArray;
import org.carrot2.attrs.AttrString;
import org.carrot2.attrs.AttrStringArray;
import org.carrot2.attrs.AttrVisitor;
/**
* {@link AttrVisitor} that responds to "flattened" key paths and values, updating corresponding
* algorithm parameters with values contained in the map.
*/
class FlatKeysAttrVisitor implements AttrVisitor {
final Function classToInstance = AliasMapper.SPI_DEFAULTS::fromName;
final ArrayDeque keyPath = new ArrayDeque<>();
final LinkedHashMap attrs;
/**
* @param attrs A map of attributes to set. Note the map has ordered keys: this is required for
* complex sub-types so that instantiation of a value precedes setting its attributes.
*/
FlatKeysAttrVisitor(LinkedHashMap attrs) {
this.attrs = attrs;
}
@Override
public void visit(String key, AttrBoolean attr) {
ifKeyExists(
key,
(path, value) -> {
attr.set(value == null ? null : Boolean.parseBoolean(value));
});
}
@Override
public void visit(String key, AttrInteger attr) {
ifKeyExists(
key,
(path, value) -> {
attr.set(value == null ? null : Integer.parseInt(value));
});
}
@Override
public void visit(String key, AttrDouble attr) {
ifKeyExists(
key,
(path, value) -> {
attr.set(value == null ? null : Double.parseDouble(value));
});
}
@Override
public void visit(String key, AttrString attr) {
ifKeyExists(
key,
(path, value) -> {
attr.set(value);
});
}
@Override
public void visit(String key, AttrStringArray attr) {
ifKeyExists(
key,
(path, value) -> {
if (value == null) {
attr.set(new String[0]);
} else {
attr.set(value.split(",\\s*"));
}
});
}
@Override
public > void visit(String key, AttrEnum attr) {
ifKeyExists(
key,
(path, value) -> {
try {
attr.set(Enum.valueOf(attr.enumClass(), value));
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Value at key '%s' should be an enum constant of class '%s', but no such "
+ "constant exists: '%s' (available constants: %s)",
key,
attr.enumClass().getSimpleName(),
toDebugString(value),
EnumSet.allOf(attr.enumClass())));
}
});
}
@Override
public void visit(String key, AttrObject attr) {
ifKeyExists(
key,
(path, value) -> {
if (value == null) {
attr.set(null);
} else {
T t = safeCast(classToInstance.apply(value), key, attr.getInterfaceClass());
attr.set(t);
}
});
T t = attr.get();
if (t != null) {
withKey(
key,
path -> {
t.accept(this);
});
}
}
@Override
public void visit(String key, AttrObjectArray attr) {
ifKeyExists(
key,
(path, value) -> {
throw new RuntimeException(
"Setting arrays of objects not implemented for attribute: "
+ key
+ " ("
+ attr.getDescription()
+ ")");
});
}
private T safeCast(Object value, String key, Class clazz) {
if (value == null) {
return null;
} else {
if (!clazz.isInstance(value)) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Value at key '%s' should be an instance of '%s', but encountered class '%s': '%s'",
key,
clazz.getSimpleName(),
value.getClass().getSimpleName(),
toDebugString(value)));
}
return clazz.cast(value);
}
}
private String toDebugString(Object value) {
if (value == null) {
return "[null]";
} else if (value instanceof Object[]) {
return Arrays.deepToString(((Object[]) value));
} else {
return Objects.toString(value);
}
}
private void withKey(String key, Consumer pathConsumer) {
keyPath.addLast(key);
try {
String path = String.join(".", keyPath);
pathConsumer.accept(path);
} finally {
keyPath.removeLast();
}
}
private void ifKeyExists(String key, BiConsumer pathConsumer) {
withKey(
key,
(path) -> {
if (attrs.containsKey(path)) {
String value = attrs.get(path);
if (value.trim().isEmpty()) {
value = null;
}
pathConsumer.accept(path, value);
}
});
}
}