Skip to content

Commit

Permalink
correct html, add helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
bobjacobsen committed May 10, 2020
1 parent 0c7a8ec commit 41de5e1
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 5 deletions.
27 changes: 22 additions & 5 deletions help/en/html/doc/Technical/XmlPersistance.shtml
Expand Up @@ -143,6 +143,11 @@

<p>Boolean values should be stored as the strings "true" and "false".</p>

<p>The <code>getAttributeIntegerValue</code> and
<code>getAttributeBooleanValue</code>
methods provide a simple way of parsing input and handling errors.
</p>

<p>Although much of the early code stored information in attributes,
consider putting the data being stored in elements instead. This
makes for simpler XML Schema and XSLT definitions, and the structured nature can
Expand All @@ -159,10 +164,20 @@
"http://jmri.org/JavaDoc/doc/jmri/configurexml/AbstractTurnoutManagerConfigXML.html">
jmri.configurexml.AbstractSensorManagerConfigXML</a>, which
does almost all the work of storing and loading sensors.</p>

<h3><a id="enum" name="refs">Persisting enum values</h3>

The “EnumIO” tool built into AbstractXmlAdapter that reads and writes enums via the names of their elements.

<h3><a id="errors" name="errors">Handling Errors</a></h3>

If theres a parsing error, exception, etc, it should be reported
via the ErrorHandler. This accumulates reports and presents them
(in a almost-useful way) to the user when appropriate.
<p>
For an example of how to do that, see the <code>getAttributeBooleanValue</code>
method.

<h3><a id="enum" name="enum">Persisting enum values</a></h3>

The "EnumIO" tool built into AbstractXmlAdapter that reads and writes enums via the names of their elements.
<p>
You add a member in your configureXml load/store class:
<pre>
Expand All @@ -172,13 +187,13 @@
variable, you pass the enum value through that map element in your <code>store</code> method:

<pre>
element.setAttribute(name", "" + enumMap.outputFromEnum(myEnumValue));
element.setAttribute("name", "" + enumMap.outputFromEnum(myEnumValue));
</pre><br>
Storing into an element is similar

To restore the value on load:
<pre>
myEnumValue = enumMap.inputFromAttribute(element.getAttribute(name"));
myEnumValue = enumMap.inputFromAttribute(element.getAttribute("name"));
</pre><br>

There are several variations available, please see
Expand All @@ -197,6 +212,8 @@ To restore the value on load:
the values loaded and stored. It's possible to provide multiple
load values that make to a single enum value, i.e. "3" and "Ralph".
</ul>


<h3><a id="refs" name="refs">Persisting References to NamedBeans</a></h3>

Classes should, but don't always,
Expand Down
58 changes: 58 additions & 0 deletions java/src/jmri/configurexml/AbstractXmlAdapter.java
Expand Up @@ -91,7 +91,65 @@ public ErrorHandler getExceptionHandler() {
return this.errorHandler;
}

/**
* Service method to handle attribute input of
* boolean (true/yes vs false/no) values. Not being present
* is not an error. Not parsing (which shouldn't happen due to
* the XML Schema checks) invokes the default error handler.
*/
final public boolean getAttributeBooleanValue(@Nonnull Element element, @Nonnull String name, boolean def) {
Attribute a = null;
String val = null;
try {
a = element.getAttribute(name);
if (a == null) return def;
val = a.getValue();
if ( val.equals("yes") || val.equals("true") ) return true; // non-externalized strings
if ( val.equals("no") || val.equals("false") ) return false;
return def;
} catch (Exception ex) {
log.debug("caught exception", ex);
ErrorMemo em = new ErrorMemo(this,
"getAttributeBooleanValue threw exception",
"element: "+element.getName(),
"attribute: "+name,
"value: "+val,
ex);
getExceptionHandler().handle(em);
return def;
}
}

/**
* Service method to handle attribute input of
* integer values. Not being present
* is not an error. Not parsing (which shouldn't happen due to
* the XML Schema checks) invokes the default error handler.
*/
final public int getAttributeIntegerValue(@Nonnull Element element, @Nonnull String name, int def) {
Attribute a = null;
String val = null;
try {
a = element.getAttribute(name);
if (a == null) return def;
val = a.getValue();
return a.getIntValue();
} catch (Exception ex) {
log.debug("caught exception", ex);
ErrorMemo em = new ErrorMemo(this,
"getAttributeIntegerValue threw exception",
"element: "+element.getName(),
"attribute: "+name,
"value: "+val,
ex);
getExceptionHandler().handle(em);
return def;
}
}

/**
* Base for support of Enum load/store to XML files
*/
public static abstract class EnumIO <T extends Enum<T>> { // public to be usable by adapters in other configXML packages

/**
Expand Down
49 changes: 49 additions & 0 deletions java/test/jmri/configurexml/AbstractXmlAdapterTest.java
@@ -1,6 +1,7 @@
package jmri.configurexml;

import java.util.HashMap;
import org.jdom2.*;
import org.junit.*;

/**
Expand All @@ -10,6 +11,54 @@
*/
public class AbstractXmlAdapterTest{

@Test
public void testGetAttributeBooleanValue() {
AbstractXmlAdapter adapter = new AbstractXmlAdapter(){
public Element store(Object o) {return null;}
public void load(Element e, Object o) {}
};

Element testEl = new Element("foo");

Assert.assertTrue(adapter.getAttributeBooleanValue(testEl, "att", true));
Assert.assertFalse(adapter.getAttributeBooleanValue(testEl, "att", false));

testEl.setAttribute("t", "true");
testEl.setAttribute("f", "false");
testEl.setAttribute("y", "yes");
testEl.setAttribute("n", "no");

Assert.assertTrue(adapter.getAttributeBooleanValue(testEl, "t", true));
Assert.assertTrue(adapter.getAttributeBooleanValue(testEl, "t", false));

Assert.assertFalse(adapter.getAttributeBooleanValue(testEl, "f", true));
Assert.assertFalse(adapter.getAttributeBooleanValue(testEl, "f", false));

Assert.assertTrue(adapter.getAttributeBooleanValue(testEl, "y", true));
Assert.assertTrue(adapter.getAttributeBooleanValue(testEl, "y", false));

Assert.assertFalse(adapter.getAttributeBooleanValue(testEl, "n", true));
Assert.assertFalse(adapter.getAttributeBooleanValue(testEl, "n", false));


}

@Test
public void testGetAttributeIintegerValue() {
AbstractXmlAdapter adapter = new AbstractXmlAdapter(){
public Element store(Object o) {return null;}
public void load(Element e, Object o) {}
};

Element testEl = new Element("foo");

Assert.assertEquals(12, adapter.getAttributeIntegerValue(testEl, "att", 12));

testEl.setAttribute("t21", "21");

Assert.assertEquals(21, adapter.getAttributeIntegerValue(testEl, "t21", 12));
}

enum testEnum {Foo, Bar, Biff}

@Test
Expand Down

0 comments on commit 41de5e1

Please sign in to comment.