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