blob: 4b5d3d92620af1c808e0cf40fada5dc6c56b24a6 [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.jackrabbit.oak.plugins.index.aggregate;
import static com.google.common.collect.Lists.newArrayList;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.oak.api.Type.STRING;
import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
import static org.apache.jackrabbit.oak.commons.PathUtils.getDepth;
import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.spi.query.QueryIndex;
import org.apache.jackrabbit.oak.spi.state.NodeState;
/**
* List based NodeAggregator
*
*/
public class SimpleNodeAggregator implements QueryIndex.NodeAggregator {
public static final String INCLUDE_ALL = "*";
private final List<ChildNameRule> aggregates = new ArrayList<ChildNameRule>();
@Override
public Iterator<String> getParents(NodeState root, String path) {
return getParents(root, path, true);
}
private Iterator<String> getParents(NodeState root, String path,
boolean acceptStarIncludes) {
int levelsUp = 0;
Set<String> primaryType = new HashSet<String>();
for (ChildNameRule r : aggregates) {
String name = getName(path);
for (String inc : r.includes) {
// check node name match
if (name.equals(getName(inc))) {
levelsUp = getDepth(inc);
primaryType.add(r.primaryType);
if (acceptStarIncludes) {
break;
}
}
if (acceptStarIncludes) {
// check '*' rule, which could span over more than one level
if (INCLUDE_ALL.equals(getName(inc))) {
// basic approach to dealing with a '*' include
levelsUp = Math.max(getDepth(inc), levelsUp);
primaryType.add(r.primaryType);
}
}
}
}
if (levelsUp > 0 && !primaryType.isEmpty()) {
List<String> parents = new ArrayList<String>();
levelsUp = Math.min(levelsUp, getDepth(path));
String parentPath = path;
for (int i = 0; i < levelsUp; i++) {
parentPath = getParentPath(parentPath);
if (isNodeType(root, parentPath, primaryType)) {
parents.add(parentPath);
parents.addAll(newArrayList(getParents(root, parentPath,
false)));
return parents.iterator();
}
}
}
return Collections.emptyIterator();
}
private static boolean isNodeType(NodeState root, String path, Set<String> types) {
NodeState state = root;
for (String p : elements(path)) {
if (state.hasChildNode(p)) {
state = state.getChildNode(p);
} else {
return false;
}
}
PropertyState ps = state.getProperty(JCR_PRIMARYTYPE);
if (ps == null) {
return false;
}
return types.contains(ps.getValue(STRING));
}
// ----- builder methods
/**
* Include children with the provided name. '*' means include all children
*
* Note: there is no support for property names yet
*
*/
public SimpleNodeAggregator newRuleWithName(String primaryType,
List<String> includes) {
aggregates.add(new ChildNameRule(primaryType, includes));
return this;
}
// ----- aggregation rules
private static interface Rule {
}
private static class ChildNameRule implements Rule {
private final String primaryType;
private final List<String> includes;
ChildNameRule(String primaryType, List<String> includes) {
this.primaryType = primaryType;
this.includes = includes;
}
}
}