Is there a way to have multiple if without else? - xquery

I need to test 3 attributes of a node.
Problem is that I must return error for each attributes in error and I don't see how to achieve that in an easy way.
xquery is not really flexible so... not so much to try...
for $theHoldingListsField in //DisplaySettingCol/theDisplaySetting/theHoldingListsFields
return
if ($theHoldingListsField/#AFL != "MANDATORY") then
(
<error id="DPR-CKSEM-DEP-SMF142-2">
<args>
<arg value="{$theHoldingListsField/ancestor::node()/#id}"/>
<arg value="AFL = {$theHoldingListsField/#AFL}"/>
</args>
<location value="{functx:path-to-node-with-pos($theHoldingListsField)}"/>
</error>
)
else if ($theHoldingListsField/#attitudeIndicator != "MANDATORY") then
(
<error id="DPR-CKSEM-DEP-SMF142-2">
<args>
<arg value="{$theHoldingListsField/ancestor::node()/#id}"/>
<arg value="attitudeIndicator = {$theHoldingListsField/#attitudeIndicator}"/>
</args>
<location value="{functx:path-to-node-with-pos($theHoldingListsField)}"/>
</error>
)
So in this example I want to be able to trigger the 3 errors at one time, not one of them (like it is now).
I don't even know if it is possible...
Thanks !

Start by putting the repeated code into a function:
declare function local:check($att as attribute()) as element(error)? {
if ($att != "MANDATORY") then (
<error id="DPR-CKSEM-DEP-SMF142-2">
<args>
<arg value="{$att/../ancestor::node()/#id}"/>
<arg value="{name($att)} = {$att}"/>
</args>
<location value="{functx:path-to-node-with-pos($att/..)}"/>
</error>
) else ()
};
Then your logic reduces to
for $theHoldingListsField in //DisplaySettingCol/theDisplaySetting/theHoldingListsFields
return (local:check($theHoldingListsField/#AFL),
local:check($theHoldingListsField/#attitudeIndicator),
...)

There is no if without else in standard XQuery, since if/then/else is an expression which has to evaluate to some return value in every case (see functional programming).
If you want to return an empty sequence when the error condition is not met, you can do that explicitly, separately for each error. You can then gather all zero-or-one-element sequences into one, which is automatically flattened:
for $theHoldingListsField in //DisplaySettingCol/theDisplaySetting/theHoldingListsFields
return (
if ($theHoldingListsField/#AFL != "MANDATORY") then (
<error id="DPR-CKSEM-DEP-SMF142-2">
<args>
<arg value="{$theHoldingListsField/ancestor::node()/#id}"/>
<arg value="AFL = {$theHoldingListsField/#AFL}"/>
</args>
<location value="{functx:path-to-node-with-pos($theHoldingListsField)}"/>
</error>
) else (),
if ($theHoldingListsField/#attitudeIndicator != "MANDATORY") then (
<error id="DPR-CKSEM-DEP-SMF142-2">
<args>
<arg value="{$theHoldingListsField/ancestor::node()/#id}"/>
<arg value="attitudeIndicator = {$theHoldingListsField/#attitudeIndicator}"/>
</args>
<location value="{functx:path-to-node-with-pos($theHoldingListsField)}"/>
</error>
) else ()
)

Another option is to take a more functional programming approach.
We can generalise your test into a function that operates on a theHoldingListsField as it has only two invariants, the attribute name ($attr-name) and the error code ($error-id).
We basically loop over the attributes (with error codes) that you want to test and call the local:test function on each, e.g.
declare function local:test($theHoldingListsField, $attr-name, $error-id) {
$theHoldingListsField/#*[local-name() eq $attr-name][. ne "MANDATORY"] !
<error id="{$error-id}">
<args>
<arg value="{$theHoldingListsField/ancestor::node()/#id}"/>
<arg value="{$attr-name} = {.}"/>
</args>
<location value="{functx:path-to-node-with-pos($theHoldingListsField)}"/>
</error>
};
let $tests := (
["AFL", "DPR-CKSEM-DEP-SMF142-2"],
["attitudeIndicator", "DPR-CKSEM-DEP-SMF142-2"]
)
for $theHoldingListsField in //DisplaySettingCol/theDisplaySetting/theHoldingListsFields
let $test-fn := local:test($theHoldingListsField, ?, ?)
return
$tests ! fn:apply($test-fn, .)
The example above makes use of XQuery 3.1 features such as arrays ([]), partial function application (?), simple map operator (!), and higher-order-functions (fn:apply). I would suggest learning about those from the XQuery 3.1 W3C spec.
This could also be rewritten to remove the for, and instead have the local:test function operate on all fields (i.e. theHoldingListsFields).

Related

XQuery error: Filtering with variable returning empty result

I try to run the following XQuery:
let $d := doc("ferry.xml")
let $x := $d/ferry/trips/trip[#depart='08:00' and start='Stockholm']/captain[#crew]
return $d/ferry/crews/crew[#crewID=$x]/name
And get an empty result.
I would like to get the value in <name> where <crew crewID="JI"> (=Jill).
I can see that $x includes <captain crew="JI"/> and when I run
return $d/ferry/crews/crew[#crewID='JI']/name
I get the expected result.
How can I make the return function read the variable correctly? Here is the XML file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ferry>
<ships>
<ship shipID="SKHM">
<name>Skeppsholm</name>
</ship>
<ship shipID="PERL">
<name>Pearl</name>
<cars>40</cars>
</ship>
<ship shipID="REVG">
<name>Revenge></name>
<length>50</length>
</ship>
</ships>
<crews>
<crew crewID="JO">
<name>Jack</name>
<job>service</job>
<job>deckhand</job>
</crew>
<crew crewID="JI">
<name>Jill</name>
<job>captain</job>
<job>firstmate</job>
</crew>
<crew crewID="HA">
<name>Harry</name>
<job>deckhand</job>
</crew>
</crews>
<trips>
<trip date="20171020" depart="08:00">
<start>Stockholm</start>
<end>Vaxholm</end>
<captain crew="JI" />
<service crew="JO" />
</trip>
<trip date="20171130" depart="10:00">
<start>Nacka</start>
<end>Gustavberg</end>
<captain crew="JI" />
<deckhand crew="HA" />
<service crew="JA" />
</trip>
</trips>
</ferry>
Try changing
let $x := $d/ferry/trips/trip[#depart='08:00' and start='Stockholm']/captain[#crew]
to
let $x := $d/ferry/trips/trip[#depart='08:00' and start='Stockholm']/captain/#crew
You need $x to be just the value of the crew attribute; currently it returns the whole element <captain crew="JI"/>.

Dynamically populate version from version.sbt

I'm trying to take the version from version.sbt and and populate it to logback.xml's log appender's applicationVersion field.
version.sbt
version in ThisBuild := "0.4.63"
logback.xml
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<appender name="ADP-MESSAGING" class="com.agoda.adp.messaging.logging.appenders.LogbackAppender">
<applicationName>MyApp</applicationName>
<applicationAssemblyName>myapp</applicationAssemblyName>
<applicationVersion>0.4.61</applicationVersion>
<!-- <applicationVersion>${application.version}</applicationVersion> -->
<apiKey>s234W##$WFW$#$#</apiKey>
<getCallerData>false</getCallerData>
</appender>
....
<root level="WARN">
<appender-ref ref="ADP-MESSAGING" />
<appender-ref ref="STDOUT" />
</root>
</configuration>
I tried by adding ${application.version}, ${version} but no success.
How can I do this?
Please share your thoughts.
Thanks
The values interpolated in a logback.xml file are simply Java system properties. All you have to do is add a value to your Java commandline defining the value you want:
// NOTE: This will only work when running through sbt. You'll have to
// append the same value to your startup scripts when running elsewhere.
javaOptions += "-Dapplication.version=" + version.value
With this flag, you should be able to interpolate the version in your XML file:
<applicationVersion>${application.version}</applicationVersion>
You can add logback PropertyDefiner implementation:
package org.mypackage
import ch.qos.logback.core.PropertyDefinerBase
class VersionPropertyDefiner extends PropertyDefinerBase {
override def getPropertyValue: String = BuildInfo.version
}
You will get autogenerated (managed) scala code BuildInfo.version if you use BuildInfoPlugin in your project build settings.
Then you can define and use variable in your logback.xml configuration:
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<define name="appVersion" class="org.mypackage.VersionPropertyDefiner"/>
<appender name="ADP-MESSAGING" class="com.agoda.adp.messaging.logging.appenders.LogbackAppender">
<applicationName>MyApp</applicationName>
<applicationAssemblyName>myapp</applicationAssemblyName>
<applicationVersion>${appVersion}</applicationVersion>
<apiKey>s234W##$WFW$#$#</apiKey>
<getCallerData>false</getCallerData>
</appender>
....
<root level="WARN">
<appender-ref ref="ADP-MESSAGING" />
<appender-ref ref="STDOUT" />
</root>
</configuration>

xquery: how to remove unused namespace in xml node?

I have a xml document same as:
<otx xmlns="http://iso.org/OTX/1.0.0" xmlns:i18n="http://iso.org/OTX/1.0.0/i18n" xmlns:diag="http://iso.org/OTX/1.0.0/DiagCom" xmlns:measure="http://iso.org/OTX/1.0.0/Measure" xmlns:string="http://iso.org/OTX/1.0.0/StringUtil" xmlns:dmd="http://iso.org/OTX/1.0.0/Auxiliaries/DiagMetaData" xmlns:fileXml="http://vwag.de/OTX/1.0.0/XmlFile" xmlns:log="http://iso.org/OTX/1.0.0/Logging" xmlns:file="http://vwag.de/OTX/1.0.0/File" xmlns:dataPlus="http://iso.org/OTX/1.0.0/DiagDataBrowsingPlus" xmlns:event="http://iso.org/OTX/1.0.0/Event" xmlns:quant="http://iso.org/OTX/1.0.0/Quantities" xmlns:hmi="http://iso.org/OTX/1.0.0/HMI" xmlns:math="http://iso.org/OTX/1.0.0/Math" xmlns:flash="http://iso.org/OTX/1.0.0/Flash" xmlns:data="http://iso.org/OTX/1.0.0/DiagDataBrowsing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dt="http://iso.org/OTX/1.0.0/DateTime" xmlns:eventPlus="http://iso.org/OTX/1.0.0/EventPlus" xmlns:corePlus="http://iso.org/OTX/1.0.0/CorePlus" xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:job="http://iso.org/OTX/1.0.0/Job" id="id_4e5722f2f81a4309860c146fd3c743e5" name="NewDocument1" package="NewOtxProject1Package1" version="1.0.0.0" timestamp="2014-04-11T09:42:50.2091628+07:00">
<declarations>
<constant id="id_fdc639e20fbb42a4b6b039f4262a0001" name="GlobalConstant">
<realisation>
<dataType xsi:type="Boolean">
<init value="false"/>
</dataType>
</realisation>
</constant>
</declarations>
<metaData>
<data key="MadeWith">Created by emotive Open Test Framework - www.emotive.de</data>
<data key="OtfVersion">4.1.0.8044</data>
</metaData>
<procedures>
<procedure id="id_1a80900324c64ee883d9c12e08c8c460" name="main" visibility="PUBLIC">
<realisation>
<flow/>
</realisation>
</procedure>
</procedures>
</otx>
I want to remove unsused namespaces in the xml by xquery but i don't know any solution to solving it. the current namespace used are "http://iso.org/OTX/1.0.0 and http://www.w3.org/2001/XMLSchema-instance".
please help me!.
Unfortunately, XQuery Update provides no expression to remove existing namespaces, but you can recursively rebuild your document:
declare function local:strip-ns($n as node()) as node() {
if($n instance of element()) then (
element { node-name($n) } {
$n/#*,
$n/node()/local:strip-ns(.)
}
) else if($n instance of document-node()) then (
document { local:strip-ns($n/node()) }
) else (
$n
)
};
let $xml :=
<A xmlns="used1" xmlns:unused="unused">
<b:B xmlns:b="used2"/>
</A>
return local:strip-ns($xml)

Pointer.new(:uint) not converting to proper type for CoreMIDI (RubyMotion)

I'm trying to use CoreMIDI in a RubyMotion project. Here's a simple example of the code:
clientName = "Client"
clientRef = Pointer.new(:uint)
MIDIClientCreate( clientName, nil, nil, clientRef )
See more detailed code and backtrace in the following gist: https://gist.github.com/4299810
That code results in the following error:
(main)> 2012-12-15 14:43:27.410 core-midi-test[42560:c07]
app_delegate.rb:5:in application:didFinishLaunchingWithOptions:':
expected instance of Pointer of type^{OpaqueMIDIClient}', got I'
(TypeError) 2012-12-15 14:43:27.412 core-midi-test[42560:c07] ***
Terminating app due to uncaught exception 'TypeError', reason:
'app_delegate.rb:5:inapplication:didFinishLaunchingWithOptions:':
expected instance of Pointer of type ^{OpaqueMIDIClient}', gotI'
(TypeError)'
The error is apparently with the fourth argument to MIDIClientCreate. The docs for MIDIClientCreate show:
OSStatus MIDIClientCreate (
CFStringRef name,
MIDINotifyProc notifyProc,
void *notifyRefCon,
MIDIClientRef *outClient
);
MIDIClientRef derives from MIDIObjectRef which is defined as a UInt32, so I'm fairly certain that Pointer.new(:uint) is the correct type to use with RubyMotion.
Here is the pertinent portion of the CoreMIDI.bridgesupport file that RubyMotion is using:
<function name='MIDIClientCreate'>
<arg name='name' type='^{__CFString=}' declared_type='CFStringRef'/>
<arg name='notifyProc' function_pointer='true' type='^?' declared_type='MIDINotifyProc'>
<arg type='^{MIDINotification=iI}' declared_type='MIDINotification*' const='true'/>
<arg type='^v' declared_type='void*'/>
<retval type='v' declared_type='void'/>
</arg>
<arg name='notifyRefCon' type='^v' declared_type='void*'/>
<arg name='outClient' type='^^{OpaqueMIDIClient}' declared_type='MIDIClientRef*'/>
<retval type='l' declared_type='OSStatus'/>
</function>
As far as I can tell, the bridgesupport definition should include the necessary plumbing to make the proper conversion. But it's of course, not working.
Is there something wrong with my code or is there something wrong the CoreMIDI.bridgesupport file included with RubyMotion?
I found out by reading the documentation for MacRuby that one can explicitly pass the desired pointer type to the Pointer constructor like so:
clientName = "Client"
clientRef = Pointer.new(MIDIClientRef.type)
MIDIClientCreate( clientName, nil, nil, clientRef )
portName = "Output"
outport = Pointer.new(MIDIPortRef.type)
MIDIOutputPortCreate( clientRef[0], portName, outport )

ASDOC Ant : error with DEFINE

[Flex4.5 - Windows XP]
I defined all my define (string type) in asdoc target like this :
<target name="build-asdoc" depends="setup-asdoc-dir, manifest">
<exec executable="${FLEX_HOME}/bin/asdoc.exe" failonerror="true">
...
<!-- Defines -->
<arg line="-define+=CONFIG::version,'${env.VERSION}'" />
<arg line="-define+=CONFIG::mode,'${mode.production}'" />
<arg line="-define+=CONFIG::label,'${env.LABEL}'" />
Even these define, when ASDOC.exe runs, there are some errors on all these define in the code; example :
[exec] C:\DATA\Trinity\Dev\trinity-client\src\fr\laposte\trinity\common\GlobalClass.as(44): col: 76 Erreur: Accès à la propriété non définie mode.
[exec] private static var WEBSERVICE_SERVER_LABEL_TOKEN:String = String(CONFIG::mode).toUpperCase();
Thank you very much,
regards,
Anthony

Resources