001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.isis.viewer.restfulobjects.applib.util; 020 021import java.util.Collections; 022import java.util.List; 023import java.util.Map; 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026 027import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 028 029import com.google.common.base.Objects; 030import com.google.common.base.Splitter; 031import com.google.common.collect.Lists; 032import com.google.common.collect.Maps; 033 034public class PathNode { 035 private static final Pattern NODE = Pattern.compile("^([^\\[]*)(\\[(.+)\\])?$"); 036 private static final Pattern WHITESPACE = Pattern.compile("\\s+"); 037 private static final Pattern LIST_CRITERIA_SYNTAX = Pattern.compile("^([^=]+)=(.+)$"); 038 039 public static final PathNode NULL = new PathNode("", Collections.<String, String> emptyMap()); 040 041 public static List<String> split(String path) { 042 List<String> parts = Lists.newArrayList(); 043 String curr = null; 044 for (String part : Splitter.on(".").split(path)) { 045 if(curr != null) { 046 if(part.contains("]")) { 047 curr = curr + "." + part; 048 parts.add(curr); 049 curr = null; 050 } else { 051 curr = curr + "." + part; 052 } 053 continue; 054 } 055 if(!part.contains("[")) { 056 parts.add(part); 057 continue; 058 } 059 if(part.contains("]")) { 060 parts.add(part); 061 } else { 062 curr = part; 063 } 064 } 065 return parts; 066 } 067 068 public static PathNode parse(final String path) { 069 final Matcher nodeMatcher = NODE.matcher(path); 070 if (!nodeMatcher.matches()) { 071 return null; 072 } 073 final int groupCount = nodeMatcher.groupCount(); 074 if (groupCount < 1) { 075 return null; 076 } 077 final String key = nodeMatcher.group(1); 078 final Map<String, String> criteria = Maps.newHashMap(); 079 final String criteriaStr = nodeMatcher.group(3); 080 if (criteriaStr != null) { 081 for (final String criterium : Splitter.on(WHITESPACE).split(criteriaStr)) { 082 final Matcher keyValueMatcher = LIST_CRITERIA_SYNTAX.matcher(criterium); 083 if (keyValueMatcher.matches()) { 084 criteria.put(keyValueMatcher.group(1), keyValueMatcher.group(2)); 085 } else { 086 // take content as a map criteria 087 criteria.put(criterium, null); 088 } 089 } 090 } 091 092 return new PathNode(key, criteria); 093 } 094 095 private final String key; 096 private final Map<String, String> criteria; 097 098 private PathNode(final String key, final Map<String, String> criteria) { 099 this.key = key; 100 this.criteria = Collections.unmodifiableMap(criteria); 101 } 102 103 public String getKey() { 104 return key; 105 } 106 107 public Map<String, String> getCriteria() { 108 return criteria; 109 } 110 111 public boolean hasCriteria() { 112 return !getCriteria().isEmpty(); 113 } 114 115 public boolean matches(final JsonRepresentation repr) { 116 if (!repr.isMap()) { 117 return false; 118 } 119 for (final Map.Entry<String, String> criterium : getCriteria().entrySet()) { 120 final String requiredValue = criterium.getValue(); 121 if(requiredValue != null) { 122 // list syntax 123 final String actualValue = repr.getString(criterium.getKey()); 124 if (!Objects.equal(requiredValue, actualValue)) { 125 return false; 126 } 127 } else { 128 // map syntax 129 return repr.getRepresentation(criterium.getKey()) != null; 130 } 131 } 132 return true; 133 } 134 135 136 @Override 137 public int hashCode() { 138 final int prime = 31; 139 int result = 1; 140 result = prime * result + ((key == null) ? 0 : key.hashCode()); 141 return result; 142 } 143 144 @Override 145 public boolean equals(Object obj) { 146 if (this == obj) 147 return true; 148 if (obj == null) 149 return false; 150 if (getClass() != obj.getClass()) 151 return false; 152 PathNode other = (PathNode) obj; 153 if (key == null) { 154 if (other.key != null) 155 return false; 156 } else if (!key.equals(other.key)) 157 return false; 158 return true; 159 } 160 161 162 @Override 163 public String toString() { 164 return key + (criteria.isEmpty() ? "" : criteria); 165 } 166 167}