/* * Copyright 2015, The OpenNMS Group * * Licensed 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.opennms.newts.cassandra.search; import static com.google.common.base.Preconditions.checkNotNull; import static org.opennms.newts.api.search.QueryBuilder.matchKeyAndValue; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Queue; import javax.inject.Inject; import org.opennms.newts.api.Context; import org.opennms.newts.api.Resource; import org.opennms.newts.api.search.SearchResults; import org.opennms.newts.api.search.SearchResults.Result; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Queues; /** * Walks the resource tree by searching for the appropriate _parent attributes. * * @author jwhite */ public class CassandraResourceTreeWalker { private static final Resource TOP_LEVEL_RESOURCE = new Resource( Constants.TOP_LEVEL_PARENT_TERM_VALUE); private final CassandraSearcher m_searcher; public static interface SearchResultVisitor { /** * Called to visit a particular search result. * * @return true if the caller should continue visiting, false otherwise */ public boolean visit(Result result); } @Inject public CassandraResourceTreeWalker(CassandraSearcher searcher) { m_searcher = checkNotNull(searcher, "searcher argument"); } /** * Visits all nodes in the resource tree using breadth-first search. */ public void breadthFirstSearch(Context context, SearchResultVisitor visitor) { breadthFirstSearch(context, visitor, TOP_LEVEL_RESOURCE); } /** * Visits all nodes in the resource tree bellow the given resource using * breadth-first search. */ public void breadthFirstSearch(Context context, SearchResultVisitor visitor, Resource root) { Queue<Resource> queue = Lists.newLinkedList(); queue.add(root); while (!queue.isEmpty()) { Resource r = queue.remove(); for (SearchResults.Result result : m_searcher .search(context, matchKeyAndValue(Constants.PARENT_TERM_FIELD, r.getId()))) { if (!visitor.visit(result)) { return; } queue.add(result.getResource()); } } } /** * Visits all nodes in the resource tree using depth-first search. */ public void depthFirstSearch(Context context, SearchResultVisitor visitor) { depthFirstSearch(context, visitor, TOP_LEVEL_RESOURCE); } /** * Visits all nodes in the resource tree bellow the given resource using * depth-first search. */ public void depthFirstSearch(Context context, SearchResultVisitor visitor, Resource root) { ArrayDeque<SearchResults.Result> stack = Queues.newArrayDeque(); // Build an instance of a SearchResult for the root resource // but don't invoke the visitor with it boolean skipFirstVisit = true; SearchResults initialResults = new SearchResults(); initialResults.addResult(root, new ArrayList<String>(0)); stack.add(initialResults.iterator().next()); while (!stack.isEmpty()) { SearchResults.Result r = stack.pop(); if (skipFirstVisit) { skipFirstVisit = false; } else { if (!visitor.visit(r)) { return; } } // Reverse the order of the results so we walk the left-most // branches first ImmutableList<SearchResults.Result> results = ImmutableList.copyOf(m_searcher.search( context, matchKeyAndValue(Constants.PARENT_TERM_FIELD, r.getResource().getId()))); for (SearchResults.Result result : results.reverse()) { stack.push(result); } } } }