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