├── sc-list.txt ├── LICENSE ├── README.md └── isc └── poi ├── Book.cls └── Utils.cls /sc-list.txt: -------------------------------------------------------------------------------- 1 | isc.poi.Book.cls 2 | isc.poi.Utils.cls 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 InterSystems 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apache-poi-os 2 | ObjectScript part for apache-poi integration. Allows reading xlsx. 3 | 4 | # Installation 5 | 6 | 1. Download [latest release](https://github.com/intersystems-ru/apache-poi-os/releases) and import it into any namespace. 7 | 2. Download [java files](https://github.com/intersystems-ru/apache-poi/releases): poi archive and `poi.jar` from latest release. 8 | 3. Extract archive and copy `poi.jar` into one directory, later referenced as ``. Caché should have access to this directory. 9 | 4. Execute: `set ^POI("DIR")=##class(%File).NormalizeDirectory("")` 10 | 5. Create Java Gateway: `Write $System.Status.GetErrorText(##class(isc.poi.Utils).createGateway(name, home, path, port))`, where: 11 | - `name` is a name of the Java Gateway, defaults to `POI`. 12 | - `home` path to Java 1.8 JRE. Defaults to `JAVA_HOME` environment variable. 13 | - `path` is a path to jars, should be set automatically. 14 | - `port` Java Gateway port. Defaults to `55556`. 15 | 6. Load jar into Caché: `Write $System.Status.GetErrorText(##class(isc.poi.Utils).updateJar())` 16 | 7. Check that everything works: `Write $System.Status.GetErrorText(##class(isc.poi.Utils).getBook(file, .book))` 17 | 18 | # API 19 | 20 | Excel books are loaded into `isc.poi.Book` objects. Check it for API documentation. 21 | -------------------------------------------------------------------------------- /isc/poi/Book.cls: -------------------------------------------------------------------------------- 1 | /// Exposes an api for Excel books 2 | Class isc.poi.Book Extends %Persistent 3 | { 4 | 5 | /// Sheet information 6 | /// $lb($lb(rowCount, listName), $lb(rowCount2, listName2), ... ) 7 | Property info As %List; 8 | 9 | /// Sheet data 10 | /// data(rownum) = $lb(Cell1, Cell2, ..., CennN) 11 | Property data As %List [ MultiDimensional ]; 12 | 13 | /// Create object from list and do some postprocessing 14 | Method %OnNew(list As %ListOfDataTypes) As %Status [ Private, ServerOnly = 1 ] 15 | { 16 | quit:list.Count()=1 $$$ERROR($$$GeneralError, list.GetAt(1)) 17 | 18 | set info = list.GetAt(list.Count()) 19 | set info = $lfs(info, $c(1,1)) 20 | for i=1:1:$ll(info) { 21 | set $list(infoFinal, i) = $lfs($lg(info, i), $c(1)) 22 | } 23 | set ..info = infoFinal 24 | do list.RemoveAt(list.Count()) 25 | 26 | merge ..data = list.Data 27 | 28 | 29 | set key = "" 30 | for { 31 | set key=$order(..data(key), 1, value) 32 | quit:key="" 33 | // replace $c(0) with empty elements 34 | set value = $replace(value, $lb($c(0)), $lb()) 35 | 36 | // Make $lb lists 37 | set value = $lfs(value, $c(1)) 38 | 39 | // TODO java-side set casted values 40 | // TODO dates 41 | if '$listvalid(value) { 42 | set ^isc.poi.Book1($i(^isc.poi.Book1))= value 43 | set ^isc.poi.Book2($i(^isc.poi.Book2))= key 44 | } 45 | 46 | set ..data(key) = value 47 | } 48 | 49 | quit $$$OK 50 | } 51 | 52 | Method getSheet(sheetNum As %Integer, Output sheet) 53 | { 54 | kill sheet 55 | quit:sheetNum>..getSheetCount() 56 | set offset = ..getSheetOffset(sheetNum) 57 | for i=offset+1:1:offset+..getSheetRowCount(sheetNum) { 58 | set sheet($i(sheet)) = ..data(i) 59 | } 60 | } 61 | 62 | Method getRow(sheetNum As %Integer, rowNum As %Integer) As %List 63 | { 64 | quit:sheetNum>..getSheetCount() "" 65 | quit:rowNum>..getSheetRowCount(sheetNum) "" 66 | set offset = ..getSheetOffset(sheetNum) + rowNum 67 | quit ..data(offset) 68 | } 69 | 70 | Method getCell(sheetNum As %Integer, rowNum As %Integer, colNum As %Integer) 71 | { 72 | quit:sheetNum>..getSheetCount() "" 73 | quit:rowNum>..getSheetRowCount(sheetNum) "" 74 | set row = ..getRow(sheetNum, rowNum) 75 | quit $lg(row, colNum) 76 | } 77 | 78 | Method getSheetOffset(sheetNum As %Integer) As %Integer 79 | { 80 | quit:sheetNum>..getSheetCount() "" 81 | set offset = 0 82 | for i=1:1:(sheetNum-1) { 83 | set offset = offset + ..getSheetRowCount(i) 84 | } 85 | quit offset 86 | } 87 | 88 | Method getSheetRowCount(sheetNum As %Integer) [ CodeMode = expression ] 89 | { 90 | $lg($lg(..info, sheetNum)) 91 | } 92 | 93 | Method getSheetCount() [ CodeMode = expression ] 94 | { 95 | $ll(..info) 96 | } 97 | 98 | Method getSheetName(sheetNum As %Integer) [ CodeMode = expression ] 99 | { 100 | $lg($lg(..info, sheetNum), 2) 101 | } 102 | 103 | Storage Default 104 | { 105 | 106 | 107 | %%CLASSNAME 108 | 109 | 110 | info 111 | 112 | 113 | ^isc.poi.BookD 114 | BookDefaultData 115 | ^isc.poi.BookD 116 | ^isc.poi.BookI 117 | ^isc.poi.BookS 118 | %Library.CacheStorage 119 | } 120 | 121 | } 122 | 123 | -------------------------------------------------------------------------------- /isc/poi/Utils.cls: -------------------------------------------------------------------------------- 1 | /// Utility methods 2 | Class isc.poi.Utils 3 | { 4 | 5 | /// Main class to import 6 | Parameter CLASS = "isc.poi.Main"; 7 | 8 | /// Main class to import 9 | Parameter CLASSBOOK = "isc.poi.WorkbookUtils"; 10 | 11 | /// Gateway name to create/use 12 | Parameter GATEWAY = "POI"; 13 | 14 | /// Path to jar dir. 15 | /// Should have a slash at the end 16 | Parameter DIR As COSEXPRESSION = "$g(^POI(""DIR""))"; 17 | 18 | /// Library files to load 19 | /// poi.jar should be first 20 | Parameter LIBS = {$lb("poi.jar", "poi-5.2.2.jar","poi-ooxml-5.2.2.jar", "poi-ooxml-full-5.2.2.jar", $lb("ooxml-lib", "xmlbeans-5.0.3.jar"), $lb("ooxml-lib", "commons-compress-1.21.jar"), $lb("lib","commons-collections4-4.4.jar"),$lb("lib","commons-io-2.11.0.jar"),$lb("lib","log4j-api-2.17.2.jar"))}; 21 | 22 | /// Test class in poi.jar 23 | Parameter CLASSTEST = "isc.poi.Test"; 24 | 25 | /// DEV tools. 26 | /// Path to built JAR. 27 | Parameter BUILDCLASSPATH As COSEXPRESSION = "$g(^POI(""BUILDCLASSPATH""))"; 28 | 29 | /// Create JGW. Java home must point to 1.8 jre. 30 | /// Write $System.Status.GetErrorText(##class(isc.poi.Utils).createGateway()) 31 | ClassMethod createGateway(gatewayName = {..#GATEWAY}, javaHome = {$SYSTEM.Util.GetEnviron("JAVA_HOME")}, path As %String = {..getPath()}, port As %Integer = 55556) 32 | { 33 | set sys = ##class(%Net.Remote.ObjectGateway).%New() 34 | set sys.Name = gatewayName 35 | set sys.Type = 1 36 | set sys.JavaHome = javaHome 37 | set sys.ClassPath = path 38 | set sys.Port = port 39 | quit sys.%Save() 40 | } 41 | 42 | /// w ##class(isc.poi.Utils).getPath() 43 | ClassMethod getPath() 44 | { 45 | set separator = $select($$$isWINDOWS:"\",1:"/") 46 | set path = "" 47 | for i=1:1:$ll(..#LIBS) { 48 | set lib = $lg(..#LIBS, i) 49 | set:$listValid(lib) lib = $lts(lib, separator) 50 | set path = path _ $lb(..#DIR _ lib) 51 | 52 | } 53 | quit $lts(path) 54 | } 55 | 56 | /// Load Jar from path. 57 | /// Write $System.Status.GetErrorText(##class(isc.poi.Utils).updateJar()) 58 | ClassMethod updateJar(gatewayName = {..#GATEWAY}, path As %String = {..getPath()}) 59 | { 60 | #Dim sc As %Status = $$$OK 61 | set sc = ##class(%Net.Remote.Service).StopGateway(gatewayName) 62 | 63 | if ..#BUILDCLASSPATH'="" { 64 | set result = 0 65 | set sc = ##class(%File).CopyFile(..#BUILDCLASSPATH,..#DIR _ $lg(..#LIBS), $$$YES, .result) 66 | if result=-32 { 67 | hang 1 68 | set sc = ##class(%File).CopyFile(..#BUILDCLASSPATH,..#DIR _ $lg(..#LIBS), $$$YES, .result) 69 | } 70 | 71 | quit:sc'=1 $$$ERROR($$$GeneralError, $$$FormatText("File copy failed with error: %1", result)) 72 | } 73 | 74 | #dim gateway As %Net.Remote.Gateway 75 | set gateway = ..connect(gatewayName, path, .sc) 76 | quit:$$$ISERR(sc) sc 77 | 78 | for class = ..#CLASS,..#CLASSBOOK { 79 | set sc = gateway.%Import(class) 80 | //set:$system["ed-pc" sc = gateway.%Import(..#CLASSTEST) 81 | quit:$$$ISERR(sc) 82 | set:'##class(%Dictionary.CompiledClass).%ExistsId(class) sc = $$$ERROR($$$GeneralError, $$$FormatText("Class '%1' does not exist",class)) 83 | } 84 | quit sc 85 | } 86 | 87 | /// Get JGW object 88 | ClassMethod connect(gatewayName As %String = {..#GATEWAY}, path As %String = {..getPath()}, Output sc As %Status) As %Net.Remote.Gateway 89 | { 90 | set gateway = "" 91 | set sc = ##class(%Net.Remote.Service).OpenGateway(gatewayName, .gatewayConfig) 92 | quit:$$$ISERR(sc) gateway 93 | set sc = ##class(%Net.Remote.Service).ConnectGateway(gatewayConfig, .gateway, path, $$$YES) 94 | quit gateway 95 | } 96 | 97 | /// Load xlsx 98 | /// Write $System.Status.GetErrorText(##class(isc.poi.Utils).getBook("C:\InterSystems\poi\Test.xlsx",.b)) 99 | ClassMethod getBook(file As %String, Output book As isc.poi.Book, debug As %Boolean = {$$$NO}) As %Status 100 | { 101 | #dim gateway as %Net.Remote.Gateway 102 | #dim exception as %Exception.AbstractException 103 | 104 | set sc = $$$OK 105 | try { 106 | set gateway = ..connect() 107 | set start = $zh 108 | 109 | set list = ##class(isc.poi.Main).getBook(gateway, file) 110 | 111 | set end1 = $zh 112 | set book = ##class(isc.poi.Book).%New(list) 113 | set end2 = $zh 114 | 115 | if debug { 116 | write !,"JGW: ",end1-start 117 | write !,"Instantiation: ",end2-end1 118 | //break 119 | } 120 | 121 | set sc = gateway.%Disconnect() 122 | 123 | } catch ex { 124 | break:debug 125 | set sc = $$$ADDSC($g(%objlasterror), ex.AsStatus()) 126 | } 127 | 128 | quit sc 129 | } 130 | 131 | /// Load xlsx 132 | /// Write $System.Status.GetErrorText(##class(isc.poi.Utils).getBookFromStream("D:\Cache\POI\Книга1.xlsx",.b)) 133 | ClassMethod getBookFromStream(file As %String, Output book As isc.poi.Book, debug As %Boolean = {$$$NO}) As %Status 134 | { 135 | #dim gateway as %Net.Remote.Gateway 136 | #dim exception as %Exception.AbstractException 137 | 138 | set sc = $$$OK 139 | try { 140 | 141 | set xlsxFile = ##class(%Stream.FileBinary).%New() 142 | do xlsxFile.LinkToFile(file) 143 | 144 | set stream = ##class(%Stream.TmpBinary).%New() 145 | do stream.CopyFrom(xlsxFile) 146 | 147 | set gateway = ..connect() 148 | set start = $zh 149 | 150 | set list = ##class(isc.poi.Main).getBookFromStream(gateway, stream) 151 | zw list 152 | set end1 = $zh 153 | set book = ##class(isc.poi.Book).%New(list) 154 | set end2 = $zh 155 | 156 | if debug { 157 | write !,"JGW: ",end1-start 158 | write !,"Instantiation: ",end2-end1 159 | 160 | //break 161 | } 162 | 163 | set sc = gateway.%Disconnect() 164 | 165 | } catch ex { 166 | break:debug 167 | set sc = $$$ADDSC($g(%objlasterror), ex.AsStatus()) 168 | } 169 | 170 | quit sc 171 | } 172 | 173 | /// Write $System.Status.GetErrorText(##class(isc.poi.Utils).getSheet("D:\Cache\POI\Книга1.xlsx",.b,1)) 174 | ClassMethod getSheet(file As %String, ByRef book As isc.poi.Book, sheetNumber, debug As %Boolean = {$$$NO}) As %Status 175 | { 176 | #dim gateway as %Net.Remote.Gateway 177 | #dim exception as %Exception.AbstractException 178 | 179 | set sc = $$$OK 180 | try { 181 | set gateway = ..connect() 182 | set start = $zh 183 | 184 | #dim list As %ListOfDataTypes 185 | set list = ##class(isc.poi.Main).getSheet(gateway, file, sheetNumber) 186 | 187 | do list.Insert($lb($lb(list.Count(), sheetNumber))) 188 | 189 | set end1 = $zh 190 | set book = ##class(isc.poi.Book).%New(list) 191 | set end2 = $zh 192 | 193 | if debug { 194 | write !,"JGW: ",end1-start 195 | write !,"Instantiation: ",end2-end1 196 | 197 | //set exec = "set list = " _result 198 | //XECUTE exec 199 | //zwrite list 200 | //zwrite result 201 | //break 202 | } 203 | 204 | set sc = gateway.%Disconnect() 205 | // do ##class(%Net.Remote.Service).StopGateway(..#GATEWAY) 206 | 207 | } catch ex { 208 | break:debug 209 | set sc = $$$ADDSC($g(%objlasterror), ex.AsStatus()) 210 | } 211 | 212 | quit sc 213 | } 214 | 215 | /// Write xlsx 216 | /// Write $System.Status.GetErrorText(##class(isc.poi.Utils).fillBook("C:\InterSystems\poi\Test.xlsx")) 217 | ClassMethod fillBook(file As %String) As %Status 218 | { 219 | #dim gateway as %Net.Remote.Gateway 220 | #dim exception as %Exception.AbstractException 221 | 222 | set sc = $$$OK 223 | try { 224 | set gateway = ..connect() 225 | set sheet = 0 226 | 227 | set cells = ##class(%ListOfDataTypes).%New() 228 | do cells.Insert("A7" _ $c(1) _ 123) 229 | do cells.Insert("A9" _ $c(1) _ 123.45) 230 | do cells.Insert("A8" _ $c(1) _ "ПРИВЕТ") 231 | 232 | set result = ##class(isc.poi.Main).fillBook(gateway, file, file _ ".new.xlsx", sheet, cells) 233 | 234 | write result 235 | 236 | set sc = gateway.%Disconnect() 237 | 238 | } catch ex { 239 | break 240 | set sc = $$$ADDSC($g(%objlasterror), ex.AsStatus()) 241 | } 242 | 243 | quit sc 244 | } 245 | 246 | } 247 | --------------------------------------------------------------------------------