/*
Akana Javascript utility library - contains various useful functions
Author: KenC - Rogue Wave Software, Inc. 2018
Disclaimer: The information provided in this document is provided "AS IS" WITHOUT ANY WARRANTIES OF ANY KIND INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT OF INTELLECTUAL PROPERTY. Akana may make changes to this document at any time without notice. The content of this document may be out of date, and Akana makes no commitment to update this content.
*/
/*
FUNCTION: initializeContext ( msgVarName, partNames, msgVarNames )
Initializes the context of a process by:
1. Normalizing the specified request message;
2. Getting the specified message part values and setting their values into processContext variables;
3. Creating the specified normalized Message objects.
PARAMETERS:
msgVarName (optional string): the name of the processContext variable containing the request message.
The Message object contained in this variable will be normalized, and the normalized Message object
will be saved back into the variable.
Default: "message"
partNames (optional string array): an array containing the names of the message parts to retrieve
and save the part values into processContext variables of the same name.
Default: ["logProcessing"]
msgVarNames (optional string array): an array containing the names
of normalized Message objects to create
and save into processContext variables of the same name.
Default: ["request", "response", "fault"]
*/
function initializeContext ( msgVarName, partNames, msgVarNames ) {
// Initialize missing param values to defaults
if ( msgVarName == undefined ) msgVarName = "message";
if ( partNames == undefined ) partNames = [ "logProcessing" ];
if ( msgVarNames == undefined ) msgVarNames =
[ "request", "response", "fault" ];
// Normalize the request Message
var normalizedMsg = normalizeMessage( msgVarName );
// Silently set the logProcessing message variable from message
var logProcessingMessagePart = getMessagePartValue(
msgVarName, "logProcessing", "logProcessing", false );
if ( logProcessingMessagePart == null
|| logProcessingMessagePart == undefined )
logProcessingMessagePart = true; //Default true if absent
var logProcessing = booleanValue( logProcessingMessagePart );
// Get the query param values from the normalized message
// and save them in processContext variables
for ( var i = 0, len = partNames.length; i < len; i++) {
getMessagePartValue(
msgVarName, partNames[i], partNames[i], logProcessing );
}
// Initialize message variables to new normalized Message objects
setupMessages( msgVarNames, logProcessing );
};
/*
FUNCTION: normalizeMessage( msgVarName, toXML )
Retrieves the Message object in the specified processContext variable name,
normalizes the Message object, saves the normalized Message object
back into the variable, and returns the normalized Message object.
PARAMETERS:
msgVarName (required string): the processContext variable containing
the Message object to be normalized.
Default: "message"
toXML (optional boolean): If true, normalize the message to XML.
RETURNS: The normalized Message object.
*/
function normalizeMessage( msgVarName, toXML ) {
// Set defaults for unspecified params
if ( msgVarName == undefined ) msgVarName = "message";
if ( toXML == undefined ) toXML = false; // default false
toXML = booleanValue( toXML );
if ( typeof msgVarName == "string" )
var message = processContext.getVariable( msgVarName );
// If we got a Message object...
if ( message instanceof com.soa.message.script.Message ) {
var normalizedMsg;
if ( toXML )
normalizedMsg = message.normalizeToXML();
else
normalizedMsg = message.normalize();
processContext.setVariable( msgVarName, normalizedMsg );
return normalizedMsg;
}
else {
auditLog.error("ERROR: Cannot normalize the message. " +
"The processContext variable '" + msgVarName +
"' does not contain a Message object." );
return null;
}
};
/*
FUNCTION: booleanValue( obj )
Returns a "logical" boolean value from the specified variable.
Several attempts are made to produce a logical boolean value.
If all attempts fail, then this function will return the
"truthy" or "falsey" value of obj.
PARAMETERS:
obj (required, any type): the object from which to obtain a boolean value.
RETURNS: A boolean value, depending on the object.
*/
function booleanValue( obj ) {
// Handle falsey values first...
if ( obj == undefined || obj == null || isNaN( obj ) ||
obj == 0 || obj == false || obj == "" )
return false;
// If it's a JS string or a Java String...
if ( typeof obj == "string" || obj instanceof String ) {
if ( obj == "true" || obj != "0" ) return true;
else if ( obj == "false" || obj == "0" || obj == "" )
return false;
}
// Else, if it's a JS number or a Java Number...
else if ( typeof obj == "number" || obj instanceof Number ) {
if ( obj == 0 ) return false
else return true;
}
else { // It's something else, so return truthy or falsey value
if ( obj ) return true;
else return false;
}
};
/*
FUNCTION:
getMessagePartValue( msgVarName, partName, partVarName, logProcessing )
1. Gets the specified part from the message in the specified processContext variable,
2. optionally saves the value of the part in the specified processContext variable,
3. and optionally logs the processing.
PARAMETERS:
msgVarName (optional string): the processContext variable name
containing the Message object.
Default: "message"
partName (optional string): the name of the message part to process
from the Message object.
Default: "entity"
partVarName (optional string): the name of the processContext
variable to set to the part value.
If the text value of the message part is a "logical boolean" -
that is, a string with a value of "true" or "false",
the variable will be set to a Boolean value to reflect this.
logProcessing (optional boolean): If true, log the processing.
Default: false
RETURNS: the message part value.
*/
function getMessagePartValue(
msgVarName, partName, partVarName, logProcessing ) {
// Set default values for unspecified parameters
if ( msgVarName == undefined ) msgVarName = "message";
if ( typeof msgVarName != "string" ) return null;
if ( partName == undefined ) partName = "entity";
if ( typeof partName != "string" ) return null;
logProcessing = booleanValue( logProcessing );
var partValue;
// Get the Message object from the variable
var message = processContext.getVariable( msgVarName );
// If we got the Message object...
if ( message instanceof com.soa.message.script.Message ) {
var part = message.getPart( partName ); // Get the part
if ( part ) { // If we got the part...
// If the part is an Array, get just the first element
if ( part instanceof Array ) partValue = part[0];
else partValue = part;
// Change the part value to boolean if "logical boolean"
if ( partValue == "true" ) partValue = true;
else if ( partValue == "false" ) partValue = false;
// If indicated, set processContext variable to part value
if ( typeof partVarName == "string" ||
partVarName instanceof String )
processContext.setVariable( partVarName, partValue );
}
}
return partValue;
};
/*
FUNCTION: setupMessages( msgVarNames, logStatus )
Creates normalized Message objects and places each one
in the processContext variable of the same name.
PARAMETERS:
msgVarNames (optional array of strings): an array of strings
containing name(s) of processContext variable(s)
to be set to normalized Message objects.
Default: ["request","response","fault"]
logProcessing (optional boolean): if true, log processing status.
Default: false
*/
function setupMessages( msgVarNames, logProcessing ) {
var msgVarNamesStr = "";
logProcessing = booleanValue( logProcessing );
// Set msgVarNames to the default variable names if not provided,
// or if the provided value is not an Array
if ( !( msgVarNames instanceof Array ) ) {
msgVarNames = ["request", "response", "fault"];
if ( logProcessing )
auditLog.debug( "Using default message variable names: " +
"'request', 'response', and 'fault'" );
}
// Create normalized Message objects and set into processContext
for ( var i = 0, len = msgVarNames.length; i < len; i++ ) {
if ( typeof msgVarNames[i] == "string" ||
msgVarNames[i] instanceof String ) {
processContext.setVariable(
msgVarNames[i], msgFactory.createNormalized() );
msgVarNamesStr += "\n" + msgVarNames[i]; // Build log str
}
}
if ( logProcessing ) {
auditLog.debug(
"Normalized Message objects created and placed in " +
"process variables:\n" + msgVarNamesStr );
}
};
/*
FUNCTION: logResponseMsg( msgVarName, formatRsp )
Formats and logs a message containing the XML content of the Message object contained in the specified processContext variable name, and optionally sets the formatted XML message text into the Message as the string content.
PARAMETERS:
msgVar (required string): the name of the processContext variable
containing the Message object whose XML content is to be formatted and logged.
formatRsp (optional boolean): If true, the string content of the
Message object will be set to the formatted XML string.
Default: false
logProcessing (optional boolean): if true, log the processing status.
Default: false
RETURNS: the Message object.
*/
function logResponseMsg( msgVarName, formatRsp, logProcessing ) {
// Handle missing or invalid params
formatRsp = booleanValue( formatRsp );
logProcessing = booleanValue( logProcessing );
// Get the variable containing the Message object
var message = processContext.getVariable( msgVarName );
// If variable does not contain a Message, log and return false
if ( !( message instanceof com.soa.message.script.Message ) ) {
auditLog.error( "ERROR: the variable '" + msgVarName +
"' does not contain a Message object." );
return false;
}
var vkb = new vkbeautify();
var xmlStr = String( message.getContentAsString() );
var formattedXML = vkb.xml( xmlStr );
if ( logProcessing )
auditLog.debug( "Response message:\n\n" + formattedXML + "\n" );
// If formatting response, set into msg & save in processContext
if ( formatRsp ) {
message.setStringContent( formattedXML );
processContext.setVariable( msgVarName, message );
}
return message;
};
/*
FUNCTION: formatJSON( xmlStr, message, logProcessing )
Formats a JSON string from the specified XML string, and optionally sets the string content of the Message object held in the specified processContext variable name to the JSON string, if a variable name is specified.
PARAMETERS:
xmlStr (required): the XML string to convert to a JSON string
forceTopLevelObject (optional boolean): If true, the root XML
element will be a named JSON object inside an outer set of {}'s.
If false, the root XML element will be treated anonymously and
the root JSON object represented with the outer {}'s.
Default: true.
msgVarName (optional string): the processContext variable name that contains a Message object.
If specified, the content of this variable will be retrieved.
If the content of the variable is a Message object, the string content of that Message object will be set to the formatted JSON string.
logProcessing (optional boolean): if true, log processing status.
Default: false
RETURNS: the formatted JSON string.
*/
function formatJSON( xmlStr, forceTopLevelObject, msgVarName, logProcessing ) {
// Handle param value types and missing params
if ( !xmlStr ) return false;
if ( !( xmlStr instanceof String) ) return false;
forceTopLevelObject = booleanValue( forceTopLevelObject );
logProcessing = booleanValue( logProcessing );
converter = com.soa.json.xml.script.XML2JSON.converter();
converter.setForceTopLevelObject( forceTopLevelObject );
converter.setRemoveNamespacePrefixes( true );
converter.setIgnoreNamespaces( true );
var jsonString = converter.convert( xmlStr );
var jsonObject = JSON.parse( jsonString );
var formattedJSONStr = JSON.stringify( jsonObject );
if ( logProcessing )
auditLog.debug('JSON:\n\n' + formattedJSONStr );
// If indicated, set the message content to the JSON string
if ( typeof msgVarName == "string" ||
msgVarName instanceof String ) {
var message = processContext.getVariable( msgVarName );
if ( message instanceof com.soa.message.script.Message ) {
message.setStringContent( formattedJSONStr );
if ( logProcessing )
auditLog.debug( "The message string content " +
"was set to JSON string:\n\n" + formattedJSONStr );
}
}
return formattedJSONStr;
};
/*
FUNCTION: convertXMLToJSON( xmlStr, forceTopLevelObj, logProcessing )
Converts an XML string to a JSON object, without namespaces.
PARAMETERS:
xmlStr (required string): XML string to convert to a JSON object.
forceTopLevelObj (optional boolean): If true, the root XML element
will be a named JSON object inside an outer set of {}.
logProcessing (optional boolean): if true, log processing status.
Default: false
RETURNS: the JSON object.
*/
function convertXMLToJSON( xmlStr, forceTopLevelObj, logProcessing ) {
if ( !(xmlStr instanceof String) && (typeof xmlStr != "string") )
return null;
forceTopLevelObj = booleanValue( forceTopLevelObj );
logProcessing = booleanValue( logProcessing );
var converter = com.soa.json.xml.script.XML2JSON.converter();
converter.setForceTopLevelObject( forceTopLevelObj );
converter.setRemoveNamespacePrefixes( true );
converter.setIgnoreNamespaces( true );
var jsonString = converter.convert( xmlStr );
var jsonObj = JSON.parse( jsonString );
if ( logProcessing ) {
var fmtJSONStr = JSON.stringify( jsonObj );
if ( logProcessing )
auditLog.debug(
"Converted XML to JSON string:\n\n" + fmtJSONStr );
}
return jsonObj;
};
/*
FUNCTION: convertJSONToXML( jsonObj, rootElemName, elemName, formatXML )
Converts a JSON object to XML.
PARAMETERS:
jsonObj (required object): JSON object to convert to an XML string.
rootElemName (required string): the name of the root XML element
to be used in the new XML string.
elemName (required string): the name of the XML element
to be used for the XML element within the XML root element.
formatXML (optional boolean): if true, the returned XML string
will be formatted for readability.
If false or absent, returned XML string will not be formatted.
RETURNS: the JSON object.
*/
function convertJSONToXML( jsonObj, rootElemName, elemName, formatXML ) {
// Early returns for undefined params
if ( jsonObj == undefined ) return null;
if ( rootElemName == undefined ) return null;
if ( elemName == undefined ) return null;
formatXML = booleanValue( formatXML ); // false if absent
// Get a converter and set the root elem and elem names
var converter = com.soa.json.xml.script.JSON2XML.converter();
converter.setRootName( rootElemName );
converter.setElementName( elemName );
converter.setUseParentPrefix( false );
// Convert the JSON object to an XML string
var formattedXML = String( converter.convert( jsonObj ) );
// Format the XML string if indicated
if ( formatXML ) {
var vkb = new vkbeautify();
formattedXML = vkb.xml( formattedXML );
}
return formattedXML;
};
/*
FUNCTION: pad( input, padLen, padChar, padRight )
Left- or right-pads the specified number or string to the specified
length, with the specified padding char or with the default padding
char for the input type.
PARAMETERS:
input: (required, string or number) The value to pad.
If not specified, this function returns immediately.
padLen: (optional, number) length to which to pad the input value.
Default: The length of the input value.
padChar: (optional, string or number) character(s) to use to pad.
Default: '0' for number or numeric string, space otherwise
padRight: (optional, boolean) Whether to pad on the right or left
(true: pad on the right, false: pad on the left).
default: false for number or numeric string, else true.
*/
function pad( input, padLen, padChar, padRight ) {
if ( !input) return input; // Return if no input
// Default null params appropriately for input type
var inputStr = input + ''; // Coerce input to a string
padChar = padChar || ( isNaN( inputStr ) ? ' ' : '0' );
padLen = padLen || inputStr.length; // Default to input len
padRight = padRight || ( isNaN( inputStr ) ? true : false );
// Create an Array of the proper length and build padding string
var padStr = new Array( ( ++padLen ) - inputStr.length )
.join( padChar );
// Build return string and trim it to a length of padLen
var returnStr = padRight ? inputStr + padStr : padStr + inputStr;
returnStr = returnStr.substr( 0, padLen);
return returnStr;
};
/*
The following functions are adapted from vkBeautify - a javascript module to pretty-print or minify text in XML, JSON, CSS and SQL formats.
*/
function createShiftArr(step) {
var limit = 12;
var space = ' ';
if ( isNaN( parseInt(step) ) ) { // argument is string
space = step;
}
else { // arg is integer - build space string to correct length
space = '';
if ( step > limit ) step = 12; // Enforce limit in indentation
for ( ix = 0; ix < step; ix++ ) space += ' ';
}
var shift = [ '\n' ]; // array of shifts
for ( ix = 0; ix < 100; ix++ ){
shift.push( shift[ ix ] + space );
}
return shift;
}
function vkbeautify() {
this.step = ' '; // 4 spaces
this.shift = createShiftArr(this.step);
};
vkbeautify.prototype.xml = function(text,step) {
var ar = ( text + "" )
.replace(/>\s{0,}<")
.replace(/ or -1 ) {
str += shift[deep] + ar[ix];
inComment = true;
// end comment or //
if ( ar[ix].search( /-->/ ) > -1
|| ar[ix].search( /\]>/ ) > -1
|| ar[ix].search( /!DOCTYPE/ ) > -1 ) {
inComment = false;
}
}
else
// end comment or //
if ( ar[ix].search( /-->/ ) > -1
|| ar[ix].search( /\]>/ ) > -1 ) {
str += ar[ix];
inComment = false;
}
else
// //
if ( /^<\w/.exec(ar[ix-1])
&& /^<\/\w/.exec( ar[ix] )
&& /^<[\w:\-\.\,]+/.exec( ar[ix-1] ) ==
/^<\/[\w:\-\.\,]+/.exec( ar[ix] )[0].replace('/', '') ) {
str += ar[ix];
if ( !inComment ) deep--;
}
else
// //
if ( ar[ix].search( /<\w/ ) > -1
&& ar[ix].search( /<\// ) == -1
&& ar[ix].search( /\/>/ ) == -1 ) {
str = !inComment
? str += shift[deep++] + ar[ix]
: str += ar[ix];
}
else
// ... //
if ( ar[ix].search( /<\w/ ) > -1
&& ar[ix].search( /<\// ) > -1 ) {
str = !inComment
? str += shift[deep] + ar[ix]
: str += ar[ix];
}
else
// //
if ( ar[ix].search( /<\// ) > -1 ) {
str = !inComment
? str += shift[--deep] + ar[ix]
: str += ar[ix];
}
else
// //
if ( ar[ix].search( /\/>/ ) > -1 ) {
str = !inComment
? str += shift[deep] + ar[ix]
: str += ar[ix];
}
else
// xml ... ?> //
if ( ar[ix].search( /<\?/ ) > -1 ) {
str += shift[deep] + ar[ix];
}
else
// xmlns //
if ( ar[ix].search( /xmlns\:/ ) > -1
|| ar[ix].search( /xmlns\=/ ) > -1 ) {
str += shift[deep] + ar[ix];
}
else {
str += ar[ix];
}
}
return ( str[0] == '\n' ) ? str.slice(1) : str;
}
vkbeautify.prototype.json = function(text,step) {
var step = step ? step : this.step;
if (typeof JSON === 'undefined' ) return text;
if ( typeof text === "string" )
return JSON.stringify( JSON.parse(text), null, step );
if ( typeof text === "object" )
return JSON.stringify( text, null, step );
return text; // text is not string nor object
}
vkbeautify.prototype.xmlmin = function(text, preserveComments) {
var str = preserveComments ? text : text.replace(
/\/g, "" )
.replace( /[ \r\n\t]{1,}xmlns/g, ' xmlns' );
return str.replace( />\s{0,}<" );
}
vkbeautify.prototype.jsonmin = function(text) {
if (typeof JSON === 'undefined' ) return text;
return JSON.stringify( JSON.parse(text), null, 0 );
};