/*
* Copyright (C) 2011 Google Inc.
*
* Licensed 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.ros.internal.node.server;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import org.apache.commons.logging.Log;
import org.ros.internal.node.client.SlaveClient;
import org.ros.log.RosLogFactory;
import org.ros.namespace.GraphName;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* A ROS parameter server.
*
* @author damonkohler@google.com (Damon Kohler)
*/
public class ParameterServer {
private static final Log log = RosLogFactory.getLog(ParameterServer.class);
private final Map<String, Object> tree;
private final Multimap<GraphName, NodeIdentifier> subscribers;
private final GraphName masterName;
public ParameterServer() {
tree = Maps.newConcurrentMap();
subscribers = Multimaps.synchronizedMultimap(HashMultimap.<GraphName, NodeIdentifier>create());
masterName = GraphName.of("/master");
}
public void subscribe(GraphName name, NodeIdentifier nodeIdentifier) {
subscribers.put(name, nodeIdentifier);
}
private Stack<String> getGraphNameParts(GraphName name) {
Stack<String> parts = new Stack<String>();
GraphName tip = name;
while (!tip.isRoot()) {
parts.add(tip.getBasename().toString());
tip = tip.getParent();
}
return parts;
}
@SuppressWarnings("unchecked")
public Object get(GraphName name) {
Preconditions.checkArgument(name.isGlobal());
Stack<String> parts = getGraphNameParts(name);
Object possibleSubtree = tree;
while (!parts.empty() && possibleSubtree != null) {
if (!(possibleSubtree instanceof Map)) {
return null;
}
possibleSubtree = ((Map<String, Object>) possibleSubtree).get(parts.pop());
}
return possibleSubtree;
}
@SuppressWarnings("unchecked")
private void setValue(GraphName name, Object value) {
Preconditions.checkArgument(name.isGlobal());
Stack<String> parts = getGraphNameParts(name);
Map<String, Object> subtree = tree;
while (!parts.empty()) {
String part = parts.pop();
if (parts.empty()) {
subtree.put(part, value);
} else if (subtree.containsKey(part) && subtree.get(part) instanceof Map) {
subtree = (Map<String, Object>) subtree.get(part);
} else {
Map<String, Object> newSubtree = Maps.newHashMap();
subtree.put(part, newSubtree);
subtree = newSubtree;
}
}
}
private interface Updater {
void update(SlaveClient client);
}
private <T> void update(GraphName name, T value, Updater updater) {
setValue(name, value);
synchronized (subscribers) {
for (NodeIdentifier nodeIdentifier : subscribers.get(name)) {
SlaveClient client = new SlaveClient(masterName, nodeIdentifier.getUri());
try {
updater.update(client);
} catch (Exception e) {
log.error(e);
}
}
}
}
public void set(final GraphName name, final boolean value) {
update(name, value, new Updater() {
@Override
public void update(SlaveClient client) {
client.paramUpdate(name, value);
}
});
}
public void set(final GraphName name, final int value) {
update(name, value, new Updater() {
@Override
public void update(SlaveClient client) {
client.paramUpdate(name, value);
}
});
}
public void set(final GraphName name, final double value) {
update(name, value, new Updater() {
@Override
public void update(SlaveClient client) {
client.paramUpdate(name, value);
}
});
}
public void set(final GraphName name, final String value) {
update(name, value, new Updater() {
@Override
public void update(SlaveClient client) {
client.paramUpdate(name, value);
}
});
}
public void set(final GraphName name, final List<?> value) {
update(name, value, new Updater() {
@Override
public void update(SlaveClient client) {
client.paramUpdate(name, value);
}
});
}
public void set(final GraphName name, final Map<?, ?> value) {
update(name, value, new Updater() {
@Override
public void update(SlaveClient client) {
client.paramUpdate(name, value);
}
});
}
@SuppressWarnings("unchecked")
public void delete(GraphName name) {
Preconditions.checkArgument(name.isGlobal());
Stack<String> parts = getGraphNameParts(name);
Map<String, Object> subtree = tree;
while (!parts.empty() && subtree.containsKey(parts.peek())) {
String part = parts.pop();
if (parts.empty()) {
subtree.remove(part);
} else {
subtree = (Map<String, Object>) subtree.get(part);
}
}
}
public Object search(GraphName name) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
public boolean has(GraphName name) {
Preconditions.checkArgument(name.isGlobal());
Stack<String> parts = getGraphNameParts(name);
Map<String, Object> subtree = tree;
while (!parts.empty() && subtree.containsKey(parts.peek())) {
String part = parts.pop();
if (!parts.empty()) {
subtree = (Map<String, Object>) subtree.get(part);
}
}
return parts.empty();
}
@SuppressWarnings("unchecked")
private Set<GraphName> getSubtreeNames(GraphName parent, Map<String, Object> subtree,
Set<GraphName> names) {
for (String name : subtree.keySet()) {
Object possibleSubtree = subtree.get(name);
if (possibleSubtree instanceof Map) {
names.addAll(getSubtreeNames(parent.join(GraphName.of(name)),
(Map<String, Object>) possibleSubtree, names));
} else {
names.add(parent.join(GraphName.of(name)));
}
}
return names;
}
public Collection<GraphName> getNames() {
Set<GraphName> names = Sets.newHashSet();
return getSubtreeNames(GraphName.root(), tree, names);
}
}