org.modeshape.jcr.sequencer.SequencerPathExpression Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
*
* 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.modeshape.jcr.sequencer;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.common.util.CheckArg;
import org.modeshape.common.util.HashCode;
import org.modeshape.common.util.ObjectUtil;
import org.modeshape.jcr.RepositoryI18n;
import org.modeshape.jcr.sequencer.PathExpression.WorkspacePath;
/**
* An expression that defines a selection of some change in the repository that signals a sequencing operation should be run, and
* the location where the sequencing output should be placed. Sequencer path expressions are used to determine whether information
* in the repository needs to be sequenced.
*
* A simple example is the following:
*
*
* /a/b/c@title => /d/e/f
*
*
* which means that a sequencer (that uses this expression in its configuration) should be run any time there is a new or modified
* title
property on the /a/b/c
node, and that the output of the sequencing should be placed at
* /d/e/f
.
*
*/
@Immutable
public class SequencerPathExpression implements Serializable {
/**
*/
private static final long serialVersionUID = 229464314137494765L;
/**
* The pattern used to break the initial input string into the two major parts, the selection and output expressions. Group 1
* contains the selection expression, and group 2 contains the output expression.
*/
private static final Pattern TWO_PART_PATTERN = Pattern.compile("((?:[^=]|=(?!>))+)(?:=>(.+))?");
protected static final String DEFAULT_OUTPUT_EXPRESSION = ".";
private static final String PARENT_PATTERN_STRING = "[^/]+/\\.\\./"; // [^/]+/\.\./
private static final Pattern PARENT_PATTERN = Pattern.compile(PARENT_PATTERN_STRING);
private static final String REPLACEMENT_VARIABLE_PATTERN_STRING = "(?" + this.outputExpression;
}
/**
* Determine if this path expression applies to content within the supplied workspace name.
*
* @param workspaceName the name of the workspace; may not be null
* @return true if this path expression matches the workspace name, or false otherwise
*/
public final boolean appliesToWorkspace( String workspaceName ) {
return selectExpression.matchesWorkspace(workspaceName);
}
/**
* Obtain a Matcher that can be used to convert the supplied workspace key and absolute path into an output workspace name and
* and output path.
*
* @param absolutePath the path in the workspace; may not be null
* @return the matcher; never null
*/
public Matcher matcher( String absolutePath ) {
PathExpression.Matcher inputMatcher = selectExpression.matcher(absolutePath);
String outputPath = null;
WorkspacePath wsPath = null;
if (inputMatcher.matches()) {
// Grab the named groups ...
Map replacements = new HashMap();
for (int i = 0, count = inputMatcher.groupCount(); i <= count; ++i) {
replacements.put(i, inputMatcher.group(i));
}
// Grab the selected path ...
String selectedPath = inputMatcher.getSelectedNodePath();
// Find the output path using the groups from the match pattern ...
wsPath = PathExpression.parsePathInWorkspace(this.outputExpression);
if (wsPath != null) {
if (wsPath.workspaceName == null) wsPath = wsPath.withWorkspaceName(inputMatcher.getSelectedWorkspaceName());
outputPath = wsPath.path;
if (!DEFAULT_OUTPUT_EXPRESSION.equals(outputPath)) {
java.util.regex.Matcher replacementMatcher = REPLACEMENT_VARIABLE_PATTERN.matcher(outputPath);
// CHECKSTYLE IGNORE check FOR NEXT 1 LINES
StringBuffer sb = new StringBuffer();
if (replacementMatcher.find()) {
do {
String variable = replacementMatcher.group(1);
String replacement = replacements.get(Integer.valueOf(variable));
if (replacement == null) replacement = replacementMatcher.group(0);
replacementMatcher.appendReplacement(sb, replacement);
} while (replacementMatcher.find());
replacementMatcher.appendTail(sb);
outputPath = sb.toString();
}
// Make sure there is a trailing '/' ...
if (!outputPath.endsWith("/")) outputPath = outputPath + "/";
// Replace all references to "/./" with "/" ...
outputPath = outputPath.replaceAll("/\\./", "/");
// Remove any path segment followed by a parent reference ...
java.util.regex.Matcher parentMatcher = PARENT_PATTERN.matcher(outputPath);
while (parentMatcher.find()) {
outputPath = parentMatcher.replaceAll("");
// Make sure there is a trailing '/' ...
if (!outputPath.endsWith("/")) outputPath = outputPath + "/";
parentMatcher = PARENT_PATTERN.matcher(outputPath);
}
// Remove all multiple occurrences of '/' ...
outputPath = outputPath.replaceAll("/{2,}", "/");
// Remove the trailing '/@property' ...
outputPath = outputPath.replaceAll("/@[^/\\[\\]]+$", "");
// Remove a trailing '/' ...
outputPath = outputPath.replaceAll("/$", "");
// If the output path is blank, then use the default output expression ...
if (outputPath.length() == 0) outputPath = DEFAULT_OUTPUT_EXPRESSION;
}
if (DEFAULT_OUTPUT_EXPRESSION.equals(outputPath)) {
// The output path is the default expression, so use the selected path ...
outputPath = selectedPath;
}
wsPath = wsPath.withPath(outputPath);
}
}
return new Matcher(inputMatcher, wsPath);
}
@Immutable
public static class Matcher {
private final PathExpression.Matcher inputMatcher;
private final WorkspacePath outputPath;
private final int hc;
protected Matcher( PathExpression.Matcher inputMatcher,
WorkspacePath outputPath ) {
this.inputMatcher = inputMatcher;
this.outputPath = outputPath;
this.hc = HashCode.compute(super.hashCode(), this.outputPath);
}
public boolean matches() {
return inputMatcher.matches() && this.outputPath != null;
}
/**
* @return inputPath
*/
public String getInputPath() {
return inputMatcher.getInputPath();
}
/**
* Get the {@link #getInputPath() input path} in standard JCR form.
*
* @return the JCR form of the input path; null only if {@link #getInputPath()} is null
*/
public String getJcrInputPath() {
// Conver the input path (which has a '@' to denote a property) to a standard JCR path ...
String inputPath = getInputPath();
if (inputPath == null) return null;
int index = inputPath.lastIndexOf("/@");
if (index != -1) {
inputPath = inputPath.substring(0, index) + '/' + inputPath.substring(index + 2);
}
return inputPath;
}
/**
* @return selectPattern
*/
public String getSelectedPath() {
return inputMatcher.getSelectedNodePath();
}
/**
* Get the path in the repository where the sequenced content should be placed.
*
* @return outputPath the output path, or null if this matcher does not match the input
*/
public String getOutputPath() {
return this.outputPath != null ? this.outputPath.path : null;
}
/**
* Get the name of the workspace where the sequenced content should be placed.
*
* @return outputPath the output path, or null if this matcher does not match the input
*/
public String getOutputWorkspaceName() {
return this.outputPath != null ? this.outputPath.workspaceName : null;
}
@Override
public int hashCode() {
return this.hc;
}
@Override
public boolean equals( Object obj ) {
if (obj == this) return true;
if (obj instanceof SequencerPathExpression.Matcher) {
SequencerPathExpression.Matcher that = (SequencerPathExpression.Matcher)obj;
if (!super.equals(that)) return false;
return ObjectUtil.isEqualWithNulls(this.outputPath, that.outputPath);
}
return false;
}
@Override
public String toString() {
return inputMatcher + " => " + this.outputPath;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy