When processing XML by means of standard DOM, attribute order is not guaranteed after you serialize back. At last that is what I just realized when using standard java XML Transform API to serialize the output.
However I do need to keep an order. I would like to know if there is any posibility on Java to keep the original order of attributes of an XML file processed by means of DOM API, or any way to force the order (maybe by using an alternative serialization API that lets you set this kind of property). In my case processing reduces to alter the value of some attributes (not all) of a sequence of the same elements with a bunch of attributes, and maybe insert a few more elements.
Is there any "easy" way or do I have to define my own XSLT transformation stylesheet to specify the output and altering the whole input XML file?
Update I must thank all your answers. The answer seems now more obvious than I expected. I never paid any attention to attribute order, since I had never needed it before.
The main reason to require an attribute order is that the resulting XML file just looks different. The target is a configuration file that holds hundreds of alarms (every alarm is defined by a set of attributes). This file usually has little modifications over time, but it is convenient to keep it ordered, since when we need to modify something it is edited by hand. Now and then some projects need light modifications of this file, such as setting one of the attributes to a customer specific code.
I just developed a little application to merge original file (common to all projects) with specific parts of each project (modify the value of some attributes), so project-specific file gets the updates of the base one (new alarm definitions or some attribute values bugfixes). My main motivation to require ordered attributes is to be able to check the output of the application againts the original file by means of a text comparation tool (such as Winmerge). If the format (mainly attribute order) remains the same, the differences can be easily spotted.
I really thought this was possible, since XML handling programs, such as XML Spy, lets you edit XML files and apply some ordering (grid mode). Maybe my only choice is to use one of these programs to manually modify the output file.
Sorry to say, but the answer is more subtle than "No you can't" or "Why do you need to do this in the first place ?".
The short answer is "DOM will not allow you to do that, but SAX will".
This is because DOM does not care about the attribute order, since it's meaningless as far as the standard is concerned, and by the time the XSL gets hold of the input stream, the info is already lost.
Most XSL engine will actually gracefully preserve the input stream attribute order (e.g.
Xalan-C (except in one case) or Xalan-J (always)). Especially if you use <xsl:copy*>.
Cases where the attribute order is not kept, best of my knowledge, are.
- If the input stream is a DOM
- Xalan-C: if you insert your result-tree tags literally (e.g. <elem att1={#att1} .../>
Here is one example with SAX, for the record (inhibiting DTD nagging as well).
SAXParserFactory spf = SAXParserFactoryImpl.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(false);
spf.setFeature("http://xml.org/sax/features/validation", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
SAXParser sp = spf.newSAXParser() ;
Source src = new SAXSource ( sp.getXMLReader(), new InputSource( input.getAbsolutePath() ) ) ;
String resultFileName = input.getAbsolutePath().replaceAll(".xml$", ".cooked.xml" ) ;
Result result = new StreamResult( new File (resultFileName) ) ;
TransformerFactory tf = TransformerFactory.newInstance();
Source xsltSource = new StreamSource( new File ( COOKER_XSL ) );
xsl = tf.newTransformer( xsltSource ) ;
xsl.setParameter( "srcDocumentName", input.getName() ) ;
xsl.setParameter( "srcDocumentPath", input.getAbsolutePath() ) ;
xsl.transform(src, result );
I'd also like to point out, at the intention of many naysayers that there are cases where attribute order does matter.
Regression testing is an obvious case.
Whoever has been called to optimise not-so-well written XSL knows that you usually want to make sure that "new" result trees are similar or identical to the "old" ones. And when the result tree are around one million lines, XML diff tools prove too unwieldy...
In these cases, preserving attribute order is of great help.
Hope this helps ;-)
Look at section 3.1 of the XML recommendation. It says, "Note that the order of attribute specifications in a start-tag or empty-element tag is not significant."
If a piece of software requires attributes on an XML element to appear in a specific order, that software is not processing XML, it's processing text that looks superficially like XML. It needs to be fixed.
If it can't be fixed, and you have to produce files that conform to its requirements, you can't reliably use standard XML tools to produce those files. For instance, you might try (as you suggest) to use XSLT to produce attributes in a defined order, e.g.:
<test>
<xsl:attribute name="foo"/>
<xsl:attribute name="bar"/>
<xsl:attribute name="baz"/>
</test>
only to find that the XSLT processor emits this:
<test bar="" baz="" foo=""/>
because the DOM that the processor is using orders attributes alphabetically by tag name. (That's common but not universal behavior among XML DOMs.)
But I want to emphasize something. If a piece of software violates the XML recommendation in one respect, it probably violates it in other respects. If it breaks when you feed it attributes in the wrong order, it probably also breaks if you delimit attributes with single quotes, or if the attribute values contain character entities, or any of a dozen other things that the XML recommendation says that an XML document can do that the author of this software probably didn't think about.
XML Canonicalisation results in a consistent attribute ordering, primarily to allow one to check a signature over some or all of the XML, though there are other potential uses. This may suit your purposes.
It's not possible to over-emphasize what Robert Rossney just said, but I'll try. ;-)
The benefit of International Standards is that, when everybody follows them, life is good. All our software gets along peacefully.
XML has to be one of the most important standards we have. It's the basis of "old web" stuff like SOAP, and still 'web 2.0' stuff like RSS and Atom. It's because of clear standards that XML is able to interoperate between different platforms.
If we give up on XML, little by little, we'll get into a situation where a producer of XML will not be able to assume that a consumer of XML will be able to consumer their content. This would have a disasterous affect on the industry.
We should push back very forcefully, on anyone who writes code that does not process XML according to the standard. I understand that, in these economic times, there is a reluctance to offend customers and business partners by saying "no". But in this case, I think it's worth it. We would be in much worse financial shape if we had to hand-craft XML for each business partner.
So, don't "enable" companies who do not understand XML. Send them the standard, with the appropriate lines highlighted. They need to stop thinking that XML is just text with angle brackets in it. It simply does not behave like text with angle brackets in it.
It's not like there's an excuse for this. Even the smallest embedded devices can have full-featured XML parser implementations in them. I have not yet heard a good reason for not being able to parse standard XML, even if one can't afford a fully-featured DOM implementation.
I think I can find some valid justifications for caring about attribute order:
You may be expecting humans to have to manually read, diagnose or edit the XML data one time or another; readability would be important in that instance, and a consistent and logical ordering of the attributes helps with that;
You may have to communicate with some tool or service that (admitedly erroneously) cares about the order; asking the provider to correct its code may not be an option: try to ask that from a government agency while your user's deadline for electronically delivering a bunch of fiscal documents looms closer and closer!
It seems like Alain Pannetier's solution is the way to go.
Also, you may want to take a look at DecentXML; it gives you full control of how the XML is formatted, even though it's not DOM-compatible. Specially useful if you want to modify some hand-edited XML without losing the formatting.
I had the same exact problem. I wanted to modify XML attributes but wanted to keep the order because of diff. I used StAX to achieve this. You have to use XMLStreamReader and XMLStreamWriter (the Cursor based solution). When you get a START_ELEMENT event type, the cursor keeps the index of the attributes. Hence, you can make appropriate modifications and write them to the output file "in order".
Look at this article/discussion. You can see how to read the attributes of the start elements in order.
You can still do this using the standard DOM and Transformation API by using a quick and dirty solution like the one I am describing:
We know that the transformation API solution orders the attributes alphabetically. You can prefix the attributes names with some easy-to-strip-later strings so that they will be output in the order you want. Simple prefixes as "a_" "b_" etc should suffice in most situations and can be easily stripped from the output xml using a one liner regex.
If you are loading an xml and resave and want to preserve attributes order, you can use the same principle, by first modifying the attribute names in the input xml text and then parsing it into a Document object. Again, make this modification based on a textual processing of the xml. This can be tricky but can be done by detecting elements and their attributes strings, again, using regex. Note that this is a dirty solution. There are many pitfalls when parsing XML on your own, even for something as simple as this, so be careful if you decide to implement this.
You really shouldn't need to keep any sort of order. As far as I know, no schema takes attribute order into account when validating an XML document either. It sounds like whatever is processing XML on the other end isn't using a proper DOM to parse the results.
I suppose one option would be to manually build up the document using string building, but I strongly recommend against that.
Robert Rossney said it well: if you're relying on the ordering of attributes, you're not really processing XML, but rather, something that looks like XML.
I can think of at least two reasons why you might care about attribute ordering. There may be others, but at least for these two I can suggest alternatives:
You're using multiple instances of attributes with the same name:
<foo myAttribute="a" myAttribute="b" myAttribute="c"/>
This is just plain invalid XML; a DOM processor will probably drop all but one of these values – if it processes the document at all. Instead of this, you want to use child elements:
<foo>
<myChild="a"/>
<myChild="b"/>
<myChild="c"/>
</foo>
You're assuming that some sort of distinction applies to the attribute(s) that come first. Make this explicit, either through other attributes or through child elements. For example:
<foo attr1="a" attr2="b" attr3="c" theMostImportantAttribute="attr1" />
Kind of works...
package mynewpackage;
// for the method
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
// for the test example
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import org.w3c.dom.Document;
import java.math.BigDecimal;
public class NodeTools {
/**
* Method sorts any NodeList by provided attribute.
* #param nl NodeList to sort
* #param attributeName attribute name to use
* #param asc true - ascending, false - descending
* #param B class must implement Comparable and have Constructor(String) - e.g. Integer.class , BigDecimal.class etc
* #return
*/
public static Node[] sortNodes(NodeList nl, String attributeName, boolean asc, Class<? extends Comparable> B)
{
class NodeComparator<T> implements Comparator<T>
{
#Override
public int compare(T a, T b)
{
int ret;
Comparable bda = null, bdb = null;
try{
Constructor bc = B.getDeclaredConstructor(String.class);
bda = (Comparable)bc.newInstance(((Element)a).getAttribute(attributeName));
bdb = (Comparable)bc.newInstance(((Element)b).getAttribute(attributeName));
}
catch(Exception e)
{
return 0; // yes, ugly, i know :)
}
ret = bda.compareTo(bdb);
return asc ? ret : -ret;
}
}
List<Node> x = new ArrayList<>();
for(int i = 0; i < nl.getLength(); i++)
{
x.add(nl.item(i));
}
Node[] ret = new Node[x.size()];
ret = x.toArray(ret);
Arrays.sort(ret, new NodeComparator<Node>());
return ret;
}
public static void main(String... args)
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
String s = "<xml><item id=\"1\" price=\"100.00\" /><item id=\"3\" price=\"29.99\" /><item id=\"2\" price=\"5.10\" /></xml>";
Document doc = null;
try
{
builder = factory.newDocumentBuilder();
doc = builder.parse(new InputSource(new StringReader(s)));
}
catch(Exception e) { System.out.println("Alarm "+e); return; }
System.out.println("*** Sort by id ***");
Node[] ret = NodeTools.sortNodes(doc.getElementsByTagName("item"), "id", true, Integer.class);
for(Node n: ret)
{
System.out.println(((Element)n).getAttribute("id")+" : "+((Element)n).getAttribute("price"));
}
System.out.println("*** Sort by price ***");
ret = NodeTools.sortNodes(doc.getElementsByTagName("item"), "price", true, BigDecimal.class);
for(Node n: ret)
{
System.out.println(((Element)n).getAttribute("id")+" : "+((Element)n).getAttribute("price"));
}
}
}
In my simple test it prints:
*** Sort by id ***
1 : 100.00
2 : 5.10
3 : 29.99
*** Sort by price ***
2 : 5.10
3 : 29.99
1 : 100.00
Inspired by the answer of Andrey Lebedenko.
Capable of sorting by a Nodes attribute or by a Nodes text content.
Ready to be used in Your XML utility class.
public static Collection<Node> nodeListCollection(final NodeList nodeList) {
if (nodeList == null) {
return Collections.emptyList();
}
final int length = nodeList.getLength();
if (length == 0) {
return Collections.emptyList();
}
return IntStream.range(0, length)
.mapToObj(nodeList::item)
.collect(Collectors.toList());
}
private static int compareString(final String str1, final String str2, final boolean nullIsLess) {
if (Objects.equals(str1, str2)) {
return 0;
}
if (str1 == null) {
return nullIsLess ? -1 : 1;
}
if (str2 == null) {
return nullIsLess ? 1 : -1;
}
return str1.compareTo(str2);
}
private static final Function<Boolean, Comparator<Node>> StringNodeValueComparatorSupplier = (asc) ->
(Node a, Node b) -> {
final String va = a == null ? null : a.getTextContent();
final String vb = b == null ? null : b.getTextContent();
return (asc ? 1 : -1) * compareString(va, vb,asc);
};
private static final BiFunction<Boolean, String, Comparator<Node>> StringNodeAttributeComparatorSupplier = (asc, attrName) ->
(Node a, Node b) -> {
final String va = a == null ? null : a.hasAttributes() ?
((Element) a).getAttribute(attrName) : null;
final String vb = b == null ? null : b.hasAttributes() ?
((Element) b).getAttribute(attrName) : null;
return (asc ? 1 : -1) * compareString(va, vb,asc);
};
private static <T extends Comparable<T>> Comparator<Node> nodeComparator(
final boolean asc,
final boolean useAttr,
final String attribute,
final Constructor<T> constructor
) {
return (Node a, Node b) -> {
if (a == null && b == null) {
return 0;
} else if (a == null) {
return (asc ? -1 : 1);
} else if (b == null) {
return (asc ? 1 : -1);
}
T aV;
try {
final String aStr;
if (useAttr) {
aStr = a.hasAttributes() ? ((Element) a).getAttribute(attribute) : null;
} else {
aStr = a.getTextContent();
}
aV = aStr == null || aStr.matches("\\s+") ? null : constructor.newInstance(aStr);
} catch (Exception ignored) {
aV = null;
}
T bV;
try {
final String bStr;
if (useAttr) {
bStr = b.hasAttributes() ? ((Element) b).getAttribute(attribute) : null;
} else {
bStr = b.getTextContent();
}
bV = bStr == null || bStr.matches("\\s+") ? null : constructor.newInstance(bStr);
} catch (Exception ignored) {
bV = null;
}
final int ret;
if (aV == null && bV == null) {
ret = 0;
} else if (aV == null) {
ret = -1;
} else if (bV == null) {
ret = 1;
} else {
ret = aV.compareTo(bV);
}
return (asc ? 1 : -1) * ret;
};
}
/**
* Method to sort any NodeList by an attribute all nodes must have. <br>If the attribute is absent for a signle
* {#link Node} or the {#link NodeList} does contain elements without Attributes, null is used instead. <br>If
* <code>asc</code> is
* <code>true</code>, nulls first, else nulls last.
*
* #param nodeList The {#link NodeList} containing all {#link Node} to sort.
* #param attribute Name of the attribute to extract and compare
* #param asc <code>true</code>: ascending, <code>false</code>: descending
* #param compareType Optional class to use for comparison. Must implement {#link Comparable} and have Constructor
* that takes a single {#link String} argument. If <code>null</code> is supplied, {#link String} is used.
* #return A collection of the {#link Node}s passed as {#link NodeList}
* #throws RuntimeException If <code>compareType</code> does not have a constructor taking a single {#link String}
* argument. Also, if the comparator created does violate the {#link Comparator} contract, an
* {#link IllegalArgumentException} is raised.
* #implNote Exceptions during calls of the single String argument constructor of <code>compareType</code> are
* ignored. Values are substituted by <code>null</code>
*/
public static <T extends Comparable<T>> Collection<Node> sortNodesByAttribute(
final NodeList nodeList,
String attribute,
boolean asc,
Class<T> compareType) {
final Comparator<Node> nodeComparator;
if (compareType == null) {
nodeComparator = StringNodeAttributeComparatorSupplier.apply(asc, attribute);
} else {
final Constructor<T> constructor;
try {
constructor = compareType.getDeclaredConstructor(String.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(
"Cannot compare Node Attribute '" + attribute + "' using the Type '" + compareType.getName()
+ "': No Constructor available that takes a single String argument.", e);
}
nodeComparator = nodeComparator(asc, true, attribute, constructor);
}
final List<Node> nodes = new ArrayList<>(nodeListCollection(nodeList));
nodes.sort(nodeComparator);
return nodes;
}
/**
* Method to sort any NodeList by their text content using an optional type. <br>If
* <code>asc</code> is
* <code>true</code>, nulls first, else nulls last.
*
* #param nodeList The {#link NodeList} containing all {#link Node}s to sort.
* #param asc <code>true</code>: ascending, <code>false</code>: descending
* #param compareType Optional class to use for comparison. Must implement {#link Comparable} and have Constructor
* that takes a single {#link String} argument. If <code>null</code> is supplied, {#link String} is used.
* #return A collection of the {#link Node}s passed as {#link NodeList}
* #throws RuntimeException If <code>compareType</code> does not have a constructor taking a single {#link String}
* argument. Also, if the comparator created does violate the {#link Comparator} contract, an
* {#link IllegalArgumentException} is raised.
* #implNote Exceptions during calls of the single String argument constructor of <code>compareType</code> are
* ignored. Values are substituted by <code>null</code>
*/
public static <T extends Comparable<T>> Collection<Node> sortNodes(
final NodeList nodeList,
boolean asc,
Class<T> compareType) {
final Comparator<Node> nodeComparator;
if (compareType == null) {
nodeComparator = StringNodeValueComparatorSupplier.apply(asc);
} else {
final Constructor<T> constructor;
try {
constructor = compareType.getDeclaredConstructor(String.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(
"Cannot compare Nodes using the Type '" + compareType.getName()
+ "': No Constructor available that takes a single String argument.", e);
}
nodeComparator = nodeComparator(asc, false, null, constructor);
}
final List<Node> nodes = new ArrayList<>(nodeListCollection(nodeList));
nodes.sort(nodeComparator);
return nodes;
}
I have a quite similar problem. I need to have always the same attribute for first.
Example :
<h50row a="1" xidx="1" c="1"></h50row>
<h50row a="2" b="2" xidx="2"></h50row>
must become
<h50row xidx="1" a="1" c="1"></h50row>
<h50row xidx="2" a="2" b="2"></h50row>
I found a solution with a regex:
test = "<h50row a=\"1\" xidx=\"1\" c=\"1\"></h50row>";
test = test.replaceAll("(<h5.*row)(.*)(.xidx=\"\\w*\")([^>]*)(>)", "$1$3$2$4$5");
Hope you find this usefull
My java project required that I create an array of objects(items), populate the array of items, and then create a main method that asks a user to enter the item code which spits back the corresponding item.
It took me a while to figure out, but I ended up "cheating" by using a public variable to avoid passing/referencing the object between classes.
Please help me properly pass the object back.
This is the class with most of my methods including insert and the find method.
public class Catalog {
private Item[] itemlist;
private int size;
private int nextInsert;
public Item queriedItem;
public Catalog (int max) {
itemlist = new Item[max];
size = 0;
}
public void insert (Item item) {
itemlist[nextInsert] = item;
++nextInsert;
++size;
}
public Item find (int key) {
queriedItem = null;
for (int posn = 0; posn < size; ++posn) {
if (itemlist[posn].getKey() == key) queriedItem = itemlist[posn];
}{
return queriedItem;
}
}
}
This is my main class:
import java.util.*;
public class Program {
public static void main (String[] args) {
Scanner kbd = new Scanner (System.in);
Catalog store;
int key = 1;
store = new Catalog (8);
store.insert(new Item(10, "food", 2.00));
store.insert(new Item(20, "drink", 1.00));
while (key != 0) {
System.out.printf("Item number (0 to quit) ?%n");
key = kbd.nextInt();
if (key == 0) {
System.out.printf("Exiting program now!");
System.exit(0);
}
store.find(key);
if (store.queriedItem != null) {
store.queriedItem.print();
}
else System.out.printf("No Item found for %d%n", key);
}
}
}
Thanks I appreciate the help!!!!!!
store.find(key); returns an Item you should use it and delete the public field from Catalog
public Item find (int key) {
Item queriedItem = null;
//....
}
Item searched = store.find(key);
if (searched != null)
searched.print();
else
System.out.printf("No Item found for %d%n", key);
Remove your use of queriedItem entirely and just return the item from find: Replace
store.find(key);
if (store.queriedItem != null){store.queriedItem.print();}else System.out.printf("No Item found for %d%n", key);
With
Item foundItem = store.find(key);
if (foundItem != null) {
foundItem.print();
} else System.out.printf("No Item found for %d%n", key);
Well, here are some suggesetions (choose complexity at your own discretion, but all of them is highly recommended):
Research Properties, for example here. Or XML. You could populate the array with values from a configuration file for greater flexibility.
Use constanst for literals in your code (where they are necessary).
Create an ApplicationFactory the initializes the whole application for you. Things like this need to be separated from your domain logic.
Create a UserInputProvider interface so you can easily change the way the input of the user is read without affecting anything else. Implement it with a ConsoleInputProvider class for example.
In general, try using interfaces for everything that's not a pure domain object (here, the only one you have is probably Item).
Try to keep your methods as short as possible. Instead of doing many things in a method, have it invoke other methods (grouping related logic) named appropriately to tell what it is doing.
If you're not allowed to cheat and use List or a Map, devise your own implementation of one, separating data structure and handling from the logic represented by Catalog (i.e. Catalog in turn will delegate to, for example, Map.get or equivalent method of your data structure implementation)
Your main should basically just have ApplicationFactory (or an IoC framework) to build and initialize your application, invoke the UserInputProvider (it should not know the exact implementation it is using) to get user input, validate and convert the data as required, invoke Catalog to find the appropriate Item and then (similarly to the input interface) send the result (the exact data it got, not some string or alike) to some implementation of a SearchResultView interface that decides how to display this result (in this case it will be a console-based implementation, that prints a string representing the Item it got).
Generally, the higher the level of decoupling you can achieve, the better your program will be.
The Single Responsibility Principle states: " every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class". This is also true for methods: they should have one and only one well defined task without any side effects.
I have several wicket tests that target a sortable DataTable, specifically ajax-clicking the sortable column headers and asserting the contents of the rendered body rows. Now the component hierarchy of the table component's descendants is auto generated by the wicket framework, and results in paths to the sorting links (ajax) similar to:
table:topToolbars:toolbars:0:headers:1:header:orderByLink
However, when the DataTable gets re-rendered across tests, the index of the toolbars component is incremented each time i.e similar to:
table:topToolbars:toolbars:1:headers:1:header:orderByLink
which then breaks the hard-coded paths of the succeeding tests as they will no longer match.
The code fragment for the datatable construction is as follows:
final PayeesProvider dataProvider = new PayeesProvider();
table = new DataTable<ResponsePayeeDetails>("payees", columns, dataProvider, rowsPerPage);
table.setOutputMarkupId(true);
table.addTopToolbar(new AjaxFallbackHeadersToolbar(table, dataProvider) {
private static final long serialVersionUID = -3509487788284410429L;
#Override
protected WebMarkupContainer newSortableHeader(final String borderId, final String property, final ISortStateLocator locator) {
return new AjaxFallbackOrderByBorder(borderId, property, locator, getAjaxCallDecorator()) {
#Override
protected void onRender() {
System.out.printf("Path: %s\n", this.getPageRelativePath());
super.onRender();
}
private static final long serialVersionUID = -6399737639959498915L;
#Override
protected void onAjaxClick(final AjaxRequestTarget target) {
target.add(getTable(), navigator, navigatorInfoContainer);
}
#Override
protected void onSortChanged() {
super.onSortChanged();
getTable().setCurrentPage(0);
}
};
}
});
table.addBottomToolbar(new NoRecordsToolbar(table));
add(table);
To be precise, when I run my tests, the above System.out.printf statement prints:
(1st test)
Path: payees:topToolbars:toolbars:0:headers:1:header
Path: payees:topToolbars:toolbars:0:headers:2:header
(2nd test)
Path: payees:topToolbars:toolbars:2:headers:1:header
Path: payees:topToolbars:toolbars:2:headers:2:header
(3rd test)
Path: payees:topToolbars:toolbars:4:headers:1:header
Path: payees:topToolbars:toolbars:4:headers:2:header
(4th test)
Path: payees:topToolbars:toolbars:6:headers:1:header
Path: payees:topToolbars:toolbars:6:headers:2:header
Path: payees:topToolbars:toolbars:6:headers:1:header
Path: payees:topToolbars:toolbars:6:headers:2:header
Path: payees:topToolbars:toolbars:6:headers:1:header
Path: payees:topToolbars:toolbars:6:headers:2:header
(5th test)
Path: payees:topToolbars:toolbars:8:headers:1:header
Path: payees:topToolbars:toolbars:8:headers:2:header
Does anyone know how I can force the index generation to be more deterministic / repeatable. Alternatively, is there a way of wild-carding or otherwise generalising the path, so as to make them immune to these increments?
Any help will be greatly appreciated chaps!
The id's are being incremented each time because of the default DataTable ItemReuseStrategy, which generates brand new items each time the table is refreshed. You may have some luck implementing a custom ItemReuseStrategy if keeping the id's the same is a top priority.
We ran into a similar issue and have settled on a strategy aligned to your alternative question. With this, you can ask the test to inspect e.g. the third row on the last rendered page. Note: Code is in Scala.
In short, to address this issue we implemented a findNthComponentOnPage method to augment the WicketTester.
/**
* #param id Wicket id of component to find
* #param n 1-based
* #tparam T class of component to find
* #return the Nth component of class T appearing on the lastRenderedPage
*/
def findNthComponentOnPage[T <: Component : ClassTag](id: String, n: Int): T = {
tester.getLastRenderedPage.visitChildren(classTag[T].runtimeClass, new IVisitor[T, T] {
var count = 0
override def component(checkbox: T, visit: IVisit[T]): Unit = {
if (checkbox.getId == id) {
count += 1
if (count == n) {
visit.stop(checkbox)
}
}
}
})
}
Determining the path can be quite hard but shouldn't be necessary, as this would consist an integration-test and not an unit-test.
Testing the sorting of a column shouldn't depend on the surrounding table, so you could create a dummy-table in your testcase adding the header and one column (the one to be sorted) for the test. If you have the toolbar-object, you can either use it right away or re-retrieve it by issuing getComponent(toolbar.getPageRelativePath()).
But ensuring that an AjaxFallbackOrderByBorder actually works shouldn't be your concern as a user of this class. This should have been done by the creator of the class. You'd only have to test your own code (like the sorting-code in your data provider)...
I try to write checks for the play-framework and see two different possibilities. I described both and want to know if my understanding is correct (So it's more a tutorial than a question, specially because I didn't get any response that I missed something).
So what possibilities exists.
The simple way: Extending the class Check:
Advantages: Easier to write, easier to read
Disadvantages: You can't parametrized the check, you can only define the message.
The advanced way: Writing an check based on OVal AbstractAnnotationCheck.
Advantages: You can parametrized the check and have a simpler to use annotation
Disadvantages: A little bit more complicated.
Before we have a look on the implementation I want to explain the messages. You can always set the message directly or use a key to refer the message in a message-properties. The last one is the cleaner and recommended way. Every validation get a least 1 parameter: The name of the property which isn't valid. So validation or check specific parameters are always referred with %i$s where i>1. The format of the message string should follows the rules of Formatter but I'm unsure if all features are supported. As far as I know only %s, %d and %f is supported togeter with positioning. So %[argument_index$][flags]conversion where conversion could only be s,d or f.
Lets have a look on two examples:
The simple way I used in my module for optimistic locking:
/**
* Check with proof if the version of the current edited object is lesser
* than the version in db.
* Messagecode: optimisticLocking.modelHasChanged
* Parameter: 1 the request URL.
* Example-Message: The object was changed. Reload and do your changes again.
*
*/
static class OptimisticLockingCheck extends Check {
/**
* {#inheritDoc}
*/
#Override
public boolean isSatisfied(Object model, Object optimisiticLockingViolatedValue) {
//The comparision of version was made in the setter. Here
//we only have to check the flag.
if (((VersionedModel) model).optimisiticLockingViolated) {
final Request request = Request.current();
//The following doesn't work in 1.0 but in 1.1 see https://bugs.launchpad.net/play/+bug/634719
//http://play.lighthouseapp.com/projects/57987-play-framework/tickets/116
//setMessage(checkWithCheck.getMessage(), request != null ? request.url : "");
setMessage("optimisticLocking.modelHasChanged", request != null ? request.url : "");
}
return !((VersionedModel) model).optimisiticLockingViolated;
}
}
You use this Check with the annotation #CheckWith(value=OptimisticLockingCheck.class, message="optimisticLocking.modelHasChanged")
So lets have a closer look how it works. The only thing we have to do is to extends the class play.data.validation.Check and overwrite the isSatisfied method. There you get your model and the value of the properties. All you have to do is to return true if everything is OK or false otherwise. In our case we want to set the current url as a parameter. This
can be easily done by calling setMessage(). We give the message or the message key which is defined in the messages properties and the parameters. Remember we only give 1 parameter but referred as with %2$s, because the first parameter is always the name of the property.
Now the complex way based on the Range-check of play:
First we need to define an Annotation
/**
* This field must be lower than and greater than.
* Message key: validation.range
* $1: field name
* $2: min reference value
* $3: max reference value
*/
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Constraint(checkWith = RangeCheck.class)
public #interface Range {
String message() default RangeCheck.mes;
double min() default Double.MIN_VALUE;
double max() default Double.MAX_VALUE;
}
and then the Check
#SuppressWarnings("serial")
public class RangeCheck extends AbstractAnnotationCheck<Range> {
final static String mes = "validation.range";
double min;
double max;
#Override
public void configure(Range range) {
this.min = range.min();
this.max = range.max();
setMessage(range.message());
}
public boolean isSatisfied(Object validatedObject, Object value, OValContext context, Validator validator) {
requireMessageVariablesRecreation();
if (value == null) {
return true;
}
if (value instanceof String) {
try {
double v = Double.parseDouble(value.toString());
return v >= min && v <= max;
} catch (Exception e) {
return false;
}
}
if (value instanceof Number) {
try {
return ((Number) value).doubleValue() >= min && ((Number) value).doubleValue() <= max;
} catch (Exception e) {
return false;
}
}
return false;
}
#Override
public Map<String, String> createMessageVariables() {
Map<String, String> messageVariables = new TreeMap<String, String>();
messageVariables.put("2-min", Double.toString(min));
messageVariables.put("3-max", Double.toString(max));
return messageVariables;
}
}
OK I think the annotation don't must be explained. Lets have look on the check. In this case it's extends net.sf.oval.configuration.annotation.AbstractAnnotationCheck. We have to write a configure-method where we get the annotation and can copy the parameters. Then we have to define our check. Which is analog to the implementation of the other check. So we only write our condition and return true or false, except one special line! If we used a parametrized message, we must call requireMessageVariablesRecreation(); in our method.
At least we must override the method createMessageVariables. Here we have to get a littlebit play-knowlegde (all the other stuff is described here). You put your messages into an map with a key and value, but play only takes the values (see ValidCheck.java in framework code). So it will be referenced by position. This is the reason I changed the implementation of the RangeCheck using TreeMap instead of HashMap. Furthermore I let the keys start with the index which they can referred.
So I hope this makes it more clear how to write custom validations/checks for play. I hope the description is correct. Therefor the question is my understanding correct?
At least your first example appears to be on the correct path. You can compare it to the documentation provided below, but I'd assume from the complexity of your example that you've already referred to it.
http://www.playframework.org/documentation/1.1/validation#custom
I don't know enough about the play framework to comment on the second example.