mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
Use new NetworkTables across WPILib (C++ and Java).
Also make sure table listeners stop listening in their destructors. This might be better handled by moving the table itself into ITableListener and providing cleanup functionality there. A submodule is used to pull in ntcore. Change-Id: I3031c1a768595cf0f8754c47e15cd423e2dbcce5
This commit is contained in:
committed by
Brad Miller (WPI)
parent
f65e697107
commit
f89c5e150f
@@ -6,20 +6,12 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
package edu.wpi.first.wpilibj;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
|
||||
import edu.wpi.first.wpilibj.communication.UsageReporting;
|
||||
import edu.wpi.first.wpilibj.networktables.NetworkTable;
|
||||
import edu.wpi.first.wpilibj.tables.ITable;
|
||||
import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
import edu.wpi.first.wpilibj.tables.TableKeyNotDefinedException;
|
||||
|
||||
/**
|
||||
* The preferences class provides a relatively simple way to save important
|
||||
@@ -28,7 +20,7 @@ import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
* <p>
|
||||
* This class loads and saves from a file inside the RoboRIO. The user can not
|
||||
* access the file directly, but may modify values at specific fields which will
|
||||
* then be saved to the file when {@link Preferences#save() save()} is called.
|
||||
* then be automatically saved to the file by the NetworkTables server.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
@@ -37,10 +29,7 @@ import edu.wpi.first.wpilibj.tables.ITableListener;
|
||||
*
|
||||
* <p>
|
||||
* This will also interact with {@link NetworkTable} by creating a table called
|
||||
* "Preferences" with all the key-value pairs. To save using
|
||||
* {@link NetworkTable}, simply set the boolean at position ~S A V E~ to true.
|
||||
* Also, if the value of any variable is " in the {@link NetworkTable}, then
|
||||
* that represents non-existence in the {@link Preferences} table
|
||||
* "Preferences" with all the key-value pairs.
|
||||
* </p>
|
||||
*
|
||||
* @author Joe Grinstead
|
||||
@@ -51,30 +40,14 @@ public class Preferences {
|
||||
* The Preferences table name
|
||||
*/
|
||||
private static final String TABLE_NAME = "Preferences";
|
||||
/**
|
||||
* The value of the save field
|
||||
*/
|
||||
private static final String SAVE_FIELD = "~S A V E~";
|
||||
/**
|
||||
* The file to save to
|
||||
*/
|
||||
private static final String FILE_NAME = "/home/lvuser/wpilib-preferences.ini";
|
||||
/**
|
||||
* The characters to put between a field and value
|
||||
*/
|
||||
private static final byte[] VALUE_PREFIX = {'=', '\"'};
|
||||
/**
|
||||
* The characters to put after the value
|
||||
*/
|
||||
private static final byte[] VALUE_SUFFIX = {'\"', '\n'};
|
||||
/**
|
||||
* The newline character
|
||||
*/
|
||||
private static final byte[] NEW_LINE = {'\n'};
|
||||
/**
|
||||
* The singleton instance
|
||||
*/
|
||||
private static Preferences instance;
|
||||
/**
|
||||
* The network table
|
||||
*/
|
||||
private NetworkTable table;
|
||||
|
||||
/**
|
||||
* Returns the preferences instance.
|
||||
@@ -89,54 +62,10 @@ public class Preferences {
|
||||
}
|
||||
|
||||
/**
|
||||
* The semaphore for beginning reads and writes to the file
|
||||
*/
|
||||
private final Object fileLock = new Object();
|
||||
/**
|
||||
* The semaphore for reading from the table
|
||||
*/
|
||||
private final Object lock = new Object();
|
||||
/**
|
||||
* The actual values (String->String)
|
||||
*/
|
||||
private Hashtable values;
|
||||
/**
|
||||
* The keys in the order they were read from the file
|
||||
*/
|
||||
private Vector keys;
|
||||
/**
|
||||
* The comments that were in the file sorted by which key they appeared over
|
||||
* (String->Comment)
|
||||
*/
|
||||
private Hashtable comments;
|
||||
/**
|
||||
* The comment at the end of the file
|
||||
*/
|
||||
private Comment endComment;
|
||||
|
||||
/**
|
||||
* Creates a preference class that will automatically read the file in a
|
||||
* different thread. Any call to its methods will be blocked until the thread
|
||||
* is finished reading.
|
||||
* Creates a preference class.
|
||||
*/
|
||||
private Preferences() {
|
||||
values = new Hashtable();
|
||||
keys = new Vector();
|
||||
|
||||
// We synchronized on fileLock and then wait
|
||||
// for it to know that the reading thread has started
|
||||
synchronized (fileLock) {
|
||||
new Thread() {
|
||||
public void run() {
|
||||
read();
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
fileLock.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
table = NetworkTable.getTable(TABLE_NAME);
|
||||
UsageReporting.report(tResourceType.kResourceType_Preferences, 0);
|
||||
}
|
||||
|
||||
@@ -144,188 +73,81 @@ public class Preferences {
|
||||
* @return a vector of the keys
|
||||
*/
|
||||
public Vector getKeys() {
|
||||
synchronized (lock) {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given value into the given key position
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains an illegal
|
||||
* character
|
||||
*/
|
||||
private void put(String key, String value) {
|
||||
synchronized (lock) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ImproperPreferenceKeyException.confirmString(key);
|
||||
if (values.put(key, value) == null) {
|
||||
keys.addElement(key);
|
||||
}
|
||||
NetworkTable.getTable(TABLE_NAME).putString(key, value);
|
||||
Vector<String> keys = new Vector<String>();
|
||||
for (String key : table.getKeys()) {
|
||||
keys.add(key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given string into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The value may not have quotation marks, nor may the key have any whitespace
|
||||
* nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care). at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws NullPointerException if value is null
|
||||
* @throws IllegalArgumentException if value contains a quotation mark
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putString(String key, String value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (value.indexOf('"') != -1) {
|
||||
throw new IllegalArgumentException("Can not put string:" + value
|
||||
+ " because it contains quotation marks");
|
||||
}
|
||||
put(key, value);
|
||||
table.putString(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given int into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putInt(String key, int value) {
|
||||
put(key, String.valueOf(value));
|
||||
table.putNumber(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given double into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putDouble(String key, double value) {
|
||||
put(key, String.valueOf(value));
|
||||
table.putNumber(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given float into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putFloat(String key, float value) {
|
||||
put(key, String.valueOf(value));
|
||||
table.putNumber(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given boolean into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putBoolean(String key, boolean value) {
|
||||
put(key, String.valueOf(value));
|
||||
table.putBoolean(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given long into the preferences table.
|
||||
*
|
||||
* <p>
|
||||
* The key may not have any whitespace nor an equals sign
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This will <b>NOT</b> save the value to memory between power cycles, to do
|
||||
* that you must call {@link Preferences#save() save()} (which must be used
|
||||
* with care) at some point after calling this.
|
||||
* </p>
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @throws ImproperPreferenceKeyException if the key contains any whitespace
|
||||
* or an equals sign
|
||||
*/
|
||||
public void putLong(String key, long value) {
|
||||
put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the given key.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the value (or null if none exists)
|
||||
* @throws NullPointerException if the key is null
|
||||
*/
|
||||
private String get(String key) {
|
||||
synchronized (lock) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return (String) values.get(key);
|
||||
}
|
||||
table.putNumber(key, value);
|
||||
table.setPersistent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,26 +155,18 @@ public class Preferences {
|
||||
*
|
||||
* @param key the key
|
||||
* @return if there is a value at the given key
|
||||
* @throws NullPointerException if key is null
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
return get(key) != null;
|
||||
return table.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a preference
|
||||
*
|
||||
* @param key the key
|
||||
* @throws NullPointerException if key is null
|
||||
*/
|
||||
public void remove(String key) {
|
||||
synchronized (lock) {
|
||||
if (key == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
values.remove(key);
|
||||
keys.removeElement(key);
|
||||
}
|
||||
table.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,11 +176,9 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws NullPointerException if the key is null
|
||||
*/
|
||||
public String getString(String key, String backup) {
|
||||
String value = get(key);
|
||||
return value == null ? backup : value;
|
||||
return table.getString(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -376,19 +188,12 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to an int
|
||||
*/
|
||||
public int getInt(String key, int backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
try {
|
||||
return (int)table.getNumber(key);
|
||||
} catch (TableKeyNotDefinedException e) {
|
||||
return backup;
|
||||
} else {
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IncompatibleTypeException(value, "int");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,20 +204,9 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to an double
|
||||
*/
|
||||
public double getDouble(String key, double backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
return backup;
|
||||
} else {
|
||||
try {
|
||||
return Double.parseDouble(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IncompatibleTypeException(value, "double");
|
||||
}
|
||||
}
|
||||
return table.getDouble(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,22 +216,9 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to a boolean
|
||||
*/
|
||||
public boolean getBoolean(String key, boolean backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
return backup;
|
||||
} else {
|
||||
if (value.equalsIgnoreCase("true")) {
|
||||
return true;
|
||||
} else if (value.equalsIgnoreCase("false")) {
|
||||
return false;
|
||||
} else {
|
||||
throw new IncompatibleTypeException(value, "boolean");
|
||||
}
|
||||
}
|
||||
return table.getBoolean(key, backup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,19 +228,12 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to a float
|
||||
*/
|
||||
public float getFloat(String key, float backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
try {
|
||||
return (float)table.getNumber(key);
|
||||
} catch (TableKeyNotDefinedException e) {
|
||||
return backup;
|
||||
} else {
|
||||
try {
|
||||
return Float.parseFloat(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IncompatibleTypeException(value, "float");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,403 +244,21 @@ public class Preferences {
|
||||
* @param key the key
|
||||
* @param backup the value to return if none exists in the table
|
||||
* @return either the value in the table, or the backup
|
||||
* @throws IncompatibleTypeException if the value in the table can not be
|
||||
* converted to a long
|
||||
*/
|
||||
public long getLong(String key, long backup) {
|
||||
String value = get(key);
|
||||
if (value == null) {
|
||||
put(key, String.valueOf(backup));
|
||||
try {
|
||||
return (long)table.getNumber(key);
|
||||
} catch (TableKeyNotDefinedException e) {
|
||||
return backup;
|
||||
} else {
|
||||
try {
|
||||
return Long.parseLong(value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IncompatibleTypeException(value, "long");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the preferences to a file on the RoboRIO.
|
||||
*
|
||||
* <p>
|
||||
* This should <b>NOT</b> be called often. Too many writes can damage the
|
||||
* RoboRIO's flash memory. While it is ok to save once or twice a match, this
|
||||
* should never be called every run of {@link IterativeRobot#teleopPeriodic()}
|
||||
* .
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The actual writing of the file is done in a separate thread. However, any
|
||||
* call to a get or put method will wait until the table is fully saved before
|
||||
* continuing.
|
||||
* </p>
|
||||
* This function is no longer required, as NetworkTables automatically
|
||||
* saves persistent values (which all Preferences values are) periodically
|
||||
* when running as a server.
|
||||
* @deprecated backwards compatibility shim
|
||||
*/
|
||||
public void save() {
|
||||
synchronized (fileLock) {
|
||||
new Thread() {
|
||||
public void run() {
|
||||
write();
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
fileLock.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method that actually writes the table to a file. This is called in
|
||||
* its own thread when {@link Preferences#save() save()} is called.
|
||||
*/
|
||||
private void write() {
|
||||
synchronized (lock) {
|
||||
synchronized (fileLock) {
|
||||
fileLock.notifyAll();
|
||||
}
|
||||
|
||||
File file = null;
|
||||
FileOutputStream output = null;
|
||||
try {
|
||||
file = new File(FILE_NAME);
|
||||
|
||||
if (file.exists())
|
||||
file.delete();
|
||||
|
||||
file.createNewFile();
|
||||
|
||||
output = new FileOutputStream(file);
|
||||
|
||||
output.write("[Preferences]\n".getBytes());
|
||||
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
String key = (String) keys.elementAt(i);
|
||||
String value = (String) values.get(key);
|
||||
|
||||
if (comments != null) {
|
||||
Comment comment = (Comment) comments.get(key);
|
||||
if (comment != null) {
|
||||
comment.write(output);
|
||||
}
|
||||
}
|
||||
|
||||
output.write(key.getBytes());
|
||||
output.write(VALUE_PREFIX);
|
||||
output.write(value.getBytes());
|
||||
output.write(VALUE_SUFFIX);
|
||||
}
|
||||
|
||||
if (endComment != null) {
|
||||
endComment.write(output);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
if (output != null) {
|
||||
try {
|
||||
output.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
NetworkTable.getTable(TABLE_NAME).putBoolean(SAVE_FIELD, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal method to read from a file. This will be called in its own
|
||||
* thread when the preferences singleton is first created.
|
||||
*/
|
||||
private void read() {
|
||||
class EndOfStreamException extends Exception {
|
||||
}
|
||||
|
||||
class Reader {
|
||||
|
||||
InputStream stream;
|
||||
|
||||
Reader(InputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public char read() throws IOException, EndOfStreamException {
|
||||
int input = stream.read();
|
||||
if (input == -1) {
|
||||
throw new EndOfStreamException();
|
||||
} else {
|
||||
// Check for carriage returns
|
||||
return input == '\r' ? '\n' : (char) input;
|
||||
}
|
||||
}
|
||||
|
||||
char readWithoutWhitespace() throws IOException, EndOfStreamException {
|
||||
while (true) {
|
||||
char value = read();
|
||||
switch (value) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
continue;
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (lock) {
|
||||
synchronized (fileLock) {
|
||||
fileLock.notifyAll();
|
||||
}
|
||||
|
||||
Comment comment = null;
|
||||
|
||||
|
||||
|
||||
File file = null;
|
||||
FileInputStream input = null;
|
||||
try {
|
||||
file = new File(FILE_NAME);
|
||||
|
||||
if (file.exists()) {
|
||||
input = new FileInputStream(file);
|
||||
Reader reader = new Reader(input);
|
||||
|
||||
StringBuffer buffer;
|
||||
|
||||
while (true) {
|
||||
char value = reader.readWithoutWhitespace();
|
||||
|
||||
if (value == '\n' || value == ';') {
|
||||
if (comment == null) {
|
||||
comment = new Comment();
|
||||
}
|
||||
|
||||
if (value == '\n') {
|
||||
comment.addBytes(NEW_LINE);
|
||||
} else {
|
||||
buffer = new StringBuffer(30);
|
||||
for (; value != '\n'; value = reader.read()) {
|
||||
buffer.append(value);
|
||||
}
|
||||
buffer.append('\n');
|
||||
comment.addBytes(buffer.toString().getBytes());
|
||||
}
|
||||
} else if (value == '[') {
|
||||
// Find the end of the section and the new line after it and throw
|
||||
// it away
|
||||
while (reader.read() != ']');
|
||||
while (reader.read() != '\n');
|
||||
} else {
|
||||
buffer = new StringBuffer(30);
|
||||
for (; value != '='; value = reader.readWithoutWhitespace()) {
|
||||
buffer.append(value);
|
||||
}
|
||||
String name = buffer.toString();
|
||||
buffer = new StringBuffer(30);
|
||||
|
||||
boolean shouldBreak = false;
|
||||
|
||||
value = reader.readWithoutWhitespace();
|
||||
if (value == '"') {
|
||||
for (value = reader.read(); value != '"'; value = reader.read()) {
|
||||
buffer.append(value);
|
||||
}
|
||||
// Clear the line
|
||||
while (reader.read() != '\n');
|
||||
} else {
|
||||
try {
|
||||
for (; value != '\n'; value = reader.readWithoutWhitespace()) {
|
||||
buffer.append(value);
|
||||
}
|
||||
} catch (EndOfStreamException e) {
|
||||
shouldBreak = true;
|
||||
}
|
||||
}
|
||||
|
||||
String result = buffer.toString();
|
||||
|
||||
keys.addElement(name);
|
||||
values.put(name, result);
|
||||
NetworkTable.getTable(TABLE_NAME).putString(name, result);
|
||||
|
||||
if (comment != null) {
|
||||
if (comments == null) {
|
||||
comments = new Hashtable();
|
||||
}
|
||||
comments.put(name, comment);
|
||||
comment = null;
|
||||
}
|
||||
|
||||
|
||||
if (shouldBreak) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (EndOfStreamException ex) {
|
||||
System.out.println("Done Reading");
|
||||
}
|
||||
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
if (comment != null) {
|
||||
endComment = comment;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkTable.getTable(TABLE_NAME).putBoolean(SAVE_FIELD, false);
|
||||
// TODO: Verify that this works even though it changes with subtables.
|
||||
// Should work since preferences shouldn't have subtables.
|
||||
NetworkTable.getTable(TABLE_NAME).addTableListener(new ITableListener() {
|
||||
public void valueChanged(ITable source, String key, Object value, boolean isNew) {
|
||||
if (key.equals(SAVE_FIELD)) {
|
||||
if (((Boolean) value).booleanValue()) {
|
||||
save();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
if (!ImproperPreferenceKeyException.isAcceptable(key)
|
||||
|| value.toString().indexOf('"') != -1) {
|
||||
if (values.contains(key) || keys.contains(key)) {
|
||||
values.remove(key);
|
||||
keys.removeElement(key);
|
||||
NetworkTable.getTable(TABLE_NAME).putString(key, "\"");
|
||||
}
|
||||
} else {
|
||||
if (values.put(key, value.toString()) == null) {
|
||||
keys.addElement(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A class representing some comment lines in the ini file. This is used so
|
||||
* that if a programmer ever directly modifies the ini file, then his/her
|
||||
* comments will still be there after {@link Preferences#save() save()} is
|
||||
* called.
|
||||
*/
|
||||
private static class Comment {
|
||||
|
||||
/**
|
||||
* A vector of byte arrays. Each array represents a line to write
|
||||
*/
|
||||
private Vector bytes = new Vector();
|
||||
|
||||
/**
|
||||
* Appends the given bytes to the comment.
|
||||
*
|
||||
* @param bytes the bytes to add
|
||||
*/
|
||||
private void addBytes(byte[] bytes) {
|
||||
this.bytes.addElement(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes this comment to the given stream
|
||||
*
|
||||
* @param stream the stream to write to
|
||||
* @throws IOException if the stream has a problem
|
||||
*/
|
||||
private void write(OutputStream stream) throws IOException {
|
||||
for (int i = 0; i < bytes.size(); i++) {
|
||||
stream.write((byte[]) bytes.elementAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This exception is thrown if the a value requested cannot be converted to
|
||||
* the requested type.
|
||||
*/
|
||||
public static class IncompatibleTypeException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Creates an exception with a description based on the input
|
||||
*
|
||||
* @param value the value that can not be converted
|
||||
* @param type the type that the value can not be converted to
|
||||
*/
|
||||
public IncompatibleTypeException(String value, String type) {
|
||||
super("Cannot convert \"" + value + "\" into " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be thrown if a string can not be used as a key in the preferences
|
||||
* file. This happens if the string contains a new line, a space, a tab, or an
|
||||
* equals sign.
|
||||
*/
|
||||
public static class ImproperPreferenceKeyException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Instantiates an exception with a descriptive message based on the input.
|
||||
*
|
||||
* @param value the illegal key
|
||||
* @param letter the specific character that made it illegal
|
||||
*/
|
||||
public ImproperPreferenceKeyException(String value, char letter) {
|
||||
super("Preference key \"" + value + "\" is not allowed to contain letter with ASCII code:"
|
||||
+ (byte) letter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given string is ok to use as a key in the preference table.
|
||||
* If not, then a {@link ImproperPreferenceKeyException} will be thrown.
|
||||
*
|
||||
* @param value the value to test
|
||||
*/
|
||||
public static void confirmString(String value) {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char letter = value.charAt(i);
|
||||
switch (letter) {
|
||||
case '=':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '[':
|
||||
case ']':
|
||||
throw new ImproperPreferenceKeyException(value, letter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the given string is ok to use in the preference
|
||||
* table.
|
||||
*
|
||||
* @param value the string to check
|
||||
* @return true if the given string is ok to use in the preference table
|
||||
*/
|
||||
public static boolean isAcceptable(String value) {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char letter = value.charAt(i);
|
||||
switch (letter) {
|
||||
case '=':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '[':
|
||||
case ']':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public abstract class RobotBase {
|
||||
// TODO: See if the next line is necessary
|
||||
// Resource.RestartProgram();
|
||||
|
||||
NetworkTable.setNetworkIdentity("Robot");
|
||||
NetworkTable.setServerMode();// must be before b
|
||||
m_ds = DriverStation.getInstance();
|
||||
NetworkTable.getTable(""); // forces network tables to initialize
|
||||
|
||||
Reference in New Issue
Block a user