Skip to content

Commit

Permalink
BL-764
Browse files Browse the repository at this point in the history
  • Loading branch information
bdw429s committed Jan 14, 2025
1 parent 0321afa commit a50ec27
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 20 deletions.
60 changes: 40 additions & 20 deletions src/main/java/ortus/boxlang/runtime/bifs/global/xml/XMLSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import ortus.boxlang.runtime.bifs.BoxBIF;
import ortus.boxlang.runtime.bifs.BoxMember;
import ortus.boxlang.runtime.context.IBoxContext;
import ortus.boxlang.runtime.dynamic.casters.BooleanCaster;
import ortus.boxlang.runtime.dynamic.casters.NumberCaster;
import ortus.boxlang.runtime.scopes.ArgumentsScope;
import ortus.boxlang.runtime.scopes.Key;
import ortus.boxlang.runtime.types.Argument;
Expand Down Expand Up @@ -68,30 +70,35 @@ public XMLSearch() {
*
*/
public Object _invoke( IBoxContext context, ArgumentsScope arguments ) {
XML xml = arguments.getAsXML( Key.XMLNode );
String xpathString = arguments.getAsString( Key.xpath );
final IStruct params = arguments.getAsStruct( Key.params );
try {
XML xml = arguments.getAsXML( Key.XMLNode );
String xpathString = arguments.getAsString( Key.xpath );
final IStruct params = arguments.getAsStruct( Key.params );

// Create an XPathFactory
XPathFactory xPathFactory = XPathFactory.newInstance();
// Create an XPathFactory
XPathFactory xPathFactory = XPathFactory.newInstance();

// Create an XPath object
XPath xpath = xPathFactory.newXPath();
// Create an XPath object
XPath xpath = xPathFactory.newXPath();

xpath.setXPathVariableResolver( new XPathVariableResolver() {
xpath.setXPathVariableResolver( new XPathVariableResolver() {

public Object resolveVariable( QName variableName ) {
return params.get( Key.of( variableName.getLocalPart() ) );
}
} );
public Object resolveVariable( QName variableName ) {
return params.get( Key.of( variableName.getLocalPart() ) );
}
} );

XPathExpression expression;
try {
// TODO: cache compiled expressions
XPathExpression expression = xpath.compile( xpathString );
expression = xpath.compile( xpathString );
} catch ( XPathExpressionException e ) {
throw new BoxRuntimeException( "Error compiling XPath: " + xpathString, e );
}

try {
// Evaluate the XPath expression on the Document
Object result = expression.evaluate( xml.getNode(), XPathConstants.NODESET );
Array results = new Array();
Object result = expression.evaluate( xml.getNode(), XPathConstants.NODESET );
Array results = new Array();
// Process the result
if ( result instanceof NodeList nodeList ) {
for ( int i = 0; i < nodeList.getLength(); i++ ) {
Expand All @@ -100,10 +107,23 @@ public Object resolveVariable( QName variableName ) {
}
return results;

} catch (

XPathExpressionException e ) {
throw new BoxRuntimeException( "Invalid XPath: " + xpathString, e );
} catch ( XPathExpressionException e ) {
// The API here is freaking worthless. It's impossible to tell what kind of return type you'll get without doing your own manual pre-parsing of the xpath string.
// So, we have to just try it as a nodeset and if that fails, guess what it should have been by analyzing the error message. Pathetic.
String message = e.getMessage() == null ? "" : e.getMessage();
try {
if ( message.indexOf( "#BOOLEAN" ) != -1 ) {
return BooleanCaster.cast( expression.evaluate( xml.getNode(), XPathConstants.BOOLEAN ) );
} else if ( message.indexOf( "#NUMBER" ) != -1 ) {
return NumberCaster.cast( expression.evaluate( xml.getNode(), XPathConstants.NUMBER ) );
} else if ( message.indexOf( "#STRING" ) != -1 ) {
return expression.evaluate( xml.getNode(), XPathConstants.STRING );
} else {
throw e;
}
} catch ( XPathExpressionException e1 ) {
throw new BoxRuntimeException( "Error evaluating XPath: " + xpathString, e1 );
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,46 @@ public void testCanSearchMember() {
.isEqualTo( "luis" );
}

@DisplayName( "It can search string" )
@Test
public void testCanSearchString() {
instance.executeSource(
"""
xml = '<Conditions NotBefore="2018-08-24T10:54:19.464Z" NotOnOrAfter="2018-08-24T11:54:19.464Z"></Conditions>';
result = XmlSearch(xml, "string(/Conditions/@NotBefore)");
""",
context );
assertThat( variables.get( result ) ).isInstanceOf( String.class );
assertThat( variables.get( result ) ).isEqualTo( "2018-08-24T10:54:19.464Z" );
}

@DisplayName( "It can search boolean" )
@Test
public void testCanSearchBoolean() {
instance.executeSource(
"""
xml = '<Conditions IsActive="true"></Conditions>';
result = XmlSearch(xml, "boolean(/Conditions/@IsActive)");
""",
context );
assertThat( variables.get( result ) ).isInstanceOf( Boolean.class );
assertThat( variables.get( result ) ).isEqualTo( true );
}

@DisplayName( "It can search numeric" )
@Test
public void testCanSearchNumeric() {
instance.executeSource(
"""
xml = '<Item Price="19.99"></Item>';
result = XmlSearch(xml, "number(/Item/@Price)");
""",
context );
assertThat( variables.get( result ) ).isInstanceOf( Double.class );
assertThat( variables.get( result ) ).isEqualTo( 19.99 );
}

}

0 comments on commit a50ec27

Please sign in to comment.