blob: 6758fcdd470f26410ddaf521f7b023ddf8ca29ab [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.sling.distribution.serialization;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeSet;
import org.apache.sling.distribution.DistributionRequest;
import org.jetbrains.annotations.NotNull;
/**
* A filter is responsible for storing information about which resources / attributes should be serialized.
*/
public class DistributionExportFilter {
private final Set<TreeFilter> nodeFilters = new HashSet<TreeFilter>();
private TreeFilter propertyFilter;
private DistributionExportFilter() {
// can only be constructed by #createFilter
}
@NotNull
public Set<TreeFilter> getNodeFilters() {
return nodeFilters;
}
@NotNull
public TreeFilter getPropertyFilter() {
return propertyFilter;
}
/**
* create a filter based on a request and global node and property filters
* @param distributionRequest the request
* @param nodeFilters the node level filters
* @param propertyFilters the property level filters
* @return a filter
*/
public static DistributionExportFilter createFilter(DistributionRequest distributionRequest,
NavigableMap<String, List<String>> nodeFilters,
NavigableMap<String, List<String>> propertyFilters) {
DistributionExportFilter exportFilter = new DistributionExportFilter();
for (String path : distributionRequest.getPaths()) {
boolean deep = distributionRequest.isDeep(path);
TreeFilter treeFilter = new TreeFilter(path);
if (deep) {
treeFilter.addDeepInclude(path);
} else {
treeFilter.addInclude(path);
}
List<String> patterns = new LinkedList<String>();
patterns.addAll(Arrays.asList(distributionRequest.getFilters(path)));
initFilter(nodeFilters, treeFilter, patterns);
exportFilter.addNodeFilter(treeFilter);
}
// Set property path filters
TreeFilter propertyFilterSet = new TreeFilter("/");
initFilter(propertyFilters, propertyFilterSet, new ArrayList<String>());
exportFilter.setPropertyFilter(propertyFilterSet);
return exportFilter;
}
private void addNodeFilter(TreeFilter filter) {
nodeFilters.add(filter);
}
private static void initFilter(NavigableMap<String, List<String>> globalFilters, TreeFilter treeFilter, List<String> patterns) {
// add the most specific filter rules
for (String key : globalFilters.descendingKeySet()) {
if (treeFilter.getPath().startsWith(key)) {
patterns.addAll(globalFilters.get(key));
break;
}
}
for (String pattern : patterns) {
TreeFilter.Entry entry = extractPathPattern(pattern);
if (entry.isInclude()) {
treeFilter.addInclude(entry.getPath());
} else {
treeFilter.addExclude(entry.getPath());
}
}
}
private static TreeFilter.Entry extractPathPattern(String pattern) {
TreeFilter.Entry result;
if (pattern.startsWith("+")) {
result = new TreeFilter.Entry(pattern.substring(1), true);
} else if (pattern.startsWith("-")) {
result = new TreeFilter.Entry(pattern.substring(1), false);
} else {
result = new TreeFilter.Entry(pattern, true);
}
return result;
}
private void setPropertyFilter(TreeFilter propertyFilter) {
this.propertyFilter = propertyFilter;
}
/**
* a filter is responsible for finding the resources that should be serialized unders a certain path
*/
public static class TreeFilter {
private final String path;
private final Collection<String> includes;
private final Collection<String> excludes;
private final Collection<String> deepIncludes;
public TreeFilter(String path) {
this.path = path;
this.includes = new TreeSet<String>();
this.deepIncludes = new TreeSet<String>();
this.excludes = new TreeSet<String>();
}
public void addInclude(String path) {
includes.add(path);
}
public void addDeepInclude(String path) {
deepIncludes.add(path);
}
public void addExclude(String path) {
excludes.add(path);
}
@NotNull
public String getPath() {
return path;
}
/**
* check whether a resource with a certain path should be included in the serialized output
* @param path a path
* @return {@code true} if the path mathces the filter, {@code false} otherwise
*/
public boolean matches(String path) {
boolean match = (includes.isEmpty() && excludes.isEmpty()) || includes.contains(path);
if (!match) {
for (String di : deepIncludes) {
match = path.startsWith(di);
if (match) {
break;
}
}
}
match &= !excludes.contains(path);
return match;
}
@Override
public String toString() {
return "TreeFilter{" +
"path='" + path + '\'' +
", includes=" + includes +
", excludes=" + excludes +
", deepIncludes=" + deepIncludes +
'}';
}
private static class Entry {
private final String path;
private final boolean include;
public Entry(String path, boolean include) {
this.path = path;
this.include = include;
}
public boolean isInclude() {
return include;
}
public String getPath() {
return path;
}
}
}
@Override
public String toString() {
return "DistributionExportFilter{" +
"nodeFilters=" + nodeFilters +
", propertyFilter=" + propertyFilter +
'}';
}
}