blob: f2f5b8a0850ba4fda9d44bc211727deff504745b [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.installer.provider.jcr.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Defines which folders are watched by JcrInstaller, based on
* their names. To be accepted, a folder must have a name that
* matches the expression, followed by optional suffixes based
* on the current RunMode.
*
* See {@link org.apache.sling.installer.provider.jcr.impl.FolderNameFilterTest} for details.
*/
class FolderNameFilter {
private final Pattern pattern;
private final String regexp;
private final Set<String> runModes;
private final String [] rootPaths;
private final Map<String, Integer> rootPriorities = new HashMap<String, Integer>();
private final Logger log = LoggerFactory.getLogger(getClass());
/** getPriority computes priorities as follows: each root gets its own base priority,
* and paths that match one or several run levels get an additional RUNMODE_PRIORITY_OFFSET
* per matched run level
*/
public static final int RUNMODE_PRIORITY_BOOST = 1;
public static final int DEFAULT_ROOT_PRIORITY = 99;
FolderNameFilter(final String [] rootsConfig, final String regexp, final Set<String> runModes) {
final List<RootPathInfo> rootPathInfos = new ArrayList<RootPathInfo>();
this.regexp = regexp;
this.pattern = Pattern.compile(regexp);
this.runModes = runModes;
// Each entry in rootsConfig is like /libs:100, where 100
// is the priority.
// Break that up into paths and priorities
this.rootPaths = new String[rootsConfig.length];
for(int i = 0; i < rootsConfig.length; i++) {
final String [] parts = rootsConfig[i].split(":");
this.rootPaths[i] = cleanupRootPath(parts[0]);
Integer priority = new Integer(DEFAULT_ROOT_PRIORITY);
if(parts.length > 1) {
try {
priority = Integer.parseInt(parts[1].trim());
} catch(NumberFormatException nfe) {
log.warn("Invalid priority in path definition '{}'", rootsConfig[i]);
}
}
rootPriorities.put(this.rootPaths[i], priority);
rootPathInfos.add(new RootPathInfo(this.rootPaths[i], priority));
log.debug("Root path {} has priority {}", this.rootPaths[i], priority);
}
// sort root paths by priority
Collections.sort(rootPathInfos);
int index = 0;
for(final RootPathInfo rpi : rootPathInfos) {
this.rootPaths[index++] = rpi.path;
}
}
private static final class RootPathInfo implements Comparable<RootPathInfo> {
public final String path;
private final Integer priority;
public RootPathInfo(final String path, final Integer prio) {
this.path = path;
this.priority = prio;
}
public int compareTo(RootPathInfo o) {
int result = -this.priority.compareTo(o.priority);
if ( result == 0 ) {
result = this.path.compareTo(o.path);
}
return result;
}
}
/**
* Return the list of root paths.
* Every entry in the list starts with a slash and the entries are
* sorted by priority - highest priority first
*/
String [] getRootPaths() {
return rootPaths;
}
static String cleanupRootPath(final String str) {
String result = str.trim();
if (!result.startsWith("/")) {
result = "/" + result;
}
if (result.endsWith("/")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
/** If a folder at given path can contain installable resources
* (according to our regexp and current RunMode), return the
* priority to use for InstallableResource found in that folder.
*
* @return -1 if path is not an installable folder, else resource priority
*/
int getPriority(final String path) {
int result = 0;
List<String> modes = null;
boolean match = false;
// If path contains dots after the last /, remove suffixes
// starting with dots until path matches regexp, and accept
// if all suffixes
// are included in our list of runmodes
final char DOT = '.';
String prefix = path;
final int lastSlash = prefix.lastIndexOf('/');
if(lastSlash > 0) {
prefix = prefix.substring(lastSlash);
}
if(prefix.indexOf(DOT) > 0) {
int pos = 0;
modes = new LinkedList<String>();
while( (pos = prefix.lastIndexOf(DOT)) >= 0) {
modes.add(prefix.substring(pos + 1));
prefix = prefix.substring(0, pos);
if(pattern.matcher(prefix).matches()) {
result = getRootPriority(path);
break;
}
}
// If path prefix matches, check that all our runmodes match
if(result > 0) {
for(String m : modes) {
if(runModes.contains(m)) {
result += RUNMODE_PRIORITY_BOOST;
} else {
result = 0;
break;
}
}
}
} else if(pattern.matcher(path).matches()) {
match = true;
result = getRootPriority(path);
}
if(modes != null) {
if(log.isDebugEnabled()) {
log.debug("getPriority(" + path + ")=" + result + " (prefix=" + prefix + ", run modes=" + modes + ")");
}
} else if(match ){
log.debug("getPriority({})={}", path, result);
} else {
log.debug("getPriority({})={}, path doesn't match regexp", path, result);
}
return result;
}
public String toString() {
return getClass().getSimpleName() + " (" + regexp + "), RunModes=" + runModes;
}
int getRootPriority(String path) {
for(Map.Entry<String, Integer> e : rootPriorities.entrySet()) {
if(path.startsWith(e.getKey())) {
return e.getValue().intValue();
}
}
return 0;
}
}