You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
5.5 KiB
154 lines
5.5 KiB
package de.superx.db.export; |
|
|
|
import java.io.File; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.util.ArrayList; |
|
import java.util.List; |
|
|
|
import org.jdom2.Document; |
|
import org.jdom2.Element; |
|
import org.jdom2.JDOMException; |
|
import org.jdom2.input.SAXBuilder; |
|
import org.springframework.stereotype.Component; |
|
|
|
/** |
|
* Reads export group configuration from an XML file. |
|
* |
|
* Expected format: |
|
* <pre>{@code |
|
* <export-config> |
|
* <group name="my_group" schema="staging"> |
|
* <table>table_one</table> |
|
* <table>table_two</table> |
|
* </group> |
|
* </export-config> |
|
* }</pre> |
|
*/ |
|
@Component |
|
public class ExportConfigReader { |
|
|
|
public ExportConfig read(File configFile) throws JDOMException, IOException { |
|
SAXBuilder builder = new SAXBuilder(); |
|
Document doc = builder.build(configFile); |
|
return parse(doc); |
|
} |
|
|
|
public ExportConfig read(InputStream in) throws JDOMException, IOException { |
|
SAXBuilder builder = new SAXBuilder(); |
|
Document doc = builder.build(in); |
|
return parse(doc); |
|
} |
|
|
|
private ExportConfig parse(Document doc) { |
|
Element root = doc.getRootElement(); |
|
List<ExportGroup> groups = new ArrayList<>(); |
|
|
|
for (Element groupEl : root.getChildren("group")) { |
|
String name = groupEl.getAttributeValue("name"); |
|
if (name == null || name.isBlank()) { |
|
throw new IllegalArgumentException("Export group must have a 'name' attribute"); |
|
} |
|
|
|
String schema = groupEl.getAttributeValue("schema"); |
|
|
|
// Parse optional filter |
|
String filterColumn = null; |
|
String filterSource = null; |
|
Element filterEl = groupEl.getChild("filter"); |
|
if (filterEl != null) { |
|
filterColumn = filterEl.getAttributeValue("column"); |
|
if (filterColumn == null || filterColumn.isBlank()) { |
|
throw new IllegalArgumentException( |
|
"Filter in group '" + name + "' must have a 'column' attribute"); |
|
} |
|
|
|
String sourceAttr = filterEl.getAttributeValue("source"); |
|
String valuesAttr = filterEl.getAttributeValue("values"); |
|
|
|
if (sourceAttr != null && !sourceAttr.isBlank()) { |
|
filterSource = sourceAttr; |
|
} else if (valuesAttr != null && !valuesAttr.isBlank()) { |
|
filterSource = buildValuesSubquery(valuesAttr); |
|
} else { |
|
throw new IllegalArgumentException( |
|
"Filter in group '" + name |
|
+ "' must have a 'source' or 'values' attribute"); |
|
} |
|
} |
|
|
|
// Parse tables with optional per-table column, source, columns, and orderBy overrides |
|
List<ExportTableConfig> tableConfigs = new ArrayList<>(); |
|
for (Element tableEl : groupEl.getChildren("table")) { |
|
String tableName = tableEl.getTextTrim(); |
|
if (!tableName.isEmpty()) { |
|
String colOverride = tableEl.getAttributeValue("column"); |
|
String sourceOverride = tableEl.getAttributeValue("source"); |
|
String columnsAttr = tableEl.getAttributeValue("columns"); |
|
String orderByAttr = tableEl.getAttributeValue("orderBy"); |
|
|
|
List<String> columnsList = null; |
|
if (columnsAttr != null && !columnsAttr.isBlank()) { |
|
columnsList = new ArrayList<>(); |
|
for (String col : columnsAttr.split(",")) { |
|
String trimmed = col.trim(); |
|
if (!trimmed.isEmpty()) { |
|
columnsList.add(trimmed); |
|
} |
|
} |
|
} |
|
|
|
tableConfigs.add(new ExportTableConfig( |
|
tableName, colOverride, sourceOverride, |
|
columnsList, orderByAttr)); |
|
} |
|
} |
|
|
|
if (tableConfigs.isEmpty()) { |
|
throw new IllegalArgumentException("Export group '" + name + "' has no tables defined"); |
|
} |
|
|
|
groups.add(new ExportGroup(name, schema, tableConfigs, |
|
filterColumn, filterSource)); |
|
} |
|
|
|
return new ExportConfig(groups); |
|
} |
|
|
|
/** |
|
* Converts a comma-separated values string into a SQL VALUES clause. |
|
* Each value is trimmed. Numeric values are kept as-is; |
|
* non-numeric values are single-quoted. |
|
* |
|
* Example: "100, 200, 300" → "VALUES (100),(200),(300)" |
|
* Example: "ZUL, FIN" → "VALUES ('ZUL'),('FIN')" |
|
*/ |
|
private String buildValuesSubquery(String valuesAttr) { |
|
String[] parts = valuesAttr.split(","); |
|
StringBuilder sb = new StringBuilder("VALUES "); |
|
for (int i = 0; i < parts.length; i++) { |
|
String val = parts[i].trim(); |
|
if (val.isEmpty()) { |
|
continue; |
|
} |
|
if (i > 0) { |
|
sb.append(","); |
|
} |
|
if (isNumeric(val)) { |
|
sb.append("(").append(val).append(")"); |
|
} else { |
|
sb.append("('").append(val.replace("'", "''")).append("')"); |
|
} |
|
} |
|
return sb.toString(); |
|
} |
|
|
|
private boolean isNumeric(String s) { |
|
try { |
|
Double.parseDouble(s); |
|
return true; |
|
} catch (NumberFormatException e) { |
|
return false; |
|
} |
|
} |
|
}
|
|
|