Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

added all files from version 0.9.6

  • Loading branch information...
commit bf5e0a7a867a89a47fb3aa38dc3770e582b059c6 1 parent 9aadd4d
@jbarmash authored
Showing with 22,519 additions and 0 deletions.
  1. +14 −0 .classpath
  2. +19 −0 .project
  3. +3 −0  .settings/org.codehaus.groovy.eclipse.preferences.prefs
  4. +60 −0 ExcelImportGrailsPlugin.groovy
  5. +203 −0 LICENSE
  6. +8 −0 application.properties
  7. +37 −0 grails-app/conf/BuildConfig.groovy
  8. +628 −0 grails-app/services/org/grails/plugins/excelimport/ExcelImportService.groovy
  9. BIN  lib/xmlbeans-2.3.0-without-w3c.jar
  10. +15 −0 plugin.xml
  11. +10 −0 scripts/_Install.groovy
  12. +5 −0 scripts/_Uninstall.groovy
  13. +10 −0 scripts/_Upgrade.groovy
  14. +45 −0 src/groovy/imexporter/AbstractImexporter.groovy
  15. +59 −0 src/groovy/org/grails/plugins/excelimport/AbstractCsvImporter.groovy
  16. +110 −0 src/groovy/org/grails/plugins/excelimport/AbstractExcelImporter.groovy
  17. +129 −0 src/groovy/org/grails/plugins/excelimport/DefaultImportCellCollector.groovy
  18. +22 −0 src/groovy/org/grails/plugins/excelimport/ExpectedPropertyType.groovy
  19. +15 −0 src/groovy/org/grails/plugins/excelimport/HorizontalExcelImporter.groovy
  20. +18 −0 src/groovy/org/grails/plugins/excelimport/ImportCellCollector.groovy
  21. +49 −0 src/groovy/org/grails/plugins/excelimport/ImportSeverityMappingEnum.groovy
  22. +30 −0 src/groovy/org/grails/plugins/excelimport/NoopImportCellCollector.groovy
  23. +14 −0 test/projects/sample/.classpath
  24. +19 −0 test/projects/sample/.project
  25. +3 −0  test/projects/sample/.settings/org.codehaus.groovy.eclipse.preferences.prefs
  26. +19 −0 test/projects/sample/application.properties
  27. BIN  test/projects/sample/cobertura.ser
  28. +67 −0 test/projects/sample/grails-app/conf/BootStrap.groovy
  29. +41 −0 test/projects/sample/grails-app/conf/BuildConfig.groovy
  30. +110 −0 test/projects/sample/grails-app/conf/Config.groovy
  31. +32 −0 test/projects/sample/grails-app/conf/DataSource.groovy
  32. +13 −0 test/projects/sample/grails-app/conf/UrlMappings.groovy
  33. +3 −0  test/projects/sample/grails-app/conf/spring/resources.groovy
  34. +6 −0 test/projects/sample/grails-app/controllers/sample/BookController.groovy
  35. +16 −0 test/projects/sample/grails-app/domain/sample/Book.groovy
  36. +55 −0 test/projects/sample/grails-app/i18n/messages.properties
  37. +54 −0 test/projects/sample/grails-app/views/error.gsp
  38. +100 −0 test/projects/sample/grails-app/views/index.gsp
  39. +16 −0 test/projects/sample/grails-app/views/layouts/main.gsp
  40. BIN  test/projects/sample/reports/bg_gradient.gif
  41. +219 −0 test/projects/sample/reports/codenarc.xslt
  42. +32 −0 test/projects/sample/reports/default.css
  43. +372 −0 test/projects/sample/reports/gmetrics.xslt
  44. BIN  test/projects/sample/reports/logo.gif
  45. BIN  test/projects/sample/reports/row_bg.png
  46. +41 −0 test/projects/sample/scripts/CodeReports.groovy
  47. +120 −0 test/projects/sample/src/groovy/sample/BookExcelImporter.groovy
  48. +74 −0 test/projects/sample/target/GMetricsReport.html
  49. BIN  test/projects/sample/target/classes/BootStrap$_closure1.class
  50. BIN  test/projects/sample/target/classes/BootStrap$_closure2.class
  51. BIN  test/projects/sample/target/classes/BootStrap$_testExcel_closure3.class
  52. BIN  test/projects/sample/target/classes/BootStrap.class
  53. BIN  test/projects/sample/target/classes/BuildConfig$_run_closure1.class
  54. BIN  test/projects/sample/target/classes/BuildConfig$_run_closure1_closure3.class
  55. BIN  test/projects/sample/target/classes/BuildConfig$_run_closure1_closure4.class
  56. BIN  test/projects/sample/target/classes/BuildConfig$_run_closure1_closure5.class
  57. BIN  test/projects/sample/target/classes/BuildConfig$_run_closure2.class
  58. BIN  test/projects/sample/target/classes/BuildConfig.class
  59. BIN  test/projects/sample/target/classes/Config$_run_closure1.class
  60. BIN  test/projects/sample/target/classes/Config$_run_closure1_closure3.class
  61. BIN  test/projects/sample/target/classes/Config$_run_closure1_closure4.class
  62. BIN  test/projects/sample/target/classes/Config$_run_closure1_closure5.class
  63. BIN  test/projects/sample/target/classes/Config$_run_closure2.class
  64. BIN  test/projects/sample/target/classes/Config$_run_closure2_closure6.class
  65. BIN  test/projects/sample/target/classes/Config$_run_closure2_closure7.class
  66. BIN  test/projects/sample/target/classes/Config.class
  67. BIN  test/projects/sample/target/classes/DataSource$_run_closure1.class
  68. BIN  test/projects/sample/target/classes/DataSource$_run_closure2.class
  69. BIN  test/projects/sample/target/classes/DataSource$_run_closure3.class
  70. BIN  test/projects/sample/target/classes/DataSource$_run_closure3_closure4.class
  71. BIN  test/projects/sample/target/classes/DataSource$_run_closure3_closure4_closure7.class
  72. BIN  test/projects/sample/target/classes/DataSource$_run_closure3_closure5.class
  73. BIN  test/projects/sample/target/classes/DataSource$_run_closure3_closure5_closure8.class
  74. BIN  test/projects/sample/target/classes/DataSource$_run_closure3_closure6.class
  75. BIN  test/projects/sample/target/classes/DataSource$_run_closure3_closure6_closure9.class
  76. BIN  test/projects/sample/target/classes/DataSource.class
  77. BIN  test/projects/sample/target/classes/UrlMappings$__clinit__closure1.class
  78. BIN  test/projects/sample/target/classes/UrlMappings$__clinit__closure1_closure2.class
  79. BIN  test/projects/sample/target/classes/UrlMappings$__clinit__closure1_closure2_closure3.class
  80. BIN  test/projects/sample/target/classes/UrlMappings.class
  81. BIN  test/projects/sample/target/classes/resources$_run_closure1.class
  82. BIN  test/projects/sample/target/classes/resources.class
  83. BIN  test/projects/sample/target/classes/sample/Book$__clinit__closure1.class
  84. BIN  test/projects/sample/target/classes/sample/Book.class
  85. BIN  test/projects/sample/target/classes/sample/BookController.class
  86. BIN  test/projects/sample/target/classes/sample/BookExcelImporter$BookCsvImporter.class
  87. BIN  test/projects/sample/target/classes/sample/BookExcelImporter.class
  88. +2 −0  test/projects/sample/target/codenarc.xml
  89. BIN  test/projects/sample/target/codenarc/bg_gradient.gif
  90. +120 −0 test/projects/sample/target/codenarc/codenarc.html
  91. +32 −0 test/projects/sample/target/codenarc/default.css
  92. BIN  test/projects/sample/target/codenarc/logo.gif
  93. BIN  test/projects/sample/target/codenarc/row_bg.png
  94. +2 −0  test/projects/sample/target/gmetrics.xml
  95. BIN  test/projects/sample/target/gmetrics/bg_gradient.gif
  96. +32 −0 test/projects/sample/target/gmetrics/default.css
  97. +374 −0 test/projects/sample/target/gmetrics/gmetrics.html
  98. BIN  test/projects/sample/target/gmetrics/logo.gif
  99. BIN  test/projects/sample/target/gmetrics/row_bg.png
  100. 0  test/projects/sample/target/stacktrace.log
  101. +16 −0 test/projects/sample/target/test-reports/TEST-integration-integration-sample.BookTests.xml
  102. +15 −0 test/projects/sample/target/test-reports/TEST-integration-spock-sample.BookSpec.xml
  103. +43 −0 test/projects/sample/target/test-reports/TESTS-TestSuites.xml
  104. +154 −0 test/projects/sample/target/test-reports/cobertura/coverage.xml
  105. +22 −0 test/projects/sample/target/test-reports/cobertura/css/help.css
  106. +131 −0 test/projects/sample/target/test-reports/cobertura/css/main.css
  107. +50 −0 test/projects/sample/target/test-reports/cobertura/css/sortabletable.css
  108. +73 −0 test/projects/sample/target/test-reports/cobertura/css/source-viewer.css
  109. +49 −0 test/projects/sample/target/test-reports/cobertura/css/tooltip.css
  110. +20 −0 test/projects/sample/target/test-reports/cobertura/frame-packages.html
  111. +29 −0 test/projects/sample/target/test-reports/cobertura/frame-sourcefiles-sample.html
  112. +29 −0 test/projects/sample/target/test-reports/cobertura/frame-sourcefiles.html
  113. +48 −0 test/projects/sample/target/test-reports/cobertura/frame-summary-sample.html
  114. +30 −0 test/projects/sample/target/test-reports/cobertura/frame-summary.html
  115. +31 −0 test/projects/sample/target/test-reports/cobertura/help.html
  116. BIN  test/projects/sample/target/test-reports/cobertura/images/blank.png
  117. BIN  test/projects/sample/target/test-reports/cobertura/images/downsimple.png
  118. BIN  test/projects/sample/target/test-reports/cobertura/images/upsimple.png
  119. +25 −0 test/projects/sample/target/test-reports/cobertura/index.html
  120. +65 −0 test/projects/sample/target/test-reports/cobertura/js/customsorttypes.js
  121. +8 −0 test/projects/sample/target/test-reports/cobertura/js/popup.js
  122. +455 −0 test/projects/sample/target/test-reports/cobertura/js/sortabletable.js
  123. +79 −0 test/projects/sample/target/test-reports/cobertura/js/stringbuilder.js
  124. +55 −0 test/projects/sample/target/test-reports/cobertura/sample.Book.html
  125. +35 −0 test/projects/sample/target/test-reports/cobertura/sample.BookController.html
  126. +253 −0 test/projects/sample/target/test-reports/cobertura/sample.BookExcelImporter.html
  127. +37 −0 test/projects/sample/target/test-reports/html/all-tests.html
  128. +18 −0 test/projects/sample/target/test-reports/html/allclasses-frame.html
  129. +22 −0 test/projects/sample/target/test-reports/html/alltests-errors.html
  130. +22 −0 test/projects/sample/target/test-reports/html/alltests-fails.html
  131. +19 −0 test/projects/sample/target/test-reports/html/index.html
  132. +18 −0 test/projects/sample/target/test-reports/html/overview-frame.html
  133. +41 −0 test/projects/sample/target/test-reports/html/overview-summary.html
  134. +3 −0  test/projects/sample/target/test-reports/html/sample/0_BookTests-err.txt
  135. +7 −0 test/projects/sample/target/test-reports/html/sample/0_BookTests-out.txt
  136. +86 −0 test/projects/sample/target/test-reports/html/sample/0_BookTests.html
  137. +4 −0 test/projects/sample/target/test-reports/html/sample/1_BookSpec-err.txt
  138. +4 −0 test/projects/sample/target/test-reports/html/sample/1_BookSpec-out.txt
  139. +89 −0 test/projects/sample/target/test-reports/html/sample/1_BookSpec.html
  140. +27 −0 test/projects/sample/target/test-reports/html/sample/package-frame.html
  141. +30 −0 test/projects/sample/target/test-reports/html/sample/package-summary.html
  142. +48 −0 test/projects/sample/target/test-reports/html/stylesheet.css
  143. +2 −0  test/projects/sample/target/test-reports/plain/TEST-integration-integration-sample.BookTests-err.txt
  144. +6 −0 test/projects/sample/target/test-reports/plain/TEST-integration-integration-sample.BookTests-out.txt
  145. +17 −0 test/projects/sample/target/test-reports/plain/TEST-integration-integration-sample.BookTests.txt
  146. +3 −0  test/projects/sample/target/test-reports/plain/TEST-integration-spock-sample.BookSpec-err.txt
  147. +3 −0  test/projects/sample/target/test-reports/plain/TEST-integration-spock-sample.BookSpec-out.txt
  148. +16 −0 test/projects/sample/target/test-reports/plain/TEST-integration-spock-sample.BookSpec.txt
  149. BIN  test/projects/sample/test-data/books-empty.xls
  150. BIN  test/projects/sample/test-data/books-empty.xlsx
  151. +3 −0  test/projects/sample/test-data/books.csv
  152. BIN  test/projects/sample/test-data/books.xls
  153. BIN  test/projects/sample/test-data/books.xlsx
  154. BIN  test/projects/sample/test-data/books.xml.xlsx
  155. +3 −0  test/projects/sample/test-data/books_one.csv
  156. +95 −0 test/projects/sample/test/integration/sample/BookSpec.groovy
  157. +50 −0 test/projects/sample/test/integration/sample/BookTests.groovy
  158. BIN  test/projects/sample/test/report-templates/bg_gradient.gif
  159. +219 −0 test/projects/sample/test/report-templates/codenarc.xslt
  160. +32 −0 test/projects/sample/test/report-templates/default.css
  161. +372 −0 test/projects/sample/test/report-templates/gmetrics.xslt
  162. BIN  test/projects/sample/test/report-templates/logo.gif
  163. BIN  test/projects/sample/test/report-templates/row_bg.png
  164. +42 −0 test/projects/sample/web-app/WEB-INF/applicationContext.xml
  165. +14 −0 test/projects/sample/web-app/WEB-INF/sitemesh.xml
  166. +563 −0 test/projects/sample/web-app/WEB-INF/tld/c.tld
  167. +671 −0 test/projects/sample/web-app/WEB-INF/tld/fmt.tld
  168. +550 −0 test/projects/sample/web-app/WEB-INF/tld/grails.tld
  169. +311 −0 test/projects/sample/web-app/WEB-INF/tld/spring.tld
  170. +270 −0 test/projects/sample/web-app/css/main.css
  171. BIN  test/projects/sample/web-app/images/favicon.ico
  172. BIN  test/projects/sample/web-app/images/grails_logo.jpg
  173. BIN  test/projects/sample/web-app/images/grails_logo.png
  174. BIN  test/projects/sample/web-app/images/leftnav_btm.png
  175. BIN  test/projects/sample/web-app/images/leftnav_midstretch.png
  176. BIN  test/projects/sample/web-app/images/leftnav_top.png
  177. BIN  test/projects/sample/web-app/images/skin/database_add.png
  178. BIN  test/projects/sample/web-app/images/skin/database_delete.png
  179. BIN  test/projects/sample/web-app/images/skin/database_edit.png
  180. BIN  test/projects/sample/web-app/images/skin/database_save.png
  181. BIN  test/projects/sample/web-app/images/skin/database_table.png
  182. BIN  test/projects/sample/web-app/images/skin/exclamation.png
  183. BIN  test/projects/sample/web-app/images/skin/house.png
  184. BIN  test/projects/sample/web-app/images/skin/information.png
  185. BIN  test/projects/sample/web-app/images/skin/shadow.jpg
  186. BIN  test/projects/sample/web-app/images/skin/sorted_asc.gif
  187. BIN  test/projects/sample/web-app/images/skin/sorted_desc.gif
  188. BIN  test/projects/sample/web-app/images/spinner.gif
  189. BIN  test/projects/sample/web-app/images/springsource.png
  190. +13 −0 test/projects/sample/web-app/js/application.js
  191. +7 −0 test/projects/sample/web-app/js/prototype/animation.js
  192. +136 −0 test/projects/sample/web-app/js/prototype/builder.js
  193. +965 −0 test/projects/sample/web-app/js/prototype/controls.js
  194. +974 −0 test/projects/sample/web-app/js/prototype/dragdrop.js
  195. +1,123 −0 test/projects/sample/web-app/js/prototype/effects.js
  196. +4,874 −0 test/projects/sample/web-app/js/prototype/prototype.js
  197. +2,691 −0 test/projects/sample/web-app/js/prototype/rico.js
  198. +68 −0 test/projects/sample/web-app/js/prototype/scriptaculous.js
  199. +275 −0 test/projects/sample/web-app/js/prototype/slider.js
  200. +59 −0 test/projects/sample/web-app/js/prototype/sound.js
  201. +568 −0 test/projects/sample/web-app/js/prototype/unittest.js
  202. BIN  test/report-templates/bg_gradient.gif
  203. +219 −0 test/report-templates/codenarc.xslt
  204. +32 −0 test/report-templates/default.css
  205. +372 −0 test/report-templates/gmetrics.xslt
  206. BIN  test/report-templates/logo.gif
  207. BIN  test/report-templates/row_bg.png
  208. +563 −0 web-app/WEB-INF/tld/c.tld
  209. +671 −0 web-app/WEB-INF/tld/fmt.tld
View
14 .classpath
@@ -0,0 +1,14 @@
+<classpath>
+ <classpathentry kind="src" path="src/java"/>
+ <classpathentry kind="src" path="src/groovy"/>
+ <classpathentry kind="src" path="grails-app/conf"/>
+ <classpathentry kind="src" path="grails-app/controllers"/>
+ <classpathentry kind="src" path="grails-app/domain"/>
+ <classpathentry kind="src" path="grails-app/services"/>
+ <classpathentry kind="src" path="grails-app/taglib"/>
+ <classpathentry kind="src" path="test/integration"/>
+ <classpathentry kind="src" path="test/unit"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="com.springsource.sts.grails.core.CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="web-app/WEB-INF/classes"/>
+</classpath>
View
19 .project
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>excel-import</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.springsource.sts.grails.core.nature</nature>
+ <nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
View
3  .settings/org.codehaus.groovy.eclipse.preferences.prefs
@@ -0,0 +1,3 @@
+#Created by grails
+eclipse.preferences.version=1
+groovy.dont.generate.class.files=true
View
60 ExcelImportGrailsPlugin.groovy
@@ -0,0 +1,60 @@
+class ExcelImportGrailsPlugin {
+ // the plugin version
+ def version = "0.9.6"
+ // the version or versions of Grails the plugin is designed for
+ def grailsVersion = "1.3.0 > *"
+ // the other plugins this plugin depends on
+ def dependsOn = [:]
+ // resources that are excluded from plugin packaging
+ def pluginExcludes = [
+ "grails-app/views/error.gsp"
+ ]
+
+ // TODO Fill in these fields
+ def author = "Jean Barmash, Oleksiy Symonenko"
+
+ def authorEmail = "Jean.Barmash@gmail.com"
+ def title = "Excel, Excel 2007 & CSV Importer Using Apache POI"
+ def description = '''\\
+ Excel-Import plugin uses Apache POI [http://poi.apache.org/] library (v 3.6) to parse Excel files.
+ It's useful for either bootstrapping data, or when you want to allow your users to enter some data using Excel spreadsheets.
+'''
+
+ def license = "APACHE"
+ def organization = [ name: "EnergyScoreCards.com", url: "http://www.energyscorecards.com/" ]
+ def developers = [
+ [ name: "Oleksiy Symonenko", email: "" ],
+ ]
+
+ def issueManagement = [ system: "JIRA", url: "http://jira.grails.org/browse/GPXLIMPORT" ]
+ def scm = [ url: "http://svn.grails-plugins.codehaus.org/browse/grails-plugins/" ]
+ // URL to the plugin's documentation
+ def documentation = "http://grails.org/plugin/excel-import"
+
+ def doWithWebDescriptor = { xml ->
+ // TODO Implement additions to web.xml (optional), this event occurs before
+ }
+
+ def doWithSpring = {
+ // TODO Implement runtime spring config (optional)
+ }
+
+ def doWithDynamicMethods = { ctx ->
+ // TODO Implement registering dynamic methods to classes (optional)
+ }
+
+ def doWithApplicationContext = { applicationContext ->
+ // TODO Implement post initialization spring config (optional)
+ }
+
+ def onChange = { event ->
+ // TODO Implement code that is executed when any artefact that this plugin is
+ // watching is modified and reloaded. The event contains: event.source,
+ // event.application, event.manager, event.ctx, and event.plugin.
+ }
+
+ def onConfigChange = { event ->
+ // TODO Implement code that is executed when the project configuration changes.
+ // The event is the same as for 'onChange'.
+ }
+}
View
203 LICENSE
@@ -0,0 +1,203 @@
+/*
+* Apache License
+* Version 2.0, January 2004
+* http://www.apache.org/licenses/
+*
+* TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+*
+* 1. Definitions.
+*
+* "License" shall mean the terms and conditions for use, reproduction,
+* and distribution as defined by Sections 1 through 9 of this document.
+*
+* "Licensor" shall mean the copyright owner or entity authorized by
+* the copyright owner that is granting the License.
+*
+* "Legal Entity" shall mean the union of the acting entity and all
+* other entities that control, are controlled by, or are under common
+* control with that entity. For the purposes of this definition,
+* "control" means (i) the power, direct or indirect, to cause the
+* direction or management of such entity, whether by contract or
+* otherwise, or (ii) ownership of fifty percent (50%) or more of the
+* outstanding shares, or (iii) beneficial ownership of such entity.
+*
+* "You" (or "Your") shall mean an individual or Legal Entity
+* exercising permissions granted by this License.
+*
+* "Source" form shall mean the preferred form for making modifications,
+* including but not limited to software source code, documentation
+* source, and configuration files.
+*
+* "Object" form shall mean any form resulting from mechanical
+* transformation or translation of a Source form, including but
+* not limited to compiled object code, generated documentation,
+* and conversions to other media types.
+*
+* "Work" shall mean the work of authorship, whether in Source or
+* Object form, made available under the License, as indicated by a
+* copyright notice that is included in or attached to the work
+* (an example is provided in the Appendix below).
+*
+* "Derivative Works" shall mean any work, whether in Source or Object
+* form, that is based on (or derived from) the Work and for which the
+* editorial revisions, annotations, elaborations, or other modifications
+* represent, as a whole, an original work of authorship. For the purposes
+* of this License, Derivative Works shall not include works that remain
+* separable from, or merely link (or bind by name) to the interfaces of,
+* the Work and Derivative Works thereof.
+*
+* "Contribution" shall mean any work of authorship, including
+* the original version of the Work and any modifications or additions
+* to that Work or Derivative Works thereof, that is intentionally
+* submitted to Licensor for inclusion in the Work by the copyright owner
+* or by an individual or Legal Entity authorized to submit on behalf of
+* the copyright owner. For the purposes of this definition, "submitted"
+* means any form of electronic, verbal, or written communication sent
+* to the Licensor or its representatives, including but not limited to
+* communication on electronic mailing lists, source code control systems,
+* and issue tracking systems that are managed by, or on behalf of, the
+* Licensor for the purpose of discussing and improving the Work, but
+* excluding communication that is conspicuously marked or otherwise
+* designated in writing by the copyright owner as "Not a Contribution."
+*
+* "Contributor" shall mean Licensor and any individual or Legal Entity
+* on behalf of whom a Contribution has been received by Licensor and
+* subsequently incorporated within the Work.
+*
+* 2. Grant of Copyright License. Subject to the terms and conditions of
+* this License, each Contributor hereby grants to You a perpetual,
+* worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+* copyright license to reproduce, prepare Derivative Works of,
+* publicly display, publicly perform, sublicense, and distribute the
+* Work and such Derivative Works in Source or Object form.
+*
+* 3. Grant of Patent License. Subject to the terms and conditions of
+* this License, each Contributor hereby grants to You a perpetual,
+* worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+* (except as stated in this section) patent license to make, have made,
+* use, offer to sell, sell, import, and otherwise transfer the Work,
+* where such license applies only to those patent claims licensable
+* by such Contributor that are necessarily infringed by their
+* Contribution(s) alone or by combination of their Contribution(s)
+* with the Work to which such Contribution(s) was submitted. If You
+* institute patent litigation against any entity (including a
+* cross-claim or counterclaim in a lawsuit) alleging that the Work
+* or a Contribution incorporated within the Work constitutes direct
+* or contributory patent infringement, then any patent licenses
+* granted to You under this License for that Work shall terminate
+* as of the date such litigation is filed.
+*
+* 4. Redistribution. You may reproduce and distribute copies of the
+* Work or Derivative Works thereof in any medium, with or without
+* modifications, and in Source or Object form, provided that You
+* meet the following conditions:
+*
+* (a) You must give any other recipients of the Work or
+* Derivative Works a copy of this License; and
+*
+* (b) You must cause any modified files to carry prominent notices
+* stating that You changed the files; and
+*
+* (c) You must retain, in the Source form of any Derivative Works
+* that You distribute, all copyright, patent, trademark, and
+* attribution notices from the Source form of the Work,
+* excluding those notices that do not pertain to any part of
+* the Derivative Works; and
+*
+* (d) If the Work includes a "NOTICE" text file as part of its
+* distribution, then any Derivative Works that You distribute must
+* include a readable copy of the attribution notices contained
+* within such NOTICE file, excluding those notices that do not
+* pertain to any part of the Derivative Works, in at least one
+* of the following places: within a NOTICE text file distributed
+* as part of the Derivative Works; within the Source form or
+* documentation, if provided along with the Derivative Works; or,
+* within a display generated by the Derivative Works, if and
+* wherever such third-party notices normally appear. The contents
+* of the NOTICE file are for informational purposes only and
+* do not modify the License. You may add Your own attribution
+* notices within Derivative Works that You distribute, alongside
+* or as an addendum to the NOTICE text from the Work, provided
+* that such additional attribution notices cannot be construed
+* as modifying the License.
+*
+* You may add Your own copyright statement to Your modifications and
+* may provide additional or different license terms and conditions
+* for use, reproduction, or distribution of Your modifications, or
+* for any such Derivative Works as a whole, provided Your use,
+* reproduction, and distribution of the Work otherwise complies with
+* the conditions stated in this License.
+*
+* 5. Submission of Contributions. Unless You explicitly state otherwise,
+* any Contribution intentionally submitted for inclusion in the Work
+* by You to the Licensor shall be under the terms and conditions of
+* this License, without any additional terms or conditions.
+* Notwithstanding the above, nothing herein shall supersede or modify
+* the terms of any separate license agreement you may have executed
+* with Licensor regarding such Contributions.
+*
+* 6. Trademarks. This License does not grant permission to use the trade
+* names, trademarks, service marks, or product names of the Licensor,
+* except as required for reasonable and customary use in describing the
+* origin of the Work and reproducing the content of the NOTICE file.
+*
+* 7. Disclaimer of Warranty. Unless required by applicable law or
+* agreed to in writing, Licensor provides the Work (and each
+* Contributor provides its Contributions) on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+* implied, including, without limitation, any warranties or conditions
+* of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+* PARTICULAR PURPOSE. You are solely responsible for determining the
+* appropriateness of using or redistributing the Work and assume any
+* risks associated with Your exercise of permissions under this License.
+*
+* 8. Limitation of Liability. In no event and under no legal theory,
+* whether in tort (including negligence), contract, or otherwise,
+* unless required by applicable law (such as deliberate and grossly
+* negligent acts) or agreed to in writing, shall any Contributor be
+* liable to You for damages, including any direct, indirect, special,
+* incidental, or consequential damages of any character arising as a
+* result of this License or out of the use or inability to use the
+* Work (including but not limited to damages for loss of goodwill,
+* work stoppage, computer failure or malfunction, or any and all
+* other commercial damages or losses), even if such Contributor
+* has been advised of the possibility of such damages.
+*
+* 9. Accepting Warranty or Additional Liability. While redistributing
+* the Work or Derivative Works thereof, You may choose to offer,
+* and charge a fee for, acceptance of support, warranty, indemnity,
+* or other liability obligations and/or rights consistent with this
+* License. However, in accepting such obligations, You may act only
+* on Your own behalf and on Your sole responsibility, not on behalf
+* of any other Contributor, and only if You agree to indemnify,
+* defend, and hold each Contributor harmless for any liability
+* incurred by, or claims asserted against, such Contributor by reason
+* of your accepting any such warranty or additional liability.
+*
+* END OF TERMS AND CONDITIONS
+*
+* APPENDIX: How to apply the Apache License to your work.
+*
+* To apply the Apache License to your work, attach the following
+* boilerplate notice, with the fields enclosed by brackets "[]"
+* replaced with your own identifying information. (Don't include
+* the brackets!) The text should be enclosed in the appropriate
+* comment syntax for the file format. We also recommend that a
+* file or class name and description of purpose be included on the
+* same "printed page" as the copyright notice for easier
+* identification within third-party archives.
+*
+* Copyright [yyyy] [name of copyright owner]
+*
+* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
View
8 application.properties
@@ -0,0 +1,8 @@
+#Grails Metadata file
+#Mon Sep 05 15:43:28 EDT 2011
+app.grails.version=1.3.6
+app.name=excel-import
+plugins.hibernate=1.3.6
+plugins.joda-time=1.2
+plugins.release=1.0.0.RC3
+plugins.tomcat=1.3.6
View
37 grails-app/conf/BuildConfig.groovy
@@ -0,0 +1,37 @@
+grails.project.class.dir = "target/classes"
+grails.project.test.class.dir = "target/test-classes"
+grails.project.test.reports.dir = "target/test-reports"
+//grails.project.war.file = "target/${appName}-${appVersion}.war"
+grails.project.dependency.resolution = {
+ // inherit Grails' default dependencies
+ inherits( "global" ) {
+ // uncomment to disable ehcache
+ // excludes 'ehcache'
+ }
+ log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
+ repositories {
+ grailsPlugins()
+ grailsHome()
+
+ // uncomment the below to enable remote dependency resolution
+ // from public Maven repositories
+ mavenLocal()
+ mavenCentral()
+ //mavenRepo "http://snapshots.repository.codehaus.org"
+ //mavenRepo "http://repository.codehaus.org"
+ //mavenRepo "http://download.java.net/maven/2/"
+ //mavenRepo "http://repository.jboss.com/maven2/"
+ }
+ dependencies {
+ // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
+ compile (group:'org.apache.poi', name:'poi', version:'3.7');
+ //xlxs file support
+ compile (group:'org.apache.poi', name:'poi-ooxml', version:'3.7') {
+ excludes 'xmlbeans'
+ }
+ //compile group:'org.apache.poi', name:'poi-contrib', version:'3.7'
+ //compile group:'org.apache.poi', name:'poi-scratchpad', version:'3.7' //ppt, word, visio, outlook support
+
+ }
+}
+
View
628 grails-app/services/org/grails/plugins/excelimport/ExcelImportService.groovy
@@ -0,0 +1,628 @@
+package org.grails.plugins.excelimport
+
+import static org.grails.plugins.excelimport.ExpectedPropertyType.*
+import org.joda.time.LocalDate
+
+import org.apache.poi.ss.util.CellReference
+import org.apache.poi.ss.usermodel.Cell
+import org.apache.poi.ss.usermodel.Sheet
+import org.apache.poi.ss.usermodel.Row
+import org.apache.poi.ss.usermodel.DateUtil
+import org.apache.poi.ss.usermodel.FormulaEvaluator
+import org.apache.poi.ss.usermodel.Workbook
+import org.codehaus.groovy.grails.commons.ApplicationHolder
+
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Jean Barmash
+ * Date: Sep 29, 2009
+ * Time: 5:08:39 PM
+ */
+public class ExcelImportService {
+
+ static scope = "prototype"
+ static transactional = false
+
+
+ //PROPERTY_TYPE_* was inlined in HEAD
+ //not removing this old definitions for a while as there could be merge into another branch which still uses it
+ static ExpectedPropertyType PROPERTY_TYPE_INT = IntType
+ static ExpectedPropertyType PROPERTY_TYPE_STRING = StringType
+ static ExpectedPropertyType PROPERTY_TYPE_DATE = DateType
+ static ExpectedPropertyType PROPERTY_TYPE_DATE_JAVA = DateJavaType
+ static ExpectedPropertyType PROPERTY_TYPE_DOUBLE = DoubleType
+
+
+ static getService() {
+ def ctx = ApplicationHolder.application.mainContext;
+ return ctx.getBean("excelImportService");
+ }
+
+
+ /**
+ * Looks through supplied index (columnIndex), and matches a siteID in that column, returning the row of the match
+ */
+ Row findRowById(Sheet sheet, String siteId, int columnIndex) {
+ for (Row r: sheet) {
+ if (row.getCell(columnIndex)?.stringCellValue == siteId) {
+ log.info "found ID $siteId"
+ return r
+ }
+ }
+ return null
+ }
+
+ /**
+ * receives a list of column names, i.e. ['Site', 'ID'], and tries to find the indexes of the columns matching those names
+ * returning a map, i.e. ['Site':2, 'ID':8]
+ */
+ Map createColumnMapFromColumnNames(List COLUMN_NAMES, Row headerRow) {
+ def columnIndexMap = [:]
+
+ for (Cell c: headerRow) {
+ if (COLUMN_NAMES.contains(c.stringCellValue)) {
+ log.debug "Found index for column ${c.stringCellValue}"
+ columnIndexMap[c.stringCellValue] = c.columnIndex
+ }
+ }
+
+ log.info "columnIndexMap $columnIndexMap"
+ columnIndexMap
+ }
+
+ //To Transpose the Values
+ Map verticalValues(Workbook workbook, Map config) {
+ return columns(workbook, config).inject([:]) {acc, nameValuePair ->
+ acc << [(nameValuePair.name): nameValuePair.value]
+ }
+ }
+
+ def columns(Workbook workbook, Map config, ImportCellCollector pcc = null, propertyConfigurationMap = [:], int lastRow = -1) {
+ convertColumnMapConfigManyRows(workbook, config, pcc, null, propertyConfigurationMap, lastRow)
+ }
+
+ /**
+ * dual columns(Workbook, ...
+ */
+ def setColumns(List inputList, Workbook workbook, Map config, propertyConfigurationMap = [:], int lastRow = -1) {
+ convertManyRowsToColumnMapConfig(inputList, workbook, config, null, propertyConfigurationMap, lastRow)
+ }
+
+
+ def convertColumnMapConfigManyRows(Workbook workbook, Map config, ImportCellCollector pcc = null, FormulaEvaluator evaluator = null, propertyConfigurationMap = [:], int lastRow = -1) {
+ if (!evaluator) evaluator = workbook.creationHelper.createFormulaEvaluator()
+ def sheet = workbook.getSheet(config.sheet)
+ if (propertyConfigurationMap == [:]) {
+ propertyConfigurationMap = config.configMap ?: [:]
+ }
+ if (config.containsKey('lastRow')) {
+ lastRow = config.lastRow
+ }
+ return convertColumnMapManyRows(sheet, config, config.startRow, pcc, evaluator, propertyConfigurationMap, lastRow)
+ }
+
+ /**
+ * dual convertColumnMapConfigManyRows(Workbook, ...
+ */
+ def convertManyRowsToColumnMapConfig(List inputList, Workbook workbook, Map config, FormulaEvaluator evaluator = null, propertyConfigurationMap = [:], int lastRow = -1) {
+ if (!evaluator) evaluator = workbook.creationHelper.createFormulaEvaluator()
+ def sheet = workbook.getSheet(config.sheet)
+ if (propertyConfigurationMap == [:]) {
+ propertyConfigurationMap = config.configMap ?: [:]
+ }
+ convertManyRowsToColumnMap(inputList, sheet, config.columnMap, config.startRow, evaluator, propertyConfigurationMap, lastRow)
+ }
+
+
+ /**
+ * Receives sheet, offset, and map of columns. Map is between a column name (i.e. B) and what will become returning map key.
+ * For example, columnMap could be ['B':'endDate', 'D':cost], which will cause going down the B and D columns and retrieving values
+ * packaging up as maps to be returned, in this case something like [[endDate:LocalDate(2009/1/2), cost:30], [endDate: LocalDate(2009,1,3), cost:20]]
+ * This method is very generic, and could be used by anything
+ */
+ def convertColumnMapManyRows(Sheet currentSheet, Map config, int firstRow, ImportCellCollector pcc = null, FormulaEvaluator evaluator = null, propertyConfigurationMap = null, int lastRow = -1) {
+ if (currentSheet == null) return []
+ boolean blankRowBreak = false
+ int blankRowCount = 0
+ def returnList = []
+ for (int rowIndex = firstRow; (rowIndex < lastRow || ((lastRow == -1)) && !blankRowBreak); rowIndex++) {
+ //println "ColumnMap $columnMap"
+ Map returnParams = convertColumnMapOneRow(currentSheet, config, rowIndex, pcc, evaluator, propertyConfigurationMap)
+ //println "Row Columns - returning $returnParams"
+ log.debug "Index $rowIndex Result map values $returnParams"
+ //println "Index $rowIndex Result map values $returnParams"
+ if (!returnParams) {
+ blankRowCount += 1
+ } else {
+ blankRowCount = 0
+ returnList << returnParams
+ }
+ blankRowBreak = (blankRowCount > 10)
+ }
+ returnList
+ }
+
+ /**
+ * dual convertColumnMapManyRows(Sheet, ...
+ */
+ def convertManyRowsToColumnMap(List inputList, Sheet currentSheet, Map columnMap, int firstRow, FormulaEvaluator evaluator = null, propertyConfigurationMap = null, int lastRow = -1) {
+ if (currentSheet == null) return
+ int rowIndex = firstRow
+ inputList.each {inputParams ->
+ convertOneRowToColumnMap(inputParams, currentSheet, columnMap, rowIndex, evaluator, propertyConfigurationMap)
+ rowIndex++
+ }
+ }
+
+
+ //Form of sitePropertyConfigurationMap: ['squareFeet':[type:"integer", defaultValue:-1]]
+ def convertColumnMapOneRow(Sheet currentSheet, Map config, int rowIndex, ImportCellCollector pcc = null, FormulaEvaluator evaluator = null, propertyConfigurationMap = null) {
+
+ pcc = pcc ?: NoopImportCellCollector.NoopInstance
+
+ def returnParams = [:]
+ def anyNonNullValues = false
+ def row = currentSheet.getRow(rowIndex)
+ if (!row) {
+ log.info "Row is null at row $rowIndex, sheet ${currentSheet.sheetName}"
+ return returnParams
+ }
+
+ config.columnMap.each { columnName, propertyName ->
+ try {
+ def value = getCellValueByColName(row, columnName, pcc, evaluator, propertyConfigurationMap?.get(propertyName))
+ log.trace "\t\tValue for $propertyName (column ${columnName}) is $value "
+ if (value == null || ''.equals(value)) { //cheking for null, because otherwise 0 value will fail here
+ log.debug "Value for column $columnName row $rowIndex is null or empty String. Was trying to find property $propertyName. Skipping setting its value in param map"
+ returnParams[propertyName] = propertyConfigurationMap?.get(propertyName)?.defaultValue
+ } else {
+ anyNonNullValues = true
+ returnParams[propertyName] = value
+ }
+ } catch (Exception e) {
+ //pcc.reportCell(cell, propertyConfiguration)
+ log.error "Exception caught at row $rowIndex column $columnName while trying to set property $propertyName", e
+ //println "Exception caught at row $rowIndex column $columnName while trying to set property $propertyName", e
+ //continue in the loop, so can collect other properties
+ }
+ }
+ log.debug "Returning $returnParams for Row $rowIndex, columnMap $config.columnMap"
+
+ if (anyNonNullValues){
+ //validate rows
+ pcc.checkReportRow(row, config, this)
+ return returnParams
+ }
+ return [:]
+ }
+
+ /**
+ * dual convertOneRowToColumnMap(Sheet, ...
+ */
+ void convertOneRowToColumnMap(inputParams, Sheet currentSheet, Map columnMap, int rowIndex, FormulaEvaluator evaluator = null, propertyConfigurationMap = null) {
+ def row = currentSheet.getRow(rowIndex) ?: currentSheet.createRow(rowIndex)
+ columnMap.each {columnName, propertyName ->
+ try {
+ def value = inputParams[propertyName]
+ setCellValueByColName(value, row, columnName, evaluator, propertyConfigurationMap?.get(propertyName))
+ } catch (Exception e) {
+ log.warn "Exception caught at row $rowIndex column $columnName while trying to get property $propertyName", e
+ //continue in the loop, so can collect other properties
+ }
+ }
+ }
+
+
+
+ def getCellValueForSheetNameAndCell(Workbook workbook, String sheetName, String cellName) {
+ def sheet = workbook.getSheet(sheetName)
+ if (!sheet) return null
+ FormulaEvaluator evaluator = workbook.creationHelper.createFormulaEvaluator()
+ try {
+ def cell = getCell(sheet, cellName)
+ def value = getCellValue(cell, null, evaluator)
+ log.debug "\t\tValue for $cellName is $value "
+ //println "\t\tValue for $cellName is $value "
+ return value
+ } catch (Exception e) {
+ log.error "Exception in cell $cellName thrown while getting cell values", e
+ //println "Exception in cell $cellName thrown while getting cell values $e"
+ return null
+ }
+ }
+
+
+ //cells and values are equivalent
+ def cells(Workbook workbook, Map config, ImportCellCollector pcc = null, Map propertyConfigurationMap = [:]) {
+ convertFromCellMapToMapWithValues(workbook, config, pcc, null, propertyConfigurationMap)
+ }
+
+ //cells and values are equivalent
+ def values(Workbook workbook, Map config, ImportCellCollector pcc = null, Map propertyConfigurationMap = [:]) {
+ convertFromCellMapToMapWithValues(workbook, config, pcc, null, propertyConfigurationMap)
+ }
+
+
+ //setCells and setValues are equivalent
+ void setCells(Map inputMap, Workbook workbook, Map config, Map propertyConfigurationMap = [:]) {
+ convertMapWithValuesToCellMap(inputMap, workbook, config, propertyConfigurationMap)
+ }
+
+ /** * dual to values(Workbook, ... */
+ void setValues(Map inputMap, Workbook workbook, Map config, Map propertyConfigurationMap = [:]) {
+ convertMapWithValuesToCellMap(inputMap, workbook, config, propertyConfigurationMap)
+ }
+
+
+ def convertFromCellMapToMapWithValues(Workbook workbook, Map config, ImportCellCollector pcc = null, FormulaEvaluator evaluator = null, Map propertyConfigurationMap = [:]) {
+ def sheet = workbook.getSheet(config.sheet)
+ if (!sheet) throw new IllegalArgumentException("Did not find sheet named ${config.sheet}")
+ if (propertyConfigurationMap == [:]) {
+ propertyConfigurationMap = config.configMap ?: [:]
+ }
+ convertFromCellMapToMapWithValues(sheet, config.cellMap, pcc, evaluator, propertyConfigurationMap)
+ }
+
+ /**
+ * dual convertFromCellMapToMapWithValues(Workbook, ...
+ */
+ void convertMapWithValuesToCellMap(Map inputMap, Workbook workbook, Map config, Map propertyConfigurationMap = [:]) {
+ def sheet = workbook.getSheet(config.sheet)
+ if (!sheet) throw new IllegalArgumentException("Did not find sheet named ${config.sheet}")
+ if (propertyConfigurationMap == [:]) {
+ propertyConfigurationMap = config.configMap ?: [:]
+ }
+ convertMapWithValuesToCellMap(inputMap, sheet, config.cellMap, propertyConfigurationMap)
+ }
+
+
+ //squareFeet:([expectedType: IntType, defaultValue:null]),
+ def convertFromCellMapToMapWithValues(Sheet currentSheet, Map cellMap, ImportCellCollector pcc = null, FormulaEvaluator evaluator = null, Map propertyConfigurationMap = [:]) {
+ Map objectParams = [:]
+ evaluator = evaluator?:currentSheet.workbook.creationHelper.createFormulaEvaluator()
+ cellMap.each { String cellName, String propertyName ->
+ try {
+ def cell = getCell(currentSheet, cellName)
+ def value = getCellValue(cell, pcc, evaluator, propertyConfigurationMap[propertyName])
+ //println "\t\tValue for $propertyName is $value "
+ log.debug "\t\tValue for $propertyName is $value "
+ //fix by Jackson
+ if (value == null) {
+ log.warn "No value in cell $cellName set. Was trying to find $propertyName"
+ } else {
+ objectParams[propertyName] = value
+ }
+
+ } catch (Exception e) {
+ log.error "Exception in cell $cellName getting $propertyName thrown while getting cell values", e
+ //println "Exception in cell $cellName getting $propertyName thrown while getting cell values $e"
+ }
+ }
+ log.debug "Returning objectParams $objectParams"
+ objectParams
+ }
+
+
+ /**
+ * dual convertFromCellMapToMapWithValues(Sheet, ...
+ */
+ void convertMapWithValuesToCellMap(Map objectParams, Sheet currentSheet, Map cellMap, Map propertyConfigurationMap = [:]) {
+ FormulaEvaluator evaluator = currentSheet.workbook.creationHelper.createFormulaEvaluator()
+ cellMap.each { String cellName, String propertyName ->
+ try {
+ def cell = getCellOrCreate(currentSheet, cellName)
+ def value = objectParams[propertyName]
+ setCellValue(value, cell, evaluator, propertyConfigurationMap[propertyName])
+ } catch (Exception e) {
+ log.error "Exception in cell $cellName setting $propertyName thrown while setting cell values", e
+ //println "Exception in cell $cellName setting $propertyName thrown while setting cell values $e"
+ }
+ }
+ }
+
+
+ Serializable getCellValueByColName(Row row, String columnName, ImportCellCollector pcc = null, FormulaEvaluator evaluator = null, Map propertyConfiguration = [:]) {
+ int colIndex = CellReference.convertColStringToIndex(columnName)
+ log.debug "getCellValueByColName $columnName row ${row.rowNum}, propConfig $propertyConfiguration, colIndex = $colIndex"
+ Cell cell = row.getCell(colIndex, Row.CREATE_NULL_AS_BLANK )
+ //println "\t\t\tCell is [$cell], colIndex $colIndex, colName $columnName, ${row.rowNum}"
+ getCellValue(cell, pcc, evaluator, propertyConfiguration)
+ }
+
+ /**
+ * dual getCellValueByColName(Row, ...
+ */
+ void setCellValueByColName(value, Row row, String columnName, FormulaEvaluator evaluator = null, Map propertyConfiguration = [:]) {
+ int colIndex = CellReference.convertColStringToIndex(columnName)
+ Cell cell = row.getCell(colIndex) ?: row.createCell(colIndex, Cell.CELL_TYPE_BLANK)
+ setCellValue(value, cell, evaluator, propertyConfiguration)
+ }
+
+ void setCellValueByColIndex(value, Row row, int colIndex, FormulaEvaluator evaluator = null, Map propertyConfiguration = [:]) {
+ Cell cell = row.getCell(colIndex) ?: row.createCell(colIndex, Cell.CELL_TYPE_BLANK)
+ setCellValue(value, cell, evaluator, propertyConfiguration)
+ }
+
+
+ def getCellValueByCellName(Sheet currentSheet, String cellName, FormulaEvaluator evaluator = null) {
+ if (evaluator == null) {
+ evaluator = currentSheet.workbook.creationHelper.createFormulaEvaluator()
+ }
+ def cell = getCell(currentSheet, cellName)
+ getCellValue(cell, null, evaluator)
+ }
+
+ /**
+ * Returns date or string, or numeric, depending on what's in the cell
+ */
+ Serializable getCellValue(Cell origcell, ImportCellCollector pcc = null, FormulaEvaluator evaluator = null, propertyConfiguration = [:]) {
+
+ pcc = pcc ?: NoopImportCellCollector.NoopInstance
+
+ Cell cell = evaluator ? evaluator.evaluateInCell(origcell) : origcell; //evaluates formula and returns value
+ if (cell == null) {
+ pcc.reportCell(origcell, propertyConfiguration)
+ return propertyConfiguration?.defaultValue?:null
+ }
+
+ switch (cell.cellType) {
+ case Cell.CELL_TYPE_STRING:
+ //println "string cell $origcell"
+ //println "string cell propertyConfig $propertyConfiguration"
+ if(!propertyConfiguration || propertyConfiguration.expectedType == StringType) {
+ String strValue = cell.stringCellValue
+ if (propertyConfiguration && strValue == propertyConfiguration?.valueEquivalentToNull) {
+ log.info("Found a value that's not null (value ${strValue}), but configuration property says to return null anyway")
+ return null
+ }
+ //log.warn "Encountered unexpected string cell Value ${cell.stringCellValue} at row ${cell.getRowIndex()} column ${cell.getColumnIndex()}"
+ return strValue
+ }
+
+ if (propertyConfiguration?.expectedType == DateType || propertyConfiguration?.expectedType == DateJavaType) {
+ def stringDate = cell.stringCellValue
+ //println "Expected type - Date for String value ${cell.stringCellValue}"
+ //println "Expected type - Date for String value ${stringDate.class}"
+ try {
+ def df = new java.text.SimpleDateFormat('MM/dd/yy')
+ df.setLenient(false) //would fail on Nonexistent dates (ie. February 30th, April 31)
+ def javaDate = df.parse(stringDate)
+
+ if (propertyConfiguration?.expectedType == DateJavaType) {
+ if(pcc.checkReportValue(javaDate, cell, propertyConfiguration)) {
+ return propertyConfiguration?.defaultValue
+ }
+ return javaDate
+ }
+
+ LocalDate localDate = LocalDate.fromDateFields(javaDate)
+ if(pcc.checkReportValue(localDate, cell, propertyConfiguration)) {
+ return propertyConfiguration?.defaultValue
+ }
+ // println "Returning javaDate ${javaDate}, ${javaDate.class}"
+ return localDate
+ } catch (e) {
+ pcc.reportCell(cell, propertyConfiguration)
+ return propertyConfiguration?.defaultValue
+ }
+ //return propertyConfiguration.defaultValue
+ }
+
+ if (propertyConfiguration?.expectedType == IntType) {
+ log.warn "${getCellAddresString(cell)} Expected Type is INT, but cell type is String, trying to extract numeric value"
+ try {
+ return cell.stringCellValue?.toInteger()
+ } catch (Exception e) {
+ log.warn "${getCellAddresString(cell)} Cannot get numeric value, returning default value specified for this type, which is ${propertyConfiguration.defaultValue}"
+ pcc.reportCell(cell, propertyConfiguration)
+ return propertyConfiguration.defaultValue
+ }
+ }
+
+ if (propertyConfiguration?.expectedType == DoubleType) {
+ log.warn "${getCellAddresString(cell)}Expected Type is DOUBLE, but cell type is String, trying to extract numeric value"
+ try {
+ return cell.stringCellValue?.toDouble()
+ } catch (Exception e) {
+ log.warn "${getCellAddresString(cell)} Cannot get double value, returning default value specified for this type", e
+ pcc.reportCell(cell, propertyConfiguration)
+ return propertyConfiguration.defaultValue
+ }
+ }
+
+ if (propertyConfiguration?.expectedType == EmailType) {
+ String strValue = cell.stringCellValue
+ if (propertyConfiguration && strValue == propertyConfiguration?.valueEquivalentToNull) {
+ log.info("Found a value that's not null (value ${strValue}), but configuration property says to return null anyway")
+ return null
+ }
+ def emailValidator = org.apache.commons.validator.EmailValidator.newInstance()
+ if(emailValidator.isValid(strValue)) {
+ return strValue
+ }
+ log.warn "${getCellAddresString(cell)} Cannot get email value, returning default value specified for this type, which is ${propertyConfiguration.defaultValue}"
+ pcc.reportCell(cell, propertyConfiguration)
+ return propertyConfiguration.defaultValue
+ }
+
+ log.warn "${getCellAddresString(cell)} Potential issue - ${getCellAddresString(cell)}the excel file claims the type is String, but expecting something else for cell with value $origcell. Returning default value of ${propertyConfiguration?.defaultValue}"
+ //println "Potential issue - the excel file claims the type is String, but expecting something else for cell with value $origcell. Returning default value of ${propertyConfiguration.defaultValue}"
+
+ pcc.reportCell(cell, propertyConfiguration)
+ return propertyConfiguration?.defaultValue
+
+ case Cell.CELL_TYPE_NUMERIC:
+ //println "numeric cell $origcell"
+ if (propertyConfiguration?.expectedType == StringType) {
+ cell.setCellType(Cell.CELL_TYPE_STRING);
+ return cell.stringCellValue
+ }
+ if (DateUtil.isCellDateFormatted(cell)) {
+ def javaDate = cell.dateCellValue
+
+ if (propertyConfiguration?.expectedType == DateJavaType) {
+ if(pcc.checkReportValue(javaDate, cell, propertyConfiguration)) {
+ return propertyConfiguration?.defaultValue
+ }
+ return javaDate
+ }
+
+ LocalDate localDate = new LocalDate(javaDate)
+ if(pcc.checkReportValue(localDate, cell, propertyConfiguration)) {
+ return propertyConfiguration?.defaultValue
+ }
+ return localDate
+ } else {
+
+/*
+ //println "numeric cell $origcell"
+ if (propertyConfiguration?.expectedType == StringType) {
+ cell.setCellType(Cell.CELL_TYPE_STRING);
+ def stringValue = cell.stringCellValue
+ if (stringValue!=null) {
+ return stringValue
+ } else {
+ return propertyConfiguration.defaultValue
+ }
+ }
+ if (DateUtil.isCellDateFormatted(cell)) {
+ if (propertyConfiguration?.expectedType == DateJavaType) {
+ return cell.dateCellValue
+ } else {
+ return new LocalDate(cell.dateCellValue)
+ }
+ } else {
+*/
+ def numeric = cell.numericCellValue
+ if(pcc.checkReportValue(numeric, cell, propertyConfiguration)) {
+ return propertyConfiguration?.defaultValue
+ }
+ if (numeric!=null) {
+ return numeric
+ } else {
+ return propertyConfiguration?.defaultValue
+ }
+ }
+ break;
+ case Cell.CELL_TYPE_ERROR:
+ log.warn "CELL Type is ERROR value: $cell.errorCellValue ${getCellAddresString(cell)}"
+ pcc.reportCell(cell, propertyConfiguration)
+ return null
+ case Cell.CELL_TYPE_FORMULA:
+ log.warn "Cell type is formula, returning null ${getCellAddresString(cell)}"
+ pcc.reportCell(cell, propertyConfiguration)
+ return null
+ case Cell.CELL_TYPE_BOOLEAN:
+ return cell.booleanCellValue
+ case Cell.CELL_TYPE_BLANK:
+ log.debug "Found blank cell at ${getCellAddresString(cell)}"
+ pcc.reportCell(cell, propertyConfiguration)
+ return null;
+ default:
+ log.warn "Unexpected cell type. ${getCellAddresString(cell)} Ignoring. Cell Value [${cell}] type ${cell.cellType}"
+ }
+ log.error "WARNING: RETURNING NULL FROM getCellValue. UNEXPECTED CONDITION ${getCellAddresString(cell)}"
+ pcc.reportCell(cell, propertyConfiguration)
+ return null;
+ }
+
+
+ def getCellAddresString(cell) {
+ "Cell row ${cell.getRowIndex()} column ${cell.getColumnIndex()}"
+ }
+
+ /**
+ * dual getCellValue(Cell, ...
+ */
+ void setCellValue(value, Cell origcell, FormulaEvaluator evaluator = null, propertyConfiguration = [:]) {
+ if (!propertyConfiguration || !propertyConfiguration.expectedType) {
+ //... null handling
+ //directly compatible with expected type of setCellValue
+ if ([Double.TYPE, String.class, Boolean.TYPE].any {it.isInstance(value)}) {
+ origcell.setCellValue(value)
+ return
+ }
+ //conversion compatible with expected type of setCellValue
+ if (value instanceof Number) {
+ //avoid float->double widening to generate double values like 441.489990234375 instead of 441.49
+ origcell.setCellValue(Double.valueOf(value.toString()))
+ return
+ }
+ //Date
+ if ([Date.class, LocalDate.class].any {it.isInstance(value)}) {
+ origcell.setCellValue((value instanceof LocalDate)? value.toDateTimeAtStartOfDay().toDate(): value)
+ return
+ }
+ return
+ }
+
+ switch (propertyConfiguration.expectedType) {
+ case EmailType:
+ case StringType:
+ if (value == null) {
+ //dsq-OSM - maybe prefer to write out the valueEquivalentToNull if defined
+ origcell.setCellValue('')
+ return
+ }
+ if (value instanceof String) {
+ origcell.setCellValue(value)
+ return
+ }
+ //... type convertion handling
+ break;
+ case IntType:
+ case DoubleType:
+ if (value == null) {
+ return
+ }
+ if (value instanceof Number) {
+ origcell.setCellValue(Double.valueOf(value.toString()))
+ origcell.setCellType(Cell.CELL_TYPE_NUMERIC)
+ return
+ }
+ log.warn "value $value was supposed tobe numeric but isn't"
+ //... type convertion handling
+ break;
+ case DateType:
+ case DateJavaType:
+ if (value == null) {
+ return
+ }
+ if ([Date.class, LocalDate.class].any {it.isInstance(value)}) {
+ //dsq-OSM - not sure how to properly handle this date related stuff
+ def style = origcell.sheet.workbook.createCellStyle()
+ style.cloneStyleFrom(origcell.getCellStyle())
+ style.setDataFormat((short)0x0e)
+ origcell.setCellStyle(style)
+ origcell.setCellValue((value instanceof LocalDate)? value.toDateTimeAtStartOfDay().toDate(): value)
+ return
+ }
+ //... type convertion handling
+ break;
+ default:
+ log.error "Unexpected property type. Ignoring. Property Value [${value}] type ${propertyConfiguration.expectedType}"
+ }
+ log.error "WARNING: RETURNING FROM setCellValue. UNEXPECTED CONDITION expectedType ${propertyConfiguration.expectedType} / ${value}"
+ return;
+ }
+
+
+
+
+ Cell getCell(Sheet currentSheet, String ref) {
+ CellReference cellReference = new CellReference(ref);
+ Row row = currentSheet.getRow(cellReference.getRow());
+ Cell cell = row.getCell(cellReference.getCol())
+ //println "returning cell $cell"
+ cell
+ }
+
+ Cell getCellOrCreate(Sheet currentSheet, String ref) {
+ CellReference cellReference = new CellReference(ref);
+ Row row = currentSheet.getRow(cellReference.getRow());
+ row.getCell(cellReference.getCol()) ?: row.createCell(cellReference.getCol(), Cell.CELL_TYPE_BLANK)
+ }
+
+
+}
View
BIN  lib/xmlbeans-2.3.0-without-w3c.jar
Binary file not shown
View
15 plugin.xml
@@ -0,0 +1,15 @@
+<plugin name='excel-import' version='0.9.1' grailsVersion='1.3.0 &gt; *'>
+ <author>Jean Barmash, Oleksiy Symonenko</author>
+ <authorEmail>Jean.Barmash@gmail.com</authorEmail>
+ <title>Excel, Excel 2007 &amp; CSV Importer Using Apache POI</title>
+ <description>\
+ Excel-Import plugin uses Apache POI [http://poi.apache.org/] library (v 3.6) to parse Excel files.
+ It's useful for either bootstrapping data, or when you want to allow your users to enter some data using Excel spreadsheets.
+</description>
+ <documentation>http://grails.org/plugin/excel-import</documentation>
+ <resources>
+ <resource>BuildConfig</resource>
+ </resources>
+ <dependencies />
+ <behavior />
+</plugin>
View
10 scripts/_Install.groovy
@@ -0,0 +1,10 @@
+//
+// This script is executed by Grails after plugin was installed to project.
+// This script is a Gant script so you can use all special variables provided
+// by Gant (such as 'baseDir' which points on project base dir). You can
+// use 'ant' to access a global instance of AntBuilder
+//
+// For example you can create directory under project tree:
+//
+// ant.mkdir(dir:"${basedir}/grails-app/jobs")
+//
View
5 scripts/_Uninstall.groovy
@@ -0,0 +1,5 @@
+//
+// This script is executed by Grails when the plugin is uninstalled from project.
+// Use this script if you intend to do any additional clean-up on uninstall, but
+// beware of messing up SVN directories!
+//
View
10 scripts/_Upgrade.groovy
@@ -0,0 +1,10 @@
+//
+// This script is executed by Grails during application upgrade ('grails upgrade'
+// command). This script is a Gant script so you can use all special variables
+// provided by Gant (such as 'baseDir' which points on project base dir). You can
+// use 'ant' to access a global instance of AntBuilder
+//
+// For example you can create directory under project tree:
+//
+// ant.mkdir(dir:"${basedir}/grails-app/jobs")
+//
View
45 src/groovy/imexporter/AbstractImexporter.groovy
@@ -0,0 +1,45 @@
+package imexporter
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: sima
+ * Date: 4/21/11 8:06 PM
+ */
+public abstract class AbstractImexporter {
+
+
+ protected abstract def read(InputStream inp)
+
+ def readFromStream(inputStream) {
+ inputStream.withStream(this.&read)
+ return this
+ }
+
+ def readFromFile(String fileName) {
+ readFromStream(new FileInputStream(fileName))
+ }
+
+ def readFromUrl(URL url) {
+ readFromStream(url.openStream())
+ }
+
+
+
+ protected abstract def write(OutputStream out)
+
+ def writeToStream(OutputStream outputStream) {
+ outputStream.withStream(this.&write)
+ return outputStream
+ }
+
+ def writeToFile(String fileName) {
+ writeToStream(new FileOutputStream(fileName))
+ return fileName
+ }
+
+ def writeToByteArray() {
+ writeToStream(new ByteArrayOutputStream()).toByteArray()
+ }
+
+
+}
View
59 src/groovy/org/grails/plugins/excelimport/AbstractCsvImporter.groovy
@@ -0,0 +1,59 @@
+package org.grails.plugins.excelimport
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: sima
+ * Date: 4/21/11 8:20 PM
+ */
+abstract class AbstractCsvImporter extends imexporter.AbstractImexporter {
+
+
+ protected List tokensList
+
+
+ @Override
+ protected def read(InputStream inp) {
+ tokensList = []
+ inp.eachCsvLine {tokens ->
+ tokensList << tokens
+ }
+ }
+
+ @Override
+ protected def write(OutputStream out) {
+ throw new IllegalStateException("not implemented")
+ }
+
+
+ def getData(Map config) {
+ getData(config.columnMap, config.startRow)
+ }
+
+ def getData(Map columnMap, int firstRow) {
+ tokensList[[firstRow, tokensList.size()].min()..<tokensList.size()].collect {tokens ->
+ columnMap.inject([:]) {acc, ent ->
+ acc[ent.value] = (ent.key < tokens.length) ? tokens[ent.key] : null
+ return acc
+ }
+ }
+ }
+
+
+ static def trivialColumnMapFromExcel(Map excelColumnMap) {
+ excelColumnMap.inject([:]) {acc, ent ->
+ def columnName = ent.key
+ def propertyName = ent.value
+ int colIndex = org.apache.poi.ss.util.CellReference.convertColStringToIndex(columnName)
+ acc[colIndex] = propertyName
+ return acc
+ }.asImmutable()
+ }
+
+ static def trivialConfigMapFromExcel(Map excelConfigMap) {
+ [
+ startRow: excelConfigMap.startRow,
+ columnMap: trivialColumnMapFromExcel(excelConfigMap.columnMap)
+ ].asImmutable()
+ }
+
+}
View
110 src/groovy/org/grails/plugins/excelimport/AbstractExcelImporter.groovy
@@ -0,0 +1,110 @@
+package org.grails.plugins.excelimport
+
+import org.apache.poi.ss.usermodel.*
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: Jean Barmash
+ * Date: Sep 28, 2009
+ * Time: 6:30:17 PM
+ */
+public abstract class AbstractExcelImporter extends imexporter.AbstractImexporter {
+
+ static def getExcelImportService() {
+ def ctx = org.codehaus.groovy.grails.commons.ApplicationHolder.application.mainContext;
+ return ctx.getBean("excelImportService");
+ }
+
+
+ @Deprecated
+ InputStream inStr = null
+
+ Workbook workbook = null
+ FormulaEvaluator evaluator = null;
+
+ Sheet sheet = null
+
+ @Deprecated
+ public AbstractExcelImporter(String fileName) {
+ inStr = new FileInputStream(fileName)
+ this.read(inStr)
+ }
+
+ @Deprecated
+ def close() {
+ inStr.close()
+ }
+
+
+ public AbstractExcelImporter() {
+ }
+
+ @Override
+ protected def read(InputStream inp) {
+ workbook = WorkbookFactory.create(inp)
+ evaluator = workbook.creationHelper.createFormulaEvaluator()
+ }
+
+ @Override
+ protected def write(OutputStream out) {
+ workbook.write(out)
+ }
+
+ def createEmpty() {
+ workbook = new org.apache.poi.hssf.usermodel.HSSFWorkbook()
+ evaluator = workbook.creationHelper.createFormulaEvaluator()
+ workbook.createSheet('Sheet1')
+ return this
+ }
+
+ def copyFromCsv(csvFormat) {
+ def currentSheet = workbook.getSheet('Sheet1')
+ csvFormat.tokensList.eachWithIndex {tokens, rowIndex ->
+ def row = currentSheet.getRow(rowIndex) ?: currentSheet.createRow(rowIndex)
+ tokens.eachWithIndex {value, colIndex ->
+ excelImportService.setCellValueByColIndex(value, row, colIndex)
+ }
+ }
+ return this
+ }
+
+
+ def evaluateAllFormulaCells() {
+ for(int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
+ def sheet = workbook.getSheetAt(sheetNum);
+ for(def r : sheet) {
+ for(def c : r) {
+ if(c.getCellType() == Cell.CELL_TYPE_FORMULA) {
+// if(c.getCellValue()==null || c.getCellValue() == '' || c.getCellValue() == 0){
+ evaluator.evaluateFormulaCell(c);
+// }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
View
129 src/groovy/org/grails/plugins/excelimport/DefaultImportCellCollector.groovy
@@ -0,0 +1,129 @@
+package org.grails.plugins.excelimport
+
+import org.apache.poi.hssf.util.CellReference
+import org.apache.poi.ss.usermodel.Cell
+import org.apache.commons.logging.Log
+import org.apache.commons.logging.LogFactory
+
+
+class DefaultImportCellCollector implements ImportCellCollector {
+
+ static Log log = LogFactory.getLog(DefaultImportCellCollector.class)
+
+ def messagesBySeverityLevel = (ImportSeverityLevelEnum.enumConstants as List).inject([:]){acc, lvl ->
+ acc[lvl] = []
+ return acc
+ }.asImmutable()
+
+ @Override
+ void reportCell(Cell cell, Object propertyConfiguration) {
+ try {
+ log.debug "Reporting cell $cell, config: $propertyConfiguration"
+ def message = [
+ cell: cell,
+ propertyConfiguration: propertyConfiguration,
+ severityLevel: severityLevel(cell, propertyConfiguration),
+ cellKey: cellKey(cell),
+ text: (propertyConfiguration?.expectedType ? """
+ expecting a ${propertyConfiguration.expectedType.userViewableName},
+ but saw ${cellTypeToString[cell.cellType]} field
+ """ : """
+ unexpected ${cellTypeToString[cell.cellType]} field
+ """)
+ ]
+ message = prependCellMessageText(message)
+ report(message)
+ } catch (e) {
+ log.error "Exception while trying to report potential problem", e
+ }
+ }
+
+ @Override
+ void checkReportRow(row, config, excelImportService) {
+ try {
+ (config?.rowValidations ?: []).collect {validation ->
+ validation(row, config, excelImportService)
+ }.findAll {it}.each {message ->
+ message = [
+ severityLevel: ImportSeverityLevelEnum.Warning, //validation closure could override this
+ cellKey: cellKey(message.cell), //validation should return cell
+ text: 'Provided value is not valid' //validation closure could override this
+ ] + message
+ message = prependCellMessageText(message)
+ report(message)
+ }
+ } catch (e) {
+ log.error "Exception while trying to report potential problem", e
+ }
+ }
+
+ @Override
+ boolean checkReportValue(Object value, Cell cell, Object propertyConfiguration) {
+ try {
+ (propertyConfiguration?.valueValidations ?: []).collect {validation ->
+ validation(value)
+ }.findAll {it}.each {message ->
+ message = [
+ cell: cell,
+ propertyConfiguration: propertyConfiguration,
+ severityLevel: ImportSeverityLevelEnum.Warning, //validation closure could override this
+ cellKey: cellKey(cell),
+ text: 'Provided value is not valid' //validation closure could override this
+ ] + message
+ message = prependCellMessageText(message)
+ report(message)
+ }.isEmpty() == false
+ } catch (e) {
+ log.error "Exception while trying to report potential problem", e
+ return false
+ }
+ }
+
+ @Override
+ void report(message) {
+ try {
+ messagesBySeverityLevel[message.severityLevel].add(message)
+ } catch (e) {
+ log.error "Exception while trying to report potential problem", e
+ }
+ }
+
+ @Override
+ void reportPrepend(message) {
+ try {
+ messagesBySeverityLevel[message.severityLevel].add(0, message)
+ } catch (e) {
+ log.error "Exception while trying to report potential problem", e
+ }
+ }
+
+ protected def severityLevel(Cell cell, Object propertyConfiguration) {
+ def severityMapping = propertyConfiguration?.severityMapping ?: ImportSeverityMappingEnum.IgnoreBlankWarningOtherwise
+ severityMapping.severityLevel(cell.cellType, propertyConfiguration?.expectedType)
+ }
+
+ protected def cellKey(Cell cell) {
+ new CellReference(cell.sheet.sheetName, cell.rowIndex, cell.columnIndex, true, true).cellRefParts.toList()
+ }
+
+ protected def prependCellMessageText(message) {
+ def cellMessageText = """
+ Problem with
+ ${message.propertyConfiguration?.userViewableName? "value for '${message.propertyConfiguration?.userViewableName}'": ''}
+ (Cell ${message.cellKey[2]}${message.cellKey[1]} on Sheet "${message.cellKey[0]}") -
+ ${message.text}
+ """
+ message + [text: cellMessageText]
+ }
+
+ //toString for Cell.CELL_TYPE_*
+ protected static def cellTypeToString = [
+ (Cell.CELL_TYPE_BLANK): 'blank',
+ (Cell.CELL_TYPE_ERROR): 'error',
+ (Cell.CELL_TYPE_STRING): 'text',
+ (Cell.CELL_TYPE_BOOLEAN): 'boolean',
+ (Cell.CELL_TYPE_FORMULA): 'formula',
+ (Cell.CELL_TYPE_NUMERIC): 'number'
+ ].asImmutable()
+
+}
View
22 src/groovy/org/grails/plugins/excelimport/ExpectedPropertyType.groovy
@@ -0,0 +1,22 @@
+package org.grails.plugins.excelimport
+
+
+public enum ExpectedPropertyType {
+
+ IntType([name: 'number']),
+ StringType([name: 'text']),
+ DateType([name: 'date']),
+ DateJavaType([name: 'date']),
+ DoubleType([name: 'number']),
+ EmailType([name: 'email']);
+
+
+ final String userViewableName
+
+
+ public ExpectedPropertyType(Map parameters = [:]) {
+ this.userViewableName = parameters?.name ?: this.name()
+ }
+
+
+}
View
15 src/groovy/org/grails/plugins/excelimport/HorizontalExcelImporter.groovy
@@ -0,0 +1,15 @@
+package org.grails.plugins.excelimport
+
+/**
+ * Assumes data is in rows of an excel spreadsheet, allows to iterate throuhg rows
+ *
+ * Created by IntelliJ IDEA.
+ * User: Jean Barmash
+ * Date: Sep 28, 2009
+ * Time: 6:30:17 PM
+ */
+public abstract class HorizontalExcelImporter extends AbstractExcelImporter {
+
+ int rowNum
+
+}
View
18 src/groovy/org/grails/plugins/excelimport/ImportCellCollector.groovy
@@ -0,0 +1,18 @@
+package org.grails.plugins.excelimport
+
+import org.apache.poi.ss.usermodel.Cell
+
+
+public interface ImportCellCollector {
+
+ void reportCell(Cell cell, propertyConfiguration)
+
+ void checkReportRow(row, config, excelImportService)
+
+ boolean checkReportValue(Object value, Cell cell, Object propertyConfiguration)
+
+ void report(message)
+
+ void reportPrepend(message)
+
+}
View
49 src/groovy/org/grails/plugins/excelimport/ImportSeverityMappingEnum.groovy
@@ -0,0 +1,49 @@
+package org.grails.plugins.excelimport
+
+import org.apache.poi.ss.usermodel.Cell
+
+enum ImportSeverityLevelEnum {
+ Ignore,
+ Warning, //just provide warnings during smaller issues
+ Error //prevent upload for severe errors
+}
+
+enum ImportSeverityMappingEnum {
+ //add another mappings as needed
+ IgnoreBlankWarningOtherwise({actualType, expectedType ->
+ actualType == Cell.CELL_TYPE_BLANK? ImportSeverityLevelEnum.Ignore: ImportSeverityLevelEnum.Warning
+ }),
+ IgnoreBlankErrorOtherwise({actualType, expectedType ->
+ actualType == Cell.CELL_TYPE_BLANK? ImportSeverityLevelEnum.Ignore: ImportSeverityLevelEnum.Error
+ }),
+ //Crucial
+ ErrorAll({actualType, expectedType ->
+ ImportSeverityLevelEnum.Error
+ }),
+ //Required - All fields indicated required (green on excel, starred on edit prop page)
+ WarningAll({actualType, expectedType ->
+ ImportSeverityLevelEnum.Warning
+ }),
+ IgnoreAll({actualType, expectedType ->
+ ImportSeverityLevelEnum.Ignore
+ });
+
+
+ private final def mappingClo;
+
+ ImportSeverityMappingEnum(mappingClo) {
+ this.mappingClo = mappingClo
+ }
+
+ /**
+ *
+ * @param actualType value from Cell.CELL_TYPE_*
+ * @param expectedType value from ExpectedPropertyType enum
+ * @return value from ImportSeverityLevelEnum
+ */
+ def severityLevel(actualType, expectedType) {
+ mappingClo(actualType, expectedType) ?: ImportSeverityLevelEnum.Warning
+ }
+}
+
+
View
30 src/groovy/org/grails/plugins/excelimport/NoopImportCellCollector.groovy
@@ -0,0 +1,30 @@
+package org.grails.plugins.excelimport
+
+import org.apache.poi.ss.usermodel.Cell
+
+class NoopImportCellCollector implements ImportCellCollector {
+
+ @Override
+ void reportCell(Cell cell, Object propertyConfiguration) {
+ }
+
+ @Override
+ void checkReportRow(Object row, Object config, Object excelImportService) {
+ }
+
+ @Override
+ boolean checkReportValue(Object value, Cell cell, Object propertyConfiguration) {
+ return false
+ }
+
+ @Override
+ void report(Object message) {
+ }
+
+ @Override
+ void reportPrepend(Object message) {
+ }
+
+ public static final NoopInstance = new NoopImportCellCollector()
+
+}
View
14 test/projects/sample/.classpath
@@ -0,0 +1,14 @@
+<classpath>
+ <classpathentry kind="src" path="src/java"/>
+ <classpathentry kind="src" path="src/groovy"/>
+ <classpathentry kind="src" path="grails-app/conf"/>
+ <classpathentry kind="src" path="grails-app/controllers"/>
+ <classpathentry kind="src" path="grails-app/domain"/>
+ <classpathentry kind="src" path="grails-app/services"/>
+ <classpathentry kind="src" path="grails-app/taglib"/>
+ <classpathentry kind="src" path="test/integration"/>
+ <classpathentry kind="src" path="test/unit"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="com.springsource.sts.grails.core.CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="web-app/WEB-INF/classes"/>
+</classpath>
View
19 test/projects/sample/.project
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>sample</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.springsource.sts.grails.core.nature</nature>
+ <nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
View
3  test/projects/sample/.settings/org.codehaus.groovy.eclipse.preferences.prefs
@@ -0,0 +1,3 @@
+#Created by grails
+eclipse.preferences.version=1
+groovy.dont.generate.class.files=true
View
19 test/projects/sample/application.properties
@@ -0,0 +1,19 @@
+#Grails Metadata file
+#Fri Sep 30 22:13:59 EDT 2011
+app.grails.version=1.3.6
+app.name=sample
+app.servlet.version=2.4
+app.version=0.1
+plugins.auto-test=0.1
+plugins.code-coverage=1.2.4
+plugins.codenarc=0.15
+plugins.console=0.2.2
+plugins.csv=0.1
+plugins.db-util=0.4
+plugins.gmetrics=0.3.1
+plugins.hibernate=1.3.6
+plugins.joda-time=0.5
+plugins.runtime-logging=0.3
+plugins.spock=0.5-groovy-1.7
+plugins.spy=0.1
+plugins.tomcat=1.3.6
View
BIN  test/projects/sample/cobertura.ser
Binary file not shown
View
67 test/projects/sample/grails-app/conf/BootStrap.groovy
@@ -0,0 +1,67 @@
+import sample.*
+
+class BootStrap {
+
+ def init = { servletContext ->
+ testExcel('xls')
+ testExcel('xlsx')
+ testCsv()
+ }
+
+ def testExcel(fileExt) {
+ println "==== testExcel = ${fileExt}"
+
+ BookExcelImporter importer = new BookExcelImporter().readFromUrl("file:test-data/books.${fileExt}".toURL())
+ println "workbook = ${importer.workbook.getClass().name} - ${importer.workbook}"
+
+ def booksMapList = importer.getBooks()
+ println "booksMapList = ${booksMapList}"
+ booksMapList.each { Map bookParams ->
+ def newBook = new Book(bookParams)
+ if (!newBook.save()) {
+ println "Book1 not saved, errors = ${newBook.errors}"
+ }
+ }
+
+ def bookParams = importer.getOneMoreBookParams()
+ println "bookParams = ${bookParams}"
+ def anotherNewBook = new Book(bookParams)
+ if (!anotherNewBook.save()) {
+ println "Book2 not saved, errors = ${anotherNewBook.errors}"
+ }
+
+
+
+ BookExcelImporter exporter = new BookExcelImporter().readFromUrl("file:test-data/books-empty.${fileExt}".toURL())
+
+ exporter.setBooks(Book.list())
+ exporter.setOneMoreBookParams([*: Book.get(3L).properties])
+
+ String exportName = exporter.writeToFile(File.createTempFile('book-', ".${fileExt}").absolutePath)
+ println "exported to file = ${exportName}"
+
+// some issues with reading from just exported model in xlsx format
+// println "booksMapList = ${exporter.getBooks()}"
+// println "bookParams = ${exporter.getOneMoreBookParams()}"
+
+ }
+
+
+ def testCsv() {
+ println "==== testCsv"
+ BookExcelImporter.BookCsvImporter csvImporter = new BookExcelImporter.BookCsvImporter().readFromUrl("file:test-data/books.csv".toURL())
+
+ def booksMapList = csvImporter.getBooks()
+ println "booksMapList = ${booksMapList}"
+
+ BookExcelImporter excelExporter = csvImporter.copyToExcel(new BookExcelImporter().readFromUrl("file:test-data/books-empty.xls".toURL()))
+ String exportName = excelExporter.writeToFile(File.createTempFile('book-', ".xls").absolutePath)
+ println "exported to file = ${exportName}"
+
+ }
+
+
+
+ def destroy = {
+ }
+}
View
41 test/projects/sample/grails-app/conf/BuildConfig.groovy
@@ -0,0 +1,41 @@
+grails.project.class.dir = "target/classes"
+grails.project.test.class.dir = "target/test-classes"
+grails.project.test.reports.dir = "target/test-reports"
+//grails.project.war.file = "target/${appName}-${appVersion}.war"
+grails.project.dependency.resolution = {
+ // inherit Grails' default dependencies
+ inherits("global") {
+ // uncomment to disable ehcache
+ // excludes 'ehcache'
+ }
+ log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
+ repositories {
+ grailsPlugins()
+ grailsHome()
+ grailsCentral()
+
+ // uncomment the below to enable remote dependency resolution
+ // from public Maven repositories
+ //mavenLocal()
+ //mavenCentral()
+ //mavenRepo "http://snapshots.repository.codehaus.org"
+ //mavenRepo "http://repository.codehaus.org"
+ //mavenRepo "http://download.java.net/maven/2/"
+ //mavenRepo "http://repository.jboss.com/maven2/"
+ }
+ dependencies {
+ // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
+
+ // runtime 'mysql:mysql-connector-java:5.1.5'
+ }
+}
+
+grails.plugin.location.excelImport = "../../../"
+
+coverage {
+ xml = true
+ exclusions = ["**/*Tests*"]
+ // list of directories to search for source to include in coverage reports
+ sourceInclusions = ['../../../grails-app/']
+}
+
View
110 test/projects/sample/grails-app/conf/Config.groovy
@@ -0,0 +1,110 @@
+
+gmetrics.reportType = 'org.gmetrics.report.XmlReportWriter'
+gmetrics.outputFile = 'target/gmetrics.xml'
+gmetrics.processTestUnit = false
+gmetrics.processTestIntegration = false
+
+codenarc.reportType='xml'
+codenarc.reportName='target/codenarc.xml'
+codenarc.processTestUnit = false
+codenarc.processTestIntegration = false
+codenarc.propertiesFile = 'codenarc.properties'
+
+
+// locations to search for config files that get merged into the main config
+// config files can either be Java properties files or ConfigSlurper scripts
+
+// grails.config.locations = [ "classpath:${appName}-config.properties",
+// "classpath:${appName}-config.groovy",
+// "file:${userHome}/.grails/${appName}-config.properties",
+// "file:${userHome}/.grails/${appName}-config.groovy"]
+
+// if(System.properties["${appName}.config.location"]) {
+// grails.config.locations << "file:" + System.properties["${appName}.config.location"]
+// }
+
+grails.project.groupId = appName // change this to alter the default package name and Maven publishing destination
+grails.mime.file.extensions = true // enables the parsing of file extensions from URLs into the request format
+grails.mime.use.accept.header = false
+grails.mime.types = [ html: ['text/html','application/xhtml+xml'],
+ xml: ['text/xml', 'application/xml'],
+ text: 'text/plain',
+ js: 'text/javascript',
+ rss: 'application/rss+xml',
+ atom: 'application/atom+xml',
+ css: 'text/css',
+ csv: 'text/csv',
+ all: '*/*',
+ json: ['application/json','text/json'],
+ form: 'application/x-www-form-urlencoded',
+ multipartForm: 'multipart/form-data'
+ ]
+// The default codec used to encode data with ${}
+grails.views.default.codec = "none" // none, html, base64
+grails.views.gsp.encoding = "UTF-8"
+grails.converters.encoding = "UTF-8"
+// enable Sitemesh preprocessing of GSP pages
+grails.views.gsp.sitemesh.preprocess = true
+// scaffolding templates configuration
+grails.scaffolding.templates.domainSuffix = 'Instance'
+
+// Set to false to use the new Grails 1.2 JSONBuilder in the render method
+grails.json.legacy.builder = false
+// enabled native2ascii conversion of i18n properties files
+grails.enable.native2ascii = true
+// whether to install the java.util.logging bridge for sl4j. Disable for AppEngine!
+grails.logging.jul.usebridge = true
+// packages to include in Spring bean scanning
+grails.spring.bean.packages = []
+
+// set per-environment serverURL stem for creating absolute links
+environments {
+ production {
+ grails.serverURL = "http://www.changeme.com"
+ }
+ development {
+ grails.serverURL = "http://localhost:8080/${appName}"
+ }
+ test {
+ grails.serverURL = "http://localhost:8080/${appName}"
+ }
+
+}
+
+// log4j configuration
+log4j = {
+ // Example of changing the log pattern for the default console
+ // appender:
+ appenders {
+ console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
+ }
+
+ error 'org.codehaus.groovy.grails.web.servlet', // controllers
+ 'org.codehaus.groovy.grails.web.pages', // GSP
+ 'org.codehaus.groovy.grails.web.sitemesh', // layouts
+ 'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
+ 'org.codehaus.groovy.grails.web.mapping', // URL mapping
+ 'org.codehaus.groovy.grails.commons', // core / classloading
+ 'org.codehaus.groovy.grails.plugins', // plugins
+ 'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
+ 'org.springframework',
+ 'org.hibernate',
+ 'net.sf.ehcache.hibernate'
+
+ warn 'org.mortbay.log'
+ info 'org.mortbay.log'
+ debug 'org.mortbay.log'
+
+
+ root {
+ //trace 'mainlog'
+ debug 'stdout'
+ info 'stdout' //, 'stdout'
+ warn 'stdout' //, 'stdout'
+ //info 'mainlog'
+ //error 'mainlog'
+ error 'stdout'
+ additivity = true
+ }
+
+}
View
32 test/projects/sample/grails-app/conf/DataSource.groovy
@@ -0,0 +1,32 @@
+dataSource {
+ pooled = true
+ driverClassName = "org.hsqldb.jdbcDriver"
+ username = "sa"
+ password = ""
+}
+hibernate {
+ cache.use_second_level_cache = true
+ cache.use_query_cache = true
+ cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
+}
+// environment specific settings
+environments {
+ development {
+ dataSource {
+ dbCreate = "create-drop" // one of 'create', 'create-drop','update'
+ url = "jdbc:hsqldb:mem:devDB"
+ }
+ }
+ test {
+ dataSource {
+ dbCreate = "update"
+ url = "jdbc:hsqldb:mem:testDb"
+ }
+ }
+ production {
+ dataSource {
+ dbCreate = "update"
+ url = "jdbc:hsqldb:file:prodDb;shutdown=true"
+ }
+ }
+}
View
13 test/projects/sample/grails-app/conf/UrlMappings.groovy
@@ -0,0 +1,13 @@
+class UrlMappings {
+
+ static mappings = {
+ "/$controller/$action?/$id?"{
+ constraints {
+ // apply constraints here
+ }
+ }
+
+ "/"(view:"/index")
+ "500"(view:'/error')
+ }
+}
View
3  test/projects/sample/grails-app/conf/spring/resources.groovy
@@ -0,0 +1,3 @@
+// Place your Spring DSL code here
+beans = {
+}
View
6 test/projects/sample/grails-app/controllers/sample/BookController.groovy
@@ -0,0 +1,6 @@
+package sample
+
+class BookController {
+
+ def scaffold = Book
+}
View
16 test/projects/sample/grails-app/domain/sample/Book.groovy
@@ -0,0 +1,16 @@
+package sample
+
+import org.joda.time.LocalDate
+
+
+class Book {
+
+ String title
+ String author
+ int numSold
+ LocalDate dateIssued
+
+ static constraints = {
+ dateIssued nullable: true
+ }
+}
View
55 test/projects/sample/grails-app/i18n/messages.properties
@@ -0,0 +1,55 @@
+default.doesnt.match.message=Property [{0}] of class [{1}] with value [{2}] does not match the required pattern [{3}]
+default.invalid.url.message=Property [{0}] of class [{1}] with value [{2}] is not a valid URL
+default.invalid.creditCard.message=Property [{0}] of class [{1}] with value [{2}] is not a valid credit card number
+default.invalid.email.message=Property [{0}] of class [{1}] with value [{2}] is not a valid e-mail address
+default.invalid.range.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid range from [{3}] to [{4}]
+default.invalid.size.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid size range from [{3}] to [{4}]
+default.invalid.max.message=Property [{0}] of class [{1}] with value [{2}] exceeds maximum value [{3}]
+default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
+default.invalid.max.size.message=Property [{0}] of class [{1}] with value [{2}] exceeds the maximum size of [{3}]
+default.invalid.min.size.message=Property [{0}] of class [{1}] with value [{2}] is less than the minimum size of [{3}]
+default.invalid.validator.message=Property [{0}] of class [{1}] with value [{2}] does not pass custom validation
+default.not.inlist.message=Property [{0}] of class [{1}] with value [{2}] is not contained within the list [{3}]
+default.blank.message=Property [{0}] of class [{1}] cannot be blank
+default.not.equal.message=Property [{0}] of class [{1}] with value [{2}] cannot equal [{3}]
+default.null.message=Property [{0}] of class [{1}] cannot be null
+default.not.unique.message=Property [{0}] of class [{1}] with value [{2}] must be unique
+
+default.paginate.prev=Previous
+default.paginate.next=Next
+default.boolean.true=True
+default.boolean.false=False
+default.date.format=yyyy-MM-dd HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} created
+default.updated.message={0} {1} updated
+default.deleted.message={0} {1} deleted
+default.not.deleted.message={0} {1} could not be deleted
+default.not.found.message={0} not found with id {1}
+default.optimistic.locking.failure=Another user has updated this {0} while you were editing
+
+default.home.label=Home
+default.list.label={0} List
+default.add.label=Add {0}
+default.new.label=New {0}
+default.create.label=Create {0}
+default.show.label=Show {0}
+default.edit.label=Edit {0}
+
+default.button.create.label=Create
+default.button.edit.label=Edit
+default.button.update.label=Update
+default.button.delete.label=Delete
+default.button.delete.confirm.message=Are you sure?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Property {0} must be a valid URL
+typeMismatch.java.net.URI=Property {0} must be a valid URI
+typeMismatch.java.util.Date=Property {0} must be a valid Date
+typeMismatch.java.lang.Double=Property {0} must be a valid number
+typeMismatch.java.lang.Integer=Property {0} must be a valid number
+typeMismatch.java.lang.Long=Property {0} must be a valid number
+typeMismatch.java.lang.Short=Property {0} must be a valid number
+typeMismatch.java.math.BigDecimal=Property {0} must be a valid number
+typeMismatch.java.math.BigInteger=Property {0} must be a valid number
View
54 test/projects/sample/grails-app/views/error.gsp
@@ -0,0 +1,54 @@
+<html>
+ <head>
+ <title>Grails Runtime Exception</title>
+ <style type="text/css">
+ .message {
+ border: 1px solid black;
+ padding: 5px;
+ background-color:#E9E9E9;
+ }
+ .stack {
+ border: 1px solid black;
+ padding: 5px;
+ overflow:auto;
+ height: 300px;
+ }
+ .snippet {
+ padding: 5px;
+ background-color:white;
+ border:1px solid black;
+ margin:3px;
+ font-family:courier;
+ }
+ </style>
+ </head>
+
+ <body>
+ <h1>Grails Runtime Exception</h1>
+ <h2>Error Details</h2>
+
+ <div class="message">
+ <strong>Error ${request.'javax.servlet.error.status_code'}:</strong> ${request.'javax.servlet.error.message'.encodeAsHTML()}<br/>
+ <strong>Servlet:</strong> ${request.'javax.servlet.error.servlet_name'}<br/>
+ <strong>URI:</strong> ${request.'javax.servlet.error.request_uri'}<br/>
+ <g:if test="${exception}">
+ <strong>Exception Message:</strong> ${exception.message?.encodeAsHTML()} <br />
+ <strong>Caused by:</strong> ${exception.cause?.message?.encodeAsHTML()} <br />
+ <strong>Class:</strong> ${exception.className} <br />