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.rendering; 020 021import java.util.Collections; 022import java.util.List; 023 024import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 025import org.apache.isis.viewer.restfulobjects.applib.util.PathNode; 026import org.apache.isis.viewer.restfulobjects.rendering.util.FollowSpecUtil; 027 028import com.google.common.collect.Lists; 029 030public final class LinkFollowSpecs { 031 032 public final static LinkFollowSpecs create(final List<List<String>> links) { 033 final List<List<PathNode>> specs = FollowSpecUtil.asFollowSpecs(links); 034 return new LinkFollowSpecs(specs, Mode.FOLLOWING, null); 035 } 036 037 private enum Mode { 038 FOLLOWING, TERMINATED; 039 } 040 041 private final List<List<PathNode>> pathSpecs; 042 private final Mode mode; 043 // don't care about the key, just the criteria 044 private final List<PathNode> criteriaSpecs; 045 046 private LinkFollowSpecs(final List<List<PathNode>> pathSpecs, final Mode mode, final List<PathNode> criteriaSpecs) { 047 this.pathSpecs = pathSpecs; 048 this.mode = mode; 049 this.criteriaSpecs = criteriaSpecs; 050 } 051 052 /** 053 * A little algebra... 054 */ 055 public LinkFollowSpecs follow(final String pathTemplate, final Object... args) { 056 final String path = String.format(pathTemplate, args); 057 if (path == null) { 058 return terminated(); 059 } 060 if (mode == Mode.TERMINATED) { 061 return terminated(); 062 } 063 final PathNode candidate = PathNode.parse(path); 064 if (mode == Mode.FOLLOWING) { 065 List<List<PathNode>> remainingPathSpecs = Lists.newArrayList(); 066 List<PathNode> firstSpecs = Lists.newArrayList(); 067 for(List<PathNode> spec: pathSpecs) { 068 if(spec.isEmpty()) { 069 continue; 070 } 071 PathNode first = spec.get(0); 072 if(candidate.equals(first)) { 073 List<PathNode> remaining = spec.subList(1, spec.size()); 074 firstSpecs.add(first); 075 remainingPathSpecs.add(remaining); 076 } 077 } 078 if(!remainingPathSpecs.isEmpty()) { 079 return new LinkFollowSpecs(remainingPathSpecs, Mode.FOLLOWING, firstSpecs); 080 } 081 return terminated(); 082 } 083 return terminated(); 084 } 085 086 private static LinkFollowSpecs terminated() { 087 return new LinkFollowSpecs(Collections.<List<PathNode>>emptyList(), Mode.TERMINATED, Collections.<PathNode>emptyList()); 088 } 089 090 /** 091 * Not public API; use {@link #matches(JsonRepresentation)}. 092 */ 093 boolean isFollowing() { 094 return mode == Mode.FOLLOWING; 095 } 096 097 public boolean isTerminated() { 098 return mode == Mode.TERMINATED; 099 } 100 101 /** 102 * Ensure that every key present in the provided map matches the criterium. 103 * 104 * <p> 105 * Any keys in the criterium are ignored (these were matched on during the 106 * {@link #follow(String, Object...)} call). 107 */ 108 public boolean matches(final JsonRepresentation jsonRepr) { 109 if (!isFollowing()) { 110 return false; 111 } 112 if(criteriaSpecs == null) { 113 return true; 114 } 115 for (PathNode criteriaSpec : criteriaSpecs) { 116 if(criteriaSpec.matches(jsonRepr)) { 117 return true; 118 } 119 } 120 return false; 121 } 122 123 @Override 124 public String toString() { 125 return mode + " : " + criteriaSpecs + " : " + pathSpecs; 126 } 127 128}