/* * 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.rendering; import java.util.Collections; import java.util.List; import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; import org.apache.isis.viewer.restfulobjects.applib.util.PathNode; import org.apache.isis.viewer.restfulobjects.rendering.util.FollowSpecUtil; import com.google.common.collect.Lists; public final class LinkFollowSpecs { public final static LinkFollowSpecs create(final List<List<String>> links) { final List<List<PathNode>> specs = FollowSpecUtil.asFollowSpecs(links); return new LinkFollowSpecs(specs, Mode.FOLLOWING, null); } private enum Mode { FOLLOWING, TERMINATED; } private final List<List<PathNode>> pathSpecs; private final Mode mode; // don't care about the key, just the criteria private final List<PathNode> criteriaSpecs; private LinkFollowSpecs(final List<List<PathNode>> pathSpecs, final Mode mode, final List<PathNode> criteriaSpecs) { this.pathSpecs = pathSpecs; this.mode = mode; this.criteriaSpecs = criteriaSpecs; } /** * A little algebra... */ public LinkFollowSpecs follow(final String pathTemplate, final Object... args) { final String path = String.format(pathTemplate, args); if (path == null) { return terminated(); } if (mode == Mode.TERMINATED) { return terminated(); } final PathNode candidate = PathNode.parse(path); if (mode == Mode.FOLLOWING) { List<List<PathNode>> remainingPathSpecs = Lists.newArrayList(); List<PathNode> firstSpecs = Lists.newArrayList(); for(List<PathNode> spec: pathSpecs) { if(spec.isEmpty()) { continue; } PathNode first = spec.get(0); if(candidate.equals(first)) { List<PathNode> remaining = spec.subList(1, spec.size()); firstSpecs.add(first); remainingPathSpecs.add(remaining); } } if(!remainingPathSpecs.isEmpty()) { return new LinkFollowSpecs(remainingPathSpecs, Mode.FOLLOWING, firstSpecs); } return terminated(); } return terminated(); } private static LinkFollowSpecs terminated() { return new LinkFollowSpecs(Collections.<List<PathNode>>emptyList(), Mode.TERMINATED, Collections.<PathNode>emptyList()); } /** * Not public API; use {@link #matches(JsonRepresentation)}. */ boolean isFollowing() { return mode == Mode.FOLLOWING; } public boolean isTerminated() { return mode == Mode.TERMINATED; } /** * Ensure that every key present in the provided map matches the criterium. * * <p> * Any keys in the criterium are ignored (these were matched on during the * {@link #follow(String, Object...)} call). */ public boolean matches(final JsonRepresentation jsonRepr) { if (!isFollowing()) { return false; } if(criteriaSpecs == null) { return true; } for (PathNode criteriaSpec : criteriaSpecs) { if(criteriaSpec.matches(jsonRepr)) { return true; } } return false; } @Override public String toString() { return mode + " : " + criteriaSpecs + " : " + pathSpecs; } }