001package edu.wpi.first.wpilibj.networktables; 002 003import edu.wpi.first.wpilibj.tables.*; 004import edu.wpi.first.wpilibj.networktables2.type.*; 005import java.io.*; 006import java.nio.ByteBuffer; 007import java.util.*; 008 009/** 010 * A network table that knows its subtable path. 011 */ 012public class NetworkTable implements ITable, IRemote { 013 /** 014 * The path separator for sub-tables and keys 015 * 016 */ 017 public static final char PATH_SEPARATOR = '/'; 018 /** 019 * The default port that network tables operates on 020 */ 021 public static final int DEFAULT_PORT = 1735; 022 023 private static boolean client = false; 024 private static boolean enableDS = true; 025 private static boolean running = false; 026 private static int port = DEFAULT_PORT; 027 private static String persistentFilename = "networktables.ini"; 028 029 private synchronized static void checkInit() { 030 if (running) 031 throw new IllegalStateException( 032 "Network tables has already been initialized"); 033 } 034 035 /** 036 * initializes network tables 037 */ 038 public synchronized static void initialize() { 039 if (running) 040 shutdown(); 041 if (client) { 042 NetworkTablesJNI.startClient(); 043 if (enableDS) 044 NetworkTablesJNI.startDSClient(port); 045 } else 046 NetworkTablesJNI.startServer(persistentFilename, "", port); 047 running = true; 048 } 049 050 /** 051 * shuts down network tables 052 */ 053 public synchronized static void shutdown() { 054 if (!running) 055 return; 056 if (client) { 057 NetworkTablesJNI.stopDSClient(); 058 NetworkTablesJNI.stopClient(); 059 } else 060 NetworkTablesJNI.stopServer(); 061 running = false; 062 } 063 064 /** 065 * set that network tables should be a server 066 * This must be called before initialize or getTable 067 */ 068 public synchronized static void setServerMode() { 069 if (!client) 070 return; 071 checkInit(); 072 client = false; 073 } 074 075 /** 076 * set that network tables should be a client 077 * This must be called before initialize or getTable 078 */ 079 public synchronized static void setClientMode() { 080 if (client) 081 return; 082 checkInit(); 083 client = true; 084 } 085 086 /** 087 * set the team the robot is configured for (this will set the mdns address that 088 * network tables will connect to in client mode) 089 * This must be called before initialize or getTable 090 * @param team the team number 091 */ 092 public synchronized static void setTeam(int team) { 093 String[] addresses = new String[4]; 094 addresses[0] = "10." + (int)(team / 100) + "." + (int)(team % 100) + ".2"; 095 addresses[1] = "172.22.11.2"; 096 addresses[2] = "roboRIO-" + team + "-FRC.local"; 097 addresses[3] = "roboRIO-" + team + "-FRC.lan"; 098 setIPAddress(addresses); 099 } 100 101 /** 102 * @param address the adress that network tables will connect to in client 103 * mode 104 */ 105 public synchronized static void setIPAddress(final String address) { 106 String[] addresses = new String[1]; 107 addresses[0] = address; 108 setIPAddress(addresses); 109 } 110 111 /** 112 * @param addresses the adresses that network tables will connect to in 113 * client mode (in round robin order) 114 */ 115 public synchronized static void setIPAddress(final String[] addresses) { 116 int[] ports = new int[addresses.length]; 117 for (int i=0; i<addresses.length; i++) 118 ports[i] = port; 119 NetworkTablesJNI.setServer(addresses, ports); 120 121 // Stop the DS client if we're explicitly connecting to localhost 122 if (addresses.length > 0 && 123 (addresses[0].equals("localhost") || addresses[0].equals("127.0.0.1"))) 124 NetworkTablesJNI.stopDSClient(); 125 else if (enableDS) 126 NetworkTablesJNI.startDSClient(port); 127 } 128 129 /** 130 * @param aport the port number that network tables will connect to in client 131 * mode or listen to in server mode 132 */ 133 public synchronized static void setPort(int aport) { 134 if (port == aport) 135 return; 136 checkInit(); 137 port = aport; 138 } 139 140 /** 141 * @param enabled whether to enable the connection to the local DS to get 142 * the robot IP address (defaults to enabled) 143 */ 144 public synchronized static void setDSClientEnabled(boolean enabled) { 145 enableDS = enabled; 146 if (enableDS) 147 NetworkTablesJNI.startDSClient(port); 148 else 149 NetworkTablesJNI.stopDSClient(); 150 } 151 152 /** 153 * Sets the persistent filename. 154 * @param filename the filename that the network tables server uses for 155 * automatic loading and saving of persistent values 156 */ 157 public synchronized static void setPersistentFilename(final String filename) { 158 if (persistentFilename.equals(filename)) 159 return; 160 checkInit(); 161 persistentFilename = filename; 162 } 163 164 /** 165 * Sets the network identity. 166 * This is provided in the connection info on the remote end. 167 * @param name identity 168 */ 169 public static void setNetworkIdentity(String name) { 170 NetworkTablesJNI.setNetworkIdentity(name); 171 } 172 173 public static boolean[] toNative(Boolean[] arr) { 174 boolean[] out = new boolean[arr.length]; 175 for (int i = 0; i < arr.length; i++) 176 out[i] = arr[i]; 177 return out; 178 } 179 180 public static double[] toNative(Number[] arr) { 181 double[] out = new double[arr.length]; 182 for (int i = 0; i < arr.length; i++) 183 out[i] = arr[i].doubleValue(); 184 return out; 185 } 186 187 public static Boolean[] fromNative(boolean[] arr) { 188 Boolean[] out = new Boolean[arr.length]; 189 for (int i = 0; i < arr.length; i++) 190 out[i] = arr[i]; 191 return out; 192 } 193 194 public static Double[] fromNative(double[] arr) { 195 Double[] out = new Double[arr.length]; 196 for (int i = 0; i < arr.length; i++) 197 out[i] = arr[i]; 198 return out; 199 } 200 201 /** 202 * Gets the table with the specified key. If the table does not exist, a new 203 *table will be created.<br> 204 * This will automatically initialize network tables if it has not been 205 *already 206 * 207 * @param key 208 * the key name 209 * @return the network table requested 210 */ 211 public synchronized static NetworkTable getTable(String key) { 212 if (!running) 213 initialize(); 214 if (key.isEmpty() || key.charAt(0) == PATH_SEPARATOR) 215 return new NetworkTable(key); 216 return new NetworkTable(PATH_SEPARATOR + key); 217 } 218 219 private final String path; 220 private final String pathWithSep; 221 222 NetworkTable(String path) { 223 this.path = path; 224 this.pathWithSep = path + PATH_SEPARATOR; 225 } 226 public String toString() { return "NetworkTable: " + path; } 227 228 public static ConnectionInfo[] connections() { 229 return NetworkTablesJNI.getConnections(); 230 } 231 232 public boolean isConnected() { 233 ConnectionInfo[] conns = NetworkTablesJNI.getConnections(); 234 return conns.length > 0; 235 } 236 237 public boolean isServer() { 238 return !client; 239 } 240 241 private static class ListenerBase { 242 public int uid; 243 } 244 245 private static class ConnectionListenerAdapter extends ListenerBase implements NetworkTablesJNI.ConnectionListenerFunction { 246 private final IRemote targetSource; 247 private final IRemoteConnectionListener targetListener; 248 249 public ConnectionListenerAdapter(IRemote targetSource, IRemoteConnectionListener targetListener) { 250 this.targetSource = targetSource; 251 this.targetListener = targetListener; 252 } 253 254 public void apply(int uid, boolean connected, ConnectionInfo conn) { 255 if (connected) 256 targetListener.connectedEx(targetSource, conn); 257 else 258 targetListener.disconnectedEx(targetSource, conn); 259 } 260 } 261 262 private static IRemote staticRemote = new IRemote() { 263 public void addConnectionListener(IRemoteConnectionListener listener, boolean immediateNotify) { 264 NetworkTable.addGlobalConnectionListener(listener, immediateNotify); 265 } 266 public void removeConnectionListener(IRemoteConnectionListener listener) { 267 NetworkTable.removeGlobalConnectionListener(listener); 268 } 269 public boolean isConnected() { 270 ConnectionInfo[] conns = NetworkTablesJNI.getConnections(); 271 return conns.length > 0; 272 } 273 public boolean isServer() { 274 return !client; 275 } 276 }; 277 278 private static final Hashtable<IRemoteConnectionListener,ConnectionListenerAdapter> globalConnectionListenerMap = new Hashtable<IRemoteConnectionListener,ConnectionListenerAdapter>(); 279 public static synchronized void addGlobalConnectionListener(IRemoteConnectionListener listener, 280 boolean immediateNotify) { 281 ConnectionListenerAdapter adapter = globalConnectionListenerMap.get(listener); 282 if (adapter != null) 283 throw new IllegalStateException("Cannot add the same listener twice"); 284 adapter = new ConnectionListenerAdapter(staticRemote, listener); 285 adapter.uid = NetworkTablesJNI.addConnectionListener(adapter, immediateNotify); 286 globalConnectionListenerMap.put(listener, adapter); 287 } 288 289 public static synchronized void removeGlobalConnectionListener(IRemoteConnectionListener listener) { 290 ConnectionListenerAdapter adapter = globalConnectionListenerMap.get(listener); 291 if (adapter != null) { 292 NetworkTablesJNI.removeConnectionListener(adapter.uid); 293 globalConnectionListenerMap.remove(listener); 294 } 295 } 296 297 private final Hashtable<IRemoteConnectionListener,ConnectionListenerAdapter> connectionListenerMap = new Hashtable<IRemoteConnectionListener,ConnectionListenerAdapter>(); 298 public synchronized void addConnectionListener(IRemoteConnectionListener listener, 299 boolean immediateNotify) { 300 ConnectionListenerAdapter adapter = connectionListenerMap.get(listener); 301 if (adapter != null) 302 throw new IllegalStateException("Cannot add the same listener twice"); 303 adapter = new ConnectionListenerAdapter(this, listener); 304 adapter.uid = NetworkTablesJNI.addConnectionListener(adapter, immediateNotify); 305 connectionListenerMap.put(listener, adapter); 306 } 307 308 public synchronized void removeConnectionListener(IRemoteConnectionListener listener) { 309 ConnectionListenerAdapter adapter = connectionListenerMap.get(listener); 310 if (adapter != null) { 311 NetworkTablesJNI.removeConnectionListener(adapter.uid); 312 connectionListenerMap.remove(listener); 313 } 314 } 315 316 /** 317 * {@inheritDoc} 318 */ 319 @Override 320 public void addTableListener(ITableListener listener) { 321 addTableListenerEx(listener, NOTIFY_NEW | NOTIFY_UPDATE); 322 } 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override 328 public void addTableListener(ITableListener listener, 329 boolean immediateNotify) { 330 int flags = NOTIFY_NEW | NOTIFY_UPDATE; 331 if (immediateNotify) 332 flags |= NOTIFY_IMMEDIATE; 333 addTableListenerEx(listener, flags); 334 } 335 336 private class TableListenerAdapter extends ListenerBase implements NetworkTablesJNI.EntryListenerFunction { 337 private final int prefixLen; 338 private final ITable targetSource; 339 private final ITableListener targetListener; 340 341 public TableListenerAdapter(int prefixLen, ITable targetSource, ITableListener targetListener) { 342 this.prefixLen = prefixLen; 343 this.targetSource = targetSource; 344 this.targetListener = targetListener; 345 } 346 347 public void apply(int uid, String key, Object value, int flags) { 348 String relativeKey = key.substring(prefixLen); 349 if (relativeKey.indexOf(PATH_SEPARATOR) != -1) 350 return; 351 targetListener.valueChangedEx(targetSource, relativeKey, value, flags); 352 } 353 } 354 355 private final Hashtable<ITableListener,List<ListenerBase>> listenerMap = new Hashtable<ITableListener,List<ListenerBase>>(); 356 public synchronized void addTableListenerEx(ITableListener listener, 357 int flags) { 358 List<ListenerBase> adapters = listenerMap.get(listener); 359 if (adapters == null) { 360 adapters = new ArrayList<ListenerBase>(); 361 listenerMap.put(listener, adapters); 362 } 363 TableListenerAdapter adapter = 364 new TableListenerAdapter(path.length() + 1, this, listener); 365 adapter.uid = NetworkTablesJNI.addEntryListener(pathWithSep, adapter, flags); 366 adapters.add(adapter); 367 } 368 369 /** 370 * {@inheritDoc} 371 */ 372 @Override 373 public void addTableListener(String key, ITableListener listener, 374 boolean immediateNotify) { 375 int flags = NOTIFY_NEW | NOTIFY_UPDATE; 376 if (immediateNotify) 377 flags |= NOTIFY_IMMEDIATE; 378 addTableListenerEx(key, listener, flags); 379 } 380 381 private class KeyListenerAdapter extends ListenerBase implements NetworkTablesJNI.EntryListenerFunction { 382 private final String relativeKey; 383 private final String fullKey; 384 private final ITable targetSource; 385 private final ITableListener targetListener; 386 387 public KeyListenerAdapter(String relativeKey, String fullKey, ITable targetSource, ITableListener targetListener) { 388 this.relativeKey = relativeKey; 389 this.fullKey = fullKey; 390 this.targetSource = targetSource; 391 this.targetListener = targetListener; 392 } 393 394 public void apply(int uid, String key, Object value, int flags) { 395 if (!key.equals(fullKey)) 396 return; 397 targetListener.valueChangedEx(targetSource, relativeKey, value, flags); 398 } 399 } 400 401 /** 402 * {@inheritDoc} 403 */ 404 @Override 405 public synchronized void addTableListenerEx(String key, 406 ITableListener listener, 407 int flags) { 408 List<ListenerBase> adapters = listenerMap.get(listener); 409 if (adapters == null) { 410 adapters = new ArrayList<ListenerBase>(); 411 listenerMap.put(listener, adapters); 412 } 413 String fullKey = pathWithSep + key; 414 KeyListenerAdapter adapter = 415 new KeyListenerAdapter(key, fullKey, this, listener); 416 adapter.uid = NetworkTablesJNI.addEntryListener(fullKey, adapter, flags); 417 adapters.add(adapter); 418 } 419 420 /** 421 * {@inheritDoc} 422 */ 423 @Override 424 public void addSubTableListener(final ITableListener listener) { 425 addSubTableListener(listener, false); 426 } 427 428 private class SubListenerAdapter extends ListenerBase implements NetworkTablesJNI.EntryListenerFunction { 429 private final int prefixLen; 430 private final ITable targetSource; 431 private final ITableListener targetListener; 432 private final Set<String> notifiedTables = new HashSet<String>(); 433 434 public SubListenerAdapter(int prefixLen, ITable targetSource, ITableListener targetListener) { 435 this.prefixLen = prefixLen; 436 this.targetSource = targetSource; 437 this.targetListener = targetListener; 438 } 439 440 public void apply(int uid, String key, Object value, int flags) { 441 String relativeKey = key.substring(prefixLen); 442 int endSubTable = relativeKey.indexOf(PATH_SEPARATOR); 443 if (endSubTable == -1) 444 return; 445 String subTableKey = relativeKey.substring(0, endSubTable); 446 if (notifiedTables.contains(subTableKey)) 447 return; 448 notifiedTables.add(subTableKey); 449 targetListener.valueChangedEx(targetSource, subTableKey, targetSource.getSubTable(subTableKey), flags); 450 } 451 } 452 453 /** 454 * {@inheritDoc} 455 */ 456 @Override 457 public synchronized void addSubTableListener(final ITableListener listener, 458 boolean localNotify) { 459 List<ListenerBase> adapters = listenerMap.get(listener); 460 if (adapters == null) { 461 adapters = new ArrayList<ListenerBase>(); 462 listenerMap.put(listener, adapters); 463 } 464 SubListenerAdapter adapter = 465 new SubListenerAdapter(path.length() + 1, this, listener); 466 int flags = NOTIFY_NEW | NOTIFY_IMMEDIATE; 467 if (localNotify) 468 flags |= NOTIFY_LOCAL; 469 adapter.uid = NetworkTablesJNI.addEntryListener(pathWithSep, adapter, flags); 470 adapters.add(adapter); 471 } 472 473 /** 474 * {@inheritDoc} 475 */ 476 @Override 477 public synchronized void removeTableListener(ITableListener listener) { 478 List<ListenerBase> adapters = listenerMap.get(listener); 479 if (adapters != null) { 480 for (int i = 0; i < adapters.size(); ++i) 481 NetworkTablesJNI.removeEntryListener(adapters.get(i).uid); 482 adapters.clear(); 483 } 484 } 485 486 /** 487 * {@inheritDoc} 488 */ 489 @Override 490 public ITable getSubTable(String key) { 491 return new NetworkTable(pathWithSep + key); 492 } 493 494 /** 495 * {@inheritDoc} 496 */ 497 @Override 498 public boolean containsKey(String key) { 499 return NetworkTablesJNI.containsKey(pathWithSep + key); 500 } 501 502 public boolean containsSubTable(String key) { 503 EntryInfo[] entries = NetworkTablesJNI.getEntries(pathWithSep + key + PATH_SEPARATOR, 0); 504 return entries.length != 0; 505 } 506 507 /** 508 * @param types bitmask of types; 0 is treated as a "don't care". 509 * @return keys currently in the table 510 */ 511 public Set<String> getKeys(int types) { 512 Set<String> keys = new HashSet<String>(); 513 int prefixLen = path.length() + 1; 514 for (EntryInfo entry : NetworkTablesJNI.getEntries(pathWithSep, types)) { 515 String relativeKey = entry.name.substring(prefixLen); 516 if (relativeKey.indexOf(PATH_SEPARATOR) != -1) 517 continue; 518 keys.add(relativeKey); 519 } 520 return keys; 521 } 522 523 /** 524 * {@inheritDoc} 525 */ 526 @Override 527 public Set<String> getKeys() { 528 return getKeys(0); 529 } 530 531 /** 532 * {@inheritDoc} 533 */ 534 @Override 535 public Set<String> getSubTables() { 536 Set<String> keys = new HashSet<String>(); 537 int prefixLen = path.length() + 1; 538 for (EntryInfo entry : NetworkTablesJNI.getEntries(pathWithSep, 0)) { 539 String relativeKey = entry.name.substring(prefixLen); 540 int endSubTable = relativeKey.indexOf(PATH_SEPARATOR); 541 if (endSubTable == -1) 542 continue; 543 keys.add(relativeKey.substring(0, endSubTable)); 544 } 545 return keys; 546 } 547 548 /** 549 * {@inheritDoc} 550 */ 551 @Override 552 public boolean putNumber(String key, double value) { 553 return NetworkTablesJNI.putDouble(pathWithSep + key, value); 554 } 555 556 /** 557 * {@inheritDoc} 558 */ 559 public boolean setDefaultNumber(String key, double defaultValue) { 560 return NetworkTablesJNI.setDefaultDouble(pathWithSep + key, 561 defaultValue); 562 } 563 564 /** 565 * {@inheritDoc} 566 * @deprecated This exception-raising method has been replaced by the 567 * default-taking method {@link #getNumber(String, double)}. 568 */ 569 @Override 570 @Deprecated 571 public double getNumber(String key) throws TableKeyNotDefinedException { 572 return NetworkTablesJNI.getDouble(pathWithSep + key); 573 } 574 575 /** 576 * {@inheritDoc} 577 */ 578 @Override 579 public double getNumber(String key, double defaultValue) { 580 return NetworkTablesJNI.getDouble(pathWithSep + key, defaultValue); 581 } 582 583 /** 584 * {@inheritDoc} 585 */ 586 @Override 587 public boolean putString(String key, String value) { 588 return NetworkTablesJNI.putString(pathWithSep + key, value); 589 } 590 591 /** 592 * {@inheritDoc} 593 */ 594 public boolean setDefaultString(String key, String defaultValue) { 595 return NetworkTablesJNI.setDefaultString(pathWithSep + key, 596 defaultValue); 597 } 598 599 /** 600 * {@inheritDoc} 601 * @deprecated This exception-raising method has been replaced by the 602 * default-taking method {@link #getString(String, String)}. 603 */ 604 @Override 605 @Deprecated 606 public String getString(String key) throws TableKeyNotDefinedException { 607 return NetworkTablesJNI.getString(pathWithSep + key); 608 } 609 610 /** 611 * {@inheritDoc} 612 */ 613 @Override 614 public String getString(String key, String defaultValue) { 615 return NetworkTablesJNI.getString(pathWithSep + key, defaultValue); 616 } 617 618 /** 619 * {@inheritDoc} 620 */ 621 @Override 622 public boolean putBoolean(String key, boolean value) { 623 return NetworkTablesJNI.putBoolean(pathWithSep + key, value); 624 } 625 626 /** 627 * {@inheritDoc} 628 */ 629 public boolean setDefaultBoolean(String key, boolean defaultValue) { 630 return NetworkTablesJNI.setDefaultBoolean(pathWithSep + key, 631 defaultValue); 632 } 633 634 /** 635 * {@inheritDoc} 636 * @deprecated This exception-raising method has been replaced by the 637 * default-taking method {@link #getBoolean(String, boolean)}. 638 */ 639 @Override 640 @Deprecated 641 public boolean getBoolean(String key) throws TableKeyNotDefinedException { 642 return NetworkTablesJNI.getBoolean(pathWithSep + key); 643 } 644 645 /** 646 * {@inheritDoc} 647 */ 648 @Override 649 public boolean getBoolean(String key, boolean defaultValue) { 650 return NetworkTablesJNI.getBoolean(pathWithSep + key, defaultValue); 651 } 652 653 /** 654 * {@inheritDoc} 655 */ 656 @Override 657 public boolean putBooleanArray(String key, boolean[] value) { 658 return NetworkTablesJNI.putBooleanArray(pathWithSep + key, value); 659 } 660 661 /** 662 * {@inheritDoc} 663 */ 664 @Override 665 public boolean putBooleanArray(String key, Boolean[] value) { 666 return putBooleanArray(key, toNative(value)); 667 } 668 669 /** 670 * {@inheritDoc} 671 */ 672 public boolean setDefaultBooleanArray(String key, boolean[] defaultValue) { 673 return NetworkTablesJNI.setDefaultBooleanArray(pathWithSep + key, 674 defaultValue); 675 } 676 677 /** 678 * {@inheritDoc} 679 */ 680 public boolean setDefaultBooleanArray(String key, Boolean[] defaultValue) { 681 return NetworkTablesJNI.setDefaultBooleanArray(pathWithSep + key, 682 toNative(defaultValue)); 683 } 684 685 /** 686 * {@inheritDoc} 687 * @deprecated This exception-raising method has been replaced by the 688 * default-taking method {@link #getBooleanArray(String, boolean[])}. 689 */ 690 @Override 691 @Deprecated 692 public boolean[] getBooleanArray(String key) throws TableKeyNotDefinedException { 693 return NetworkTablesJNI.getBooleanArray(pathWithSep + key); 694 } 695 696 /** 697 * {@inheritDoc} 698 */ 699 @Override 700 public boolean[] getBooleanArray(String key, boolean[] defaultValue) { 701 return NetworkTablesJNI.getBooleanArray(pathWithSep + key, defaultValue); 702 } 703 704 /** 705 * {@inheritDoc} 706 */ 707 @Override 708 public Boolean[] getBooleanArray(String key, Boolean[] defaultValue) { 709 try { 710 return fromNative(getBooleanArray(key)); 711 } catch (TableKeyNotDefinedException e) { 712 return defaultValue; 713 } 714 } 715 716 /** 717 * {@inheritDoc} 718 */ 719 @Override 720 public boolean putNumberArray(String key, double[] value) { 721 return NetworkTablesJNI.putDoubleArray(pathWithSep + key, value); 722 } 723 724 /** 725 * {@inheritDoc} 726 */ 727 @Override 728 public boolean putNumberArray(String key, Double[] value) { 729 return putNumberArray(key, toNative(value)); 730 } 731 732 /** 733 * {@inheritDoc} 734 */ 735 public boolean setDefaultNumberArray(String key, double[] defaultValue) { 736 return NetworkTablesJNI.setDefaultDoubleArray(pathWithSep + key, 737 defaultValue); 738 } 739 740 /** 741 * {@inheritDoc} 742 */ 743 public boolean setDefaultNumberArray(String key, Double[] defaultValue) { 744 return NetworkTablesJNI.setDefaultDoubleArray(pathWithSep + key, 745 toNative(defaultValue)); 746 } 747 748 /** 749 * {@inheritDoc} 750 * @deprecated This exception-raising method has been replaced by the 751 * default-taking method {@link #getNumberArray(String, double[])}. 752 */ 753 @Override 754 @Deprecated 755 public double[] getNumberArray(String key) throws TableKeyNotDefinedException { 756 return NetworkTablesJNI.getDoubleArray(pathWithSep + key); 757 } 758 759 /** 760 * {@inheritDoc} 761 */ 762 @Override 763 public double[] getNumberArray(String key, double[] defaultValue) { 764 return NetworkTablesJNI.getDoubleArray(pathWithSep + key, defaultValue); 765 } 766 767 /** 768 * {@inheritDoc} 769 */ 770 @Override 771 public Double[] getNumberArray(String key, Double[] defaultValue) { 772 try { 773 return fromNative(getNumberArray(key)); 774 } catch (TableKeyNotDefinedException e) { 775 return defaultValue; 776 } 777 } 778 779 /** 780 * {@inheritDoc} 781 */ 782 @Override 783 public boolean putStringArray(String key, String[] value) { 784 return NetworkTablesJNI.putStringArray(pathWithSep + key, value); 785 } 786 787 /** 788 * {@inheritDoc} 789 */ 790 public boolean setDefaultStringArray(String key, String[] defaultValue) { 791 return NetworkTablesJNI.setDefaultStringArray(pathWithSep + key, 792 defaultValue); 793 } 794 795 /** 796 * {@inheritDoc} 797 * @deprecated This exception-raising method has been replaced by the 798 * default-taking method {@link #getStringArray(String, String[])}. 799 */ 800 @Override 801 @Deprecated 802 public String[] getStringArray(String key) throws TableKeyNotDefinedException { 803 return NetworkTablesJNI.getStringArray(pathWithSep + key); 804 } 805 806 /** 807 * {@inheritDoc} 808 */ 809 @Override 810 public String[] getStringArray(String key, String[] defaultValue) { 811 return NetworkTablesJNI.getStringArray(pathWithSep + key, defaultValue); 812 } 813 814 /** 815 * {@inheritDoc} 816 */ 817 @Override 818 public boolean putRaw(String key, byte[] value) { 819 return NetworkTablesJNI.putRaw(pathWithSep + key, value); 820 } 821 822 /** 823 * {@inheritDoc} 824 */ 825 public boolean setDefaultRaw(String key, byte[] defaultValue) { 826 return NetworkTablesJNI.setDefaultRaw(pathWithSep + key, 827 defaultValue); 828 } 829 830 /** 831 * {@inheritDoc} 832 */ 833 @Override 834 public boolean putRaw(String key, ByteBuffer value, int len) { 835 if (!value.isDirect()) 836 throw new IllegalArgumentException("must be a direct buffer"); 837 if (value.capacity() < len) 838 throw new IllegalArgumentException("buffer is too small, must be at least " + len); 839 return NetworkTablesJNI.putRaw(pathWithSep + key, value, len); 840 } 841 842 /** 843 * {@inheritDoc} 844 * @deprecated This exception-raising method has been replaced by the 845 * default-taking method {@link #getRaw(String, byte[])}. 846 */ 847 @Override 848 @Deprecated 849 public byte[] getRaw(String key) throws TableKeyNotDefinedException { 850 return NetworkTablesJNI.getRaw(pathWithSep + key); 851 } 852 853 /** 854 * {@inheritDoc} 855 */ 856 @Override 857 public byte[] getRaw(String key, byte[] defaultValue) { 858 return NetworkTablesJNI.getRaw(pathWithSep + key, defaultValue); 859 } 860 861 /** 862 * {@inheritDoc} 863 */ 864 @Override 865 public boolean putValue(String key, Object value) throws IllegalArgumentException { 866 if (value instanceof Boolean) 867 return NetworkTablesJNI.putBoolean(pathWithSep + key, ((Boolean)value).booleanValue()); 868 else if (value instanceof Number) 869 return NetworkTablesJNI.putDouble(pathWithSep + key, ((Number)value).doubleValue()); 870 else if (value instanceof String) 871 return NetworkTablesJNI.putString(pathWithSep + key, (String)value); 872 else if (value instanceof byte[]) 873 return NetworkTablesJNI.putRaw(pathWithSep + key, (byte[])value); 874 else if (value instanceof boolean[]) 875 return NetworkTablesJNI.putBooleanArray(pathWithSep + key, (boolean[])value); 876 else if (value instanceof double[]) 877 return NetworkTablesJNI.putDoubleArray(pathWithSep + key, (double[])value); 878 else if (value instanceof Boolean[]) 879 return NetworkTablesJNI.putBooleanArray(pathWithSep + key, toNative((Boolean[])value)); 880 else if (value instanceof Number[]) 881 return NetworkTablesJNI.putDoubleArray(pathWithSep + key, toNative((Number[])value)); 882 else if (value instanceof String[]) 883 return NetworkTablesJNI.putStringArray(pathWithSep + key, (String[])value); 884 else if (value instanceof BooleanArray) 885 return NetworkTablesJNI.putBooleanArray(pathWithSep + key, toNative((Boolean[])((ArrayData)value).getDataArray())); 886 else if (value instanceof NumberArray) 887 return NetworkTablesJNI.putDoubleArray(pathWithSep + key, toNative((Double[])((ArrayData)value).getDataArray())); 888 else if (value instanceof StringArray) 889 return NetworkTablesJNI.putStringArray(pathWithSep + key, (String[])((ArrayData)value).getDataArray()); 890 else 891 throw new IllegalArgumentException("Value of type " + value.getClass().getName() + " cannot be put into a table"); 892 } 893 894 /** 895 * {@inheritDoc} 896 * @deprecated Use get*Array functions instead. 897 */ 898 @Override 899 @Deprecated 900 public void retrieveValue(String key, Object externalData) throws TableKeyNotDefinedException { 901 Object value = getValue(key); 902 if (value instanceof boolean[] && externalData instanceof BooleanArray) 903 ((ArrayData)externalData).setDataArray(fromNative((boolean[])value)); 904 else if (value instanceof double[] && externalData instanceof NumberArray) 905 ((ArrayData)externalData).setDataArray(fromNative((double[])value)); 906 else if (value instanceof String[] && externalData instanceof StringArray) 907 ((ArrayData)externalData).setDataArray((String[])value); 908 else 909 throw new TableKeyNotDefinedException(key); 910 } 911 912 /** 913 * {@inheritDoc} 914 * @deprecated This exception-raising method has been replaced by the 915 * default-taking method {@link #getValue(String, Object)}. 916 */ 917 @Override 918 @Deprecated 919 public Object getValue(String key) throws TableKeyNotDefinedException { 920 return NetworkTablesJNI.getValue(pathWithSep + key); 921 } 922 923 /** 924 * {@inheritDoc} 925 */ 926 @Override 927 public Object getValue(String key, Object defaultValue) { 928 return NetworkTablesJNI.getValue(pathWithSep + key, defaultValue); 929 } 930 931 /** The persistent flag value. */ 932 public static final int PERSISTENT = 1; 933 934 /** 935 * {@inheritDoc} 936 */ 937 @Override 938 public void setPersistent(String key) { 939 setFlags(key, PERSISTENT); 940 } 941 942 /** 943 * {@inheritDoc} 944 */ 945 @Override 946 public void clearPersistent(String key) { 947 clearFlags(key, PERSISTENT); 948 } 949 950 /** 951 * {@inheritDoc} 952 */ 953 @Override 954 public boolean isPersistent(String key) { 955 return (getFlags(key) & PERSISTENT) != 0; 956 } 957 958 /** 959 * {@inheritDoc} 960 */ 961 @Override 962 public void setFlags(String key, int flags) { 963 NetworkTablesJNI.setEntryFlags(pathWithSep + key, getFlags(key) | flags); 964 } 965 966 /** 967 * {@inheritDoc} 968 */ 969 @Override 970 public void clearFlags(String key, int flags) { 971 NetworkTablesJNI.setEntryFlags(pathWithSep + key, getFlags(key) & ~flags); 972 } 973 974 /** 975 * {@inheritDoc} 976 */ 977 @Override 978 public int getFlags(String key) { 979 return NetworkTablesJNI.getEntryFlags(pathWithSep + key); 980 } 981 982 /** 983 * {@inheritDoc} 984 */ 985 @Override 986 public void delete(String key) { 987 NetworkTablesJNI.deleteEntry(pathWithSep + key); 988 } 989 990 /** 991 * Deletes ALL keys in ALL subtables. Use with caution! 992 */ 993 public static void globalDeleteAll() { 994 NetworkTablesJNI.deleteAllEntries(); 995 } 996 997 /** 998 * Flushes all updated values immediately to the network. 999 * Note: This is rate-limited to protect the network from flooding. 1000 * This is primarily useful for synchronizing network updates with 1001 * user code. 1002 */ 1003 public static void flush() { 1004 NetworkTablesJNI.flush(); 1005 } 1006 1007 /** 1008 * Set the periodic update rate. 1009 * 1010 * @param interval update interval in seconds (range 0.01 to 1.0) 1011 */ 1012 public static void setUpdateRate(double interval) { 1013 NetworkTablesJNI.setUpdateRate(interval); 1014 } 1015 1016 /** 1017 * Saves persistent keys to a file. The server does this automatically. 1018 * 1019 * @param filename file name 1020 * @throws PersistentException if error saving file 1021 */ 1022 public static void savePersistent(String filename) throws PersistentException { 1023 NetworkTablesJNI.savePersistent(filename); 1024 } 1025 1026 /** 1027 * Loads persistent keys from a file. The server does this automatically. 1028 * 1029 * @param filename file name 1030 * @return List of warnings (errors result in an exception instead) 1031 * @throws PersistentException if error reading file 1032 */ 1033 public static String[] loadPersistent(String filename) throws PersistentException { 1034 return NetworkTablesJNI.loadPersistent(filename); 1035 } 1036 1037 /* 1038 * Deprecated Methods 1039 */ 1040 1041 /** 1042 * {@inheritDoc} 1043 * @deprecated Use {@link #putNumber(String, double)} instead. 1044 */ 1045 @Override 1046 @Deprecated 1047 public boolean putInt(String key, int value) { 1048 return putNumber(key, value); 1049 } 1050 1051 /** 1052 * {@inheritDoc} 1053 * @deprecated Use {@link #getNumber(String, double)} instead. 1054 */ 1055 @Override 1056 @Deprecated 1057 public int getInt(String key) throws TableKeyNotDefinedException { 1058 return (int)getNumber(key); 1059 } 1060 1061 /** 1062 * {@inheritDoc} 1063 * @deprecated Use {@link #getNumber(String, double)} instead. 1064 */ 1065 @Override 1066 @Deprecated 1067 public int getInt(String key, int defaultValue) throws TableKeyNotDefinedException { 1068 try { 1069 return (int)getNumber(key); 1070 } catch (NoSuchElementException ex) { 1071 return defaultValue; 1072 } 1073 } 1074 1075 /** 1076 * {@inheritDoc} 1077 * @deprecated Use {@link #putNumber(String, double)} instead. 1078 */ 1079 @Override 1080 @Deprecated 1081 public boolean putDouble(String key, double value) { 1082 return putNumber(key, value); 1083 } 1084 1085 /** 1086 * {@inheritDoc} 1087 * @deprecated Use {@link #getNumber(String, double)} instead. 1088 */ 1089 @Override 1090 @Deprecated 1091 public double getDouble(String key) throws TableKeyNotDefinedException { 1092 return getNumber(key); 1093 } 1094 1095 /** 1096 * {@inheritDoc} 1097 * @deprecated Use {@link #getNumber(String, double)} instead. 1098 */ 1099 @Override 1100 @Deprecated 1101 public double getDouble(String key, double defaultValue) { 1102 return getNumber(key, defaultValue); 1103 } 1104}