universe@130: package de.uapcore.lightpit; universe@130: universe@130: import java.util.ArrayList; universe@130: import java.util.List; universe@130: universe@130: public final class PathPattern { universe@130: universe@130: private final List nodePatterns; universe@130: private final boolean collection; universe@130: universe@130: /** universe@130: * Constructs a new path pattern. universe@130: * The special directories . and .. are disallowed in the pattern. universe@130: * universe@130: * @param pattern universe@130: */ universe@130: public PathPattern(String pattern) { universe@130: nodePatterns = parse(pattern); universe@130: collection = pattern.endsWith("/"); universe@130: } universe@130: universe@130: private List parse(String pattern) { universe@130: universe@130: var nodes = new ArrayList(); universe@130: var parts = pattern.split("/"); universe@130: universe@130: for (var part : parts) { universe@130: if (part.isBlank()) continue; universe@130: if (part.equals(".") || part.equals("..")) universe@130: throw new IllegalArgumentException("Path must not contain '.' or '..' nodes."); universe@130: nodes.add(part); universe@130: } universe@130: universe@130: return nodes; universe@130: } universe@130: universe@130: /** universe@130: * Matches a path against this pattern. universe@130: * The path must be canonical in the sense that no . or .. parts occur. universe@130: * universe@130: * @param path the path to match universe@130: * @return true if the path matches the pattern, false otherwise universe@130: */ universe@130: public boolean matches(String path) { universe@130: if (collection ^ path.endsWith("/")) universe@130: return false; universe@130: universe@130: var nodes = parse(path); universe@130: if (nodePatterns.size() != nodes.size()) universe@130: return false; universe@130: universe@130: for (int i = 0 ; i < nodePatterns.size() ; i++) { universe@130: var pattern = nodePatterns.get(i); universe@130: var node = nodes.get(i); universe@130: if (pattern.startsWith("$")) universe@130: continue; universe@130: if (!pattern.equals(node)) universe@130: return false; universe@130: } universe@130: universe@130: return true; universe@130: } universe@130: universe@130: /** universe@130: * Returns the path parameters found in the specified path using this pattern. universe@130: * The return value of this method is undefined, if the patter does not match. universe@130: * universe@130: * @param path the path universe@130: * @return the path parameters, if any, or an empty map universe@130: * @see #matches(String) universe@130: */ universe@130: public PathParameters obtainPathParameters(String path) { universe@130: var params = new PathParameters(); universe@130: universe@130: var nodes = parse(path); universe@130: universe@130: for (int i = 0 ; i < Math.min(nodes.size(), nodePatterns.size()) ; i++) { universe@130: var pattern = nodePatterns.get(i); universe@130: var node = nodes.get(i); universe@130: if (pattern.startsWith("$")) { universe@130: params.put(pattern.substring(1), node); universe@130: } universe@130: } universe@130: universe@130: return params; universe@130: } universe@130: universe@130: @Override universe@130: public int hashCode() { universe@130: var str = new StringBuilder(); universe@130: for (var node : nodePatterns) { universe@130: if (node.startsWith("$")) { universe@130: str.append("/$"); universe@130: } else { universe@130: str.append('/'); universe@130: str.append(node); universe@130: } universe@130: } universe@130: if (collection) universe@130: str.append('/'); universe@130: universe@130: return str.toString().hashCode(); universe@130: } universe@130: universe@130: @Override universe@130: public boolean equals(Object obj) { universe@130: if (!obj.getClass().equals(PathPattern.class)) universe@130: return false; universe@130: universe@130: var other = (PathPattern) obj; universe@130: if (collection ^ other.collection || nodePatterns.size() != other.nodePatterns.size()) universe@130: return false; universe@130: universe@130: for (int i = 0 ; i < nodePatterns.size() ; i++) { universe@130: var left = nodePatterns.get(i); universe@130: var right = other.nodePatterns.get(i); universe@130: if (left.startsWith("$") && right.startsWith("$")) universe@130: continue; universe@130: if (!left.equals(right)) universe@130: return false; universe@130: } universe@130: universe@130: return true; universe@130: } universe@130: }