/* * 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.isis.viewer.restfulobjects.applib.util; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.base.Objects; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; public class PathNode { private static final Pattern NODE = Pattern.compile("^([^\\[]*)(\\[(.+)\\])?$"); private static final Pattern WHITESPACE = Pattern.compile("\\s+"); private static final Pattern LIST_CRITERIA_SYNTAX = Pattern.compile("^([^=]+)=(.+)$"); public static final PathNode NULL = new PathNode("", Collections.<String, String> emptyMap()); public static List<String> split(String path) { List<String> parts = Lists.newArrayList(); String curr = null; for (String part : Splitter.on(".").split(path)) { if(curr != null) { if(part.contains("]")) { curr = curr + "." + part; parts.add(curr); curr = null; } else { curr = curr + "." + part; } continue; } if(!part.contains("[")) { parts.add(part); continue; } if(part.contains("]")) { parts.add(part); } else { curr = part; } } return parts; } public static PathNode parse(final String path) { final Matcher nodeMatcher = NODE.matcher(path); if (!nodeMatcher.matches()) { return null; } final int groupCount = nodeMatcher.groupCount(); if (groupCount < 1) { return null; } final String key = nodeMatcher.group(1); final Map<String, String> criteria = Maps.newHashMap(); final String criteriaStr = nodeMatcher.group(3); if (criteriaStr != null) { for (final String criterium : Splitter.on(WHITESPACE).split(criteriaStr)) { final Matcher keyValueMatcher = LIST_CRITERIA_SYNTAX.matcher(criterium); if (keyValueMatcher.matches()) { criteria.put(keyValueMatcher.group(1), keyValueMatcher.group(2)); } else { // take content as a map criteria criteria.put(criterium, null); } } } return new PathNode(key, criteria); } private final String key; private final Map<String, String> criteria; private PathNode(final String key, final Map<String, String> criteria) { this.key = key; this.criteria = Collections.unmodifiableMap(criteria); } public String getKey() { return key; } public Map<String, String> getCriteria() { return criteria; } public boolean hasCriteria() { return !getCriteria().isEmpty(); } public boolean matches(final JsonRepresentation repr) { if (!repr.isMap()) { return false; } for (final Map.Entry<String, String> criterium : getCriteria().entrySet()) { String requiredValue = criterium.getValue(); if(requiredValue != null) { // list syntax String actualValue = repr.getString(criterium.getKey()); if(actualValue == null) { return false; } // determine if fuzzy match (ie without additional parameters) // eg [rel=urn:org.restfulobjects:rel/details;action="list"] matches [rel=urn:org.restfulobjects:rel/details] final int actualValueSemiIndex = actualValue.indexOf(";"); final int requiredValueSemiIndex = requiredValue.indexOf(";"); if(actualValueSemiIndex != -1 && requiredValueSemiIndex == -1 ) { actualValue = actualValue.substring(0, actualValueSemiIndex); } if(actualValueSemiIndex == -1 && requiredValueSemiIndex != -1) { requiredValue = requiredValue.substring(0, requiredValueSemiIndex); } if (!Objects.equal(requiredValue, actualValue)) { return false; } } else { // map syntax return repr.getRepresentation(criterium.getKey()) != null; } } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((key == null) ? 0 : key.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PathNode other = (PathNode) obj; if (key == null) { if (other.key != null) return false; } else if (!key.equals(other.key)) return false; return true; } @Override public String toString() { return key + (criteria.isEmpty() ? "" : criteria); } }