Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 48 additions & 25 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,42 +1,65 @@
name: Run test suite

on:
[ workflow_dispatch, workflow_call ]
on: [workflow_dispatch, workflow_call]

jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
cfengine: [ "lucee@6", "lucee@7", "adobe@2025" ]
cfengine: ["lucee@6", "lucee@7", "adobe@2025", "boxlang"]
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Checkout Repository
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: "21"
distribution: "temurin"

- name: Install the ortus security key
run: curl -fsSl https://downloads.ortussolutions.com/debs/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/ortussolutions.gpg > /dev/null
- name: Setup CommandBox CLI
uses: Ortus-Solutions/setup-commandbox@7be599028dcf77743854b2f6c720b15316f39be2 # v2.0.1
with:
version: latest

- name: Add the commandbox source
run: echo "deb [signed-by=/usr/share/keyrings/ortussolutions.gpg] https://downloads.ortussolutions.com/debs/noarch /" | sudo tee /etc/apt/sources.list.d/commandbox.list
- name: Install dependencies
run: box install

- name: Update apt and install commandbox
run: sudo apt-get update && sudo apt-get install apt-transport-https commandbox
- name: Start a server
run: box server start cfengine=${{ matrix.cfengine }} port=8080 host=0.0.0.0

- name: Install dependencies
run: box install
- name: Install ACF packages
if: ${{ matrix.cfengine == 'adobe@2025' }}
run: box run-script cfpmInstall

- name: Start a server
run: box server start cfengine=${{ matrix.cfengine }} port=8080
- name: Install BX packages
if: ${{ matrix.cfengine == 'boxlang' }}
run: box run-script boxLangInstall

- name: Install ACF packages
if: ${{ matrix.cfengine == 'adobe@2025' }}
run: box run-script cfpmInstall
- name: Run TestBox Tests
id: run_tests
run: |
mkdir -p test/results
box testbox run runner="http://localhost:8080/test/index.cfm" outputFile="test/results/test-results" outputFormats="json"
continue-on-error: true
timeout-minutes: 10

- name: Run TestBox Tests
run: box testbox run runner="http://localhost:8080/test/index.cfm"
- name: Display Test Results
if: always()
run: |
if [ -f "test/results/test-results.json" ]; then
echo "=== Test Results Summary (${{ matrix.cfengine }}) ==="
cat test/results/test-results.json | jq -r '
"Total Bundles: \(.totalBundles)",
"Total Suites: \(.totalSuites)",
"Total Specs: \(.totalSpecs)",
"Total Pass: \(.totalPass)",
"Total Fail: \(.totalFail)",
"Total Error: \(.totalError)",
"Total Skipped: \(.totalSkipped)"
'
else
echo "No test results found!"
ls -la test/results/ || echo "Results directory doesn't exist"
fi
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
testbox/
server.json
test/results/*
bx-image/
139 changes: 73 additions & 66 deletions Spreadsheet.cfc

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions box.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
},
"engines" : [
{ "type" : "lucee", "version" : ">=5.x.x" },
{ "type" : "adobe", "version" : ">=2021.0.0" }
{ "type" : "adobe", "version" : ">=2021.0.0" },
{ "type" : "boxlang", "version": ">=1.13.0+50" }
],
"defaultEngine" : "lucee",
"license" : [
Expand All @@ -29,6 +30,7 @@
"private" : "false",
"ignore" : [ ".github", "build", "src", "test", ".gitattributes", "CHANGELOG.md", "README.md" ],
"scripts":{
"cfpmInstall": "cfpm install image"
"cfpmInstall": "cfpm install image",
"boxLangInstall":"install bx-image"
}
}
17 changes: 10 additions & 7 deletions helpers/cell.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ component extends="base"{

any function getDataFormatPropertyType(){
if( IsNull( variables.dataFormatPropertyType ) )
variables.dataFormatPropertyType = library().createJavaObject( "org.apache.poi.ss.usermodel.CellPropertyType" ).DATA_FORMAT;
variables.dataFormatPropertyType = library().createJavaObject( "org.apache.poi.ss.usermodel.CellPropertyType" ).valueOf( JavaCast( "string", "DATA_FORMAT" ) );
return variables.dataFormatPropertyType;
}

Expand All @@ -24,7 +24,7 @@ component extends="base"{
}

boolean function cellIsOfType( required cell, required string type ){
return arguments.cell.getCellType().Equals( arguments.cell.getCellType()[ arguments.type ] );
return arguments.cell.getCellType().Equals( arguments.cell.getCellType().valueOf( JavaCast( "string", arguments.type ) ) );
}

any function createCell( required row, numeric cellNum=arguments.row.getLastCellNum(), overwrite=true ){
Expand All @@ -39,7 +39,10 @@ component extends="base"{

any function getCellAt( required workbook, required numeric rowNumber, required numeric columnNumber ){
var columnIndex = ( arguments.columnNumber -1 );
return getRowHelper().getRowFromActiveSheet( arguments.workbook, arguments.rowNumber )?.getCell( JavaCast( "int", columnIndex ) );
var row = getRowHelper().getRowFromActiveSheet( arguments.workbook, arguments.rowNumber );
if( IsNull( row ) )
return;
return row.getCell( JavaCast( "int", columnIndex ) );
}

any function getCellFormulaValue( required workbook, required cell, boolean forceEvaluation=false ){
Expand Down Expand Up @@ -94,7 +97,7 @@ component extends="base"{
if( Trim( arguments.value ).IsEmpty() )
return setEmptyValue( arguments.cell );
var validCellTypes = getDataTypeHelper().validCellOverrideTypes().Append( "blank" );
if( !arguments.KeyExists( "type" ) ) //autodetect type
if( !arguments.KeyExists( "type" ) || isNull( arguments.type ) ) //autodetect type
arguments.type = getDataTypeHelper().detectValueDataType( arguments.value );
else if( !validCellTypes.FindNoCase( arguments.type ) )
Throw( type=library().getExceptionType() & ".invalidDatatype", message="Invalid data type: '#arguments.type#'", detail="The data type must be one of the following: #validCellTypes.ToList( ', ' )#." );
Expand Down Expand Up @@ -236,9 +239,9 @@ component extends="base"{
}

private any function getCachedFormulaValue( required cell ){
if( arguments.cell.getCachedFormulaResultType().Equals( arguments.cell.getCellType().NUMERIC ) )
return getCellNumericOrDateValue( arguments.cell );
if( arguments.cell.getCachedFormulaResultType().Equals( arguments.cell.getCellType().BOOLEAN ) )
if( arguments.cell.getCachedFormulaResultType().Equals( arguments.cell.getCellType().valueOf( JavaCast( "string", "NUMERIC" ) ) ) )
return getCellNumericOrDateValue( arguments.cell );
if( arguments.cell.getCachedFormulaResultType().Equals( arguments.cell.getCellType().valueOf( JavaCast( "string", "BOOLEAN" ) ) ) )
return arguments.cell.getBooleanCellValue();
return getStringValue( arguments.cell );
}
Expand Down
13 changes: 10 additions & 3 deletions helpers/color.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ component extends="base"{
array function getRGBFromCellFont( required workbook, required any cellFont ){
if( library().isXmlFormat( arguments.workbook ) )
return getRGBFromXSSFCellFont( arguments.cellFont );
return arguments.cellFont.getHSSFColor( arguments.workbook )?.getTriplet()?:[];
var hssfColor = arguments.cellFont.getHSSFColor( arguments.workbook );
if( IsNull( hssfColor ) )
return [];
return hssfColor.getTriplet();
}

any function getColor( required workbook, required string colorValue ){
Expand Down Expand Up @@ -148,9 +151,13 @@ component extends="base"{
}

private array function getRGBFromXSSFCellFont( required any cellFont ){
if( IsNull( arguments.cellFont.getXSSFColor()?.getRGB() ) )
var xssfColor = arguments.cellFont.getXSSFColor();
if( IsNull( xssfColor ) )
return [];
var rgb = xssfColor.getRGB();
if( IsNull( rgb ) )
return [];
return convertSignedRGBToPositiveTriplet( arguments.cellFont.getXSSFColor().getRGB() );
return convertSignedRGBToPositiveTriplet( rgb );
}

}
16 changes: 8 additions & 8 deletions helpers/comment.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ component extends="base"{
if( !commentHasFontStyles( arguments.comment ) )
return this;
var font = arguments.workbook.createFont();
if( arguments.comment.KeyExists( "bold" ) )
if( arguments.comment.KeyExists( "bold" ) && !isNull(arguments.comment.bold))
font.setBold( JavaCast( "boolean", arguments.comment.bold ) );
if( arguments.comment.KeyExists( "color" ) )
if( arguments.comment.KeyExists( "color" ) && !isNull(arguments.comment.color))
font.setColor( getColorHelper().getColor( arguments.workbook, arguments.comment.color ) );
if( arguments.comment.KeyExists( "font" ) )
if( arguments.comment.KeyExists( "font" ) && !isNull(arguments.comment.front))
font.setFontName( JavaCast( "string", arguments.comment.font ) );
if( arguments.comment.KeyExists( "italic" ) )
if( arguments.comment.KeyExists( "italic" ) && !isNull(arguments.comment.italic))
font.setItalic( JavaCast( "string", arguments.comment.italic ) );
if( arguments.comment.KeyExists( "size" ) )
if( arguments.comment.KeyExists( "size" ) && !isNull(arguments.comment.size))
font.setFontHeightInPoints( JavaCast( "int", arguments.comment.size ) );
if( arguments.comment.KeyExists( "strikeout" ) )
if( arguments.comment.KeyExists( "strikeout" ) && !isNull(arguments.comment.strikeout))
font.setStrikeout( JavaCast( "boolean", arguments.comment.strikeout ) );
if( arguments.comment.KeyExists( "underline" ) )
font.setUnderline( JavaCast( "byte", arguments.comment.underline ) );
if( arguments.comment.KeyExists( "underline" ) && !isNull(arguments.comment.underline))
font.setUnderline( JavaCast( "byte", booleanFormat(arguments.comment.underline) ? 1 : 0 ) );
arguments.commentString.applyFont( font );
return this;
}
Expand Down
2 changes: 1 addition & 1 deletion helpers/csv.cfc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
component extends="base"{

any function getFormatObject( string type="DEFAULT" ){
return library().createJavaObject( "org.apache.commons.csv.CSVFormat" )[ JavaCast( "string", arguments.type ) ];
return library().createJavaObject( "org.apache.commons.csv.CSVFormat" ).valueOf( JavaCast( "string", arguments.type ) );
}

boolean function delimiterIsTab( required string delimiter ){
Expand Down
12 changes: 6 additions & 6 deletions helpers/dataType.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ component extends="base"{
/* Data type overriding */

any function checkDataTypesArgument( required struct args ){
if( arguments.args.KeyExists( "datatypes" ) && datatypeOverridesContainInvalidTypes( arguments.args.datatypes ) )
if( arguments.args.KeyExists( "datatypes" ) && !isNull( arguments.args.datatypes ) && datatypeOverridesContainInvalidTypes( arguments.args.datatypes ) )
Throw( type=library().getExceptionType() & ".invalidDatatype", message="Invalid datatype(s)", detail="One or more of the datatypes specified is invalid. Valid types are #validCellOverrideTypes().ToList( ', ' )# and the columns they apply to should be passed as an array" );
return this;
}
Expand All @@ -47,11 +47,11 @@ component extends="base"{
for( var type in arguments.datatypeOverrides ){
var columnRefs = arguments.datatypeOverrides[ type ];
var totalColumnRefs = columnRefs.Len();
cfloop( from=1, to=totalColumnRefs, index="local.index" ){
if( IsNumeric( columnRefs[ index ] ) ) //position already given
for( var i = 1; i <= totalColumnRefs; i++ ){
if( IsNumeric( columnRefs[ i ] ) ) //position already given
continue;
var columnNumber = ArrayFindNoCase( columnNames, columnRefs[ index ] );//ACF won't accept member function on this array for some reason
columnRefs[ index ] = columnNumber;
var columnNumber = ArrayFindNoCase( columnNames, columnRefs[ i ] );//ACF won't accept member function on this array for some reason
columnRefs[ i ] = columnNumber;
}
arguments.datatypeOverrides[ type ] = columnRefs;
}
Expand All @@ -78,7 +78,7 @@ component extends="base"{
}
}
// if no override, use an already set default (i.e. query column type)
if( arguments.KeyExists( "defaultType" ) ){
if( arguments.KeyExists( "defaultType" ) && !isNull( arguments.defaultType ) ){
getCellHelper().setCellValueAsType( arguments.workbook, arguments.cell, arguments.cellValue, arguments.defaultType );
return this;
}
Expand Down
22 changes: 18 additions & 4 deletions helpers/date.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ component extends="base"{
}

struct function defaultFormats(){
if( library().getIsBoxlang() ) {
return {
DATE: "yyyy-MM-dd"
,DATETIME: "yyyy-MM-dd HH:mm:ss"
,TIME: "hh:mm:ss"
,TIMESTAMP: "yyyy-MM-dd HH:mm:ss"
};
}

return {
DATE: "yyyy-mm-dd"
,DATETIME: "yyyy-mm-dd HH:nn:ss"
Expand Down Expand Up @@ -43,13 +52,18 @@ component extends="base"{
}

boolean function isDateObject( required input ){
return IsInstanceOf( arguments.input, "java.util.Date" );
if( IsInstanceOf( arguments.input, "java.util.Date" ) )
return true;
if( library().getIsBoxlang() )
return IsInstanceOf( arguments.input, "ortus.boxlang.runtime.types.DateTime" );
return false;
}


//TODO improve these imperfect tests!
boolean function isDateOnlyValue( required date value ){
if( library().getIsBoxlang() )
return ( arguments.value.TimeFormat( "hh:mm:ss" ) == "00:00:00" );
return ( arguments.value.TimeFormat( "HH:mm:ss" ) == "00:00:00" );
var dateOnly = CreateDate( Year( arguments.value ), Month( arguments.value ), Day( arguments.value ) );
return ( DateCompare( arguments.value, dateOnly, "s" ) == 0 );
}
Expand Down Expand Up @@ -109,10 +123,10 @@ component extends="base"{
return ParseDateTime( arguments.value, "EEE MMM d HH:mm:ss zzz yyyy" );
//e.g. 08:21
if( arguments.value.REFindNoCase( "^\d{2,2}:\d{2,2}$" ) )
return ParseDateTime( "1899-12-30T#arguments.value#:00Z" );
return ParseDateTime( "1899-12-30T#arguments.value#:00" );
//e.g. 08:21:30
if( arguments.value.REFindNoCase( "^\d{2,2}:\d{2,2}:\d{2,2}$" ) )
return ParseDateTime( "1899-12-30T#arguments.value#Z" );
return ParseDateTime( "1899-12-30T#arguments.value#" );
return ParseDateTime( arguments.value );
}

Expand Down
8 changes: 6 additions & 2 deletions helpers/exception.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ component extends="base"{
}

void function throwExceptionIFreadFormatIsInvalid(){
if( arguments.KeyExists( "format" ) && !ListFindNoCase( "query,array,arrayOfStructs,html,csv", arguments.format ) )
if( arguments.KeyExists( "format" ) && !isNull( arguments.format ) && !ListFindNoCase( "query,array,arrayOfStructs,html,csv", arguments.format ) )
Throw( type=library().getExceptionType() & ".invalidReadFormat", message="Invalid format", detail="Supported formats are: 'query', 'array', 'arrayOfStructs', 'html' and 'csv'" );
}

Expand All @@ -27,8 +27,12 @@ component extends="base"{
for some reason ACF won't match the exception type as a catch() arg here, i.e.
catch( com.github.pjfanning.xlsx.exceptions.ReadException exception ){} hence using an if-test
*/
if( arguments.exception.type == "com.github.pjfanning.xlsx.exceptions.ReadException" )
if(
arguments.exception.type == "com.github.pjfanning.xlsx.exceptions.ReadException"
|| library().getIsBoxlang() && arguments.exception.getClass().getName() == "com.github.pjfanning.xlsx.exceptions.ReadException"
) {
Throw( type=library().getExceptionType() & ".invalidSpreadsheetType", message="Invalid spreadsheet file", detail="readLargeFile() and processLargeFile() can only be used with XLSX files. The file you are trying to read does not appear to be an XLSX file." );
}
}

void function throwNonExistentRowException( required numeric rowNumber ){
Expand Down
2 changes: 1 addition & 1 deletion helpers/file.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ component extends="base"{
Throw( type=library().getExceptionType() & ".invalidAlgorithm", message="Invalid algorithm", detail="'#arguments.algorithm#' is not a valid algorithm. Supported algorithms are: #validAlgorithms.ToList( ', ')#" );
lock name="#arguments.filepath#" timeout=5 {
var mode = library().createJavaObject( "org.apache.poi.poifs.crypt.EncryptionMode" );
var info = library().createJavaObject( "org.apache.poi.poifs.crypt.EncryptionInfo" ).init( mode[ arguments.algorithm ] );
var info = library().createJavaObject( "org.apache.poi.poifs.crypt.EncryptionInfo" ).init( mode.valueOf( JavaCast( "string", arguments.algorithm ) ) );
var encryptor = info.getEncryptor();
encryptor.confirmPassword( JavaCast( "string", arguments.password ) );
try{
Expand Down
2 changes: 1 addition & 1 deletion helpers/font.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ component extends="base"{
case "bold":
return "font-weight:" & ( arguments.styleValue? "bold;": "normal;" );
case "color":
if( !arguments.KeyExists( "workbook" ) )
if( !arguments.KeyExists( "workbook" ) || isNull( arguments.workbook ) )
Throw( type=library().getExceptionType() & ".missingRequiredArgument", message="Missing required 'workbook' argument", detail="The 'workbook' argument is required when generating color css styles" );
//http://ragnarock99.blogspot.co.uk/2012/04/getting-hex-color-from-excel-cell.html
var rgb = arguments.workbook.getCustomPalette().getColor( arguments.styleValue ).getTriplet();
Expand Down
Loading
Loading