/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.gatein.api.navigation;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.WeakHashMap;
import org.exoplatform.portal.mop.navigation.NodeContext;
import org.gatein.api.Portal;
import org.gatein.api.PortalRequest;
import org.gatein.api.common.Filter;
import org.gatein.api.internal.Parameters;
import org.gatein.api.page.Page;
import org.gatein.api.security.Permission;
import org.gatein.api.security.User;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ApiFilteredNode extends ApiNode implements FilteredNode {
private final FilteredNodeMap map;
@SuppressWarnings("unchecked")
public ApiFilteredNode(NavigationImpl navigation, NodeContext<ApiNode> context) {
super(navigation, context);
this.map = null;
}
private ApiFilteredNode(NavigationImpl navigation, NodeContext<ApiNode> context, FilteredNodeMap map) {
super(navigation, context);
this.map = map;
}
@Override
public Node addChild(int index, String childName) {
return getFiltered(super.addChild(realIndex(index), childName));
}
@Override
public Node addChild(String childName) {
return getFiltered(super.addChild(childName));
}
@Override
public Node getChild(int index) {
int i = 0;
for (Iterator<Node> itr = iterator(); itr.hasNext();) {
if (i == index) {
return getFiltered(itr.next());
}
i++;
itr.next();
}
throw new IndexOutOfBoundsException();
}
@Override
public Node getChild(String childName) {
Node n = super.getChild(childName);
return isAccepted(n) ? getFiltered(n) : null;
}
@Override
public int getChildCount() {
int i = 0;
for (Iterator<Node> itr = iterator(); itr.hasNext(); itr.next()) {
i++;
}
return i;
}
@Override
public Node getNode(NodePath nodePath) {
Node n = this;
for (String e : nodePath) {
n = n.getChild(e);
if (n == null) {
return null;
}
}
return n;
}
@Override
public Node getParent() {
return super.getParent() != null ? getFiltered(super.getParent()) : null;
}
@Override
public boolean hasChild(String childName) {
Node n = super.getChild(childName);
return n != null ? isAccepted(n) : false;
}
@Override
public int indexOf(String childName) {
int i = 0;
for (Iterator<Node> itr = iterator(); itr.hasNext();) {
if (itr.next().getName().equals(childName)) {
return i;
}
i++;
}
return -1;
}
@Override
public Iterator<Node> iterator() {
return new FilteredNodeIterator();
}
@Override
public void moveTo(int index) {
super.moveTo(getFiltered(super.getParent()).realIndex(index));
}
@Override
public void moveTo(int index, Node parent) {
super.moveTo(getFiltered(parent).realIndex(index), parent);
}
@Override
@SuppressWarnings("unchecked")
public FilteredNode showAll() {
return new ApiFilteredNode(navigation, context, new FilteredNodeMap(Collections.EMPTY_LIST));
}
@Override
public FilteredNode showDefault() {
return showVisible().showHasAccess(PortalRequest.getInstance().getUser());
}
@Override
public FilteredNode showHasAccess(User user) {
return show(new PermissionFilter(user, true));
}
public FilteredNode showHasEdit(User user) {
return show(new PermissionFilter(user, false));
}
@Override
public FilteredNode showVisible() {
return show(new VisibleFilter());
}
@Override
public FilteredNode show(Filter<Node> filter) {
List<Filter<Node>> filters = new LinkedList<Filter<Node>>();
if (map != null) {
filters.addAll(map.filters);
}
filters.add(Parameters.requireNonNull(filter, "filter"));
return new ApiFilteredNode(navigation, context, new FilteredNodeMap(filters));
}
private int realIndex(int index) {
int i = 0;
int j = 0;
Iterator<Node> itr = super.iterator();
while (itr.hasNext() && j <= index) {
if (isAccepted(itr.next())) {
j++;
}
i++;
}
if (j < index) {
throw new IndexOutOfBoundsException();
}
return j > index ? i - 1 : i;
}
private class FilteredNodeIterator implements Iterator<Node> {
private Iterator<ApiNode> itr = context.iterator();
private ApiNode last;
private ApiNode next = findNext();
@Override
public boolean hasNext() {
return next != null;
}
@Override
public Node next() {
if (next == null) {
throw new NoSuchElementException();
}
last = next;
next = findNext();
return last;
}
@Override
public void remove() {
if (last == null) {
throw new IllegalStateException();
}
last.context.remove();
}
private ApiNode findNext() {
while (itr.hasNext()) {
ApiNode n = itr.next();
if (isAccepted(n)) {
return getFiltered(n);
}
}
return null;
}
}
private ApiFilteredNode getFiltered(Node node) {
ApiNode apiNode = (ApiNode) node;
if (map == null) {
return new ApiFilteredNode(apiNode.navigation, apiNode.context);
} else if (map.filteredMap.containsKey(apiNode)) {
return map.filteredMap.get(apiNode);
} else {
ApiFilteredNode filteredNode = new ApiFilteredNode(apiNode.navigation, apiNode.context, map);
map.filteredMap.put(apiNode, filteredNode);
return filteredNode;
}
}
private boolean isAccepted(Node node) {
ApiNode apiNode = (ApiNode) node;
if (map == null) {
return true;
} else if (map.acceptedMap.containsKey(apiNode)) {
return map.acceptedMap.get(apiNode);
} else {
Boolean accepted = true;
for (Filter<Node> f : map.filters) {
accepted = f.accept(node);
if (!accepted) {
break;
}
}
map.acceptedMap.put(apiNode, accepted);
return accepted;
}
}
private static class FilteredNodeMap implements Serializable {
private transient Map<ApiNode, Boolean> acceptedMap = new WeakHashMap<ApiNode, Boolean>();
private List<Filter<Node>> filters;
private transient Map<ApiNode, ApiFilteredNode> filteredMap = new WeakHashMap<ApiNode, ApiFilteredNode>();
private FilteredNodeMap(List<Filter<Node>> filters) {
this.filters = filters;
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
acceptedMap = new WeakHashMap<ApiNode, Boolean>();
filteredMap = new WeakHashMap<ApiNode, ApiFilteredNode>();
}
}
private static class VisibleFilter implements Filter<Node> {
@Override
public boolean accept(Node element) {
return element.isVisible();
}
}
private static class PermissionFilter implements Filter<Node> {
private User user;
private boolean access;
public PermissionFilter(User user, boolean access) {
this.user = user;
this.access = access;
}
@Override
public boolean accept(Node element) {
Portal portal = PortalRequest.getInstance().getPortal();
if (element.getPageId() == null) {
return true;
}
Page page = portal.getPage(element.getPageId());
Permission permission = access ? page.getAccessPermission() : page.getEditPermission();
return portal.hasPermission(user, permission);
}
}
}