XSLT - Accessing Key's by index - For example, in Muenchian Grouping - xslt

<listings>
<property rln="r317080" firm="f102" agent="a2140">
<street>2638 Maple Avenue</street>
<city>Padua</city>
<state>WI</state>
<zip>53701</zip>
<price>229000</price>
<style>2 Story Contemporary, Transitional</style>
<sqfeet>2328</sqfeet>
<bathrooms>2 1/2</bathrooms>
<bedrooms>4</bedrooms>
<garage>2 car, attached</garage>
<age>22</age>
<description>Very nice home on a one block dead end street with woods nearby.
Very special location for quiet and privacy! Home features open floor plan with
large rooms - new patio doors to pretty yard. updates: shingles, vinyl siding,
refrig and dishwasher, garage door. Fireplace in family room flanked by great
built-ins. add first floor laundry and award winning Padua schools.
</description>
</property>
<property ...>
<city>Broxton</city>
...
</property>
<property ...>
<city>Cutler</city>
...
</property>
<property ...>
<city>Argyle</city>
...
</property>
<property ...>
<city>Stratmore</city>
...
</property>
<property ...>
<city>Padua</city>
...
</property>
<property ...>
<city>Oseola</city>
...
</property>
<property ...>
<city>Fenmore</city>
...
</property>
<property ...>
<city>Cutler</city>
...
</property>
<property ...>
<city>Padua</city>
...
</property>
<property ...>
<city>Cutler</city>
...
</property>
<property ...>
<city>Oseola</city>
...
</property>
</listings>
In my textbook (XML 2nd Edition by Patrick Carey) it provides an example of using 'Muenchian Grouping' to find unique selections. The part I don't understand is thus:
It gets to here, in the progression of the example where it states: "
property[generate-id()=generate-id(key("cityNames", "Cutler")[1])]
" which says that this will find the first 'Cutler' in the selection, due to the index of '[1]'. Which given the XML above will return "Cutler"
Now the example progresses to thus: "
property[generate-id()=generate-id(key("cityNames", city)[1])]
" which says that this will find the first and only the first (therefore unique) of each city within the key. Creating a group of unique values of all the city's within. Which given the XML above will return "Argyle Broxton Cutler Fenmore Padua Stratmore Oseola" (note that there is no multiples).
Now, my question is thus: why does the second statement return a range of values, instead of just one?
Thanks

When you define your key, the match expression can match multiple nodes. That node-set is returned when accessing the key by name.
Adding the predicate filter for the first one ensures that you will only get at most one(the first) node returned from the key.

Ok, I suppose the answer I was looking for is thus:
property[generate-id()=generate-id(key("cityNames", city)[1])]
This code finds the first of each city
property[generate-id()=generate-id(key("cityNames", city[1]))]
and this code finds the first of all city's
easy enough, just couldn't see it before.

Related

How to use pattern and group in property mediator to validate

how to use pattern and group for validating property.
<property name="string" [action=set|remove] [type="string"]
(value="literal" | expression="xpath")
[scope=default|transport|axis2|axis2-client] [pattern="regex"
[group="integer"]]>
</property>
Pattern in Property mediator can be used to match the value or the result of an expression against a given pattern. If the pattern matches, Property mediator returns the value. Else, it returns an empty string.
For example, in the following example, only Test1 property value matches with the pattern. Hence, only Test1 returns the value. Test2 returns an empty string.
<property name="Test1" value="5" scope="default" type="STRING" pattern="[0-9]" group="0"/>
<property name="Test2" value="20" scope="default" type="STRING" pattern="[0-9]" group="0"/>
<log level="custom">
<property name="Test1 Value : " expression="get-property('Test1')"/>
<property name="Test2 Value : " expression="get-property('Test2')"/>
</log>
And, "group" attribute in Property mediator is intended to be used to evaluate against the number of capturing groups in this matcher's pattern, similar to java.util.regex.Matcher.groupCount() in Java. However, apparently the group support is not available in WSO2 EI 6.5.0 at the moment.

XSLT: Xpath to count items with attribute value matching another specific sibling

I have the following (simplified; there are more Property-Lines) XML-File that I need to process by XSLT:
<Collection Name="Columns" Type="OutputColumn">
<SubRecord>
<Property Name="Name">SID</Property>
<Property Name="SqlType">BigInt</Property>
<Property Name="Derivation">inputstream1.SID</Property>
<Property Name="Description">Main key</Property>
</SubRecord>
<SubRecord>
<Property Name="Name">name</Property>
<Property Name="SqlType">Char</Property>
<Property Name="Derivation">inputstream2.name</Property>
<Property Name="Description">Surname</Property>
</SubRecord>
<SubRecord>
<Property Name="Name">street</Property>
<Property Name="SqlType">Char</Property>
<Property Name="Derivation">inputstream1.streetname + inputstream1.number</Property>
<Property Name="Description">Full street</Property>
</SubRecord></Collection>
I now need to know two things as a variable in XSLT to decide what to do further in an if-Condition:
Is there at least one SubRecord where substring-after(Derivation, '.') matches the sibling Property "Name" (so like in the first two SubRecords)?
Is there at least one SubRecord where substring-after(Derivation, '.') does not match the sibling Property "Name" (so like in the third SubRecord)?
I tried to do this by Xpath with the Count()-Functionality but simply can't figure out how to do the comparison to the sibling Property node with "Name" as it's name...I'm also open for other ideas if you know an alternative to Count().
I'm not sure I completely understood the issue, so check below and let me know if it's not what you actually want
First can be done with below XPath:
boolean(//SubRecord[substring-after(./Property[#Name="Derivation"]/text(), '.')=./Property[#Name="Name"]/text()])
This should return true because XPath matches first two SubRecord elements.
Using not() should allow to return true because XPath matches third SubRecord element:
boolean(//SubRecord[not(substring-after(./Property[#Name="Derivation"]/text(), '.')=./Property[#Name="Name"]/text())])

Glade/gtkmm TreeView

In following up with an answer to my previous question on this issue, I've made some progress and am looking for the next bit of guidance. In brief, I'm having trouble loading up a Treeview from Glade with a gtkmm application.
To start, here's the source. The class:
typedef struct
{
Gtk::Window *window_info;
Gtk::TreeView *treeview_info;
Glib::RefPtr<Gtk::ListStore> liststore_info;
class InfoColumns : public Gtk::TreeModel::ColumnRecord
{
public:
InfoColumns() { add(time); add(message); }
Gtk::TreeModelColumn<string> time;
Gtk::TreeModelColumn<string> message;
} info_columns;
}UiElements;
class GuiManager
{
Glib::RefPtr<Gtk::Builder> builder;
UiElements elements;
public:
GuiManager();
void info_handler(string msg);
};
The definition:
GuiManager::GuiManager()
{
builder = Gtk::Builder::create();
builder->add_from_file("GUI3.glade");
builder->get_widget("window_info", elements.window_info);
builder->get_widget("treeview_info", elements.treeview_info);
//these two methods of loading the liststore appear to function identically
elements.liststore_info = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(builder->get_object("liststore_info"));
// elements.liststore_info = Gtk::ListStore::create(elements.info_columns);
elements.treeview_info->set_model(elements.liststore_info);
//if I append the columns, the data appears at the end of the list
// elements.treeview_info->append_column("Time", elements.info_columns.time);
// elements.treeview_info->append_column("Message", elements.info_columns.message);
}
void GuiManager::info_handler(string msg)
{
Gtk::TreeModel::Row row = *(elements.liststore_info->append());
row[elements.info_columns.time] = "Now";
row[elements.info_columns.message] = msg;
}
And the XML:
<object class="GtkListStore" id="liststore_info">
<columns>
<!-- column-name Time -->
<column type="gchararray"/>
<!-- column-name Message -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkWindow" id="window_info">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Info</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow_info">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="min_content_width">400</property>
<property name="min_content_height">600</property>
<child>
<object class="GtkTreeView" id="treeview_info">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">liststore_info</property>
<property name="enable_search">False</property>
<property name="enable_grid_lines">both</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview_info_selection"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="treeview_info_column_time">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="min_width">100</property>
<property name="title" translatable="yes">Time</property>
<property name="clickable">True</property>
<child>
<object class="GtkCellRendererText" id="treeview_info_cellrenderer_time"/>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="treeview_info_column_message">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="min_width">300</property>
<property name="title" translatable="yes">Message</property>
<property name="clickable">True</property>
<child>
<object class="GtkCellRendererText" id="treeview_info_cellrenderer_message"/>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
When I compile and run this code (plus one call to info_handler()) I get a treeview with one row and two blank columns. When I uncomment the two lines appending the columns, I get a treeview with one row and four columns. The first two columns are blank (and sized according to the glade file) and the second two have the "Now" and msg strings (and are sized automatically to the contents).
What I gather from this is that elements.info_columns is not associated with elements.treeview_info via elements.liststore_info. It looks like I'm just missing a step to connect the two. liststore_info is listed as the model for treeview_info in the glade file, as well as being set in GuiManager's constructor.
Where's the disconnect?
I think you need to set which list store field should be displayed in each table column.
In Glade, right-click on the tree widget and click Edit... (not Edit separately)
Click on the Hierarchy tab
Click on a column
Under Properties and Attributes ensure Text is ticked and the correct list store column is chosen from the drop down list (which will update the index accordingly.) For Pixbuf columns, you want Pixbuf Object instead of Text.
Repeat for each column
In your case, I believe you should end up with an index of 0 for the Time column, and 1 for the Message column, and -1 in all the other fields.
This will populate the column's text from the selected field in the list store. I believe it's done this way so you can populate a column's other attributes (font, colour, etc.) through the list store as well, if you want.
Unrelated to this, I would also consider changing Gtk::TreeModelColumn<string> to Gtk::TreeModelColumn<Glib::ustring> as I believe the way you have it now may cause a conversion to/from a Glib::ustring every time the tree view redraws itself. Keeping it as a Glib::ustring from the start should avoid most of this conversion.

Event builder XML mapping in WSO2CEP 3.0.0

Lets say, a stream named inputStream is defined with attributes name:string, surname:string, address:string. For this stream, if an event builder is defined like the following,
<property>
<from xpath="xpathForSurname"/>
<to default="NULL" name="surname" type="string"/>
</property>
<property>
<from xpath="xpathForName"/>
<to default="NULL" name="name" type="string"/>
</property>
<property>
<from xpath="xpathForAddress"/>
<to default="NULL" name="address" type="string"/>
</property>
When I send an input like ('John', 'Lennon', 'Liverpool') I expect inputStream to be ['John', 'Lennon', 'Liverpool'], but the result stream is ['Lennon', 'John', 'Liverpool']. The reason is that values of the attributes are added to stream following the mapping sequence in builder definition.
Therefore, <to> tags in definition becomes pointless (the value upon xpathForSurname evaluation is not mapped to surname but name). Is this a bug or is it done on purpose?
Yes, this seems to be a bug in CEP 3.0.0 and will be fixed in a future release. I have created a JIRA with the information you provided in CEP-640.
For now, the workaround would be to let the input stream come directly as it is via the event builder without reordering the attributes and doing any manipulations to the ordering at the level of execution plans. Hope this workaround will work for you.

XSLT counting elements with a given value

I need to count the number of elements in an XML file that have a particular value (to verify uniqueness). The XML file looks like this:
EDIT: I updated the original "simplified" XML with the actual hairy mess someone designed. Unfortunately, this is going to make all the previous Answers really confusing and wrong unless edited.
<root>
<ac>
<Properties>
<Property Name="Alive">
<Properties>
<Property Name="ID">
<Properties>
<Property Name="Value">
<long>11007</long>
</Property>
</Properties>
</Property>
</Properties>
</Property>
<Property Name="Dead">
<Properties>
<Property Name="ID">
<Properties>
<Property Name="Value">
<long>11008</long>
</Property>
</Properties>
</Property>
</Properties>
</Property>
...
<Property Name="MostlyDeadAllDay">
<Properties>
<Property Name="ID">
<Properties>
<Property Name="Value">
<long>99001</long>
</Property>
</Properties>
</Property>
</Properties>
</Property>
</Properties>
</ac>
</root>
I am trying to define a variable to see how many of the properties at the Alive/Dead level have the long value (ID) as defined in a template parameter. Something along these lines (though I suspect this is wrong)...
<xsl:param name="parPropId"/>
<xsl:variable name="countProperties">
<xsl:value-of select="count(/root/ac/
Properties/Property/
Properties/Property[#Name = 'ID']/
Properites/Property[#Name = 'Value']/long = $parPropId)"/>
</xsl:variable>
There can be multiple Property elements defined at the "ID" level. But I am fairly certain I can count on just one Property element ("Value") under "ID", and only one "long" element under "Value".
[disclaimer] Whoever designed the overall XML file I'm stuck working with REALLY didn't know how to structure XML (e.g., backwards use of attributes versus elements). I fear my XSLT thinking has temporarily been warped :) as a result. [/disclaimer]
This XPath:
count(//Property[long = '11007'])
returns the same value as:
count(//Property/long[text() = '11007'])
...except that the first counts Property nodes that match the criterion and the second counts long child nodes that match the criterion.
As per your comment and reading your question a couple of times, I believe that you want to find uniqueness based on a combination of criteria. Therefore, in actuality, I think you are actually checking multiple conditions. The following would work as well:
count(//Property[#Name = 'Alive'][long = '11007'])
because it means the same thing as:
count(//Property[#Name = 'Alive' and long = '11007'])
Of course, you would substitute the values for parameters in your template. The above code only illustrates the point.
EDIT (after question edit)
You were quite right about the XML being horrible. In fact, this is a downright CodingHorror candidate! I had to keep recounting to keep track of the "Property" node I was on presently. I feel your pain!
Here you go:
count(/root/ac/Properties/Property[Properties/Property/Properties/Property/long = $parPropId])
Note that I have removed all the other checks (for ID and Value). They appear not to be required since you are able to arrive at the relevant node using the hierarchy in the XML. Also, you already mentioned that the check for uniqueness is based only on the contents of the long element.
Your xpath is just a little off:
count(//Property/long[text()=$parPropId])
Edit: Cerebrus quite rightly points out that the code in your OP (using the implicit value of a node) is absolutely fine for your purposes. In fact, since it's quite likely you want to work with the "Property" node rather than the "long" node, it's probably superior to ask for //Property[long=$parPropId] than the text() xpath, though you could make a case for the latter on readability grounds.
What can I say, I'm a bit tired today :)
<xsl:variable name="count" select="count(/Property/long = $parPropId)"/>
Un-tested but I think that should work. I'm assuming the Property nodes are direct children of the root node and therefor taking out your descendant selector for peformance