| /* |
| * 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 + |
| '}'; |
| } |
| } |