/* The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is comprised of the ADF directory The Initial Developer of the Original Code is PaperThin, Inc. Copyright (c) 2009-2019. All Rights Reserved. By downloading, modifying, distributing, using and/or accessing any files in this directory, you agree to the terms and conditions of the applicable end user license agreement. */ /* *************************************************************** */ /* Author: PaperThin, Inc. Name: data_2_0.cfc Summary: Data Utils component functions for the ADF Library Version: 2.0 History: 2015-08-31 - GAC - Created 2016-12-05 - GAC - Added the capFirstAllWords() method 2017-01-27 - GAC - Added compareStructData() with subfunctions structCompare() and arrayCompare() 2019-07-01 - GAC - Added the padNumericValue method 2019-07-18 - GAC - Added the parseNumberWithUnits method */ component displayname="data_2_0" extends="data_1_2" hint="Data Utils component functions for the ADF Library" output="no" { /* PROPERTIES */ property name="version" type="string" default="2_0_8"; property name="type" value="singleton"; property name="wikiTitle" value="Data_2_0"; /* Author: PaperThin, Inc. Name: $listDiffExtended Summary: Compares a old list to a new list and returns a struct of added, deleted, and matched values Returns: Struct added deleted matched Arguments: String - oldlist String - newlist String - delimiters History: 2016-08-26 - GAC - Created */ public struct function listDiffExtended(string oldlist="", string newlist="", string delimiters=",") { var listData = StructNew(); var i = 1; var newItem = ""; listData.added = ""; listData.deleted = ""; listData.matched = ""; // Loop over NewList to get deleted items (no need to get matched items) for (i=1; i LTE ListLen(arguments.newlist,arguments.delimiters); i=i+1) { newItem = ListGetAt(arguments.newlist, i,arguments.delimiters); // Check Old List for new items if ( ListFindNoCase(arguments.oldlist, newItem , arguments.delimiters) ) { // the NewItem already Exists... so add it to the matched list if ( ListFindNoCase(listData.matched, newItem, arguments.delimiters) EQ 0 ) listData.matched = ListAppend(listData.matched, newItem, arguments.delimiters); } else { // the NewItem was NOT found... so add it to the added list if ( ListFindNoCase(listData.added, newItem, arguments.delimiters) EQ 0 ) listData.added = ListAppend(listData.added, newItem, arguments.delimiters); } } // Loop over OldList to get deleted items (no need to get matched items) for (i=1; i LTE ListLen(arguments.oldlist,arguments.delimiters); i=i+1) { oldItem = ListGetAt(arguments.oldlist, i,arguments.delimiters); // Check Old List for new items if ( ListFindNoCase(arguments.newlist, oldItem , arguments.delimiters) EQ 0 ) { // the OldItem was NOT found... so add it to the deleted list if ( ListFindNoCase(listData.deleted , oldItem, arguments.delimiters) EQ 0 ) listData.deleted = ListAppend(listData.deleted , oldItem, arguments.delimiters); } } return listData; } /* Author: PaperThin, Inc. Name: $capFirstAllWords Summary: Updates a string to capitalize each word with options to skip specified words and/or to skip words wrapped in open and close punctuation marks eg. "no fix" or (no fix) Returns: String Arguments: String - str - string to capitalize each work String - skipWordsList - list of words to skip Boolean - preserveCaseOfWrappedWords - do not convert words that have been wrapped with punctuation History: 2016-11-22 - GAC - Created 2016-12-13 - GAC - Fixed issue with single character words 2017-11-09 - GAC - Fixed issue with the function removing ampersands from sentences */ public string function capFirstAllWords(string str="",string skipWordsList="", boolean preserveCaseOfWrappedWords=true) { var rtnStr = ""; var strPartsArr = ArrayNew(1); var strCapsArr = ArrayNew(1); var word = ""; var i = 0; var s = 0; var fixWord = false; var ampPlaceHolderStr = "[[-xAmp-]]"; var wordPrefix = ""; var wordSuffix = ""; // encode '&' chars to '&' arguments.str = encodeAmpersands(str=arguments.str,type="html"); // Create the word array strPartsArr = reMatch('([[:punct:]])?([[:word:]]+)([[:punct:]])?([[:word:]]+)?',arguments.str); for ( i=1; i LTE ArrayLen(strPartsArr); i=i+1){ fixWord = false; wordPrefix = ""; wordSuffix = ""; word = strPartsArr[i]; // Skip words if found in the skipWordsList (case-sensitive) if ( ListFind(arguments.skipWordsList,word) EQ 0 ) fixWord = true; // Find words surrounded by punctuation if ( REFind('[[:punct:]]', LEFT(word,1), 1) AND REFind('[[:punct:]]', RIGHT(word,1), 1) ) { // Skip words surrounded by punctuation if ( arguments.preserveCaseOfWrappedWords ) fixWord = false; else { wordPrefix = LEFT(word,1); wordSuffix = RIGHT(word,1); word = MID(word, 2, len(word)-2); } } if ( fixWord AND LEN(word) ) { word = lcase(word); if ( LEN(word) GT 1 ) word = uCase(left(word,1)) & right(word ,len(word)-1); else word = uCase(left(word,1)); if ( LEN(TRIM(wordPrefix)) AND LEN(TRIM(wordSuffix)) ) word = wordPrefix & word & wordSuffix; } // decode '&' back to '&' word = decodeAmpersands(str=word,type="html"); ArrayAppend(strCapsArr,word); } for ( s=1; s LTE ArrayLen(strCapsArr); s=s+1){ rtnStr = ListAppend(rtnStr,strCapsArr[s]," "); } return rtnStr; } /* Author: PaperThin, Inc. G. Cronkright Name: $compareStructData Summary: Compares two data structures and then returns a true if they are the same Returns: Boolean Arguments: Struct - structDataA Struct - structDataB String - excludeKeyList String - objectFieldKeyList - List of Keys whose values contain JSON object data History: 2017-01-03 - GAC - Created new version - GAC - Updated to duplicate the compare structs incase the excludeKey values are passed in - GAC - Updated to remove the unreliable .Equals() JAVA method and replaced it with the structCompare() subfunctions to better compare complex structs */ public boolean function compareStructData(required struct structDataA,required struct structDataB,string excludeKeyList="", string objectFieldKeyList="") { var isEqual = false; var i=1; var currentKey = ""; var dataAObjectValue = ""; var dataBObjectValue = ""; var dataA = duplicateStruct(arguments.structDataA); var dataB = duplicateStruct(arguments.structDataB); // Remove the exclude Keys before doing compare for ( i=1;i LTE ListLen(arguments.excludeKeyList);i=i+1 ) { currentKey = ListGetAt(arguments.excludeKeyList,i); if ( LEN(TRIM(currentKey)) ) { if ( StructKeyExists(dataA,currentKey) ) StructDelete(dataA,currentKey); if ( StructKeyExists(dataB,currentKey) ) StructDelete(dataB,currentKey); } } // Loop over the object fields to compare them individually for ( i=1;i LTE ListLen(arguments.objectFieldKeyList);i=i+1 ) { currentKey = ListGetAt(arguments.objectFieldKeyList,i); if ( LEN(TRIM(currentKey)) ) { // Compare the object fields if ( isJSON(dataA[currentKey]) AND isJSON(dataB[currentKey]) ) { // Set into variables to run the comparison dataAObjectValue = deserializeJSON(dataA[currentKey]); dataBObjectValue = deserializeJSON(dataB[currentKey]); // If not equal, then end all the remaining comparisons if ( NOT dataAObjectValue.EQUALS(dataBObjectValue) ) return false; } // If equal, then remove from the structs for the final compare if ( StructKeyExists(dataA,currentKey) ) StructDelete(dataA,currentKey); if ( StructKeyExists(dataB,currentKey) ) StructDelete(dataB,currentKey); } } // Check the entire object because it is faster. //if ( dataA.EQUALS(dataB) ) // .EQUALS() fails under various CF dataType conditions // isEqual = true; // Compare the two Structs - returns true if the same isEqual = structCompare(LeftStruct=dataA,RightStruct=dataB); return isEqual; } /* Author: Ja Carter (ja@nuorbit.com) Name: $structCompare Summary: Recursive functions to compare structures and arrays. Fix by Jose Alfonso. @version 2, October 14, 2005 Based on the structCompare() by Ja Carter (ja@nuorbit.com) and Jose Alfonso Returns: Boolean Arguments: Struct - LeftStruct Struct - RightStruct Usage: structCompare(LeftStruct,RightStruct) History: 2017-01-03 - GAC - Added */ public boolean function structCompare(struct LeftStruct,struct RightStruct) { var result = true; var LeftStructKeys = ""; var RightStructKeys = ""; var key = ""; //Make sure both params are structures if (NOT (isStruct(LeftStruct) AND isStruct(RightStruct))) return false; //Make sure both structures have the same keys LeftStructKeys = ListSort(StructKeyList(LeftStruct),"TextNoCase","ASC"); RightStructKeys = ListSort(StructKeyList(RightStruct),"TextNoCase","ASC"); if(LeftStructKeys neq RightStructKeys) return false; // Loop through the keys and compare them one at a time for (key in LeftStruct) { //Key is a structure, call structCompare() if (isStruct(LeftStruct[key])){ result = structCompare(LeftStruct[key],RightStruct[key]); if (NOT result) return false; //Key is an array, call arrayCompare() } else if (isArray(LeftStruct[key])){ result = arrayCompare(LeftStruct[key],RightStruct[key]); if (NOT result) return false; // A simple type comparison here } else { if(LeftStruct[key] IS NOT RightStruct[key]) return false; } } return true; } /* Author: Ja Carter (ja@nuorbit.com) Name: $arrayCompare Summary: Recursive functions to compare arrays and nested structures. @version 1, September 23, 2004 Returns: Boolean Arguments: Array - LeftArray Array - RightArray Usage: arrayCompare(LeftArray,RightArray) History: 2017-01-03 - GAC - Added */ public boolean function arrayCompare(array LeftArray,array RightArray) { var result = true; var i = ""; //Make sure both params are arrays if (NOT (isArray(LeftArray) AND isArray(RightArray))) return false; //Make sure both arrays have the same length if (NOT arrayLen(LeftArray) EQ arrayLen(RightArray)) return false; // Loop through the elements and compare them one at a time for (i=1;i lte arrayLen(LeftArray); i = i+1) { //elements is a structure, call structCompare() if (isStruct(LeftArray[i])){ result = structCompare(LeftArray[i],RightArray[i]); if (NOT result) return false; //elements is an array, call arrayCompare() } else if (isArray(LeftArray[i])){ result = arrayCompare(LeftArray[i],RightArray[i]); if (NOT result) return false; //A simple type comparison here } else { if(LeftArray[i] IS NOT RightArray[i]) return false; } } return true; } public string function encodeAmpersands(string str="",string type="html") { var placeHolderStr = "[[-xAmp-]]"; var replaceChrs = StructNew(); var replaceStr = ""; var replaceTypeDefault = "html"; var allowedTypesList = ""; replaceChrs['html'] = "&"; replaceChrs['url'] = "%26"; replaceChrs['slash'] = "\&"; allowedTypesList= StructKeyList(replaceChrs,","); if ( !FindNoCase(arguments.type,allowedTypesList,1) ) arguments.type = replaceTypeDefault; replaceStr = replaceChrs[arguments.type]; // Preserve & that are already encoded arguments.str = replaceNoCase(arguments.str,replaceStr,placeHolderStr,"all"); // Encode the remaining '&' chars arguments.str = replace(arguments.str,"&",replaceStr,"all"); // Replace the placeHolderStr with & return replaceNoCase(arguments.str,placeHolderStr,replaceStr,"all"); } public string function decodeAmpersands(string str="",string type="html") { var replaceChrs = StructNew(); var replaceStr = ""; var replaceTypeDefault = "html"; var allowedTypesList = ""; replaceChrs['html'] = "&"; replaceChrs['url'] = "%26"; replaceChrs['slash'] = "\&"; allowedTypesList = StructKeyList(replaceChrs,","); if ( !FindNoCase(arguments.type,allowedTypesList,1) ) arguments.type = replaceTypeDefault; replaceStr = replaceChrs[arguments.type]; return replacenocase(arguments.str,replaceStr,"&","all"); } /* *************************************************************** */ /* Author: PaperThin, Inc. Name: $padNumericValue Summary: Builds a string from a number padding it with leading Zeros (0) Returns: string Arguments: Numeric - numberVal (number to be padded) Numeric - padSize (number of chars of padding) History: 2019-06-25 - GAC - Created */ public string function padNumericValue(required numeric numberVal,numeric padSize=9) { var padMaskStr = ""; // Get a safe pad size if ( IsNumeric(arguments.padSize) ) { if ( arguments.padsize GTE 2 ) arguments.padsize = arguments.padsize - 1; else arguments.padsize = 1; } // Build the string for the padding mask padMaskStr = RepeatString("0",arguments.padsize) & 9; // Pad the number Value passed in return NumberFormat( arguments.numberVal, padMaskStr ); } /* *************************************************************** */ /* Author: PaperThin, Inc. Name: $parseNumberWithUnits Summary: Returns a structure from a string parsed into 'number', 'unit' and 'orig' keys Returns: Struct number unit orig Arguments: String - str History: 2019-07-18 - GAC - Created */ struct function parseNumbersWithUnits(string str="") { var retData = {}; retData.number = 0; retData.unit = ""; retData.orig = arguments.str; if ( !isNumeric(arguments.str) ) { retData.number = VAL(arguments.str); retData.unit = REReplace(arguments.str,"(\+|\-)?[0-9]+","","all"); } else retData.number = VAL(arguments.str); return retData; } }