I have the following declarations:
<xsl:call-template name="ad_state800x100"><xsl:with-param name="state" select="/*/locations/#state" /></xsl:call-template>
And for the xpos
<xsl:variable name="xpos" select="substring-before(position(),'0')" />
What I want is the following:
<xsl:call-template name="ad$xpos_state800x100">
So if the position is 10, it will be:
<xsl:call-template name="ad1_state800x100">
How do I achieve this
If you want to make a dynamic decision which of several templates to invoke, the correct XSLT mechanism for doing that is xsl:apply-templates.
You haven't told us enough about your problem for me to know exactly how you would do this in your case, or indeed whether dynamic despatch needs to be part of the solution at all. In fact, you haven't told us anything about your problem - only an attempt at a solution that won't work.
Related
I know that an immutable array on XSLT 3.0 can be created as follows:
<xsl:variable name="array" as="element()*">
<Item>a</Item>
<Item>b</Item>
<Item>c</Item>
</xsl:variable>
Also, that is it possible to create a mutable variable using Saxon's assign function:
<xsl:variable name="var" select="'val'" saxon:assignable="yes"/>
Is it possible to combine the two? i.e. creating a mutable array that allows editing individual items?
Note that due to some technical restrictions I am to use only XSLT/Saxon functions, so JavaScript is off the table, unfortunately.
Thanks!
Having a mutable variable is a quite different thing from having a variable that contains a mutable value. The only way you can create a mutable value in Saxon is to use an external Java object.
But why would you want to? Tell us what you want to achieve, and we can help you achieve it using XSLT the way it was designed to be used, working with the language rather than fighting against it.
I'm experiencing some weird typecasting issues here, not specific to Lucee (also in Railo). Could be that I'm just missing some crucial point here though...
I have a component:
<cfcomponent output="false">
<cfproperty name="thisId" type="String" default="-1" />
<cfproperty name="thatId" type="String" default="-1" />
</cfcomponent>
Both properties are clearly typed as strings. I would expect that when I try to set an object or a number to one of these, the code would return an error.
But, seeing as I'm used to cfml doing the typecasting for me by now, I never thought twice about the fact that setting a number here is no problem at all. I was in fact under the assumption that all number I would try to set here would be cast to strings for me.
It seems this is not the case though.
After implementing some REST calls that contain derivatives of these components in the form of a serialized struct, I onticed that some where included as an integer and some were included as a string. When I noticed that, I dumped out the component itself and notice that setting a number where a string was expected as a property, the typing had been overwritten as a number.
The fact that Railo / Lucee still validates is in my opinion beyond useless. Either validate strict typing and throw an error / pass in a correctly typed variable, or validate loose typing and convert to the type the CFC is expecting if that is possible.
Railo / lucee implemented loose type validation here, but still decides to pass in the variable in its original type, not what the cfc expects per sé.
Seeing as I don't want to be typecasting every number to a string right now, is there a simple oversight here that could salvage my typing?
(I've already posted this in the Lucee mailing list, but without any results, just people confirming what I already said / disregarding the possibility that this is not expected behaviour.)
update (as asked by Adam):
What I see is the following (in my cfc component described above):
<!--- setting a string returns a string afterwards, as expected since the property is a type string initialy --->
<cfset componentName.setThisId('1') />
<cfset local.thisIsStillAString = componentName.getThisId() />
<!--- setting a number returns a number, which means we can no longer assume the property is a string, as it was initially set up --->
<cfset componentName.setThatId(12345) />
<cfset local.thisIsNoLongerAString = componentName.getThatId() />
In both cases I would expect that either:
- the input variable would get strictly evaluated as a string, which means that the second example would raise an error, seeing that it is actually a number
- the input variable would get loosely evaluated as a string, but would be cast to a string when passing the evaluation, which would mean that the second example would pass but would ultimately return a string, no longer a number.
In any case I would expect the property's original typing to be preserved, instead it gets changed to whatever type you're trying to set, as long as it passes the current loose evaluation.
As I understand it, the built-in accessor setters will do automatic type-validation, but will only do casting where necessary and possible.
Numeric/string/date/boolean values are all considered "simple", which is why the numeric data is passing the "string" type-validation. Therefore, because it passed the validation, the casting is skipped. On a personal note, I would prefer if it did more rigorous validation, but that's an issue for the bugtracker.
Now, if you must ensure that only actual string data can make its way into those properties, you can override the generated setter for the property to do more rigorous type-casting and/or validation (I've tested this only on Lucee):
/** Example.cfc */
component accessors=true {
property type="string" name="thisId";
property type="string" name="thatId";
public function setThisId(required string newId) {
// convert numeric value to string value
if (isNumeric(newId)) {
newId = toString(newId);
// throw an exception for non-string/numeric values
// !isSimpleValue() is a catch-all btw, structs and arrays will
// be prevented by the "newId" argument's type hint
} else if (isBoolean(newId) || isDate(newId) || !isSimpleValue(newId)) {
throw(message="Invalid value specified for thisId");
}
variables.thisId = newId;
return this;
}
}
var example = new Example();
example.setThisId(54321);
example.setThatId(54321);
writeoutput(serializeJson(example)); //{"thisId":"54321","thatId":54321}
// throws exceptions:
example.setThisId(true);
example.setThisId({});
Finally, getting back to the "casting where necessary and possible" part. For the given example, if you were to try passing a component instance to the setThisId() method, it fails the type-validation step, meaning type-casting is necessary for the operation to succeed. So then the value is checked for the possibility of type-casting. If the component (and this only works on Railo/Lucee) has a _toString() "magic method" defined, then type-casting is possible. Since it is possible, the component is then cast to a string, and the result then passed into setThisId(). If that magic method is not defined on the component, it is not possible to do type casting, and an exception is thrown. Similarly for structs/arrays, type-casting is necessary, but not possible as there is no automatic serialization defined for those types, thus resulting in an exception being thrown.
TL;DR
You can override the setter accessor to do more rigorous type-validation/type-casting.
To enforce them as string (not array nor struct), use accessor=true together with your <cfproperty>'s and use setters.
However, if you're talking about SerializeJSON() treating strings of numbers as integers, that is not something type="string" can enforce. It has to do with the behavior of the SerializeJSON() function. If you really want to enforce them as string, try https://github.com/bennadel/JsonSerializer.cfc
Can anyone tell me whether it is possible to call only a part of a template?
I have a large template which I must use, yet the first two lines in the template need to be adjusted/changed.
How can I do this? Any suggestions?
Thanks in advance
Greets
You need to refactor the large template so that it calls the first couple of lines as a child template or function which can then be called directly from outside.
It's generally true in XSLT as in any other language that templates/functions have a habit of growing too big, which reduces the reusability of the code, and the answer to this is always to be prepared to refactor the code to isolate the reusable parts into individually callable (and overridable) components.
Since XSLT files have XML format, you could have an XSLT script that changes the original template as needed. After that you use that changed template to do the actual work.
Problems while accessing Java method through XSL .'I have a java class DirectoryReader.java with a static method totalPhotos which returns a int. In my XSL I have defined a namespace: xmlns:dirReader="xalan://com.mngi.eidos.util.DirectoryReader and I am trying to access the totalPhotos method like:
<xsl:variable name="totalPhotos" select="dirReader:totalPhotos($PhotoPath)"/>
Can someone please tell me what is wrong in my approach ?
I still get the following error
ERROR: 'The first argument to the
non-static Java function 'totalPhotos'
is not a valid object reference
Either the method totalPhotos must be static, or you must first create an instance of the class and pass that as the first argument to the call.
<xsl:variable name="dr" select="dirReader:new(....)"/>
<xsl:variable name="totalPhotos" select="dirReader:totalPhotos($dr,$PhotoPath)"/>
Adjust dirReader constructor arguments to your situation
Is there a way of supplying enum values as method-args in pococapsule without resorting to factory-methods?
Let say I have a class that take an enum value in its constructor
class A
{
A(myEnum val);
}
Using Pococapsule xml configuration:
I would like to express something like this:
<bean id="A" class="A">
<method-arg type="MyEnum" value="MyEnum::Value1" />
</bean>
However, since pococapsule's basic types only includes built in types such as short, char, etc this is not possible.
How would I go about to instantiate a class A using pococapsule?
I could resort to using factory methods something like this:
MyEnum GetMyEnumValue1()
{
return MyEnum::Value1;
}
<bean id="A" class="A">
<method-arg factory-method="GetMyEnumValue1" />
</bean>
Which isn't very practical. I would have to implement a new factory method for every possible value of each and every enum used.
Some would argue that enum:s shouldn't be passed in constructors or setter methods as it is a sign of a class doing to much. Yes I agree. However, there is a lot of third party code and c++ frameworks out there that uses this style, so I need to be able to do this.
Edit:
The issue was resolved on Pococapsule's discussion forum. The work-around in this specific case was to have factory methods perform the desired action. It is not as flexible as declaring enum-use in the xml-config file, but it moved the project forward. Thanks Ke for your help.
(repost, as the XML code was filtered out in previous reply)
In C/C++ enums are able to be passed as int implicitly, therefore, you can simply have type="long" in the method-arg element.
You can also use the DSM feature to define your own extend schema that supports your specific enum (it should be similar to the user example in examples/basic-ioc/ext-schema where DSM is used to typesafely support user defined Map type).
-Ke
In C/C++ enums are able to be passed as int implicitly, therefore, you can simply do:
You can also use the DSM feature to define your own extend schema that supports your specific enum (it should be similar to the user example in examples/basic-ioc/ext-schema where DSM is used to typesafely support user defined Map type).
-Ke