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 */
019 package org.apache.isis.viewer.restfulobjects.applib.util;
020
021 import java.util.Collections;
022 import java.util.Map;
023 import java.util.regex.Matcher;
024 import java.util.regex.Pattern;
025
026 import com.google.common.base.Objects;
027 import com.google.common.base.Splitter;
028 import com.google.common.collect.Maps;
029
030 import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
031
032 public class PathNode {
033 private static final Pattern NODE = Pattern.compile("^([^\\[]*)(\\[(.+)\\])?$");
034 private static final Pattern WHITESPACE = Pattern.compile("\\s+");
035 private static final Pattern KEY_VALUE = Pattern.compile("^([^=]+)=(.+)$");
036
037 public static final PathNode NULL = new PathNode("", Collections.<String, String> emptyMap());
038
039 public static PathNode parse(final String path) {
040 final Matcher nodeMatcher = NODE.matcher(path);
041 if (!nodeMatcher.matches()) {
042 return null;
043 }
044 final int groupCount = nodeMatcher.groupCount();
045 if (groupCount < 1) {
046 return null;
047 }
048 final String key = nodeMatcher.group(1);
049 final Map<String, String> criteria = Maps.newHashMap();
050 final String criteriaStr = nodeMatcher.group(3);
051 if (criteriaStr != null) {
052 for (final String criterium : Splitter.on(WHITESPACE).split(criteriaStr)) {
053 final Matcher keyValueMatcher = KEY_VALUE.matcher(criterium);
054 if (keyValueMatcher.matches()) {
055 criteria.put(keyValueMatcher.group(1), keyValueMatcher.group(2));
056 }
057 }
058 }
059
060 return new PathNode(key, criteria);
061 }
062
063 private final String key;
064 private final Map<String, String> criteria;
065
066 private PathNode(final String key, final Map<String, String> criteria) {
067 this.key = key;
068 this.criteria = Collections.unmodifiableMap(criteria);
069 }
070
071 public String getKey() {
072 return key;
073 }
074
075 public Map<String, String> getCriteria() {
076 return criteria;
077 }
078
079 public boolean hasCriteria() {
080 return !getCriteria().isEmpty();
081 }
082
083 public boolean matches(final JsonRepresentation repr) {
084 if (!repr.isMap()) {
085 return false;
086 }
087 for (final Map.Entry<String, String> criterium : getCriteria().entrySet()) {
088 final String requiredValue = criterium.getValue();
089 final String actualValue = repr.getString(criterium.getKey());
090 if (!Objects.equal(requiredValue, actualValue)) {
091 return false;
092 }
093 }
094 return true;
095 }
096
097 @Override
098 public int hashCode() {
099 final int prime = 31;
100 int result = 1;
101 result = prime * result + ((key == null) ? 0 : key.hashCode());
102 return result;
103 }
104
105 @Override
106 public boolean equals(final Object obj) {
107 if (this == obj) {
108 return true;
109 }
110 if (obj == null) {
111 return false;
112 }
113 if (getClass() != obj.getClass()) {
114 return false;
115 }
116 final PathNode other = (PathNode) obj;
117 if (key == null) {
118 if (other.key != null) {
119 return false;
120 }
121 } else if (!key.equals(other.key)) {
122 return false;
123 }
124 return true;
125 }
126
127 @Override
128 public String toString() {
129 return key + (criteria.isEmpty() ? "" : criteria);
130 }
131
132 }