00001 /* 00002 * Copyright (C) 2011 Google Inc. 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 00005 * use this file except in compliance with the License. You may obtain a copy of 00006 * the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 00012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 00013 * License for the specific language governing permissions and limitations under 00014 * the License. 00015 */ 00016 00017 package org.ros.namespace; 00018 00019 import com.google.common.annotations.VisibleForTesting; 00020 import com.google.common.base.Preconditions; 00021 00022 import org.ros.exception.RosRuntimeException; 00023 00024 import java.util.concurrent.atomic.AtomicInteger; 00025 import java.util.regex.Pattern; 00026 00034 public class GraphName { 00035 00036 @VisibleForTesting 00037 static final String ANONYMOUS_PREFIX = "anonymous_"; 00038 00039 private static final String ROOT = "/"; 00040 private static final String SEPARATOR = "/"; 00041 00042 // TODO(damonkohler): Why make empty names valid? 00048 private static final Pattern VALID_GRAPH_NAME_PATTERN = Pattern 00049 .compile("^([\\~\\/A-Za-z][\\w_\\/]*)?$"); 00050 00051 private static AtomicInteger anonymousCounter = new AtomicInteger(); 00052 00053 private final String name; 00054 00055 // TODO(damonkohler): This is not safe across multiple hosts/processes. 00056 // Instead, try to use the same algorithm as in cpp and Python. 00062 public static GraphName newAnonymous() { 00063 return new GraphName(ANONYMOUS_PREFIX + anonymousCounter.incrementAndGet()); 00064 } 00065 00069 public static GraphName root() { 00070 return new GraphName(ROOT); 00071 } 00072 00076 public static GraphName empty() { 00077 return new GraphName(""); 00078 } 00079 00087 public static GraphName of(String name) { 00088 return new GraphName(canonicalize(name)); 00089 } 00090 00091 private GraphName(String name) { 00092 Preconditions.checkNotNull(name); 00093 this.name = name; 00094 } 00095 00104 private static String canonicalize(String name) { 00105 if (!VALID_GRAPH_NAME_PATTERN.matcher(name).matches()) { 00106 throw new RosRuntimeException("Invalid graph name: " + name); 00107 } 00108 // Trim trailing slashes for canonical representation. 00109 while (!name.equals(GraphName.ROOT) && name.endsWith(SEPARATOR)) { 00110 name = name.substring(0, name.length() - 1); 00111 } 00112 if (name.startsWith("~/")) { 00113 name = "~" + name.substring(2); 00114 } 00115 return name; 00116 } 00117 00135 public boolean isGlobal() { 00136 return !isEmpty() && name.charAt(0) == '/'; 00137 } 00138 00143 public boolean isRoot() { 00144 return name.equals(GraphName.ROOT); 00145 } 00146 00151 public boolean isEmpty() { 00152 return name.isEmpty(); 00153 } 00154 00171 public boolean isPrivate() { 00172 return !isEmpty() && name.charAt(0) == '~'; 00173 } 00174 00189 public boolean isRelative() { 00190 return !isPrivate() && !isGlobal(); 00191 } 00192 00197 public GraphName getParent() { 00198 if (name.length() == 0) { 00199 return GraphName.empty(); 00200 } 00201 if (name.equals(GraphName.ROOT)) { 00202 return GraphName.root(); 00203 } 00204 int slashIdx = name.lastIndexOf('/'); 00205 if (slashIdx > 1) { 00206 return new GraphName(name.substring(0, slashIdx)); 00207 } 00208 if (isGlobal()) { 00209 return GraphName.root(); 00210 } 00211 return GraphName.empty(); 00212 } 00213 00217 public GraphName getBasename() { 00218 int slashIdx = name.lastIndexOf('/'); 00219 if (slashIdx > -1) { 00220 if (slashIdx + 1 < name.length()) { 00221 return new GraphName(name.substring(slashIdx + 1)); 00222 } 00223 return GraphName.empty(); 00224 } 00225 return this; 00226 } 00227 00235 public GraphName toRelative() { 00236 if (isPrivate() || isGlobal()) { 00237 return new GraphName(name.substring(1)); 00238 } 00239 return this; 00240 } 00241 00248 public GraphName toGlobal() { 00249 if (isGlobal()) { 00250 return this; 00251 } 00252 if (isPrivate()) { 00253 return new GraphName(GraphName.ROOT + name.substring(1)); 00254 } 00255 return new GraphName(GraphName.ROOT + name); 00256 } 00257 00267 public GraphName join(GraphName other) { 00268 if (other.isGlobal() || isEmpty()) { 00269 return other; 00270 } 00271 if (isRoot()) { 00272 return other.toGlobal(); 00273 } 00274 if (other.isEmpty()) { 00275 return this; 00276 } 00277 return new GraphName(toString() + SEPARATOR + other.toString()); 00278 } 00279 00283 public GraphName join(String other) { 00284 return join(GraphName.of(other)); 00285 } 00286 00287 @Override 00288 public String toString() { 00289 return name; 00290 } 00291 00292 @Override 00293 public int hashCode() { 00294 return name.hashCode(); 00295 } 00296 00297 @Override 00298 public boolean equals(Object obj) { 00299 if (this == obj) 00300 return true; 00301 if (obj == null) 00302 return false; 00303 if (getClass() != obj.getClass()) 00304 return false; 00305 GraphName other = (GraphName) obj; 00306 if (name == null) { 00307 if (other.name != null) 00308 return false; 00309 } else if (!name.equals(other.name)) 00310 return false; 00311 return true; 00312 } 00313 }