blob: 9b9a16fce90840c0618cc295d5e1b858169691d7 [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.ignite.internal.configuration.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.apache.ignite.internal.configuration.tree.ConfigurationVisitor;
import org.apache.ignite.internal.configuration.tree.InnerNode;
import org.apache.ignite.internal.configuration.tree.NamedListNode;
/** Visitor that accumulates keys while descending. */
public abstract class KeysTrackingConfigurationVisitor<T> implements ConfigurationVisitor<T> {
/** Current key, aggregated by visitor. */
private StringBuilder currentKey = new StringBuilder();
/** Current keys list, almost the same as {@link #currentKey}. */
private List<String> currentPath = new ArrayList<>();
/** {@inheritDoc} */
@Override public final T visitLeafNode(String key, Serializable val) {
int prevPos = startVisit(key, false, true);
try {
return doVisitLeafNode(key, val);
}
finally {
endVisit(prevPos);
}
}
/** {@inheritDoc} */
@Override public final T visitInnerNode(String key, InnerNode node) {
int prevPos = startVisit(key, false, false);
try {
return doVisitInnerNode(key, node);
}
finally {
endVisit(prevPos);
}
}
/** {@inheritDoc} */
@Override public final <N extends InnerNode> T visitNamedListNode(String key, NamedListNode<N> node) {
int prevPos = startVisit(key, false, false);
try {
return doVisitNamedListNode(key, node);
}
finally {
endVisit(prevPos);
}
}
/**
* To be used instead of {@link ConfigurationVisitor#visitLeafNode(String, Serializable)}.
*
* @param key Name of the node retrieved from its holder object.
* @param val Configuration value.
* @return Anything that implementation decides to return.
*/
protected T doVisitLeafNode(String key, Serializable val) {
return null;
}
/**
* To be used instead of {@link ConfigurationVisitor#visitInnerNode(String, InnerNode)}.
*
* @param key Name of the node retrieved from its holder object.
* @param node Inner configuration node.
* @return Anything that implementation decides to return.
*/
protected T doVisitInnerNode(String key, InnerNode node) {
node.traverseChildren(this, true);
return null;
}
/**
* To be used instead of {@link ConfigurationVisitor#visitNamedListNode(String, NamedListNode)}.
*
* @param key Name of the node retrieved from its holder object.
* @param node Named list inner configuration node.
* @param <N> Type of element nodes in the named list.
* @return Anything that implementation decides to return.
*/
protected <N extends InnerNode> T doVisitNamedListNode(String key, NamedListNode<N> node) {
for (String namedListKey : node.namedListKeys()) {
int prevPos = startVisit(namedListKey, true, false);
try {
doVisitInnerNode(namedListKey, node.get(namedListKey));
}
finally {
endVisit(prevPos);
}
}
return null;
}
/**
* Tracks passed key to reflect it in {@link #currentKey()} and {@link #currentPath()}.
*
* @param key Key itself.
* @param escape Whether the key needs escaping or not.
* @param leaf Add dot at the end of {@link #currentKey()} if {@code leaf} is {@code false}.
* @param closure Closure to execute when {@link #currentKey()} and {@link #currentPath()} have updated values.
* @return Closure result.
*/
protected final T withTracking(String key, boolean escape, boolean leaf, Supplier<T> closure) {
int prevPos = startVisit(key, escape, leaf);
try {
return closure.get();
}
finally {
endVisit(prevPos);
}
}
/**
* @return Current key, with a dot at the end if it's not a leaf.
*/
protected final String currentKey() {
return currentKey.toString();
}
/**
* @return List representation of the current key.
*/
protected final List<String> currentPath() {
return Collections.unmodifiableList(currentPath);
}
/**
* Prepares values of {@link #currentKey} and {@link #currentPath} for further processing.
*
* @param key Key.
* @param escape Whether we need to escape the key before appending it to {@link #currentKey}.
* @return Previous length of {@link #currentKey} so it can be passed to {@link #endVisit(int)} later.
*/
private int startVisit(String key, boolean escape, boolean leaf) {
int previousKeyLength = currentKey.length();
currentKey.append(escape ? ConfigurationUtil.escape(key) : key);
if (!leaf)
currentKey.append('.');
currentPath.add(key);
return previousKeyLength;
}
/**
* Puts {@link #currentKey} and {@link #currentPath} in the same state as they were before
* {@link #startVisit(String, boolean)}.
*
* @param previousKeyLength Value return by corresponding {@link #startVisit(String, boolean)} invocation.
*/
private void endVisit(int previousKeyLength) {
currentKey.setLength(previousKeyLength);
currentPath.remove(currentPath.size() - 1);
}
}