Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLWithoutAPIMacro2.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLWithoutAPIMacro2.groovy	(revision 3209)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLWithoutAPIMacro2.groovy	(revision 3210)
@@ -1,282 +0,0 @@
-package org.txm.macro.table
-
-import org.kohsuke.args4j.*
-import groovy.transform.Field
-import java.nio.charset.Charset
-import org.txm.rcpapplication.swt.widget.parameters.*
-import org.txm.utils.*
-import javax.xml.stream.*
-import java.net.URL
-import java.text.SimpleDateFormat
-
-import org.apache.poi.ss.usermodel.*
-import org.apache.poi.hssf.usermodel.*
-import org.apache.poi.xssf.usermodel.*
-import org.apache.poi.ss.usermodel.WorkbookFactory
-import org.apache.poi.ss.util.*
-
-def stringToIndent = { str -> org.txm.utils.AsciiUtils.buildAttributeId(org.txm.utils.AsciiUtils.convertNonAscii(str)).toLowerCase() }
-
-// from http://www.java-connect.com/apache-poi-tutorials/read-all-type-of-excel-cell-value-as-string-using-poi
-def getCellValueAsString = { cell ->
-	strCellValue = null
-	if (cell != null) {
-		switch (cell.getCellType()) {
-			case CellType.STRING:
-				strCellValue = cell.toString()
-				break
-			case CellType.NUMERIC:
-				if (DateUtil.isCellDateFormatted(cell)) {
-					SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy")
-					strCellValue = dateFormat.format(cell.getDateCellValue())
-				} else {
-					value = cell.getNumericCellValue()
-					longValue = value.longValue()
-					strCellValue = new String(longValue.toString())
-				}
-				break
-			case CellType.BOOLEAN:
-				strCellValue = new String(new Boolean(cell.getBooleanCellValue()).toString())
-				break
-			case CellType.BLANK:
-				strCellValue = ""
-				break
-		}
-	}
-	if (strCellValue == null) strCellValue = ""
-	return strCellValue
-}
-
-@Field @Option(name="inputFile", usage="fichier Excel à traiter", widget="File", required=true, def="")
-		File inputFile
-
-@Field @Option(name="sheetName", usage="sheet name (if no name is given the first sheet will be used)", widget="String", required=false, def="")
-		def sheetName
-
-@Field @Option(name="rootTag", usage="root tag name", widget="String", required=false, def="root")
-		def rootTag
-
-@Field @Option(name="textTag", usage="line unit tag name", widget="String", required=false, def="unit")
-		def textTag
-
-@Field @Option(name="metadataColumnList", usage="metadata columns list separated by comma", widget="String", required=false, def="meta1,meta2")
-		def metadataColumnList
-
-@Field @Option(name="dateColumnList", usage="date columns list separated by comma", widget="String", required=false, def="meta1,meta2")
-		def dateColumnList
-
-@Field @Option(name="textColumnList", usage="text columns list separated by comma", widget="String", required=false, def="textColumnList1,textColumnList2")
-		def textColumnList
-
-@Field @Option(name="EmbedInTEI", usage="text columns list separated by comma", widget="Boolean", required=false, def="false")
-		def EmbedInTEI
-
-if (!ParametersDialog.open(this)) return
-
-	if (!inputFile.exists()) {
-		println "** Excel2XML: no '"+inputFile.name+"' file found. Aborting."
-		return false
-	}
-
-if (!inputFile.canRead()) {
-	println "** Excel2XML: '"+inputFile.name+"' file not readable. Aborting."
-	return false
-}
-
-try {
-	
-	metadataColumnList = metadataColumnList.split(",").collect { it.trim() }
-	dateColumnList = dateColumnList.split(",").collect { it.trim() }
-	textColumnList = textColumnList.split(",").collect { it.trim() }
-	textTag = textTag.trim()
-	rootTag = rootTag.trim()
-	
-	wb = WorkbookFactory.create(inputFile)
-	
-	if (sheetName.length() == 0) {
-		ws = wb.getSheetAt(0)
-	} else {
-		ws = wb.getSheet(sheetName)
-		if (ws == null) {
-			println "** Excel2XML: no '"+sheetName+" found. Aborting."
-			return false
-		}
-	}
-	
-	if (ws == null) {
-		println "** Excel2XML: no sheet found. Aborting."
-		return false
-	}
-	
-	nRows = ws.getPhysicalNumberOfRows()
-	println nRows+" rows."
-	
-	firstRow = ws.getRow(0)
-	colMax = firstRow.getLastCellNum()
-	
-	headers = (0..colMax-1).collect { getCellValueAsString(firstRow.getCell(it)) }
-	
-	println "Headers: $headers"
-	
-	normalizedHeaders = headers.collect { stringToIndent(it) }
-	
-	ok = true
-	metadataColumnList.each { m ->
-		if (!headers.contains(m)) {
-			println "** Excel2XML: missing metadataColumnList column: $m"
-			ok = false
-		}
-	}
-	textColumnList.each { t ->
-		if (!headers.contains(t)) {
-			println "** Excel2XML: missing textColumnList column: $t"
-			ok = false
-		}
-	}
-	
-	if (!ok) { return false }
-	
-	metadataColumnIndex = metadataColumnList.collect { headers.indexOf(it) }
-	dateColumnsIndex = dateColumnList.collect { headers.indexOf(it) }
-	textColumnIndex = textColumnList.collect { headers.indexOf(it) }
-	
-	println "metadataColumnList = "+metadataColumnList
-	println "metadataColumnIndex = "+metadataColumnIndex
-	
-	println "dateColumnList = "+dateColumnList
-	println "dateColumnsIndex = "+dateColumnsIndex
-	
-	println "textColumnList = "+textColumnList
-	println "textColumnIndex = "+textColumnIndex
-	
-	name = inputFile.getName()
-	idx = name.lastIndexOf(".")
-	
-	if (idx > 0) name = name.substring(0, idx)
-	outputFile = new File(inputFile.getParentFile(), name+".xml")
-	
-	factory = XMLOutputFactory.newInstance()
-	output = new FileOutputStream(outputFile)
-	writer = factory.createXMLStreamWriter(output, "UTF-8")
-	
-	writer.writeStartDocument("UTF-8","1.0")
-	writer.writeCharacters("\n") // simple XML formating
-	
-	if (EmbedInTEI) {
-		writer.writeStartElement("TEI")
-		writer.writeStartElement("teiHeader")
-		writer.writeEndElement() // teiHeader
-		writer.writeStartElement("text")
-		writer.writeCharacters("\n")
-	}
-	
-	writer.writeStartElement(rootTag)
-	writer.writeCharacters("\n")
-	
-	pb_n = 1
-	
-	(1..nRows-1).each { rowIndex ->
-		
-		writer.writeCharacters("  ")
-		writer.writeEmptyElement("pb") // <pb/> to get one page per input line (don't forget high number of words per page in import module)
-		writer.writeAttribute("n", ""+pb_n++)
-		writer.writeCharacters("\n") // simple XML formating
-		
-		writer.writeCharacters("  ")
-		writer.writeStartElement(textTag)
-		metadataColumnIndex.each { colIndex -> // build an attribute for each metadata
-			def row = ws.getRow(rowIndex)
-			if (row != null) {
-				String s = getCellValueAsString(row.getCell(colIndex));
-				if (s == null) s ="";
-				value = s.replaceAll("\n", ";").trim()
-				writer.writeAttribute(normalizedHeaders[colIndex], value)
-				if (colIndex in dateColumnsIndex) { // also split date attributes in day+month+year attributes
-					matches = (value =~ /([0-9]{2})\/([0-9]{2})\/([0-9]{4})/)
-					writer.writeAttribute(normalizedHeaders[colIndex]+"jour", matches[0][1])
-					writer.writeAttribute(normalizedHeaders[colIndex]+"joursemaine", new java.text.SimpleDateFormat('EEEE').format(Date.parse("dd/MM/yyyy", value)))
-					writer.writeAttribute(normalizedHeaders[colIndex]+"mois", matches[0][2])
-					writer.writeAttribute(normalizedHeaders[colIndex]+"annee", matches[0][3])
-				}
-			}
-		}
-		writer.writeCharacters("\n")
-		
-		writer.writeCharacters("    ")
-		writer.writeStartElement("metadata")
-		writer.writeStartElement("list")
-		writer.writeAttribute("type", "unordered")
-		writer.writeCharacters("\n")
-		
-		metadataColumnIndex.each { colIndex ->
-			
-			def row = ws.getRow(rowIndex)
-			if (row != null) {
-				writer.writeStartElement("item")
-				writer.writeCharacters(headers[colIndex]+" : "+getCellValueAsString(row.getCell(colIndex)).replaceAll("\n", ";"))
-				writer.writeEndElement() // item
-				writer.writeCharacters("\n")
-			}
-		}
-		writer.writeCharacters("    ")
-		writer.writeEndElement() // list
-		writer.writeEndElement() // head
-		writer.writeCharacters("\n")
-		
-		textColumnIndex.each { colIndex ->
-			
-			writer.writeCharacters("    ")
-			writer.writeStartElement(normalizedHeaders[colIndex])
-			writer.writeStartElement("p")
-			writer.writeStartElement("head")
-			writer.writeStartElement("hi")
-			writer.writeCharacters(headers[colIndex]+" : ")
-			writer.writeEndElement() // hi
-			writer.writeEndElement() // head
-			def row = ws.getRow(rowIndex)
-			if (row != null) {
-				value = getCellValueAsString(row.getCell(colIndex))
-				
-				if (value ==~ /(?s)^[A-Z]{3}:  [^;\n]+? +[;\n].*/) {
-					value.findAll( /(?s)[A-Z]{3}:  ([^;\n]+?) +[;\n]/ ).each { desc ->
-						writer.writeStartElement("descripteur")
-						matches = (desc =~ /(?s)([A-Z]{3}):  ([^;\n]+?) +[;\n]/)
-						writer.writeAttribute("type", matches[0][1])
-						writer.writeCharacters(matches[0][2])
-						writer.writeEndElement() // descripteur
-					}
-				} else {
-					writer.writeCharacters(value)
-				}
-				writer.writeEndElement() // p
-				writer.writeEndElement() // textColumn
-				writer.writeCharacters("\n")
-			}
-		}
-		
-		writer.writeCharacters("  ")
-		writer.writeEndElement() // textTag
-		writer.writeCharacters("\n")
-	}
-	
-	writer.writeEndElement() // rootTag
-	writer.writeCharacters("\n")
-	if (EmbedInTEI) {
-		writer.writeEndElement() // text
-		writer.writeCharacters("\n")
-		writer.writeEndElement() // TEI
-		writer.writeCharacters("\n")
-	}
-	
-	writer.close()
-	output.close()
-	println "Result file: $outputFile"
-	
-} catch (Exception e) {
-	println "** Excel2XML: unable to read input file. Aborting."
-	println e.getLocalizedMessage()
-	println e.printStackTrace()
-	return false
-}
-
-return true
Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLMacro2.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLMacro2.groovy	(revision 3209)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLMacro2.groovy	(revision 3210)
@@ -1,245 +0,0 @@
-package org.txm.macro.table
-
-import org.kohsuke.args4j.*
-import groovy.transform.Field
-import java.nio.charset.Charset
-
-import org.txm.libs.msoffice.ReadExcel
-import org.txm.rcpapplication.swt.widget.parameters.*
-import org.txm.utils.*
-import javax.xml.stream.*
-import java.net.URL
-import java.text.SimpleDateFormat
-
-import org.apache.poi.ss.usermodel.*
-import org.apache.poi.hssf.usermodel.*
-import org.apache.poi.xssf.usermodel.*
-import org.apache.poi.ss.usermodel.WorkbookFactory
-import org.apache.poi.ss.util.*
-
-def stringToIndent = { str -> org.txm.utils.AsciiUtils.buildAttributeId(org.txm.utils.AsciiUtils.convertNonAscii(str)).toLowerCase() }
-
-@Field @Option(name="inputFile", usage="fichier Excel à traiter", widget="File", required=true, def="")
-		File inputFile
-
-@Field @Option(name="sheetName", usage="sheet name (if no name is given the first sheet will be used)", widget="String", required=false, def="")
-		def sheetName
-
-@Field @Option(name="EmbedInTEI", usage="embed xml content in  TEI text element", widget="Boolean", required=false, def="false")
-		def EmbedInTEI
-		
-@Field @Option(name="rootTag", usage="root tag name", widget="String", required=false, def="root")
-		def rootTag
-
-@Field @Option(name="textTag", usage="line unit tag name", widget="String", required=false, def="unit")
-		def textTag
-
-@Field @Option(name="metadataColumnList", usage="metadata columns list separated by comma", widget="String", required=false, def="meta1,meta2")
-		def metadataColumnList
-
-@Field @Option(name="dateColumnList", usage="date columns list separated by comma", widget="String", required=false, def="meta1,meta2")
-		def dateColumnList
-
-@Field @Option(name="textColumnList", usage="text columns list separated by comma", widget="String", required=false, def="textColumnList1,textColumnList2")
-		def textColumnList
-		
-@Field @Option(name="prefixContentColumnList", usage="columns which content is a semi-colon separated prefixes list", widget="String", required=false, def="")
-		def prefixContentColumnList
-		
-@Field @Option(name="listContentColumnList", usage="columns which content is a semi-colon separated list", widget="String", required=false, def="")
-		def listContentColumnList
-
-
-if (!ParametersDialog.open(this)) return
-
-	if (!inputFile.exists()) {
-		println "** Excel2XML: no '"+inputFile.name+"' file found. Aborting."
-		return false
-	}
-
-if (!inputFile.canRead()) {
-	println "** Excel2XML: '"+inputFile.name+"' file not readable. Aborting."
-	return false
-}
-
-try {
-	metadataColumnList = metadataColumnList.split(",").collect { it.trim() }
-	dateColumnList = dateColumnList.split(",").collect { it.trim() }
-	textColumnList = textColumnList.split(",").collect { it.trim() }
-	prefixContentColumnList = prefixContentColumnList.split(",").collect { it.trim() }
-	listContentColumnList = listContentColumnList.split(",").collect { it.trim() }
-	textTag = textTag.trim()
-	rootTag = rootTag.trim()
-	
-	ReadExcel excel = new ReadExcel(inputFile, sheetName);
-	excel.readHeaders();
-	
-	headers = excel.getHeaders()
-	
-	println "Headers: $headers"
-	
-	ok = true
-	metadataColumnList.each { m ->
-		if (!headers.contains(m)) {
-			println "** Excel2XML: missing metadataColumnList column: $m"
-			ok = false
-		}
-	}
-	textColumnList.each { t ->
-		if (!headers.contains(t)) {
-			println "** Excel2XML: missing textColumnList column: $t"
-			ok = false
-		}
-	}
-	
-	if (!ok) { return false }
-	
-	println "metadataColumnList = "+metadataColumnList
-	println "dateColumnList = "+dateColumnList
-	println "textColumnList = "+textColumnList
-	
-	name = inputFile.getName()
-	idx = name.lastIndexOf(".")
-	
-	if (idx > 0) name = name.substring(0, idx)
-	outputFile = new File(inputFile.getParentFile(), name+".xml")
-	
-	factory = XMLOutputFactory.newInstance()
-	output = new FileOutputStream(outputFile)
-	writer = factory.createXMLStreamWriter(output, "UTF-8")
-	
-	writer.writeStartDocument("UTF-8","1.0")
-	writer.writeCharacters("\n") // simple XML formating
-	
-	if (EmbedInTEI) {
-		writer.writeStartElement("TEI")
-		writer.writeStartElement("teiHeader")
-		writer.writeEndElement() // teiHeader
-		writer.writeStartElement("text")
-		writer.writeCharacters("\n")
-	}
-	
-	if (rootTag != null && rootTag.length() > 0) {
-		writer.writeStartElement(rootTag)
-		writer.writeCharacters("\n")
-	}
-	
-	pb_n = 1
-	
-	//(1..nRows-1).each { rowIndex ->
-	def record = null
-	while (excel.readRecord()) {
-		
-		record = excel.getRecord()
-		if (record.isEmpty()) continue;
-		
-		//println "record=$record"
-		
-		writer.writeCharacters("  ")
-		writer.writeEmptyElement("pb") // <pb/> to get one page per input line (don't forget high number of words per page in import module)
-		writer.writeAttribute("n", ""+pb_n++)
-		writer.writeCharacters("\n") // simple XML formating
-		
-		writer.writeCharacters("  ")
-		if (textTag != null && textTag.length() > 0) {
-			writer.writeStartElement(textTag)
-			
-			//metadataColumnIndex.each { colIndex -> // build an attribute for each metadata
-			for (def metadataName : metadataColumnList) {
-				String s = record.get(metadataName);
-				if (s == null) s = "";
-				
-				value = s.replaceAll("\n", ";").trim()
-				writer.writeAttribute(stringToIndent(metadataName), value)
-				
-				if (metadataName in dateColumnList) { // also split date attributes in day+month+year attributes
-					matches = (value =~ /([0-9]{2})\/([0-9]{2})\/([0-9]{4})/)
-					writer.writeAttribute(metadataName+"-jour", matches[0][1])
-					writer.writeAttribute(metadataName+"-joursemaine", new java.text.SimpleDateFormat('EEEE').format(Date.parse("dd/MM/yyyy", value)))
-					writer.writeAttribute(metadataName+"-mois", matches[0][2])
-					writer.writeAttribute(metadataName+"-annee", matches[0][3])
-				}
-			}
-			writer.writeCharacters("\n")
-		}
-		
-		writer.writeCharacters("    ")
-		writer.writeStartElement("metadata")
-		writer.writeStartElement("list")
-		writer.writeAttribute("type", "unordered")
-		writer.writeCharacters("\n")
-		
-		//metadataColumnIndex.each { colIndex ->
-		for (def metadataName : metadataColumnList) {
-			writer.writeStartElement("item")
-			writer.writeCharacters(metadataName+" : "+record.get(metadataName).replaceAll("\n", ";"))
-			writer.writeEndElement() // item
-			writer.writeCharacters("\n")
-		}
-		writer.writeCharacters("    ")
-		writer.writeEndElement() // list
-		writer.writeEndElement() // head
-		writer.writeCharacters("\n")
-		
-		//textColumnIndex.each { colIndex ->
-		for (def textColumnName : textColumnList) {
-			
-			writer.writeCharacters("    ")
-			writer.writeStartElement(stringToIndent(textColumnName))
-			writer.writeStartElement("p")
-			writer.writeStartElement("head")
-			writer.writeStartElement("hi")
-			writer.writeCharacters(textColumnName+" : ")
-			writer.writeEndElement() // hi
-			writer.writeEndElement() // head
-			
-			value = record.get(textColumnName)
-			
-			if (value ==~ /(?s)^[A-Z]{3}:  [^;\n]+? +[;\n].*/) {
-				value.findAll( /(?s)[A-Z]{3}:  ([^;\n]+?) +[;\n]/ ).each { desc ->
-					writer.writeStartElement("descripteur")
-					matches = (desc =~ /(?s)([A-Z]{3}):  ([^;\n]+?) +[;\n]/)
-					writer.writeAttribute("type", matches[0][1])
-					writer.writeCharacters(matches[0][2])
-					writer.writeEndElement() // descripteur
-				}
-			} else {
-				writer.writeCharacters(value)
-			}
-			writer.writeEndElement() // p
-			writer.writeEndElement() // textColumn
-			writer.writeCharacters("\n")
-		}
-		
-		writer.writeCharacters("  ")
-		
-		if (textTag != null && textTag.length() > 0) {
-			writer.writeEndElement() // textTag
-			writer.writeCharacters("\n")
-		}
-	}
-	
-	if (rootTag != null && rootTag.length() > 0) {
-		writer.writeEndElement() // rootTag
-		writer.writeCharacters("\n")
-	}
-	
-	if (EmbedInTEI) {
-		writer.writeEndElement() // text
-		writer.writeCharacters("\n")
-		writer.writeEndElement() // TEI
-		writer.writeCharacters("\n")
-	}
-	
-	writer.close()
-	output.close()
-	println "Result file: $outputFile"
-	
-} catch (Exception e) {
-	println "** Excel2XML: unable to read input file. Aborting."
-	println e.getLocalizedMessage()
-	println e.printStackTrace()
-	return false
-}
-
-return true
Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2TextCorpusMacro.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2TextCorpusMacro.groovy	(revision 3209)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2TextCorpusMacro.groovy	(revision 3210)
@@ -1,396 +0,0 @@
-package org.txm.macro.csv
-
-import org.kohsuke.args4j.*
-import groovy.transform.Field
-import java.nio.charset.Charset
-import org.txm.rcp.swt.widget.parameters.*
-import org.txm.utils.*
-import javax.xml.stream.*
-import java.net.URL
-import org.txm.libs.msoffice.ReadExcel
-
-@Field @Option(name="inputFile", usage="CSV File", widget="File", required=false, def="file.xlsx")
-		File inputFile;
-
-@Field @Option(name="outputDirectory", usage="output directory", widget="Folder", required=false, def="directory")
-		File outputDirectory;
-
-@Field @Option(name="textsSeparator", usage="Texts", widget="Separator", required=false, def="Texts")
-		def textsSeparator
-
-@Field @Option(name="textIDColumn", usage="<text> id column", widget="String", required=false, def="Identifiant de la notice")
-		def textIDColumn;
-
-@Field @Option(name="textSelector", usage="<text> lines selector format is column=value", widget="String", required=false, def="Type de notice=Notice sommaire")
-		def textSelector;
-
-@Field @Option(name="textMetadataColumnList", usage="<text> metadata columns", widget="String", required=false, def="Identifiant de la notice,Date de diffusion,Durée,Genre,Identifiant Matériels (info.)")
-		def textMetadataColumnList;
-
-@Field @Option(name="textContentColumnList", usage="<text> textual content columns", widget="String", required=false, def="Titre propre,Résumé,Descripteurs (Aff. Lig.),Descripteurs (Aff. Col.),Séquences")
-		def textContentColumnList;
-
-@Field @Option(name="structuresSeparator", usage="Structures", widget="Separator", required=false, def="Structures")
-		def structuresSeparator
-
-@Field @Option(name="structureTag", usage="structure element to create", widget="String", required=false, def="div")
-		def structureTag;
-
-@Field @Option(name="structureSelector", usage="column_to_test=regexp", widget="String", required=false, def="Type de notice=Notice sujet")
-		def structureSelector;
-
-@Field @Option(name="textJoinColumn", usage="jointure column, values should point to the textIDColumn values", widget="String", required=false, def="Lien notice principale")
-		def textJoinColumn;
-
-@Field @Option(name="structureMetadataColumnList", usage="structure metadata columns", widget="String", required=false, def="Identifiant de la notice,Date de diffusion,Durée,Genre,Identifiant Matériels (info.)")
-		def structureMetadataColumnList;
-
-@Field @Option(name="structureContentColumnList", usage="structure textual content columns", widget="String", required=false, def="Titre propre,Résumé,Descripteurs (Aff. Lig.),Descripteurs (Aff. Col.),Séquences")
-		def structureContentColumnList;
-
-@Field @Option(name="typesSeparator", usage="Columns types", widget="Separator", required=false, def="Columns types")
-		def typesSeparator
-
-@Field @Option(name="dateColumnTypeList", usage="metadata columns of type=Date", widget="String", required=false, def="")
-		def dateColumnTypeList
-
-@Field @Option(name="prefixesColumnTypeList", usage="metadata columns of type=Prefixes", widget="String", required=false, def="")
-		def prefixesColumnTypeList
-
-@Field @Option(name="listColumnTypeList", usage="metadata columns of type=List semi-colon separated", widget="String", required=false, def="")
-		def listColumnTypeList
-
-		@Field @Option(name="debug", usage="Show devug messages", widget="Boolean", required=false, def="false")
-		def debug
-//@Field @Option(name="structureOrderColumn", usage="structure column coding structure order", widget="String", required=false, def="Titre propre,Résumé,Descripteurs (Aff. Lig.),Descripteurs (Aff. Col.),Séquences")
-//		def structureOrderColumn;
-
-if (!ParametersDialog.open(this)) return;
-
-textMetadataColumnList = textMetadataColumnList.split(",").collect { it.trim() }
-textContentColumnList = textContentColumnList.split(",").collect { it.trim() }
-structureMetadataColumnList = structureMetadataColumnList.split(",").collect { it.trim() }
-structureContentColumnList = structureContentColumnList.split(",").collect { it.trim() }
-dateColumnTypeList = dateColumnTypeList.split(",").collect { it.trim() }
-prefixesColumnTypeList = prefixesColumnTypeList.split(",").collect { it.trim() }
-listColumnTypeList = listColumnTypeList.split(",").collect { it.trim() }
-if (textSelector != null && textSelector.contains("=")) {
-	textSelector = textSelector.trim()
-	textSelector = [textSelector.substring(0, textSelector.indexOf("=")).trim(), textSelector.substring(textSelector.indexOf("=")+1).trim()]
-} else {
-	textSelector = [null, null]
-}
-if (structureSelector != null && structureSelector.contains("=")) {
-	structureSelector = structureSelector.trim()
-	structureSelector = [structureSelector.substring(0, structureSelector.indexOf("=")).trim(), structureSelector.substring(structureSelector.indexOf("=")+1).trim()]
-} else {
-	structureSelector = [null, null]
-}
-
-println "textIDColumn, textJoinColumn=$textIDColumn, $textJoinColumn"
-println "textMetadataColumnList columns: $textMetadataColumnList"
-println "textContentColumnList columns: $textContentColumnList"
-println "structureMetadataColumnList columns: $structureMetadataColumnList"
-println "structureContentColumnList columns: $structureContentColumnList"
-
-println "text selector="+textSelector
-println "structure selector="+structureSelector
-println "structureTag="+structureTag
-
-def reader = null
-try {
-	reader = new TableReader(inputFile);
-	println "Reading $inputFile with TableReader..."
-} catch(Exception e) {
-	reader = new ReadExcel(inputFile, null);
-	println "Reading $inputFile with ReadExcel..."
-}
-
-if (!reader.readHeaders()) {
-	println "** Error: no header"
-	return
-}
-def headers = Arrays.asList(reader.getHeaders())
-println "$inputFile table column names: $headers"
-
-def ok = true
-def hash = ["selection":[textIDColumn, textJoinColumn], "textMetadataColumnList":textMetadataColumnList,
-	"textContentColumnList":textContentColumnList, "structureMetadataColumnList":structureMetadataColumnList,
-	"structureContentColumnList":structureContentColumnList]
-for (def key : hash.keySet()) {
-	for (def m : hash[key]) {
-		if (!headers.contains(m)) {
-			println "** Error: missing $key column: $m"
-			ok = false
-		}
-}
-}
-
-if (!ok) { return; }
-
-// group records by text
-def texts = new LinkedHashMap()
-def nRecord = 0
-def nRecordToWrite = 0
-while (reader.readRecord()) {
-nRecord++
-//println "record="+reader.getRecord().get(textSelector[0])+" "+reader.getRecord().get(structureSelector[0])
-
-String id = reader.get(textIDColumn).trim()
-String join = reader.get(textJoinColumn).trim()
-String textSelectorValue = reader.get(textSelector[0]).trim()
-String structureSelectorValue = reader.get(structureSelector[0]).trim()
-
-if (textIDColumn != null && textJoinColumn != null && textIDColumn.length() > 0 && textJoinColumn.length() > 0) {
-	if (textSelectorValue != null && structureSelectorValue != null) {
-		
-		if (textSelectorValue != null && textSelectorValue.matches(textSelector[1])) {
-			if (!texts.containsKey(id)) texts[id] = []
-			texts[id].add(0, reader.getRecord())
-		} else if (structureSelectorValue != null && structureSelectorValue.matches(structureSelector[1])) {
-			if (!texts.containsKey(join)) texts[join] = []
-			texts[join].add(reader.getRecord())
-			nRecordToWrite++
-		} else {
-			// ignore record
-		}
-	} else { // no text&structure selector set, take the record if 'id' or 'join' are set
-		if (id.length() > 0) {
-			if (!texts.containsKey(id)) texts[id] = []
-			texts[id].add(0, reader.getRecord())
-		} else if (join.length() > 0) {
-			if (!texts.containsKey(join)) texts[join] = []
-			texts[join].add(reader.getRecord())
-			nRecordToWrite++
-		} else {
-			// ignore record
-		}
-	}
-} else { // no id & join parameter set -> regroup all record in one file
-	if (!texts.containsKey("result.xml")) texts["result.xml"] = []
-	texts["result.xml"].add(reader.getRecord())
-	nRecordToWrite++
-}
-}
-
-println "N records: "+nRecord
-println "N records to write: "+nRecordToWrite
-println "N texts to build: "+texts.size()
-if (texts.size()  == 0) {
-println "No text found. Aborting."
-return
-}
-outputDirectory.mkdir()
-
-for (def id : texts.keySet()) {
-def toWrite = texts[id]
-def text = toWrite[0]
-String textSelectorValue = text.get(textSelector[0]).trim()
-if (textSelectorValue == null || (textSelectorValue != null && textSelectorValue.matches(textSelector[1]))) {
-	if (debug) println "Processing text: $id"
-	
-	File outputfile = new File(outputDirectory, id+".xml")
-	XMLOutputFactory factory = XMLOutputFactory.newInstance()
-	FileOutputStream output = new FileOutputStream(outputfile)
-	XMLStreamWriter writer = factory.createXMLStreamWriter(output, "UTF-8")
-	
-	writer.writeStartDocument("UTF-8","1.0")
-	writer.writeCharacters("\n") // simple XML formating
-	writer.writeStartElement("TEI")
-	writer.writeCharacters("\n") // simple XML formating
-	writer.writeStartElement("teiHeader")
-	writer.writeEndElement() // teiHeader
-	writer.writeCharacters("\n") // simple XML formating
-	writer.writeStartElement("text")
-	writer.writeAttribute("id", id)
-	
-	for (String att : textMetadataColumnList) {
-		if (att in dateColumnTypeList) {
-			writeMetadataDate(text, att, writer)
-		} else {
-			writer.writeAttribute(AsciiUtils.buildAttributeId(att), text.get(att)) // struct
-		}
-	}
-	
-	writer.writeCharacters("\n") // simple XML formating
-	
-	writer.writeStartElement("metadata")
-	writer.writeStartElement("list")
-	writer.writeAttribute("type", "unordered")
-	writer.writeCharacters("\n")
-	textMetadataColumnList.each { att ->
-		writer.writeStartElement("item")
-		writer.writeCharacters(att+" : "+text.get(att).replaceAll("\n", ";"))
-		writer.writeEndElement() // item
-		writer.writeCharacters("\n")
-	}
-	writer.writeEndElement() // list
-	writer.writeEndElement() // metadata
-	writer.writeCharacters("\n")
-	
-	for (String att : textContentColumnList) {
-		
-		if (att in prefixesColumnTypeList) {
-			
-			writePrefixTextContent(text, att, writer)
-		} else if (att in listColumnTypeList) {
-			
-			writeListTextContent(text, att, writer)
-		} else {
-			
-			writer.writeStartElement("p");
-			writer.writeAttribute("type", att.trim())
-			writer.writeCharacters("\n") // simple XML formating
-			
-			writer.writeStartElement("head")
-			writer.writeStartElement("hi")
-			writer.writeCharacters(att+" : ")
-			writer.writeEndElement() // hi
-			writer.writeEndElement() // head
-			
-			writer.writeCharacters(text.get(att)) // get textColumnList content
-			writer.writeEndElement() // t
-			writer.writeCharacters("\n") // simple XML formating)
-		}
-	}
-	
-	int pb_n = 1;
-	for (int i = 1 ; i < toWrite.size() ; i++) {
-		def record = toWrite[i]
-		
-		writer.writeEmptyElement("pb") // <pb/>
-		writer.writeAttribute("n", ""+pb_n++)
-		writer.writeAttribute("type", "record")
-		
-		writer.writeStartElement(structureTag)
-		
-		for (String att : structureMetadataColumnList) {
-			if (att in dateColumnTypeList) {
-				writeMetadataDate(record, att, writer)
-			} else {
-				writer.writeAttribute(AsciiUtils.buildAttributeId(att), record.get(att)) // struct
-			}
-		}
-		writer.writeCharacters("\n")
-		
-		writer.writeStartElement("metadata")
-		writer.writeStartElement("list")
-		writer.writeAttribute("type", "unordered")
-		writer.writeCharacters("\n")
-		structureMetadataColumnList.each { att ->
-			writer.writeStartElement("item")
-			writer.writeCharacters(att+" : "+record.get(att).replaceAll("\n", ";"))
-			writer.writeEndElement() // item
-			writer.writeCharacters("\n")
-		}
-		writer.writeEndElement() // list
-		writer.writeEndElement() // metadata
-		writer.writeCharacters("\n")
-		
-		for (String att : structureContentColumnList) {
-			if (att in prefixesColumnTypeList) {
-				writePrefixTextContent(text, att, writer)
-			} else if (att in listColumnTypeList) {
-				writeListTextContent(text, att, writer)
-			} else {
-				writer.writeStartElement("p");
-				writer.writeAttribute("type", att.trim())
-				writer.writeAttribute("id", record.get(textIDColumn))
-				writer.writeCharacters("\n") // simple XML formating
-				
-				writer.writeStartElement("head")
-				writer.writeStartElement("hi")
-				writer.writeCharacters(att+" : ")
-				writer.writeEndElement() // hi
-				writer.writeEndElement() // head
-				
-				writer.writeCharacters(record.get(att)) // get textColumnList content
-				writer.writeEndElement() // t
-				writer.writeCharacters("\n") // simple XML formating
-			}
-		}
-		
-		writer.writeEndElement() // struct
-		writer.writeCharacters("\n") // simple XML formating
-	}
-	
-	writer.writeEndElement() // text
-	writer.writeCharacters("\n") // simple XML formating
-	writer.writeEndElement() // TEI
-	writer.close()
-	output.close()
-	reader.close()
-} else {
-	// error
-	println "ERROR: '$id' text group with  no text line"
-}
-}
-
-def writeListTextContent(def record, def att, def writer) {
-writer.writeCharacters("\n")
-found = false
-def value = record.get(att)
-value.findAll( /(?s)([^\n]+?)[\n]/ ).each { desc ->
-	found = true
-	writer.writeCharacters("\t")
-	writer.writeStartElement("p")
-	writer.writeAttribute("rend", "list")
-	matches = (desc =~ /(?s)([^\n]+?)[\n]/)
-	writer.writeCharacters(matches[0][1])
-	writer.writeEndElement() // p
-	writer.writeCharacters("\n")
-}
-if (!found) {
-	writer.writeCharacters("\t")
-	writer.writeStartElement("p")
-	writer.writeAttribute("rend", "no-list")
-	writer.writeCharacters(value)
-	writer.writeEndElement() // p
-	writer.writeCharacters("\n")
-}
-}
-
-def writePrefixTextContent(def record, def att, def writer) {
-writer.writeCharacters("\n")
-writer.writeStartElement("list")
-writer.writeAttribute("rend", "prefixes")
-writer.writeAttribute("type", "unordered")
-writer.writeCharacters("\n")
-found = false
-def value = record.get(att)
-value.findAll( /(?s)[A-Z]{3}:? *([^;\n]+?) +[;\n]/ ).each { desc ->
-	found = true
-	writer.writeCharacters("\t")
-	writer.writeStartElement("item")
-	matches = (desc =~ /(?s)([A-Z]{3}):? *([^;\n]+?) +[;\n]/)
-	writer.writeAttribute("type", matches[0][1])
-	writer.writeStartElement("span")
-	writer.writeCharacters(matches[0][1]+" ")
-	writer.writeEndElement() // span
-	writer.writeCharacters(matches[0][2])
-	writer.writeEndElement() // item
-	writer.writeCharacters("\n")
-}
-if (!found) {
-	writer.writeCharacters(value)
-}
-writer.writeEndElement() // list
-writer.writeCharacters("\n")
-}
-
-def writeMetadataDate(def record, def att, def writer) {
-String value = record.get(att)
-String att_normalized = AsciiUtils.buildAttributeId(att)
-matches = (value =~ /([0-9]{2})\/([0-9]{2})\/([0-9]{4})/)
-writer.writeAttribute(att_normalized+"jour", matches[0][1])
-writer.writeAttribute(att_normalized+"joursemaine", new java.text.SimpleDateFormat('EEEE').format(Date.parse("dd/MM/yyyy", value)))
-writer.writeAttribute(att_normalized+"mois", matches[0][2])
-writer.writeAttribute(att_normalized+"annee", matches[0][3])
-writer.writeAttribute(att_normalized+"tri", matches[0][3]+"-"+matches[0][2]+"-"+matches[0][1])
-}
-
-/*
- String name = inputFile.getName()
- int idx = name.lastIndexOf(".")
- if (idx > 0) name = name.substring(0, idx)
- */
\ No newline at end of file
Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/TableDir2XMLMacro.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/TableDir2XMLMacro.groovy	(revision 0)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/TableDir2XMLMacro.groovy	(revision 3210)
@@ -0,0 +1,133 @@
+package org.txm.macro.table
+
+import org.kohsuke.args4j.*
+import groovy.transform.Field
+import java.nio.charset.Charset
+import org.txm.rcpapplication.swt.widget.parameters.*
+import org.txm.utils.*
+import javax.xml.stream.*
+import java.net.URL
+
+import org.apache.poi.ss.usermodel.*
+import org.apache.poi.hssf.usermodel.*
+import org.apache.poi.xssf.usermodel.*
+import org.apache.poi.ss.usermodel.WorkbookFactory
+import org.apache.poi.ss.util.*
+
+@Field @Option(name="inputFile", usage="CSV File", widget="File", required=false, def="file.xlsx")
+		File inputFile;
+
+@Field @Option(name="outputDirectory", usage="output directory", widget="Folder", required=false, def="directory")
+		File outputDirectory;
+
+@Field @Option(name="textsSeparator", usage="Texts", widget="Separator", required=false, def="Texts")
+		def textsSeparator
+
+@Field @Option(name="textIDColumn", usage="<text> id column", widget="String", required=false, def="Identifiant de la notice")
+		def textIDColumn;
+
+@Field @Option(name="textSelector", usage="<text> lines selector format is column=value", widget="String", required=false, def="Type de notice=Notice sommaire")
+		def textSelector;
+
+@Field @Option(name="textMetadataColumnList", usage="<text> metadata columns", widget="String", required=false, def="Identifiant de la notice,Date de diffusion,Durée,Genre,Identifiant Matériels (info.)")
+		def textMetadataColumnList;
+
+@Field @Option(name="textContentColumnList", usage="<text> textual content columns", widget="String", required=false, def="Titre propre,Résumé,Descripteurs (Aff. Lig.),Descripteurs (Aff. Col.),Séquences")
+		def textContentColumnList;
+
+@Field @Option(name="structuresSeparator", usage="Structures", widget="Separator", required=false, def="Structures")
+		def structuresSeparator
+
+@Field @Option(name="structureTag", usage="structure element to create", widget="String", required=false, def="div")
+		def structureTag;
+
+@Field @Option(name="structureSelector", usage="column_to_test=regexp", widget="String", required=false, def="Type de notice=Notice sujet")
+		def structureSelector;
+
+@Field @Option(name="textJoinColumn", usage="jointure column, values should point to the textIDColumn values", widget="String", required=false, def="Lien notice principale")
+		def textJoinColumn;
+
+@Field @Option(name="structureMetadataColumnList", usage="structure metadata columns", widget="String", required=false, def="Identifiant de la notice,Date de diffusion,Durée,Genre,Identifiant Matériels (info.)")
+		def structureMetadataColumnList;
+
+@Field @Option(name="structureContentColumnList", usage="structure textual content columns", widget="String", required=false, def="Titre propre,Résumé,Descripteurs (Aff. Lig.),Descripteurs (Aff. Col.),Séquences")
+		def structureContentColumnList;
+
+@Field @Option(name="typesSeparator", usage="Columns types", widget="Separator", required=false, def="Columns types")
+		def typesSeparator
+
+@Field @Option(name="dateColumnTypeList", usage="metadata columns of type=Date", widget="String", required=false, def="")
+		def dateColumnTypeList
+
+@Field @Option(name="prefixesColumnTypeList", usage="metadata columns of type=Prefixes", widget="String", required=false, def="")
+		def prefixesColumnTypeList
+
+@Field @Option(name="listColumnTypeList", usage="metadata columns of type=List semi-colon separated", widget="String", required=false, def="")
+		def listColumnTypeList
+
+@Field @Option(name="debug", usage="Show devug messages", widget="Boolean", required=false, def="false")
+		def debug
+
+if (!ParametersDialog.open(this)) return
+
+if (!inputDirectory.exists()) {
+	println "** TableDir2XML: no '"+inputDirectory.name+"' directory found. Aborting."
+	return false
+}
+
+if (!inputDirectory.canRead()) {
+	println "** TableDir2XML: '"+inputDirectory.name+"' directory not readable. Aborting."
+	return false
+}
+
+def f = []
+inputDirectory.eachFileMatch(~/.*(xlsx|ods|tsv|csv)/) { f << it }
+
+if (f.size() == 0) {
+	println "** TableDir2XML: no .xlsx file found. Aborting."
+	return false
+}
+
+try {
+
+f.sort { it.name }.each { inputFile ->
+
+	res = gse.run(Table2XMLMacro, ["args":[
+
+		"inputFile":inputFile, 
+		"outputDirectory":outputDirectory, 
+		"textsSeparator":textsSeparator, 
+		"textIDColumn":textIDColumn, 
+		"textSelector":textSelector, 
+		"textMetadataColumnList":textMetadataColumnList, 
+		"textContentColumnList":textContentColumnList, 
+		"structuresSeparator":structuresSeparator, 
+		"structureTag":structureTag, 
+		"structureSelector":structureSelector, 
+		"textJoinColumn":textJoinColumn, 
+		"structureMetadataColumnList":structureMetadataColumnList, 
+		"structureContentColumnList":structureContentColumnList, 
+		"typesSeparator":typesSeparator, 
+		"dateColumnTypeList":dateColumnTypeList, 
+		"prefixesColumnTypeList":prefixesColumnTypeList, 
+		"listColumnTypeList":listColumnTypeList, 
+		"debug":debug
+		],
+			
+		"selection":selection,
+		"selections":selections,
+		"corpusViewSelection":corpusViewSelection,
+		"corpusViewSelections":corpusViewSelections,
+		"monitor":monitor]])
+
+	if (!res) println "** problem calling Table2XMLMacro."
+}
+
+} catch (Exception e) {
+	println "** TableDir2XML: unable to read input files. Aborting."
+	println e.getLocalizedMessage()
+	println e.printStackTrace()
+	return false
+}
+
+return true
Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2XMLMacro.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2XMLMacro.groovy	(revision 0)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Table2XMLMacro.groovy	(revision 3210)
@@ -0,0 +1,402 @@
+package org.txm.macro.csv
+
+import org.kohsuke.args4j.*
+import groovy.transform.Field
+import java.nio.charset.Charset
+import org.txm.rcp.swt.widget.parameters.*
+import org.txm.utils.*
+import javax.xml.stream.*
+import java.net.URL
+import org.txm.libs.msoffice.ReadExcel
+
+@Field @Option(name="inputFile", usage="CSV File", widget="File", required=false, def="file.xlsx")
+		File inputFile;
+
+@Field @Option(name="outputDirectory", usage="output directory", widget="Folder", required=false, def="directory")
+		File outputDirectory;
+
+@Field @Option(name="textsSeparator", usage="Texts", widget="Separator", required=false, def="Texts")
+		def textsSeparator
+
+@Field @Option(name="textIDColumn", usage="<text> id column", widget="String", required=false, def="Identifiant de la notice")
+		def textIDColumn;
+
+@Field @Option(name="textSelector", usage="<text> lines selector format is column=value", widget="String", required=false, def="Type de notice=Notice sommaire")
+		def textSelector;
+
+@Field @Option(name="textMetadataColumnList", usage="<text> metadata columns", widget="String", required=false, def="Identifiant de la notice,Date de diffusion,Durée,Genre,Identifiant Matériels (info.)")
+		def textMetadataColumnList;
+
+@Field @Option(name="textContentColumnList", usage="<text> textual content columns", widget="String", required=false, def="Titre propre,Résumé,Descripteurs (Aff. Lig.),Descripteurs (Aff. Col.),Séquences")
+		def textContentColumnList;
+
+@Field @Option(name="structuresSeparator", usage="Structures", widget="Separator", required=false, def="Structures")
+		def structuresSeparator
+
+@Field @Option(name="structureTag", usage="structure element to create", widget="String", required=false, def="div")
+		def structureTag;
+
+@Field @Option(name="structureSelector", usage="column_to_test=regexp", widget="String", required=false, def="Type de notice=Notice sujet")
+		def structureSelector;
+
+@Field @Option(name="textJoinColumn", usage="jointure column, values should point to the textIDColumn values", widget="String", required=false, def="Lien notice principale")
+		def textJoinColumn;
+
+@Field @Option(name="structureMetadataColumnList", usage="structure metadata columns", widget="String", required=false, def="Identifiant de la notice,Date de diffusion,Durée,Genre,Identifiant Matériels (info.)")
+		def structureMetadataColumnList;
+
+@Field @Option(name="structureContentColumnList", usage="structure textual content columns", widget="String", required=false, def="Titre propre,Résumé,Descripteurs (Aff. Lig.),Descripteurs (Aff. Col.),Séquences")
+		def structureContentColumnList;
+
+@Field @Option(name="typesSeparator", usage="Columns types", widget="Separator", required=false, def="Columns types")
+		def typesSeparator
+
+@Field @Option(name="dateColumnTypeList", usage="metadata columns of type=Date", widget="String", required=false, def="")
+		def dateColumnTypeList
+
+@Field @Option(name="prefixesColumnTypeList", usage="metadata columns of type=Prefixes", widget="String", required=false, def="")
+		def prefixesColumnTypeList
+
+@Field @Option(name="listColumnTypeList", usage="metadata columns of type=List semi-colon separated", widget="String", required=false, def="")
+		def listColumnTypeList
+
+@Field @Option(name="debug", usage="Show devug messages", widget="Boolean", required=false, def="false")
+		def debug
+//@Field @Option(name="structureOrderColumn", usage="structure column coding structure order", widget="String", required=false, def="Titre propre,Résumé,Descripteurs (Aff. Lig.),Descripteurs (Aff. Col.),Séquences")
+//		def structureOrderColumn;
+
+if (!ParametersDialog.open(this)) return false;
+
+textMetadataColumnList = textMetadataColumnList.split(",").collect { it.trim() }
+textContentColumnList = textContentColumnList.split(",").collect { it.trim() }
+structureMetadataColumnList = structureMetadataColumnList.split(",").collect { it.trim() }
+structureContentColumnList = structureContentColumnList.split(",").collect { it.trim() }
+dateColumnTypeList = dateColumnTypeList.split(",").collect { it.trim() }
+prefixesColumnTypeList = prefixesColumnTypeList.split(",").collect { it.trim() }
+listColumnTypeList = listColumnTypeList.split(",").collect { it.trim() }
+if (textSelector != null && textSelector.contains("=")) {
+	textSelector = textSelector.trim()
+	textSelector = [textSelector.substring(0, textSelector.indexOf("=")).trim(), textSelector.substring(textSelector.indexOf("=")+1).trim()]
+} else {
+	textSelector = [null, null]
+}
+if (structureSelector != null && structureSelector.contains("=")) {
+	structureSelector = structureSelector.trim()
+	structureSelector = [structureSelector.substring(0, structureSelector.indexOf("=")).trim(), structureSelector.substring(structureSelector.indexOf("=")+1).trim()]
+} else {
+	structureSelector = [null, null]
+}
+
+println "textIDColumn, textJoinColumn=$textIDColumn, $textJoinColumn"
+println "textMetadataColumnList columns: $textMetadataColumnList"
+println "textContentColumnList columns: $textContentColumnList"
+println "structureMetadataColumnList columns: $structureMetadataColumnList"
+println "structureContentColumnList columns: $structureContentColumnList"
+
+println "text selector="+textSelector
+println "structure selector="+structureSelector
+println "structureTag="+structureTag
+
+def reader = null
+try {
+	reader = new TableReader(inputFile);
+	println "Reading $inputFile with TableReader..."
+} catch(Exception e) {
+	reader = new ReadExcel(inputFile, null);
+	println "Reading $inputFile with ReadExcel..."
+}
+
+if (!reader.readHeaders()) {
+	println "** Error: no header"
+	return false
+}
+def headers = Arrays.asList(reader.getHeaders())
+println "$inputFile table column names: $headers"
+
+def ok = true
+def hash = ["selection":[textIDColumn, textJoinColumn], "textMetadataColumnList":textMetadataColumnList,
+	"textContentColumnList":textContentColumnList, "structureMetadataColumnList":structureMetadataColumnList,
+	"structureContentColumnList":structureContentColumnList]
+for (def key : hash.keySet()) {
+	for (def m : hash[key]) {
+		if (!headers.contains(m)) {
+			println "** Error: missing $key column: $m"
+			ok = false
+		}
+}
+}
+
+if (!ok) { return false; }
+
+inputName = inputFile.getName()
+idx = inputName.lastIndexOf(".")
+if (idx > 0) name = inputName.substring(0, idx)
+
+// group records by text
+def texts = new LinkedHashMap()
+def nRecord = 0
+def nRecordToWrite = 0
+while (reader.readRecord()) {
+	nRecord++
+	//	println "record="+reader.getRecord().get(textSelector[0])+" "+reader.getRecord().get(structureSelector[0])
+
+	String id = reader.get(textIDColumn).trim()
+	String join = reader.get(textJoinColumn).trim()
+	String textSelectorValue = reader.get(textSelector[0]).trim()
+	String structureSelectorValue = reader.get(structureSelector[0]).trim()
+	
+	if (textIDColumn != null && textJoinColumn != null && textIDColumn.length() > 0 && textJoinColumn.length() > 0) {
+		if (textSelectorValue != null && structureSelectorValue != null) {
+			
+			if (textSelectorValue != null && textSelectorValue.matches(textSelector[1])) {
+				if (!texts.containsKey(id)) texts[id] = []
+				texts[id].add(0, reader.getRecord())
+			} else if (structureSelectorValue != null && structureSelectorValue.matches(structureSelector[1])) {
+				if (!texts.containsKey(join)) texts[join] = []
+				texts[join].add(reader.getRecord())
+				nRecordToWrite++
+			} else {
+				// 	ignore record
+			}
+		} else { // no text&structure selector set, take the record if 'id' or 'join' are set
+			if (id.length() > 0) {
+				if (!texts.containsKey(id)) texts[id] = []
+				texts[id].add(0, reader.getRecord())
+			} else if (join.length() > 0) {
+				if (!texts.containsKey(join)) texts[join] = []
+				texts[join].add(reader.getRecord())
+				nRecordToWrite++
+			} else {
+				// 	ignore record
+			}
+		}
+	} else { // no id & join parameter set -> regroup all record in one file
+		
+		if (!texts.containsKey("$inputName.xml")) texts["$inputName.xml"] = []
+		texts["$inputName.xml"].add(reader.getRecord())
+		nRecordToWrite++
+	}
+}
+
+println "N records: "+nRecord
+println "N records to write: "+nRecordToWrite
+println "N texts to build: "+texts.size()
+if (texts.size()  == 0) {
+	println "No text found. Aborting."
+	return false
+}
+outputDirectory.mkdir()
+
+for (def id : texts.keySet()) {
+def toWrite = texts[id]
+def text = toWrite[0]
+String textSelectorValue = text.get(textSelector[0]).trim()
+if (textSelectorValue == null || (textSelectorValue != null && textSelectorValue.matches(textSelector[1]))) {
+	if (debug) println "Processing text: $id"
+	
+	File outputfile = new File(outputDirectory, id+".xml")
+	
+	XMLOutputFactory factory = XMLOutputFactory.newInstance()
+	FileOutputStream output = new FileOutputStream(outputfile)
+	XMLStreamWriter writer = factory.createXMLStreamWriter(output, "UTF-8")
+	
+	writer.writeStartDocument("UTF-8","1.0")
+	writer.writeCharacters("\n") // simple XML formating
+	writer.writeStartElement("TEI")
+	writer.writeCharacters("\n") // simple XML formating
+	writer.writeStartElement("teiHeader")
+	writer.writeEndElement() // teiHeader
+	writer.writeCharacters("\n") // simple XML formating
+	writer.writeStartElement("text")
+	writer.writeAttribute("id", id)
+	
+	for (String att : textMetadataColumnList) {
+		if (att in dateColumnTypeList) {
+			writeMetadataDate(text, att, writer)
+		} else {
+			writer.writeAttribute(AsciiUtils.buildAttributeId(att), text.get(att)) // struct
+		}
+	}
+	
+	writer.writeCharacters("\n") // simple XML formating
+	
+	writer.writeStartElement("metadata")
+	writer.writeStartElement("list")
+	writer.writeAttribute("type", "unordered")
+	writer.writeCharacters("\n")
+	textMetadataColumnList.each { att ->
+		writer.writeStartElement("item")
+		writer.writeCharacters(att+" : "+text.get(att).replaceAll("\n", ";"))
+		writer.writeEndElement() // item
+		writer.writeCharacters("\n")
+	}
+	writer.writeEndElement() // list
+	writer.writeEndElement() // metadata
+	writer.writeCharacters("\n")
+	
+	for (String att : textContentColumnList) {
+		
+		if (att in prefixesColumnTypeList) {
+			
+			writePrefixTextContent(text, att, writer)
+		} else if (att in listColumnTypeList) {
+			
+			writeListTextContent(text, att, writer)
+		} else {
+			
+			writer.writeStartElement("p");
+			writer.writeAttribute("type", att.trim())
+			writer.writeCharacters("\n") // simple XML formating
+			
+			writer.writeStartElement("head")
+			writer.writeStartElement("hi")
+			writer.writeCharacters(att+" : ")
+			writer.writeEndElement() // hi
+			writer.writeEndElement() // head
+			
+			writer.writeCharacters(text.get(att)) // get textColumnList content
+			writer.writeEndElement() // t
+			writer.writeCharacters("\n") // simple XML formating)
+		}
+	}
+	
+	int pb_n = 1;
+	for (int i = 1 ; i < toWrite.size() ; i++) {
+		def record = toWrite[i]
+		
+		writer.writeEmptyElement("pb") // <pb/>
+		writer.writeAttribute("n", ""+pb_n++)
+		writer.writeAttribute("type", "record")
+		
+		writer.writeStartElement(structureTag)
+		
+		for (String att : structureMetadataColumnList) {
+			if (att in dateColumnTypeList) {
+				writeMetadataDate(record, att, writer)
+			} else {
+				writer.writeAttribute(AsciiUtils.buildAttributeId(att), record.get(att)) // struct
+			}
+		}
+		writer.writeCharacters("\n")
+		
+		writer.writeStartElement("metadata")
+		writer.writeStartElement("list")
+		writer.writeAttribute("type", "unordered")
+		writer.writeCharacters("\n")
+		structureMetadataColumnList.each { att ->
+			writer.writeStartElement("item")
+			writer.writeCharacters(att+" : "+record.get(att).replaceAll("\n", ";"))
+			writer.writeEndElement() // item
+			writer.writeCharacters("\n")
+		}
+		writer.writeEndElement() // list
+		writer.writeEndElement() // metadata
+		writer.writeCharacters("\n")
+		
+		for (String att : structureContentColumnList) {
+			if (att in prefixesColumnTypeList) {
+				writePrefixTextContent(text, att, writer)
+			} else if (att in listColumnTypeList) {
+				writeListTextContent(text, att, writer)
+			} else {
+				writer.writeStartElement("p");
+				writer.writeAttribute("type", att.trim())
+				writer.writeAttribute("id", record.get(textIDColumn))
+				writer.writeCharacters("\n") // simple XML formating
+				
+				writer.writeStartElement("head")
+				writer.writeStartElement("hi")
+				writer.writeCharacters(att+" : ")
+				writer.writeEndElement() // hi
+				writer.writeEndElement() // head
+				
+				writer.writeCharacters(record.get(att)) // get textColumnList content
+				writer.writeEndElement() // t
+				writer.writeCharacters("\n") // simple XML formating
+			}
+		}
+		
+		writer.writeEndElement() // struct
+		writer.writeCharacters("\n") // simple XML formating
+	}
+	
+	writer.writeEndElement() // text
+	writer.writeCharacters("\n") // simple XML formating
+	writer.writeEndElement() // TEI
+	writer.close()
+	output.close()
+	reader.close()
+} else {
+	// error
+	println "ERROR: '$id' text group with  no text line"
+}
+}
+
+def writeListTextContent(def record, def att, def writer) {
+writer.writeCharacters("\n")
+found = false
+def value = record.get(att)
+value.findAll( /(?s)([^\n]+?)[\n]/ ).each { desc ->
+	found = true
+	writer.writeCharacters("\t")
+	writer.writeStartElement("p")
+	writer.writeAttribute("rend", "list")
+	matches = (desc =~ /(?s)([^\n]+?)[\n]/)
+	writer.writeCharacters(matches[0][1])
+	writer.writeEndElement() // p
+	writer.writeCharacters("\n")
+}
+if (!found) {
+	writer.writeCharacters("\t")
+	writer.writeStartElement("p")
+	writer.writeAttribute("rend", "no-list")
+	writer.writeCharacters(value)
+	writer.writeEndElement() // p
+	writer.writeCharacters("\n")
+}
+}
+
+def writePrefixTextContent(def record, def att, def writer) {
+writer.writeCharacters("\n")
+writer.writeStartElement("list")
+writer.writeAttribute("rend", "prefixes")
+writer.writeAttribute("type", "unordered")
+writer.writeCharacters("\n")
+found = false
+def value = record.get(att)
+value.findAll( /(?s)[A-Z]{3}:? *([^;\n]+?) +[;\n]/ ).each { desc ->
+	found = true
+	writer.writeCharacters("\t")
+	writer.writeStartElement("item")
+	matches = (desc =~ /(?s)([A-Z]{3}):? *([^;\n]+?) +[;\n]/)
+	writer.writeAttribute("type", matches[0][1])
+	writer.writeStartElement("span")
+	writer.writeCharacters(matches[0][1]+" ")
+	writer.writeEndElement() // span
+	writer.writeCharacters(matches[0][2])
+	writer.writeEndElement() // item
+	writer.writeCharacters("\n")
+}
+if (!found) {
+	writer.writeCharacters(value)
+}
+writer.writeEndElement() // list
+writer.writeCharacters("\n")
+}
+
+def writeMetadataDate(def record, def att, def writer) {
+String value = record.get(att)
+String att_normalized = AsciiUtils.buildAttributeId(att)
+matches = (value =~ /([0-9]{2})\/([0-9]{2})\/([0-9]{4})/)
+writer.writeAttribute(att_normalized+"-jour", matches[0][1])
+writer.writeAttribute(att_normalized+"-joursemaine", new java.text.SimpleDateFormat('EEEE').format(Date.parse("dd/MM/yyyy", value)))
+writer.writeAttribute(att_normalized+"-mois", matches[0][2])
+writer.writeAttribute(att_normalized+"-annee", matches[0][3])
+writer.writeAttribute(att_normalized+"-tri", matches[0][3]+"-"+matches[0][2]+"-"+matches[0][1])
+}
+
+/*
+ String name = inputFile.getName()
+ int idx = name.lastIndexOf(".")
+ if (idx > 0) name = name.substring(0, idx)
+ */
\ No newline at end of file
Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLWithoutAPIMacro.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLWithoutAPIMacro.groovy	(revision 0)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLWithoutAPIMacro.groovy	(revision 3210)
@@ -0,0 +1,282 @@
+package org.txm.macro.table
+
+import org.kohsuke.args4j.*
+import groovy.transform.Field
+import java.nio.charset.Charset
+import org.txm.rcpapplication.swt.widget.parameters.*
+import org.txm.utils.*
+import javax.xml.stream.*
+import java.net.URL
+import java.text.SimpleDateFormat
+
+import org.apache.poi.ss.usermodel.*
+import org.apache.poi.hssf.usermodel.*
+import org.apache.poi.xssf.usermodel.*
+import org.apache.poi.ss.usermodel.WorkbookFactory
+import org.apache.poi.ss.util.*
+
+def stringToIndent = { str -> org.txm.utils.AsciiUtils.buildAttributeId(org.txm.utils.AsciiUtils.convertNonAscii(str)).toLowerCase() }
+
+// from http://www.java-connect.com/apache-poi-tutorials/read-all-type-of-excel-cell-value-as-string-using-poi
+def getCellValueAsString = { cell ->
+	strCellValue = null
+	if (cell != null) {
+		switch (cell.getCellType()) {
+			case CellType.STRING:
+				strCellValue = cell.toString()
+				break
+			case CellType.NUMERIC:
+				if (DateUtil.isCellDateFormatted(cell)) {
+					SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy")
+					strCellValue = dateFormat.format(cell.getDateCellValue())
+				} else {
+					value = cell.getNumericCellValue()
+					longValue = value.longValue()
+					strCellValue = new String(longValue.toString())
+				}
+				break
+			case CellType.BOOLEAN:
+				strCellValue = new String(new Boolean(cell.getBooleanCellValue()).toString())
+				break
+			case CellType.BLANK:
+				strCellValue = ""
+				break
+		}
+	}
+	if (strCellValue == null) strCellValue = ""
+	return strCellValue
+}
+
+@Field @Option(name="inputFile", usage="fichier Excel à traiter", widget="File", required=true, def="")
+		File inputFile
+
+@Field @Option(name="sheetName", usage="sheet name (if no name is given the first sheet will be used)", widget="String", required=false, def="")
+		def sheetName
+
+@Field @Option(name="rootTag", usage="root tag name", widget="String", required=false, def="root")
+		def rootTag
+
+@Field @Option(name="textTag", usage="line unit tag name", widget="String", required=false, def="unit")
+		def textTag
+
+@Field @Option(name="metadataColumnList", usage="metadata columns list separated by comma", widget="String", required=false, def="meta1,meta2")
+		def metadataColumnList
+
+@Field @Option(name="dateColumnList", usage="date columns list separated by comma", widget="String", required=false, def="meta1,meta2")
+		def dateColumnList
+
+@Field @Option(name="textColumnList", usage="text columns list separated by comma", widget="String", required=false, def="textColumnList1,textColumnList2")
+		def textColumnList
+
+@Field @Option(name="EmbedInTEI", usage="text columns list separated by comma", widget="Boolean", required=false, def="false")
+		def EmbedInTEI
+
+if (!ParametersDialog.open(this)) return
+
+	if (!inputFile.exists()) {
+		println "** Excel2XML: no '"+inputFile.name+"' file found. Aborting."
+		return false
+	}
+
+if (!inputFile.canRead()) {
+	println "** Excel2XML: '"+inputFile.name+"' file not readable. Aborting."
+	return false
+}
+
+try {
+	
+	metadataColumnList = metadataColumnList.split(",").collect { it.trim() }
+	dateColumnList = dateColumnList.split(",").collect { it.trim() }
+	textColumnList = textColumnList.split(",").collect { it.trim() }
+	textTag = textTag.trim()
+	rootTag = rootTag.trim()
+	
+	wb = WorkbookFactory.create(inputFile)
+	
+	if (sheetName.length() == 0) {
+		ws = wb.getSheetAt(0)
+	} else {
+		ws = wb.getSheet(sheetName)
+		if (ws == null) {
+			println "** Excel2XML: no '"+sheetName+" found. Aborting."
+			return false
+		}
+	}
+	
+	if (ws == null) {
+		println "** Excel2XML: no sheet found. Aborting."
+		return false
+	}
+	
+	nRows = ws.getPhysicalNumberOfRows()
+	println nRows+" rows."
+	
+	firstRow = ws.getRow(0)
+	colMax = firstRow.getLastCellNum()
+	
+	headers = (0..colMax-1).collect { getCellValueAsString(firstRow.getCell(it)) }
+	
+	println "Headers: $headers"
+	
+	normalizedHeaders = headers.collect { stringToIndent(it) }
+	
+	ok = true
+	metadataColumnList.each { m ->
+		if (!headers.contains(m)) {
+			println "** Excel2XML: missing metadataColumnList column: $m"
+			ok = false
+		}
+	}
+	textColumnList.each { t ->
+		if (!headers.contains(t)) {
+			println "** Excel2XML: missing textColumnList column: $t"
+			ok = false
+		}
+	}
+	
+	if (!ok) { return false }
+	
+	metadataColumnIndex = metadataColumnList.collect { headers.indexOf(it) }
+	dateColumnsIndex = dateColumnList.collect { headers.indexOf(it) }
+	textColumnIndex = textColumnList.collect { headers.indexOf(it) }
+	
+	println "metadataColumnList = "+metadataColumnList
+	println "metadataColumnIndex = "+metadataColumnIndex
+	
+	println "dateColumnList = "+dateColumnList
+	println "dateColumnsIndex = "+dateColumnsIndex
+	
+	println "textColumnList = "+textColumnList
+	println "textColumnIndex = "+textColumnIndex
+	
+	name = inputFile.getName()
+	idx = name.lastIndexOf(".")
+	
+	if (idx > 0) name = name.substring(0, idx)
+	outputFile = new File(inputFile.getParentFile(), name+".xml")
+	
+	factory = XMLOutputFactory.newInstance()
+	output = new FileOutputStream(outputFile)
+	writer = factory.createXMLStreamWriter(output, "UTF-8")
+	
+	writer.writeStartDocument("UTF-8","1.0")
+	writer.writeCharacters("\n") // simple XML formating
+	
+	if (EmbedInTEI) {
+		writer.writeStartElement("TEI")
+		writer.writeStartElement("teiHeader")
+		writer.writeEndElement() // teiHeader
+		writer.writeStartElement("text")
+		writer.writeCharacters("\n")
+	}
+	
+	writer.writeStartElement(rootTag)
+	writer.writeCharacters("\n")
+	
+	pb_n = 1
+	
+	(1..nRows-1).each { rowIndex ->
+		
+		writer.writeCharacters("  ")
+		writer.writeEmptyElement("pb") // <pb/> to get one page per input line (don't forget high number of words per page in import module)
+		writer.writeAttribute("n", ""+pb_n++)
+		writer.writeCharacters("\n") // simple XML formating
+		
+		writer.writeCharacters("  ")
+		writer.writeStartElement(textTag)
+		metadataColumnIndex.each { colIndex -> // build an attribute for each metadata
+			def row = ws.getRow(rowIndex)
+			if (row != null) {
+				String s = getCellValueAsString(row.getCell(colIndex));
+				if (s == null) s ="";
+				value = s.replaceAll("\n", ";").trim()
+				writer.writeAttribute(normalizedHeaders[colIndex], value)
+				if (colIndex in dateColumnsIndex) { // also split date attributes in day+month+year attributes
+					matches = (value =~ /([0-9]{2})\/([0-9]{2})\/([0-9]{4})/)
+					writer.writeAttribute(normalizedHeaders[colIndex]+"jour", matches[0][1])
+					writer.writeAttribute(normalizedHeaders[colIndex]+"joursemaine", new java.text.SimpleDateFormat('EEEE').format(Date.parse("dd/MM/yyyy", value)))
+					writer.writeAttribute(normalizedHeaders[colIndex]+"mois", matches[0][2])
+					writer.writeAttribute(normalizedHeaders[colIndex]+"annee", matches[0][3])
+				}
+			}
+		}
+		writer.writeCharacters("\n")
+		
+		writer.writeCharacters("    ")
+		writer.writeStartElement("metadata")
+		writer.writeStartElement("list")
+		writer.writeAttribute("type", "unordered")
+		writer.writeCharacters("\n")
+		
+		metadataColumnIndex.each { colIndex ->
+			
+			def row = ws.getRow(rowIndex)
+			if (row != null) {
+				writer.writeStartElement("item")
+				writer.writeCharacters(headers[colIndex]+" : "+getCellValueAsString(row.getCell(colIndex)).replaceAll("\n", ";"))
+				writer.writeEndElement() // item
+				writer.writeCharacters("\n")
+			}
+		}
+		writer.writeCharacters("    ")
+		writer.writeEndElement() // list
+		writer.writeEndElement() // head
+		writer.writeCharacters("\n")
+		
+		textColumnIndex.each { colIndex ->
+			
+			writer.writeCharacters("    ")
+			writer.writeStartElement(normalizedHeaders[colIndex])
+			writer.writeStartElement("p")
+			writer.writeStartElement("head")
+			writer.writeStartElement("hi")
+			writer.writeCharacters(headers[colIndex]+" : ")
+			writer.writeEndElement() // hi
+			writer.writeEndElement() // head
+			def row = ws.getRow(rowIndex)
+			if (row != null) {
+				value = getCellValueAsString(row.getCell(colIndex))
+				
+				if (value ==~ /(?s)^[A-Z]{3}:  [^;\n]+? +[;\n].*/) {
+					value.findAll( /(?s)[A-Z]{3}:  ([^;\n]+?) +[;\n]/ ).each { desc ->
+						writer.writeStartElement("descripteur")
+						matches = (desc =~ /(?s)([A-Z]{3}):  ([^;\n]+?) +[;\n]/)
+						writer.writeAttribute("type", matches[0][1])
+						writer.writeCharacters(matches[0][2])
+						writer.writeEndElement() // descripteur
+					}
+				} else {
+					writer.writeCharacters(value)
+				}
+				writer.writeEndElement() // p
+				writer.writeEndElement() // textColumn
+				writer.writeCharacters("\n")
+			}
+		}
+		
+		writer.writeCharacters("  ")
+		writer.writeEndElement() // textTag
+		writer.writeCharacters("\n")
+	}
+	
+	writer.writeEndElement() // rootTag
+	writer.writeCharacters("\n")
+	if (EmbedInTEI) {
+		writer.writeEndElement() // text
+		writer.writeCharacters("\n")
+		writer.writeEndElement() // TEI
+		writer.writeCharacters("\n")
+	}
+	
+	writer.close()
+	output.close()
+	println "Result file: $outputFile"
+	
+} catch (Exception e) {
+	println "** Excel2XML: unable to read input file. Aborting."
+	println e.getLocalizedMessage()
+	println e.printStackTrace()
+	return false
+}
+
+return true
Index: tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLMacro.groovy
===================================================================
--- tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLMacro.groovy	(revision 0)
+++ tmp/org.txm.groovy.core/src/groovy/org/txm/macro/table/Excel2XMLMacro.groovy	(revision 3210)
@@ -0,0 +1,245 @@
+package org.txm.macro.table
+
+import org.kohsuke.args4j.*
+import groovy.transform.Field
+import java.nio.charset.Charset
+
+import org.txm.libs.msoffice.ReadExcel
+import org.txm.rcpapplication.swt.widget.parameters.*
+import org.txm.utils.*
+import javax.xml.stream.*
+import java.net.URL
+import java.text.SimpleDateFormat
+
+import org.apache.poi.ss.usermodel.*
+import org.apache.poi.hssf.usermodel.*
+import org.apache.poi.xssf.usermodel.*
+import org.apache.poi.ss.usermodel.WorkbookFactory
+import org.apache.poi.ss.util.*
+
+def stringToIndent = { str -> org.txm.utils.AsciiUtils.buildAttributeId(org.txm.utils.AsciiUtils.convertNonAscii(str)).toLowerCase() }
+
+@Field @Option(name="inputFile", usage="fichier Excel à traiter", widget="File", required=true, def="")
+		File inputFile
+
+@Field @Option(name="sheetName", usage="sheet name (if no name is given the first sheet will be used)", widget="String", required=false, def="")
+		def sheetName
+
+@Field @Option(name="EmbedInTEI", usage="embed xml content in  TEI text element", widget="Boolean", required=false, def="false")
+		def EmbedInTEI
+		
+@Field @Option(name="rootTag", usage="root tag name", widget="String", required=false, def="root")
+		def rootTag
+
+@Field @Option(name="textTag", usage="line unit tag name", widget="String", required=false, def="unit")
+		def textTag
+
+@Field @Option(name="metadataColumnList", usage="metadata columns list separated by comma", widget="String", required=false, def="meta1,meta2")
+		def metadataColumnList
+
+@Field @Option(name="dateColumnList", usage="date columns list separated by comma", widget="String", required=false, def="meta1,meta2")
+		def dateColumnList
+
+@Field @Option(name="textColumnList", usage="text columns list separated by comma", widget="String", required=false, def="textColumnList1,textColumnList2")
+		def textColumnList
+		
+@Field @Option(name="prefixContentColumnList", usage="columns which content is a semi-colon separated prefixes list", widget="String", required=false, def="")
+		def prefixContentColumnList
+		
+@Field @Option(name="listContentColumnList", usage="columns which content is a semi-colon separated list", widget="String", required=false, def="")
+		def listContentColumnList
+
+
+if (!ParametersDialog.open(this)) return
+
+	if (!inputFile.exists()) {
+		println "** Excel2XML: no '"+inputFile.name+"' file found. Aborting."
+		return false
+	}
+
+if (!inputFile.canRead()) {
+	println "** Excel2XML: '"+inputFile.name+"' file not readable. Aborting."
+	return false
+}
+
+try {
+	metadataColumnList = metadataColumnList.split(",").collect { it.trim() }
+	dateColumnList = dateColumnList.split(",").collect { it.trim() }
+	textColumnList = textColumnList.split(",").collect { it.trim() }
+	prefixContentColumnList = prefixContentColumnList.split(",").collect { it.trim() }
+	listContentColumnList = listContentColumnList.split(",").collect { it.trim() }
+	textTag = textTag.trim()
+	rootTag = rootTag.trim()
+	
+	ReadExcel excel = new ReadExcel(inputFile, sheetName);
+	excel.readHeaders();
+	
+	headers = excel.getHeaders()
+	
+	println "Headers: $headers"
+	
+	ok = true
+	metadataColumnList.each { m ->
+		if (!headers.contains(m)) {
+			println "** Excel2XML: missing metadataColumnList column: $m"
+			ok = false
+		}
+	}
+	textColumnList.each { t ->
+		if (!headers.contains(t)) {
+			println "** Excel2XML: missing textColumnList column: $t"
+			ok = false
+		}
+	}
+	
+	if (!ok) { return false }
+	
+	println "metadataColumnList = "+metadataColumnList
+	println "dateColumnList = "+dateColumnList
+	println "textColumnList = "+textColumnList
+	
+	name = inputFile.getName()
+	idx = name.lastIndexOf(".")
+	
+	if (idx > 0) name = name.substring(0, idx)
+	outputFile = new File(inputFile.getParentFile(), name+".xml")
+	
+	factory = XMLOutputFactory.newInstance()
+	output = new FileOutputStream(outputFile)
+	writer = factory.createXMLStreamWriter(output, "UTF-8")
+	
+	writer.writeStartDocument("UTF-8","1.0")
+	writer.writeCharacters("\n") // simple XML formating
+	
+	if (EmbedInTEI) {
+		writer.writeStartElement("TEI")
+		writer.writeStartElement("teiHeader")
+		writer.writeEndElement() // teiHeader
+		writer.writeStartElement("text")
+		writer.writeCharacters("\n")
+	}
+	
+	if (rootTag != null && rootTag.length() > 0) {
+		writer.writeStartElement(rootTag)
+		writer.writeCharacters("\n")
+	}
+	
+	pb_n = 1
+	
+	//(1..nRows-1).each { rowIndex ->
+	def record = null
+	while (excel.readRecord()) {
+		
+		record = excel.getRecord()
+		if (record.isEmpty()) continue;
+		
+		//println "record=$record"
+		
+		writer.writeCharacters("  ")
+		writer.writeEmptyElement("pb") // <pb/> to get one page per input line (don't forget high number of words per page in import module)
+		writer.writeAttribute("n", ""+pb_n++)
+		writer.writeCharacters("\n") // simple XML formating
+		
+		writer.writeCharacters("  ")
+		if (textTag != null && textTag.length() > 0) {
+			writer.writeStartElement(textTag)
+			
+			//metadataColumnIndex.each { colIndex -> // build an attribute for each metadata
+			for (def metadataName : metadataColumnList) {
+				String s = record.get(metadataName);
+				if (s == null) s = "";
+				
+				value = s.replaceAll("\n", ";").trim()
+				writer.writeAttribute(stringToIndent(metadataName), value)
+				
+				if (metadataName in dateColumnList) { // also split date attributes in day+month+year attributes
+					matches = (value =~ /([0-9]{2})\/([0-9]{2})\/([0-9]{4})/)
+					writer.writeAttribute(metadataName+"-jour", matches[0][1])
+					writer.writeAttribute(metadataName+"-joursemaine", new java.text.SimpleDateFormat('EEEE').format(Date.parse("dd/MM/yyyy", value)))
+					writer.writeAttribute(metadataName+"-mois", matches[0][2])
+					writer.writeAttribute(metadataName+"-annee", matches[0][3])
+				}
+			}
+			writer.writeCharacters("\n")
+		}
+		
+		writer.writeCharacters("    ")
+		writer.writeStartElement("metadata")
+		writer.writeStartElement("list")
+		writer.writeAttribute("type", "unordered")
+		writer.writeCharacters("\n")
+		
+		//metadataColumnIndex.each { colIndex ->
+		for (def metadataName : metadataColumnList) {
+			writer.writeStartElement("item")
+			writer.writeCharacters(metadataName+" : "+record.get(metadataName).replaceAll("\n", ";"))
+			writer.writeEndElement() // item
+			writer.writeCharacters("\n")
+		}
+		writer.writeCharacters("    ")
+		writer.writeEndElement() // list
+		writer.writeEndElement() // head
+		writer.writeCharacters("\n")
+		
+		//textColumnIndex.each { colIndex ->
+		for (def textColumnName : textColumnList) {
+			
+			writer.writeCharacters("    ")
+			writer.writeStartElement(stringToIndent(textColumnName))
+			writer.writeStartElement("p")
+			writer.writeStartElement("head")
+			writer.writeStartElement("hi")
+			writer.writeCharacters(textColumnName+" : ")
+			writer.writeEndElement() // hi
+			writer.writeEndElement() // head
+			
+			value = record.get(textColumnName)
+			
+			if (value ==~ /(?s)^[A-Z]{3}:  [^;\n]+? +[;\n].*/) {
+				value.findAll( /(?s)[A-Z]{3}:  ([^;\n]+?) +[;\n]/ ).each { desc ->
+					writer.writeStartElement("descripteur")
+					matches = (desc =~ /(?s)([A-Z]{3}):  ([^;\n]+?) +[;\n]/)
+					writer.writeAttribute("type", matches[0][1])
+					writer.writeCharacters(matches[0][2])
+					writer.writeEndElement() // descripteur
+				}
+			} else {
+				writer.writeCharacters(value)
+			}
+			writer.writeEndElement() // p
+			writer.writeEndElement() // textColumn
+			writer.writeCharacters("\n")
+		}
+		
+		writer.writeCharacters("  ")
+		
+		if (textTag != null && textTag.length() > 0) {
+			writer.writeEndElement() // textTag
+			writer.writeCharacters("\n")
+		}
+	}
+	
+	if (rootTag != null && rootTag.length() > 0) {
+		writer.writeEndElement() // rootTag
+		writer.writeCharacters("\n")
+	}
+	
+	if (EmbedInTEI) {
+		writer.writeEndElement() // text
+		writer.writeCharacters("\n")
+		writer.writeEndElement() // TEI
+		writer.writeCharacters("\n")
+	}
+	
+	writer.close()
+	output.close()
+	println "Result file: $outputFile"
+	
+} catch (Exception e) {
+	println "** Excel2XML: unable to read input file. Aborting."
+	println e.getLocalizedMessage()
+	println e.printStackTrace()
+	return false
+}
+
+return true
