package rocks.inspectit.shared.cs.indexing;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import rocks.inspectit.shared.all.cmr.cache.IObjectSizes;
import rocks.inspectit.shared.all.indexing.IIndexQuery;
import rocks.inspectit.shared.cs.indexing.buffer.impl.Branch;
import rocks.inspectit.shared.cs.indexing.impl.IndexingException;
import rocks.inspectit.shared.cs.indexing.indexer.IBranchIndexer;
/**
* Abstract class for all {@link ITreeComponent}s that are a branch.
*
* @author Ivan Senic
*
* @param <R>
* Type of the element returned by the branch when querying.
* @param <E>
* Type of the element that can be indexed by the branch.
*/
public abstract class AbstractBranch<R, E> implements ITreeComponent<R, E> {
/**
* Initial concurrency level for the {@link ConcurrentHashMap}.
*/
private static final int CONCURRENCY_LEVEL = 4;
/**
* Branch indexer.
*/
private IBranchIndexer<E> branchIndexer;
/**
* Map for holding references.
*/
private ConcurrentHashMap<Object, ITreeComponent<R, E>> map;
/**
* Default constructor. {@link Branch} can only be initialized with proper branch indexer
* supplied. If null is passed, {@link IllegalArgumentException} will be thrown.
*
* @param branchIndexer
* Branch indexer used in this branch.
*/
public AbstractBranch(IBranchIndexer<E> branchIndexer) {
this.branchIndexer = branchIndexer;
map = new ConcurrentHashMap<>(1, 0.75f, CONCURRENCY_LEVEL);
}
/**
* Returns branch indexer.
*
* @return Branch indexer
*/
protected IBranchIndexer<E> getBranchIndexer() {
return branchIndexer;
}
/**
* Each concrete implementation should create new component and put the element in it.
*
* @param element
* Element to put in new component.
* @return Return type by the tree component.
*/
protected abstract ITreeComponent<R, E> getNextTreeComponent(E element);
/**
* {@inheritDoc}
*/
@Override
public R put(E element) throws IndexingException {
// get key for object
Object key = branchIndexer.getKey(element);
if (null == key) {
throw new IndexingException("Branch indexer " + branchIndexer + " can not create the key for the object " + element + ".");
}
// get the tree component for key
ITreeComponent<R, E> treeComponent = map.get(key);
if (null != treeComponent) {
// if component exists, put element into the component
return treeComponent.put(element);
} else {
// check again if the new component already exists
treeComponent = map.get(key);
if (null == treeComponent) {
// otherwise create new tree component, add it to the map and put element into
treeComponent = this.getNextTreeComponent(element);
ITreeComponent<R, E> existing = map.putIfAbsent(key, treeComponent);
if (null != existing) {
treeComponent = existing;
}
}
return treeComponent.put(element);
}
}
/**
* {@inheritDoc}
*/
@Override
public R get(E template) {
// get key for template
Object key = branchIndexer.getKey(template);
// get the tree component for key
ITreeComponent<R, E> treeComponent = null;
if (null != key) {
treeComponent = map.get(key);
}
if (null != treeComponent) {
// if component exists, get element from the component
return treeComponent.get(template);
} else if (null == key) {
// if key could not be created, search into all branches/leafs
// keys can not be created when the template object does not carry the information that
// branch indexer on this branch needs
Iterator<ITreeComponent<R, E>> iterator = map.values().iterator();
R result = null;
while (iterator.hasNext()) {
result = iterator.next().get(template);
if (null != result) {
return result;
}
}
return null;
} else {
// finally return nothing cause temple object was never put before
return null;
}
};
/**
* {@inheritDoc}
*/
@Override
public R getAndRemove(E template) {
// get key for template
Object key = getBranchIndexer().getKey(template);
// get the tree component for key
ITreeComponent<R, E> treeComponent = null;
if (null != key) {
treeComponent = map.get(key);
}
if (null != treeComponent) {
// if component exists, get element from the component
return treeComponent.getAndRemove(template);
} else if (null == key) {
// if key could not be created, search into all branches/leafs
// keys can not be created when the template object does not carry the information that
// branch indexer on this branch needs
Iterator<ITreeComponent<R, E>> iterator = map.values().iterator();
R result = null;
while (iterator.hasNext()) {
result = iterator.next().getAndRemove(template);
if (null != result) {
return result;
}
}
return null;
} else {
// finally return nothing cause temple object was never put before
return null;
}
};
/**
* {@inheritDoc}
*/
@Override
public List<R> query(IIndexQuery query) {
// get all keys for query
Object[] keys = getBranchIndexer().getKeys(query);
if (ArrayUtils.isEmpty(keys)) {
// if key can not be created search in next level
return queryAllTreeComponents(query);
} else if (1 == keys.length) {
// if only one key is returned, search in exactly this one
return querySingleKey(query, keys[0]);
} else {
// combine results for all keys
List<R> results = new ArrayList<>();
for (Object key : keys) {
List<R> componentResult = querySingleKey(query, key);
if ((null != componentResult) && !componentResult.isEmpty()) {
results.addAll(componentResult);
}
}
return results;
}
}
/**
* {@inheritDoc}
*/
@Override
public List<R> query(IIndexQuery query, ForkJoinPool forkJoinPool) {
return forkJoinPool.invoke(getTaskForForkJoinQuery(query));
}
/**
* Queries the single {@link ITreeComponent} that is mapped with key. If passed key is null, or
* if there is no component mapped with given key, result will be empty list.
*
* @param query
* Query to process.
* @param key
* Mapping key value for {@link ITreeComponent}.
* @return Result from queried {@link ITreeComponent} or empty list if key is null or none of
* {@link ITreeComponent} is mapped with given key.
*/
protected List<R> querySingleKey(IIndexQuery query, Object key) {
// get tree component for key
ITreeComponent<R, E> treeComponent = null;
if (null != key) {
treeComponent = map.get(key);
}
if (null != treeComponent) {
// if it is found search in that one
return treeComponent.query(query);
} else {
// finally this brunch did not find anything that matches the search
return Collections.emptyList();
}
}
/**
* Returns results from all branches for given query.
*
* @param query
* Query to process.
* @return Combined result from all {@link ITreeComponent}s that are mapped in this branch.
*/
protected List<R> queryAllTreeComponents(IIndexQuery query) {
List<R> results = new ArrayList<>();
Iterator<ITreeComponent<R, E>> iterator = map.values().iterator();
while (iterator.hasNext()) {
List<R> componentResult = iterator.next().query(query);
if ((null != componentResult) && !componentResult.isEmpty()) {
results.addAll(componentResult);
}
}
return results;
}
/**
* {@inheritDoc}
*/
@Override
public long getComponentSize(IObjectSizes objectSizes) {
int mapSize = map.size();
long size = objectSizes.getSizeOfObjectHeader();
size += objectSizes.getPrimitiveTypesSize(2, 0, 0, 0, 0, 0);
size += objectSizes.getSizeOfConcurrentHashMap(mapSize, CONCURRENCY_LEVEL);
size += mapSize * objectSizes.getSizeOfLongObject(); // for a Long key in a Map.entry
for (ITreeComponent<R, E> treeComponent : map.values()) {
size += treeComponent.getComponentSize(objectSizes);
}
return size;
}
/**
* {@inheritDoc}
*/
public void clearAll() {
map.clear();
}
/**
* @return Returns component map, so that subclasses can handle specific tasks.
*/
public Map<Object, ITreeComponent<R, E>> getComponentMap() {
return map;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((branchIndexer == null) ? 0 : branchIndexer.hashCode());
result = (prime * result) + ((map == null) ? 0 : map.hashCode());
return result;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AbstractBranch<R, E> other = (AbstractBranch<R, E>) obj;
if (branchIndexer == null) {
if (other.branchIndexer != null) {
return false;
}
} else if (!branchIndexer.equals(other.branchIndexer)) {
return false;
}
if (map == null) {
if (other.map != null) {
return false;
}
} else if (!map.equals(other.map)) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
ToStringBuilder toStringBuilder = new ToStringBuilder(this);
toStringBuilder.append("branchIndexer", branchIndexer);
toStringBuilder.append("branchMap", map);
return toStringBuilder.toString();
}
/**
* Returns the branches to Query.
*
* @param query
* query
* @return the list of branches
*/
public Collection<ITreeComponent<R, E>> getBranchesToQuery(IIndexQuery query) {
// The given keys which fit to the query
Object[] keys = this.getBranchIndexer().getKeys(query);
// The map, which holds all branches of the next level
if (ArrayUtils.isEmpty(keys)) {
return map.values();
} else {
// the branches which have to be queried
Collection<ITreeComponent<R, E>> branchesToQuery = new ArrayList<>();
for (Object key : keys) {
ITreeComponent<R, E> branch = map.get(key);
if (branch != null) {
branchesToQuery.add(branch);
}
}
return branchesToQuery;
}
}
/**
* {@inheritDoc}
*/
@Override
public RecursiveTask<List<R>> getTaskForForkJoinQuery(IIndexQuery query) {
return new QueryTask<>(this.getBranchesToQuery(query), query);
}
}