diff --git a/admin/oss/jailer-engine.pom b/admin/oss/jailer-engine.pom index 599952e22..06a823f14 100644 --- a/admin/oss/jailer-engine.pom +++ b/admin/oss/jailer-engine.pom @@ -48,7 +48,7 @@ com.h2database h2 - 2.2.224 + 2.3.230 org.apache.logging.log4j diff --git a/docs/api.html b/docs/api.html index 5efbf7013..d90a64d9e 100644 --- a/docs/api.html +++ b/docs/api.html @@ -150,7 +150,7 @@ new BasicDataSource( "org.h2.Driver", "jdbc:h2:" + new File(baseFolder, "demo-scott-h2").getAbsolutePath(), "sa", "", POOL_SIZE, - new File(baseFolder, "lib/h2-2.2.224.jar")), + new File(baseFolder, "lib/h2-2.3.230.jar")), null, new File(baseFolder, "datamodel/Demo-Scott"), new File(baseFolder, "extractionmodel/Demo-Scott.jm"), @@ -162,7 +162,7 @@ new BasicDataSource( "org.h2.Driver", "jdbc:h2:" + new File(baseFolder, "demo-scott-h2-subset").getAbsolutePath(), "sa", "", POOL_SIZE, - new File(baseFolder, "lib/h2-2.2.224.jar"))); + new File(baseFolder, "lib/h2-2.3.230.jar")));   /** * Exports data related with employee "SCOTT" diff --git a/lib/h2-2.2.224.jar b/lib/h2-2.2.224.jar deleted file mode 100644 index ff1997a23..000000000 Binary files a/lib/h2-2.2.224.jar and /dev/null differ diff --git a/lib/h2-2.3.230.jar b/lib/h2-2.3.230.jar new file mode 100644 index 000000000..3a8f7f277 Binary files /dev/null and b/lib/h2-2.3.230.jar differ diff --git a/src/main/engine/net/sf/jailer/api_example/APIExample.java b/src/main/engine/net/sf/jailer/api_example/APIExample.java index a809a7fd8..0049c0bee 100644 --- a/src/main/engine/net/sf/jailer/api_example/APIExample.java +++ b/src/main/engine/net/sf/jailer/api_example/APIExample.java @@ -45,7 +45,7 @@ public class APIExample { new BasicDataSource( "org.h2.Driver", "jdbc:h2:" + new File(baseFolder, "demo-scott-h2").getAbsolutePath(), "sa", "", POOL_SIZE, - new File(baseFolder, "lib/h2-2.2.224.jar")), + new File(baseFolder, "lib/h2-2.3.230.jar")), null, new File(baseFolder, "datamodel/Demo-Scott"), new File(baseFolder, "extractionmodel/Demo-Scott.jm"), @@ -57,7 +57,7 @@ public class APIExample { new BasicDataSource( "org.h2.Driver", "jdbc:h2:" + new File(baseFolder, "demo-scott-h2-subset").getAbsolutePath(), "sa", "", POOL_SIZE, - new File(baseFolder, "lib/h2-2.2.224.jar"))); + new File(baseFolder, "lib/h2-2.3.230.jar"))); /** * Exports data related with employee "SCOTT" diff --git a/src/main/engine/net/sf/jailer/configuration/LocalDatabaseConfiguration.java b/src/main/engine/net/sf/jailer/configuration/LocalDatabaseConfiguration.java index 42c3b5b57..773437d00 100644 --- a/src/main/engine/net/sf/jailer/configuration/LocalDatabaseConfiguration.java +++ b/src/main/engine/net/sf/jailer/configuration/LocalDatabaseConfiguration.java @@ -35,7 +35,7 @@ public class LocalDatabaseConfiguration { private String password = ""; private String driver = "org.h2.Driver"; - private String lib = "lib/h2-2.2.224.jar"; + private String lib = "lib/h2-2.3.230.jar"; /** * @return the localPKType diff --git a/src/main/engine/net/sf/jailer/configuration/jailer.json b/src/main/engine/net/sf/jailer/configuration/jailer.json index 6d3ea2b5b..87ca7b995 100644 --- a/src/main/engine/net/sf/jailer/configuration/jailer.json +++ b/src/main/engine/net/sf/jailer/configuration/jailer.json @@ -8,7 +8,7 @@ "user": "", "password": "", "driver": "org.h2.Driver", - "lib": "lib/h2-2.2.224.jar" + "lib": "lib/h2-2.3.230.jar" }, "commentNullColumnPlaceholder": "For DBUnit flat XML files, null columns can carry a placeholder value which can later be replaced using a ReplacementDataSet. In Flat XML files the first row of a table defines the metadata. If a null column is omitted, none of the following rows can have this column! This is the default behaviour of Jailer. Thus, define a null placeholder and replace it with a null value when you load the data set with DBUnit.", diff --git a/src/main/engine/net/sf/jailer/datamodel/Association.java b/src/main/engine/net/sf/jailer/datamodel/Association.java index fe9e004cd..71ca9af61 100644 --- a/src/main/engine/net/sf/jailer/datamodel/Association.java +++ b/src/main/engine/net/sf/jailer/datamodel/Association.java @@ -434,7 +434,9 @@ public class Association extends ModelElement { String tag; Set otherNames = null; if (aggregationTagName == null) { - otherNames = source.associations.stream().map(a -> a.aggregationTagName).filter(t -> t != null).collect(Collectors.toSet()); + otherNames = new HashSet<>(); + otherNames.addAll(source.associations.stream().map(a -> a.aggregationTagName).filter(t -> t != null).collect(Collectors.toSet())); + otherNames.addAll(source.getColumns().stream().map(c -> c.name.toLowerCase()).collect(Collectors.toSet())); tag = destination.getUnqualifiedName().toLowerCase(Locale.ENGLISH); } else { tag = aggregationTagName; diff --git a/src/main/engine/net/sf/jailer/datamodel/Table.java b/src/main/engine/net/sf/jailer/datamodel/Table.java index 0dad4a76b..9f15e89e9 100644 --- a/src/main/engine/net/sf/jailer/datamodel/Table.java +++ b/src/main/engine/net/sf/jailer/datamodel/Table.java @@ -405,10 +405,6 @@ public class Table extends ModelElement implements Comparable { // TODO should use " as quot-char in GUI // TODO whatabout MySQL/MariaBD? Requoting? How? // TODO default-quoting("/caseIns.) + heuristic re-quoting in XmlRowWriter: if content like 'SQL:T."xy"' -> 'SQL:T. { /** * Gets template for XML exports as DOM. */ - private Document getXmlTemplateAsDocument(String xmlTemplate, Quoting quoting) throws ParserConfigurationException, SAXException, IOException { + public Document getXmlTemplateAsDocument(String xmlTemplate, Quoting quoting) throws ParserConfigurationException, SAXException, IOException { Document template; if (xmlTemplate == null) { template = createInitialXmlTemplate(quoting); diff --git a/src/main/engine/net/sf/jailer/subsetting/ObjectNotationOutputException.java b/src/main/engine/net/sf/jailer/subsetting/ObjectNotationOutputException.java new file mode 100644 index 000000000..d3005dda1 --- /dev/null +++ b/src/main/engine/net/sf/jailer/subsetting/ObjectNotationOutputException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2007 - 2024 Ralf Wisser. + * + * 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. + */ +package net.sf.jailer.subsetting; + +/** + * Thrown if something goes wrong when exporting to an XML/JSON/YAML file. + * + * @author Ralf Wisser + */ +public class ObjectNotationOutputException extends RuntimeException { + + private static final long serialVersionUID = -3712636626146836549L; + + public ObjectNotationOutputException(String message) { + super(message); + } + +} diff --git a/src/main/engine/net/sf/jailer/subsetting/RowLimitExceededException.java b/src/main/engine/net/sf/jailer/subsetting/RowLimitExceededException.java index f87810a1a..a3a336a41 100644 --- a/src/main/engine/net/sf/jailer/subsetting/RowLimitExceededException.java +++ b/src/main/engine/net/sf/jailer/subsetting/RowLimitExceededException.java @@ -16,7 +16,7 @@ package net.sf.jailer.subsetting; /** - * Throws if row limit is exceeded. + * Thrown if row limit is exceeded. * * @author Ralf Wisser */ diff --git a/src/main/engine/net/sf/jailer/subsetting/SubsettingEngine.java b/src/main/engine/net/sf/jailer/subsetting/SubsettingEngine.java index 3d237d7dd..c1d60462c 100644 --- a/src/main/engine/net/sf/jailer/subsetting/SubsettingEngine.java +++ b/src/main/engine/net/sf/jailer/subsetting/SubsettingEngine.java @@ -1228,6 +1228,21 @@ public class SubsettingEngine { } catch (TransformerConfigurationException e) { throw new RuntimeException(e); } + + sortedTables.sort(new Comparator
() { + @Override + public int compare(Table t1, Table t2) { + boolean s1 = subjects.contains(t1); + boolean s2 = subjects.contains(t2); + if (s1 && !s2) { + return -1; + } + if (!s1 && s2) { + return 1; + } + return 0; + } + }); for (Table table: sortedTables) { entityGraph.markRoots(table); @@ -1235,6 +1250,10 @@ public class SubsettingEngine { for (Table table: sortedTables) { _log.info("exporting table " + datamodel.getDisplayName(table)); reader.setTable(table); + reader.setTableIsSubject(subjects.contains(table)); + if (executionContext.isIgnoreNonAggregated() && !subjects.contains(table)) { + continue; + } entityGraph.readMarkedEntities(table, reader, reader.getTableMapping(table).selectionSchema, reader.getTableMapping(table).originalPKAliasPrefix, true); } reader.endDocument(); @@ -1697,7 +1716,7 @@ public class SubsettingEngine { datamodel.transpose(); } - if (scriptFile != null && scriptFormat != ScriptFormat.XML && exportStatistic.getTotal() != exportedCount) { + if (scriptFile != null && !scriptFormat.isObjectNotation() && exportStatistic.getTotal() != exportedCount) { String message = "The number of rows collected (" + exportStatistic.getTotal() + ") differs from that of the exported ones (" + exportedCount + ").\n" + "This may have been caused by an invalid primary key definition.\nPlease note that each primary key must be unique.\n" + diff --git a/src/main/engine/net/sf/jailer/xml/JSONWriter.java b/src/main/engine/net/sf/jailer/xml/JSONWriter.java new file mode 100644 index 000000000..56ce7882b --- /dev/null +++ b/src/main/engine/net/sf/jailer/xml/JSONWriter.java @@ -0,0 +1,142 @@ +/* + * Copyright 2007 - 2024 Ralf Wisser. + * + * 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. + */ +package net.sf.jailer.xml; + +import java.io.IOException; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; + +import net.sf.jailer.xml.XmlUtil.ObjectNotationWriter; + +/** + * A writer for JSON. + * + * @author Ralf Wisser + */ +public class JSONWriter implements ObjectNotationWriter { + private final JsonGenerator jGen; + + public JSONWriter(Writer out) throws IOException { + JsonFactory jfactory = new JsonFactory(); + this.jGen = jfactory.createGenerator(out); + this.jGen.useDefaultPrettyPrinter(); + } + + @Override + public void writeStartObject() throws IOException { + this.jGen.writeStartObject(); + } + + @Override + public void writeStartArray() throws IOException { + this.jGen.writeStartArray();; + } + + @Override + public void writeEndObject() throws IOException { + this.jGen.writeEndObject();; + } + + @Override + public void writeEndArray() throws IOException { + this.jGen.writeEndArray(); + } + + @Override + public void flush() throws IOException { + this.jGen.flush(); + } + + @Override + public void writeFieldName(String qName) throws IOException { + this.jGen.writeFieldName(qName); + } + + @Override + public void writeArrayFieldStart(String qName) throws IOException { + this.jGen.writeArrayFieldStart(qName); + } + + @Override + public void writeNull() throws IOException { + this.jGen.writeNull(); + } + + @Override + public void writeBinary(byte[] content) throws IOException { + this.jGen.writeBinary(content); + } + + @Override + public void writeBoolean(Boolean content) throws IOException { + this.jGen.writeBoolean(content); + } + + @Override + public void writeNumber(BigInteger content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(BigDecimal content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Double content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Float content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Integer content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Long content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Short content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeString(String string) throws IOException { + this.jGen.writeString(string); + } + + @Override + public void writeComment(String comment) throws IOException { + try { + jGen.writeStringField("j:comment", comment + " /j:comment"); + } catch (Exception e) { + e.printStackTrace(); + // ignore + } + } + +} \ No newline at end of file diff --git a/src/main/engine/net/sf/jailer/xml/XmlExportTransformer.java b/src/main/engine/net/sf/jailer/xml/XmlExportTransformer.java index ab62e740a..2f733c294 100644 --- a/src/main/engine/net/sf/jailer/xml/XmlExportTransformer.java +++ b/src/main/engine/net/sf/jailer/xml/XmlExportTransformer.java @@ -44,6 +44,7 @@ import net.sf.jailer.datamodel.Column; import net.sf.jailer.datamodel.RowIdSupport; import net.sf.jailer.datamodel.Table; import net.sf.jailer.entitygraph.EntityGraph; +import net.sf.jailer.subsetting.ObjectNotationOutputException; import net.sf.jailer.subsetting.ScriptFormat; import net.sf.jailer.util.CellContentConverter; import net.sf.jailer.util.Quoting; @@ -59,7 +60,12 @@ public class XmlExportTransformer extends AbstractResultSetReader { * The table to read from. */ private Table table; - + + /** + * true iff table is subject. + */ + private boolean tableIsSubject; + /** * For writing rows as xml. */ @@ -114,9 +120,13 @@ public class XmlExportTransformer extends AbstractResultSetReader { * {@link RowIdSupport}. */ private final RowIdSupport rowIdSupport; + + private final ExecutionContext executionContext; private final Quoting quoting; - + + private ScriptFormat scriptFormat; + /** * Constructor. * @@ -133,21 +143,33 @@ public class XmlExportTransformer extends AbstractResultSetReader { public XmlExportTransformer(OutputStream out, String commentHeader, EntityGraph entityGraph, Set
totalProgress, Set
cyclicAggregatedTables, String rootTag, String datePattern, String timestampPattern, Session session, ScriptFormat scriptFormat, Charset charset, ExecutionContext executionContext) throws TransformerConfigurationException, SAXException, SQLException { - this.xmlRowWriter = new XmlRowWriter(out, commentHeader, rootTag, datePattern, timestampPattern, scriptFormat, charset); + this.xmlRowWriter = new XmlRowWriter(out, commentHeader, rootTag, datePattern, timestampPattern, scriptFormat, charset, executionContext); this.entityGraph = entityGraph; + this.scriptFormat = scriptFormat; this.totalProgress = totalProgress; this.cyclicAggregatedTables = cyclicAggregatedTables; this.session = session; this.quoting = Quoting.getQuoting(session); this.rowIdSupport = new RowIdSupport(entityGraph.getDatamodel(), session.dbms, executionContext); + this.executionContext = executionContext; } + private int rootCount = 0; + /** * Reads result-set and writes into export-script. */ @Override public void readCurrentRow(ResultSet resultSet) throws SQLException { try { + if (executionContext.isDisallowNonAggregated() && !tableIsSubject) { + throw new ObjectNotationOutputException("Non-aggregated objects (\"" + table.getName() + "\") are disallowed at root level.\n(" + scriptFormat + ")"); + } + if (++rootCount > 1) { + if (executionContext.isSingleRoot()) { + throw new ObjectNotationOutputException("Multiple root objects are not allowed. (\"" + table.getName() + "\")\n(" + scriptFormat + ")"); + } + } writeEntity(table, null, resultSet, new ArrayList(), getCellContentConverter(resultSet, session, session.dbms)); } catch (SAXException e) { throw new RuntimeException(e); @@ -225,7 +247,7 @@ public class XmlExportTransformer extends AbstractResultSetReader { } }; try { - xmlRowWriter.startList(sa); + xmlRowWriter.startList(sa, name); entityGraph.readDependentEntities(sa.destination, sa, resultSet, getMetaData(resultSet), reader, getTypeCache(sa.destination), getTableMapping(sa.destination).selectionSchema, getTableMapping(sa.destination).originalPKAliasPrefix); if (cyclicAggregatedTables.contains(sa.destination)) { entityGraph.markDependentEntitiesAsTraversed(sa, resultSet, getMetaData(resultSet), getTypeCache(sa.destination)); @@ -265,6 +287,13 @@ public class XmlExportTransformer extends AbstractResultSetReader { this.table = table; } + /** + * @param tableIsSubject true iff table is subject + */ + public void setTableIsSubject(boolean tableIsSubject) { + this.tableIsSubject = tableIsSubject; + } + /** * Closes the XML document. */ diff --git a/src/main/engine/net/sf/jailer/xml/XmlRowWriter.java b/src/main/engine/net/sf/jailer/xml/XmlRowWriter.java index 91abeca78..92dc79c54 100644 --- a/src/main/engine/net/sf/jailer/xml/XmlRowWriter.java +++ b/src/main/engine/net/sf/jailer/xml/XmlRowWriter.java @@ -30,6 +30,7 @@ import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Stack; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.sax.TransformerHandler; @@ -38,6 +39,7 @@ import javax.xml.transform.stream.StreamResult; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; +import net.sf.jailer.ExecutionContext; import net.sf.jailer.configuration.DBMS; import net.sf.jailer.database.Session; import net.sf.jailer.datamodel.AggregationSchema; @@ -50,7 +52,7 @@ import net.sf.jailer.util.SqlUtil; /** - * Writes rows into XML file. + * Writes rows into XML/JSON/YAML file. * * @author Ralf Wisser */ @@ -86,6 +88,13 @@ public class XmlRowWriter { */ private final Map> typeCachesForStringKey = new HashMap>(); + protected boolean forSketch = false; + + /** + * Execution context. + */ + private final ExecutionContext executionContext; + /** * Constructor. * @@ -95,41 +104,72 @@ public class XmlRowWriter { * @param datePattern pattern for dates * @param timestampPattern pattern for time-stamps * @param scriptFormat + * @param executionContext */ - public XmlRowWriter(OutputStream out, String commentHeader, String rootTag, String datePattern, String timestampPattern, ScriptFormat scriptFormat, Charset charset) throws SAXException, TransformerConfigurationException { + public XmlRowWriter(OutputStream out, String commentHeader, String rootTag, String datePattern, String timestampPattern, ScriptFormat scriptFormat, Charset charset, ExecutionContext executionContext) throws SAXException, TransformerConfigurationException { this.rootTag = rootTag; - this.datePattern = new SimpleDateFormat(datePattern, Locale.ENGLISH); - this.timestampPattern = new SimpleDateFormat(timestampPattern, Locale.ENGLISH); + this.executionContext = executionContext; + this.datePattern = datePattern == null? new SimpleDateFormat() : new SimpleDateFormat(datePattern, Locale.ENGLISH); + this.timestampPattern = timestampPattern == null? new SimpleDateFormat() : new SimpleDateFormat(timestampPattern, Locale.ENGLISH); StreamResult streamResult = new StreamResult(new OutputStreamWriter(out, charset)); transformerHandler = scriptFormat == ScriptFormat.JSON - ? XmlUtil.createJSONTransformerHandler(commentHeader, rootTag, streamResult, charset) - : XmlUtil.createTransformerHandler(commentHeader, rootTag, streamResult, charset); + ? XmlUtil.createObjectNotationTransformerHandler(commentHeader, executionContext.isSingleRoot()? "" : rootTag, streamResult.getWriter(), false, scriptFormat, executionContext) + : XmlUtil.createTransformerHandler(commentHeader, executionContext.isSingleRoot()? "" : rootTag, streamResult, charset); + } + + /** + * Constructor. + * + * @param out output stream to write the xml into + * @param commentHeader comment at top of document + * @param rootTag root tag name + * @param datePattern pattern for dates + * @param timestampPattern pattern for time-stamps + * @param scriptFormat + * @param executionContext + */ + public XmlRowWriter(OutputStream out, String commentHeader, String rootTag, String datePattern, String timestampPattern, ScriptFormat scriptFormat, Charset charset, TransformerHandler transformerHandler, ExecutionContext executionContext) throws SAXException, TransformerConfigurationException { + this.rootTag = rootTag; + this.executionContext = executionContext; + this.datePattern = datePattern == null? new SimpleDateFormat() : new SimpleDateFormat(datePattern, Locale.ENGLISH); + this.timestampPattern = timestampPattern == null? new SimpleDateFormat() : new SimpleDateFormat(timestampPattern, Locale.ENGLISH); + forSketch = true; + this.transformerHandler = transformerHandler; } /** * Closes the writer. */ public void close() throws SAXException { - // TODO - // TODO rootTag is never empty. multipleObject attribute - if (rootTag.length() > 0 && !(transformerHandler instanceof ObjectFormatTransformer)) { + if (!executionContext.isSingleRoot() && !(transformerHandler instanceof ObjectFormatTransformer)) { transformerHandler.endElement("", "", rootTag); } transformerHandler.endDocument(); } - + + private Map> associationTempAggregationTagName = new HashMap<>(); + /** * Writes start element for a list of rows. * * @param association association describing the list + * @param name */ - public void startList(Association association) throws SAXException { + public void startList(Association association, String name) throws SAXException { + if (association != null) { + Stack stack = associationTempAggregationTagName.get(association); + if (stack == null) { + stack = new Stack<>(); + associationTempAggregationTagName.put(association, stack); + } + stack.push(name); + } if (association != null && getAggregationSchema(association) == AggregationSchema.EXPLICIT_LIST) { if (ifLevel == 0) { if (transformerHandler instanceof ObjectFormatTransformer) { ((ObjectFormatTransformer) transformerHandler).startArray(); } - transformerHandler.startElement(null, null, association.getAggregationTagName(), null); + transformerHandler.startElement(null, null, name != null? name : association.getAggregationTagName(), null); } } } @@ -148,6 +188,12 @@ public class XmlRowWriter { } } } + if (association != null) { + Stack stack = associationTempAggregationTagName.get(association); + if (stack != null && !stack.isEmpty()) { + stack.pop(); + } + } } private AggregationSchema getAggregationSchema(Association association) { @@ -200,6 +246,8 @@ public class XmlRowWriter { */ protected final CellContentConverter cellContentConverter; + private String lastElementName; + /** * Constructor. * @@ -211,7 +259,7 @@ public class XmlRowWriter { this.table = table; this.association = association; this.session = session; - this.cellContentConverter = new CellContentConverter(resultSetMetaData, session, session.dbms); + this.cellContentConverter = session == null? null : new CellContentConverter(resultSetMetaData, session, session.dbms); } /** @@ -221,7 +269,7 @@ public class XmlRowWriter { * @param returnNull if true, return null instead of empty string if sql-result is null * @return the xml to write out */ - private Object toContent(String text, boolean returnNull) { + protected Object toContent(String text, boolean returnNull) { if (text != null && text.startsWith(XmlUtil.SQL_PREFIX)) { String columnName = "C" + nr++; int type; @@ -231,45 +279,52 @@ public class XmlRowWriter { typeCache = new HashMap(); typeCachesForStringKey.put(table, typeCache); } - type = SqlUtil.getColumnType(resultSet, resultSetMetaData, columnName, typeCache); - if ((type == Types.BLOB || type == Types.CLOB|| type == Types.NCLOB) && !DBMS.SQLITE.equals(session.dbms)) { - Object object = resultSet.getObject(columnName); - if (returnNull && (object == null || resultSet.wasNull())) { - return null; - } - if (object instanceof Blob) { - Blob blob = (Blob) object; - byte[] blobValue = blob.getBytes(1, (int) blob.length()); - return Base64.encodeBytes(blobValue); - } + try { + type = SqlUtil.getColumnType(resultSet, resultSetMetaData, columnName, typeCache); + if ((type == Types.BLOB || type == Types.CLOB || type == Types.NCLOB) + && !DBMS.SQLITE.equals(session.dbms)) { + Object object = resultSet.getObject(columnName); + if (returnNull && (object == null || resultSet.wasNull())) { + return null; + } + if (object instanceof Blob) { + Blob blob = (Blob) object; + byte[] blobValue = blob.getBytes(1, (int) blob.length()); + return Base64.encodeBytes(blobValue); + } - if (object instanceof Clob) { - Clob clobValue = (Clob) object; - int length = (int) clobValue.length(); - if (length > 0) { - return clobValue.getSubString(1, length); + if (object instanceof Clob) { + Clob clobValue = (Clob) object; + int length = (int) clobValue.length(); + if (length > 0) { + return clobValue.getSubString(1, length); + } + return ""; } - return ""; - } - } else { - Object o = cellContentConverter.getObject(resultSet, columnName); - if (returnNull && (o == null || resultSet.wasNull())) { - return null; - } - if (o != null) { - Object value; - - if (o instanceof Timestamp) { - value = timestampPattern.format((Timestamp) o); - } else if (o instanceof Date) { - value = datePattern.format((Date) o); - } else { - value = o; + } else { + Object o = cellContentConverter.getObject(resultSet, columnName); + if (returnNull && (o == null || resultSet.wasNull())) { + return null; + } + if (o != null) { + Object value; + + if (o instanceof Timestamp) { + value = timestampPattern.format((Timestamp) o); + } else if (o instanceof Date) { + value = datePattern.format((Date) o); + } else { + value = o; + } + return value; } - return value; } + return null; + } catch (OutOfMemoryError e) { + throw new OutOfMemoryError("Out of Memory. \nNot enough memory to read field " + + (table == null ? "" : ("\"" + table.getName() + "\".")) + " \"" + lastElementName + + "\""); } - return null; } catch (SQLException e) { throw new RuntimeException(e); } @@ -280,7 +335,7 @@ public class XmlRowWriter { @Override public void visitComment(String comment) { try { - if (ifLevel == 0) { + if (ifLevel == 0 && forSketch) { transformerHandler.comment(comment.toCharArray(), 0, comment.length()); } } catch (SAXException e) { @@ -308,6 +363,7 @@ public class XmlRowWriter { @Override public void visitElementStart(String elementName, boolean isRoot, String[] aNames, String[] aValues) { + lastElementName = elementName; if (ifLevel > 0) { ++ifLevel; } @@ -332,8 +388,6 @@ public class XmlRowWriter { } else if (!aNames[i].equals(jailerNamespaceDeclaration)) { Object content = toContent(aValues[i], false); attr.addAttribute("", "", aNames[i], "CDATA", content == null? "" : content.toString()); - // TODO - // TODO if instanceof ObjectFormatTransformer then startElement(); content(); endElement(); } } } @@ -342,7 +396,19 @@ public class XmlRowWriter { ++ifLevel; } else { if (!isRoot || association == null || getAggregationSchema(association) != AggregationSchema.FLAT) { - String tagName = isRoot && association != null && getAggregationSchema(association) != AggregationSchema.EXPLICIT_LIST? association.getAggregationTagName() : elementName; + String tagName; + if (isRoot && association != null && getAggregationSchema(association) != AggregationSchema.EXPLICIT_LIST) { + tagName = null; + Stack stack = associationTempAggregationTagName.get(association); + if (stack != null && !stack.isEmpty()) { + tagName = stack.peek(); + } + if (tagName == null) { + tagName = association.getAggregationTagName(); + } + } else { + tagName = elementName; + } transformerHandler.startElement("", "", tagName, attr); } } @@ -375,21 +441,14 @@ public class XmlRowWriter { } -// TODO -// TODO Sketch also in JSON -// TODO no Sketch-view in ExMoEd, only in template-editor -// TODO "Sketch" dann in "Template"-Dialog (fuer beide Tabellen der Asoziation?) - -// TODO -// TODO template-editor (GUI): -// TODO add sketch view (JScrollPane) -// TODO make it bigger - // TODO // TODO docu about "j:if-not-null"/"j:is-null" in template dialog +// TODO 1 +// TODO JSON/YAML/XML +// TODO "unformatted" export. in XMLSettingsDialog+CLI+SubesstingEngine(Facade) + // TODO -// TODO warn if "singleObject" and "incudeNonAggr" and later exists. "dont warn again" button (transient) - +// TODO new maven dep, testen diff --git a/src/main/engine/net/sf/jailer/xml/XmlUtil.java b/src/main/engine/net/sf/jailer/xml/XmlUtil.java index 93b6e68fa..ac08e3f46 100644 --- a/src/main/engine/net/sf/jailer/xml/XmlUtil.java +++ b/src/main/engine/net/sf/jailer/xml/XmlUtil.java @@ -22,6 +22,7 @@ import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.Charset; +import java.util.HashMap; import java.util.Map; import java.util.Stack; @@ -39,7 +40,6 @@ import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; -import org.apache.commons.collections4.map.HashedMap; import org.w3c.dom.Attr; import org.w3c.dom.Comment; import org.w3c.dom.Document; @@ -53,9 +53,9 @@ import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; - +import net.sf.jailer.ExecutionContext; +import net.sf.jailer.datamodel.Association; +import net.sf.jailer.subsetting.ScriptFormat; import net.sf.jailer.util.PrintUtil; import net.sf.jailer.xml.XmlRowWriter.ObjectFormatTransformer; @@ -65,15 +65,41 @@ import net.sf.jailer.xml.XmlRowWriter.ObjectFormatTransformer; * @author Ralf Wisser */ public class XmlUtil { + + public static interface ObjectNotationWriter { + public void writeStartObject() throws IOException; + public void writeStartArray() throws IOException; + public void writeEndObject() throws IOException; + public void writeEndArray() throws IOException; + public void flush() throws IOException; + public void writeFieldName(String qName) throws IOException; + public void writeArrayFieldStart(String qName) throws IOException; + public void writeNull() throws IOException; + public void writeBinary(byte[] content) throws IOException; + public void writeBoolean(Boolean content) throws IOException; + public void writeNumber(BigInteger content) throws IOException; + public void writeNumber(BigDecimal content) throws IOException; + public void writeNumber(Double content) throws IOException; + public void writeNumber(Float content) throws IOException; + public void writeNumber(Integer content) throws IOException; + public void writeNumber(Long content) throws IOException; + public void writeNumber(Short content) throws IOException; + public void writeString(String string) throws IOException; + public void writeComment(String comment) throws IOException; + } - private static class JSONTransformerHandler implements TransformerHandler, ObjectFormatTransformer { + public static class ObjectNotationTransformerHandler implements TransformerHandler, ObjectFormatTransformer { private final Writer out; - private final JsonGenerator jGenerator; + private final ObjectNotationWriter jGenerator; + private final ExecutionContext executionContext; + private final boolean forSketch; Stack states = new Stack<>(); - - private JSONTransformerHandler(Writer out, JsonGenerator jGenerator) { + + private ObjectNotationTransformerHandler(Writer out, ObjectNotationWriter jGenerator, boolean forSketch, ExecutionContext executionContext) { this.out = out; this.jGenerator = jGenerator; + this.forSketch = forSketch; + this.executionContext = executionContext; } @Override @@ -83,8 +109,13 @@ public class XmlUtil { @Override public void startDocument() throws SAXException { try { - jGenerator.writeStartObject(); - states.push('{'); + if (forSketch || executionContext.isSingleRoot()) { + jGenerator.writeStartObject(); + states.push('R'); + } else { + jGenerator.writeStartArray(); + states.push('M'); + } } catch (IOException e) { throw new RuntimeException(e); } @@ -93,8 +124,12 @@ public class XmlUtil { @Override public void endDocument() throws SAXException { try { - jGenerator.writeEndObject(); - states.pop(); + Character c = states.pop(); + if (c == 'R') { + jGenerator.writeEndObject(); + } else if (c == 'M') { + jGenerator.writeEndArray(); + } jGenerator.flush(); } catch (IOException e) { try { @@ -124,12 +159,17 @@ public class XmlUtil { jGenerator.writeStartObject(); states.push('='); jGenerator.writeFieldName(qName); + } else if (states.peek() == 'M') { + jGenerator.writeStartObject(); + states.push('m'); } else if (states.peek() == '[') { jGenerator.writeArrayFieldStart(qName); states.push('-'); } else if (states.peek() == '-') { jGenerator.writeStartObject(); states.push('{'); + } else if (states.peek() == 'R') { + states.push('r'); } else { states.push('='); jGenerator.writeFieldName(qName); @@ -155,6 +195,9 @@ public class XmlUtil { if (pop == '{') { jGenerator.writeEndObject(); } + if (pop == 'm') { + jGenerator.writeEndObject(); + } if (pop == '-') { jGenerator.writeEndArray(); } @@ -163,7 +206,7 @@ public class XmlUtil { } } - private Map constants = new HashedMap<>(); + private Map constants = new HashMap<>(); @Override public void characters(char[] ch, int start, int length) throws SAXException { @@ -242,6 +285,14 @@ public class XmlUtil { @Override public void comment(char[] ch, int start, int length) throws SAXException { + try { + String comment = new String(ch, start, length).trim(); + if (!comment.isEmpty()) { + jGenerator.writeComment(comment); + } + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override @@ -312,12 +363,56 @@ public class XmlUtil { @Override public void startArray() { - states.push('['); + try { + if (states.peek() == '=') { + jGenerator.writeStartObject(); + states.push('['); + } else { + states.push('['); + } + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override public void endArray() { - states.pop(); + try { + states.pop(); + if (states.peek() == '=') { + jGenerator.writeEndObject(); + states.pop(); + states.push('?'); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void associationSketch(Association a, String associationName, String name) { + try { + if (states.peek() != '=') { + jGenerator.writeFieldName(name); + } else { + jGenerator.writeStartObject(); + states.pop(); + states.push('?'); + associationSketch(a, associationName, name); + jGenerator.writeEndObject(); + return; + } + boolean isArray = !a.isInsertDestinationBeforeSource(); + + if (isArray) { + jGenerator.writeStartArray(); + jGenerator.writeEndArray(); + } else { + jGenerator.writeStartObject(); + jGenerator.writeEndObject(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } } } @@ -538,35 +633,38 @@ public class XmlUtil { return transformerHandler; } - public static TransformerHandler createJSONTransformerHandler(String commentHeader, String rootTag, - StreamResult streamResult, Charset charset) { + public static ObjectNotationTransformerHandler createObjectNotationTransformerHandler(String commentHeader, String rootTag, + Writer out, boolean forSketch, ScriptFormat scriptFormat, ExecutionContext executionContext) { - Writer out = streamResult.getWriter(); - JsonFactory jfactory = new JsonFactory(); - JsonGenerator jGenerator; + ObjectNotationWriter jGenerator; try { - jGenerator = jfactory - .createGenerator(out); - jGenerator.useDefaultPrettyPrinter(); + if (scriptFormat == ScriptFormat.JSON) { + jGenerator = new JSONWriter(out); + } else if (scriptFormat == ScriptFormat.YAML) { + // TODO + jGenerator = new YAMLWriter(out); + } else { + throw new RuntimeException("unknown script format: " + scriptFormat); + } } catch (IOException e) { throw new RuntimeException(e); } - // TODO - // TODO c|blob: size-limit (editable). As watch-dog (error if exceeded) (show path of field on error) - // TODO // TODO count XML/JSON/YMAML exports (s16-19) - - // TODO - // TODO ? default in XML/JSON/... Settings: dont allow arrays. max straenge. dann in der Fehlermeldung auf Mögl. Array zuzulassen aufmerksam machen? - // TODO ? gute gruende, das gegenteil zu machen. im fehlerfall nur warnen. - TransformerHandler th = new JSONTransformerHandler(out, jGenerator); + + ObjectNotationTransformerHandler th = new ObjectNotationTransformerHandler(out, jGenerator, forSketch, executionContext); try { th.startDocument(); - } catch (SAXException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + if (commentHeader != null && !commentHeader.isEmpty()) { + // TODO + // TODO nicht bei JSON, noch testen + if (!(jGenerator instanceof JSONWriter)) { + jGenerator.writeComment(commentHeader); + } + } + } catch (Exception e) { + throw new RuntimeException(e); } return th; } diff --git a/src/main/engine/net/sf/jailer/xml/YAMLWriter.java b/src/main/engine/net/sf/jailer/xml/YAMLWriter.java new file mode 100644 index 000000000..5882c7738 --- /dev/null +++ b/src/main/engine/net/sf/jailer/xml/YAMLWriter.java @@ -0,0 +1,144 @@ +/* + * Copyright 2007 - 2024 Ralf Wisser. + * + * 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. + */ +package net.sf.jailer.xml; + +import java.io.IOException; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; + +import net.sf.jailer.xml.XmlUtil.ObjectNotationWriter; + +/** + * A writer for JSON. + * + * @author Ralf Wisser + */ +public class YAMLWriter implements ObjectNotationWriter { + private final JsonGenerator jGen; + + public YAMLWriter(Writer out) throws IOException { + // TODO + // TODO implement using SnakeYaml + JsonFactory jfactory = new JsonFactory(); + this.jGen = jfactory.createGenerator(out); + this.jGen.useDefaultPrettyPrinter(); + } + + @Override + public void writeStartObject() throws IOException { + this.jGen.writeStartObject(); + } + + @Override + public void writeStartArray() throws IOException { + this.jGen.writeStartArray();; + } + + @Override + public void writeEndObject() throws IOException { + this.jGen.writeEndObject();; + } + + @Override + public void writeEndArray() throws IOException { + this.jGen.writeEndArray(); + } + + @Override + public void flush() throws IOException { + this.jGen.flush(); + } + + @Override + public void writeFieldName(String qName) throws IOException { + this.jGen.writeFieldName(qName); + } + + @Override + public void writeArrayFieldStart(String qName) throws IOException { + this.jGen.writeArrayFieldStart(qName); + } + + @Override + public void writeNull() throws IOException { + this.jGen.writeNull(); + } + + @Override + public void writeBinary(byte[] content) throws IOException { + this.jGen.writeBinary(content); + } + + @Override + public void writeBoolean(Boolean content) throws IOException { + this.jGen.writeBoolean(content); + } + + @Override + public void writeNumber(BigInteger content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(BigDecimal content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Double content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Float content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Integer content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Long content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeNumber(Short content) throws IOException { + this.jGen.writeNumber(content); + } + + @Override + public void writeString(String string) throws IOException { + this.jGen.writeString(string); + } + + @Override + public void writeComment(String comment) throws IOException { + try { + jGen.writeStringField("j:comment", comment + " /j:comment"); + } catch (Exception e) { + e.printStackTrace(); + // ignore + } + } + +} \ No newline at end of file diff --git a/src/main/gui/net/sf/jailer/ui/ColumnMapperDialog.form b/src/main/gui/net/sf/jailer/ui/ColumnMapperDialog.form index 10ae1e2b3..e40616bf2 100644 --- a/src/main/gui/net/sf/jailer/ui/ColumnMapperDialog.form +++ b/src/main/gui/net/sf/jailer/ui/ColumnMapperDialog.form @@ -22,53 +22,129 @@ - + - + + + + + + - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -128,20 +204,6 @@ - - - - - - - - - - - - - - diff --git a/src/main/gui/net/sf/jailer/ui/ColumnMapperDialog.java b/src/main/gui/net/sf/jailer/ui/ColumnMapperDialog.java index 433f0ec8a..446bcdac8 100644 --- a/src/main/gui/net/sf/jailer/ui/ColumnMapperDialog.java +++ b/src/main/gui/net/sf/jailer/ui/ColumnMapperDialog.java @@ -15,10 +15,13 @@ */ package net.sf.jailer.ui; +import java.awt.Container; import java.awt.GridBagConstraints; import java.awt.event.ActionListener; import java.util.Collections; import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.swing.DefaultComboBoxModel; import javax.swing.ImageIcon; @@ -28,8 +31,10 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.w3c.dom.Document; +import net.sf.jailer.ExecutionContext; import net.sf.jailer.datamodel.DataModel; import net.sf.jailer.datamodel.Table; +import net.sf.jailer.subsetting.ScriptFormat; import net.sf.jailer.ui.syntaxtextarea.RSyntaxTextAreaWithTheme; import net.sf.jailer.xml.XmlUtil; @@ -43,13 +48,16 @@ public class ColumnMapperDialog extends javax.swing.JDialog { private final java.awt.Frame parent; private Table table; private DataModel dataModel; + private ScriptFormat scriptFormat; private boolean ok; private ParameterSelector parameterSelector; private String initialTemplate = ""; + private ExecutionContext executionContext; /** Creates new form ColumnMapperDialog */ - public ColumnMapperDialog(java.awt.Frame parent, ParameterSelector.ParametersGetter parametersGetter) { + public ColumnMapperDialog(java.awt.Frame parent, ParameterSelector.ParametersGetter parametersGetter, ExecutionContext executionContext) { super(parent, true); + this.executionContext = executionContext; this.parent = parent; this.mappingField = new RSyntaxTextAreaWithTheme(); mappingField.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); @@ -70,20 +78,42 @@ public class ColumnMapperDialog extends javax.swing.JDialog { gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new java.awt.Insets(4, 4, 0, 4); - jPanel1.add(jScrollPane2, gridBagConstraints); + jPanel4.add(jScrollPane2, gridBagConstraints); jScrollPane2.setViewportView(mappingField); - AutoCompletion.enable(tableCombobox); + JScrollPane tab = new JScrollPane(); + xmlSketch = new RSyntaxTextAreaWithTheme(); + + xmlSketch.setEditable(false); + tab.setViewportView(xmlSketch); + xmlSketch.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); + xmlSketch.setCodeFoldingEnabled(true); + + xmlSketch.setText(""); + xmlSketch.setCaretPosition(0); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.weightx = 1; + gridBagConstraints.weighty = 1; + gridBagConstraints.fill = GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0); + jPanel5.add(tab, gridBagConstraints); + + AutoCompletion.enable(tableCombobox); paramPanel.add(parameterSelector = new ParameterSelector(this, mappingField, parametersGetter)); tableCombobox.addActionListener(new ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { try { - if (table != null) { - table = dataModel.getTableByDisplayName((String) tableCombobox.getSelectedItem()); + Table t = dataModel.getTableByDisplayName((String) tableCombobox.getSelectedItem()); + if (t != null) { + table = t; setMappingFieldText(XmlUtil.build(table.getXmlTemplateAsDocument(null))); mappingField.discardAllEdits(); + updateSketch(table); } } catch (Exception ex) { UIUtil.showException(ColumnMapperDialog.this.parent, "Error", ex); @@ -98,9 +128,11 @@ public class ColumnMapperDialog extends javax.swing.JDialog { * @param dataModel the data model * @param table the table */ - public boolean edit(DataModel dataModel, Table table) { + public boolean edit(DataModel dataModel, Table table, ScriptFormat scriptFormat) { + this.table = table; + this.dataModel = dataModel; + this.scriptFormat = scriptFormat; parameterSelector.updateParameters(); - this.table = null; Vector tableNames = new Vector(); for (Table t: dataModel.getTables()) { tableNames.add(dataModel.getDisplayName(t)); @@ -109,11 +141,13 @@ public class ColumnMapperDialog extends javax.swing.JDialog { tableCombobox.setModel(new DefaultComboBoxModel(tableNames)); tableCombobox.setMaximumRowCount(40); tableCombobox.setSelectedItem(dataModel.getDisplayName(table)); - int w = 600, h = 600; + int w = 1200, h = 600; setSize(w, h); setLocation(Math.max(0, parent.getX() + parent.getWidth() / 2 - w / 2), Math.max(0, parent.getY() + parent.getHeight() / 2 - h / 2)); + jSplitPane1.setDividerLocation((int) (w * 0.66)); invalidate(); + updateSketch(table); try { setMappingFieldText(XmlUtil.build(table.getXmlTemplateAsDocument(null))); initialTemplate = mappingField.getText(); @@ -130,12 +164,53 @@ public class ColumnMapperDialog extends javax.swing.JDialog { } mappingField.discardAllEdits(); ok = false; - this.table = table; - this.dataModel = dataModel; setVisible(true); return ok; } + private RSyntaxTextArea xmlSketch; + + /** + * Adds a tab to the sketch-tabbedpane for a given table. + * + * @param table the table + */ + private void updateSketch(Table table) { + String sketch = ""; + try { + sketch = XmlSketchBuilder.buildSketch(table, 1, scriptFormat, executionContext); + if (scriptFormat == ScriptFormat.JSON) { + Pattern pattern = Pattern.compile("\"j\\:comment\"\\s*\\:\\s*\"(.*)/j\\:comment\"(?:\\,)?"); + Matcher matcher = pattern.matcher(sketch); + boolean result = matcher.find(); + StringBuffer sb = new StringBuffer(); + if (result) { + do { + String comment = matcher.group(1); + matcher.appendReplacement(sb, "// " + Matcher.quoteReplacement(comment.replace("\\\"", "\f").replace("\"", "").replace("\f", "\""))); + result = matcher.find(); + } while (result); + } + matcher.appendTail(sb); + sketch = sb.toString(); + } + } catch (Exception e) { + e.printStackTrace(); + sketch = e.getMessage(); + } + if (scriptFormat == ScriptFormat.XML) { + xmlSketch.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); + } + if (scriptFormat == ScriptFormat.JSON) { + xmlSketch.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JSON_WITH_COMMENTS); + } + if (scriptFormat == ScriptFormat.YAML) { + xmlSketch.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_YAML); + } + Container sParent = xmlSketch.getParent(); + xmlSketch.setText(sketch); + } + /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is @@ -145,20 +220,27 @@ public class ColumnMapperDialog extends javax.swing.JDialog { private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; + jPanel3 = new javax.swing.JPanel(); jPanel1 = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); tableCombobox = new JComboBox2(); + jSplitPane1 = new javax.swing.JSplitPane(); + jPanel4 = new javax.swing.JPanel(); jLabel2 = new javax.swing.JLabel(); + paramPanel = new javax.swing.JPanel(); + jPanel5 = new javax.swing.JPanel(); + jLabel3 = new javax.swing.JLabel(); jPanel2 = new javax.swing.JPanel(); formatButton = new javax.swing.JButton(); resetButton = new javax.swing.JButton(); okButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton(); - paramPanel = new javax.swing.JPanel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("XML Column Mapping"); - getContentPane().setLayout(new javax.swing.BoxLayout(getContentPane(), javax.swing.BoxLayout.LINE_AXIS)); + getContentPane().setLayout(new java.awt.GridBagLayout()); + + jPanel3.setLayout(new java.awt.GridBagLayout()); jPanel1.setLayout(new java.awt.GridBagLayout()); @@ -178,13 +260,54 @@ public class ColumnMapperDialog extends javax.swing.JDialog { gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0); jPanel1.add(tableCombobox, gridBagConstraints); + jSplitPane1.setDividerLocation(300); + + jPanel4.setLayout(new java.awt.GridBagLayout()); + jLabel2.setText(" Mapping "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 10; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0); - jPanel1.add(jLabel2, gridBagConstraints); + jPanel4.add(jLabel2, gridBagConstraints); + + paramPanel.setMinimumSize(new java.awt.Dimension(150, 0)); + paramPanel.setLayout(new javax.swing.BoxLayout(paramPanel, javax.swing.BoxLayout.LINE_AXIS)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 3; + gridBagConstraints.gridy = 20; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + jPanel4.add(paramPanel, gridBagConstraints); + + jSplitPane1.setLeftComponent(jPanel4); + + jPanel5.setLayout(new java.awt.GridBagLayout()); + + jLabel3.setText("Sketch"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 0); + jPanel5.add(jLabel3, gridBagConstraints); + + jSplitPane1.setRightComponent(jPanel5); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 10; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + jPanel1.add(jSplitPane1, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + jPanel3.add(jPanel1, gridBagConstraints); jPanel2.setLayout(new java.awt.GridBagLayout()); @@ -229,23 +352,19 @@ public class ColumnMapperDialog extends javax.swing.JDialog { jPanel2.add(cancelButton, new java.awt.GridBagConstraints()); gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 1; + gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 30; gridBagConstraints.gridwidth = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(4, 4, 4, 4); - jPanel1.add(jPanel2, gridBagConstraints); + jPanel3.add(jPanel2, gridBagConstraints); - paramPanel.setMinimumSize(new java.awt.Dimension(150, 0)); - paramPanel.setLayout(new javax.swing.BoxLayout(paramPanel, javax.swing.BoxLayout.LINE_AXIS)); gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 3; - gridBagConstraints.gridy = 20; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; - jPanel1.add(paramPanel, gridBagConstraints); - - getContentPane().add(jPanel1); + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + getContentPane().add(jPanel3, gridBagConstraints); pack(); }// //GEN-END:initComponents @@ -253,7 +372,7 @@ public class ColumnMapperDialog extends javax.swing.JDialog { private void resetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetButtonActionPerformed try { mappingField.beginAtomicEdit(); - setMappingFieldText(XmlUtil.build(table.getXmlTemplateAsDocument(null))); + setMappingFieldText(XmlUtil.build(table.getXmlTemplateAsDocument(XmlUtil.build(table.getDefaultXmlTemplate(null)), null))); mappingField.endAtomicEdit(); mappingField.grabFocus(); } catch (Exception e) { @@ -299,13 +418,18 @@ public class ColumnMapperDialog extends javax.swing.JDialog { mappingField.setCaretPosition(0); } - // Variables declaration - do not modify//GEN-BEGIN:variables + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton cancelButton; private javax.swing.JButton formatButton; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; + private javax.swing.JPanel jPanel3; + private javax.swing.JPanel jPanel4; + private javax.swing.JPanel jPanel5; + private javax.swing.JSplitPane jSplitPane1; private javax.swing.JButton okButton; private javax.swing.JPanel paramPanel; private javax.swing.JButton resetButton; @@ -326,6 +450,20 @@ public class ColumnMapperDialog extends javax.swing.JDialog { private static final long serialVersionUID = -5437578641818236294L; } +// TODO +// TODO parameter panel: +// TODO docu? grundsaetzlich, as link to docu? + +// TODO +// TODO doku: "tutorial"/"preparation": too(ooooo) old! + +// TODO +// TODO H2, Demo DB, somtimes tables get empty- (H2-Bug?) +// TODO !!! 2.3.230 !!! +// TODO find work-around +// TODO symptom: all tables are empty but still exists +// TODO f.e.: copy demo-db before connecting. Check if demo-db is valid ("select count(*) from Employee" > 0, same with sakila) + // TODO // TODO no timer. "auto update" checkbox + "update" button in dialog. + stale-indication. initially check checkbox iff template is small (heuristically, maybe if size < 10000 char?) // TODO was: show sketch. use timer for preventing lags bcof expensive sketch creation (1000+ column templates f.e.) diff --git a/src/main/gui/net/sf/jailer/ui/DbConnectionDialog.java b/src/main/gui/net/sf/jailer/ui/DbConnectionDialog.java index 062549183..13a7a4d23 100644 --- a/src/main/gui/net/sf/jailer/ui/DbConnectionDialog.java +++ b/src/main/gui/net/sf/jailer/ui/DbConnectionDialog.java @@ -891,7 +891,7 @@ public class DbConnectionDialog extends javax.swing.JDialog { ConnectionInfo ci = new ConnectionInfo(executionContext); ci.alias = "Demo Scott"; ci.driverClass = "org.h2.Driver"; - ci.jar1 = "lib" + File.separator + "h2-2.2.224.jar"; + ci.jar1 = "lib" + File.separator + "h2-2.3.230.jar"; ci.url = "jdbc:h2:" + Environment.newFile("demo-scott-h2").getAbsolutePath(); ci.user = "sa"; ci.password = ""; @@ -901,7 +901,7 @@ public class DbConnectionDialog extends javax.swing.JDialog { ci = new ConnectionInfo(executionContext); ci.alias = "Demo Sakila"; ci.driverClass = "org.h2.Driver"; - ci.jar1 = "lib" + File.separator + "h2-2.2.224.jar"; + ci.jar1 = "lib" + File.separator + "h2-2.3.230.jar"; ci.url = "jdbc:h2:" + Environment.newFile("demo-sakila-h2").getAbsolutePath(); ci.user = "sa"; ci.password = ""; diff --git a/src/main/gui/net/sf/jailer/ui/ExtractionModelEditor.form b/src/main/gui/net/sf/jailer/ui/ExtractionModelEditor.form index 1d114a47e..9180e0150 100644 --- a/src/main/gui/net/sf/jailer/ui/ExtractionModelEditor.form +++ b/src/main/gui/net/sf/jailer/ui/ExtractionModelEditor.form @@ -593,7 +593,7 @@ - + diff --git a/src/main/gui/net/sf/jailer/ui/ExtractionModelEditor.java b/src/main/gui/net/sf/jailer/ui/ExtractionModelEditor.java index 011bed43f..785ebc64e 100644 --- a/src/main/gui/net/sf/jailer/ui/ExtractionModelEditor.java +++ b/src/main/gui/net/sf/jailer/ui/ExtractionModelEditor.java @@ -48,6 +48,7 @@ import java.io.PrintWriter; import java.sql.SQLException; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -71,8 +72,6 @@ import javax.swing.DefaultListCellRenderer; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JCheckBoxMenuItem; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; @@ -112,12 +111,6 @@ import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; -import org.apache.commons.lang3.stream.Streams; -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rsyntaxtextarea.SyntaxConstants; - -import com.fasterxml.jackson.annotation.JsonTypeInfo.None; - import net.sf.jailer.ExecutionContext; import net.sf.jailer.database.BasicDataSource; import net.sf.jailer.database.Session; @@ -146,7 +139,6 @@ import net.sf.jailer.ui.graphical_view.GraphicalDataModelView; import net.sf.jailer.ui.scrollmenu.JScrollPopupMenu; import net.sf.jailer.ui.syntaxtextarea.DataModelBasedSQLCompletionProvider; import net.sf.jailer.ui.syntaxtextarea.RSyntaxTextAreaWithSQLSyntaxStyle; -import net.sf.jailer.ui.syntaxtextarea.RSyntaxTextAreaWithTheme; import net.sf.jailer.ui.syntaxtextarea.SQLCompletionProvider; import net.sf.jailer.ui.undo.CompensationAction; import net.sf.jailer.ui.undo.UndoManager; @@ -300,7 +292,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa return dataModel.getParameters(condition.getText(), extractionModel.additionalSubjects); } }; - columnMapperDialog = new ColumnMapperDialog(extractionModelFrame, parametersGetter); + columnMapperDialog = new ColumnMapperDialog(extractionModelFrame, parametersGetter, executionContext); boolean isNew; if (extractionModelFile == null || !new File(extractionModelFile).exists()) { needsSave = extractionModelFile != null; @@ -945,7 +937,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa } DefaultComboBoxModel formatComboBoxModel = new DefaultComboBoxModel(); - Streams.of(ScriptFormat.values()).forEach(sf -> { + Arrays.stream(ScriptFormat.values()).forEach(sf -> { formatComboBoxModel.addElement(sf); if (sf.separatorFollowed) { formatComboBoxModel.addElement(null); @@ -1459,14 +1451,38 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa ((GridLayout) editorPanel.getLayout()).setVgap(1); ((GridLayout) editorPanel.getLayout()).setColumns(1); if (false && scriptFormat.isObjectNotation()) { // TODO - gridBagConstraints = new java.awt.GridBagConstraints(); + + JPanel sketchPanel = new JPanel(); + sketchPanel.setLayout(new java.awt.GridBagLayout()); + + jLabel12.setFont(jLabel12.getFont().deriveFont(jLabel12.getFont().getStyle() | java.awt.Font.BOLD)); + jLabel12.setText(" " + scriptFormat + " Sketches"); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 5; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new java.awt.Insets(8, 0, 4, 0); + sketchPanel.add(jLabel12, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 11; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 8, 0, 0); + sketchPanel.add(sketchTabbedPane, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 3; gridBagConstraints.weightx = 1; gridBagConstraints.weighty = 0.6; gridBagConstraints.fill = GridBagConstraints.BOTH; gridBagConstraints.insets = new Insets(0, 0, 0, 0); - panel2.add(xmlMappingPanel, gridBagConstraints); + panel2.add(sketchPanel, gridBagConstraints); // editorPanel.add(xmlMappingPanel); ((GridLayout) editorPanel.getLayout()).setRows(1); } else { @@ -1559,7 +1575,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa jPanel8 = new javax.swing.JPanel(); jLabel7 = new javax.swing.JLabel(); jPanel10 = new javax.swing.JPanel(); - exportFormat = new javax.swing.JComboBox(); + exportFormat = new JComboBox2(); exportButton = new javax.swing.JButton(); openXmlSettings = new javax.swing.JButton(); jLabel10 = new javax.swing.JLabel(); @@ -1581,7 +1597,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); jLabel3 = new javax.swing.JLabel(); - aggregationCombobox = new javax.swing.JComboBox(); + aggregationCombobox = new JComboBox2(); tagField = new javax.swing.JTextField(); jPanel5 = new javax.swing.JPanel(); xmlTagApply = new javax.swing.JButton(); @@ -1714,7 +1730,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa layeredPane.setLayer(focusPanel, javax.swing.JLayeredPane.PALETTE_LAYER); layeredPane.add(focusPanel); - focusPanel.setBounds(0, 0, 359, 32); + focusPanel.setBounds(0, 0, 345, 32); rightBorderPanel.setOpaque(false); rightBorderPanel.setLayout(new java.awt.GridBagLayout()); @@ -1851,11 +1867,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa exportFormat.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); exportFormat.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { - if (exportFormat.getSelectedItem() == null) { - UIUtil.invokeLater(() -> exportFormat.setSelectedItem(scriptFormat)); - } else { - onExportModusChanged(evt); - } + onExportModusChanged(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -1960,7 +1972,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa gridBagConstraints.gridy = 7; gridBagConstraints.gridwidth = 4; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - gridBagConstraints.insets = new java.awt.Insets(4, 0, 4, 0); + gridBagConstraints.insets = new java.awt.Insets(4, 0, 6, 0); jPanel3.add(jPanel16, gridBagConstraints); jPanel17.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 8)); @@ -2976,46 +2988,23 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa * Updates the XML sketch component. */ private void updateSketch() { - try { - sketchTabbedPane.removeAll(); - - if (currentAssociation != null) { - addSketchTab(currentAssociation.source); - if (currentAssociation.source != currentAssociation.destination) { - addSketchTab(currentAssociation.destination); - } - sketchTabbedPane.setSelectedIndex(0); - } else { - if (root != null) { - addSketchTab(root); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * Adds a tab to the sketch-tabbedpane for a given table. - * - * @param table the table - */ - private void addSketchTab(Table table) throws Exception { - JScrollPane tab = new JScrollPane(); - RSyntaxTextArea xmlSketch = new RSyntaxTextAreaWithTheme(); - - xmlSketch.setEditable(false); - tab.setViewportView(xmlSketch); - xmlSketch.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); - xmlSketch.setCodeFoldingEnabled(true); - - String tabName = null; - String sketch = ""; - tabName = dataModel.getDisplayName(table); - sketch = XmlSketchBuilder.buildSketch(table, 1); - xmlSketch.setText(sketch); - xmlSketch.setCaretPosition(0); - sketchTabbedPane.addTab(tabName, tab); +// try { +// sketchTabbedPane.removeAll(); +// +// if (currentAssociation != null) { +// addSketchTab(currentAssociation.source); +// if (currentAssociation.source != currentAssociation.destination) { +// addSketchTab(currentAssociation.destination); +// } +// sketchTabbedPane.setSelectedIndex(0); +// } else { +// if (root != null) { +// addSketchTab(root); +// } +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } } /** @@ -3925,7 +3914,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa public void openColumnMapper(Table table) { String old = table.getXmlTemplate(); columnMapperDialog.setTitle(scriptFormat + " Column Mapping"); - if (columnMapperDialog.edit(dataModel, table)) { + if (columnMapperDialog.edit(dataModel, table, scriptFormat)) { String template = table.getXmlTemplate(); table.setXmlTemplate(old); changeXmlTemplate(table, template); @@ -4161,7 +4150,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton activateDesictionPendingButton; private javax.swing.JButton additionalSubjectsButton; - private javax.swing.JComboBox aggregationCombobox; + private JComboBox2 aggregationCombobox; private javax.swing.JLabel assocStatsLabel; private javax.swing.JLabel associatedWith; javax.swing.JTextField condition; @@ -4169,7 +4158,7 @@ public class ExtractionModelEditor extends javax.swing.JPanel implements PlafAwa private javax.swing.JLabel dependsOn; private javax.swing.JPanel editorPanel; public javax.swing.JButton exportButton; - private javax.swing.JComboBox exportFormat; + private JComboBox2 exportFormat; private javax.swing.JPanel focusLabelPanel; javax.swing.JPanel focusPanel; private javax.swing.JPanel graphContainer; diff --git a/src/main/gui/net/sf/jailer/ui/UIUtil.java b/src/main/gui/net/sf/jailer/ui/UIUtil.java index f9699ec5d..4150f8ec7 100644 --- a/src/main/gui/net/sf/jailer/ui/UIUtil.java +++ b/src/main/gui/net/sf/jailer/ui/UIUtil.java @@ -152,6 +152,7 @@ import net.sf.jailer.datamodel.DataModel; import net.sf.jailer.datamodel.Table; import net.sf.jailer.ddl.DDLCreator; import net.sf.jailer.progress.ProgressListener; +import net.sf.jailer.subsetting.ObjectNotationOutputException; import net.sf.jailer.subsetting.RowLimitExceededException; import net.sf.jailer.ui.databrowser.DetailsView; import net.sf.jailer.ui.databrowser.Row; @@ -864,7 +865,7 @@ public class UIUtil { if (t instanceof DataModel.NoPrimaryKeyException || t instanceof CycleFinder.CycleFoundException) { context = EXCEPTION_CONTEXT_USER_ERROR; } - if (t instanceof RowLimitExceededException) { + if (t instanceof RowLimitExceededException || t instanceof ObjectNotationOutputException) { context = EXCEPTION_CONTEXT_USER_WARNING; } if (t instanceof SqlException) { diff --git a/src/main/gui/net/sf/jailer/ui/XmlSettingsDialog.form b/src/main/gui/net/sf/jailer/ui/XmlSettingsDialog.form index 23c3bffa2..c79ba5b55 100644 --- a/src/main/gui/net/sf/jailer/ui/XmlSettingsDialog.form +++ b/src/main/gui/net/sf/jailer/ui/XmlSettingsDialog.form @@ -147,13 +147,14 @@ - + - + + @@ -208,13 +209,13 @@ - + - + diff --git a/src/main/gui/net/sf/jailer/ui/XmlSettingsDialog.java b/src/main/gui/net/sf/jailer/ui/XmlSettingsDialog.java index 770f7c0ba..c445da43b 100644 --- a/src/main/gui/net/sf/jailer/ui/XmlSettingsDialog.java +++ b/src/main/gui/net/sf/jailer/ui/XmlSettingsDialog.java @@ -42,8 +42,8 @@ public class XmlSettingsDialog extends javax.swing.JDialog { setModal(true); ButtonGroup buttonGroup = new ButtonGroup(); - buttonGroup.add(singleRoot); buttonGroup.add(multipleRoots); + buttonGroup.add(singleRoot); buttonGroup = new ButtonGroup(); buttonGroup.add(include); @@ -91,8 +91,8 @@ public class XmlSettingsDialog extends javax.swing.JDialog { rootTag.setVisible(scriptFormat == ScriptFormat.XML); xmlRootTagLabel.setVisible(scriptFormat == ScriptFormat.XML); - multipleRoots.setSelected(!xmlSettings.singleRoot); singleRoot.setSelected(xmlSettings.singleRoot); + multipleRoots.setSelected(!xmlSettings.singleRoot); ignore.setSelected(xmlSettings.ignoreNonAggregated); include.setSelected(xmlSettings.includeNonAggregated); disallow.setSelected(xmlSettings.disallowNonAggregated); @@ -226,12 +226,12 @@ public class XmlSettingsDialog extends javax.swing.JDialog { rootTag = new javax.swing.JTextField(); dateExample = new javax.swing.JLabel(); timestampExample = new javax.swing.JLabel(); - multipleRoots = new javax.swing.JCheckBox(); + singleRoot = new javax.swing.JCheckBox(); jPanel2 = new javax.swing.JPanel(); Ok = new javax.swing.JButton(); cancelButton = new javax.swing.JButton(); xmlRootTagLabel1 = new javax.swing.JLabel(); - singleRoot = new javax.swing.JCheckBox(); + multipleRoots = new javax.swing.JCheckBox(); include = new javax.swing.JCheckBox(); xmlRootTagLabel2 = new javax.swing.JLabel(); xmlRootTagLabel3 = new javax.swing.JLabel(); @@ -343,11 +343,16 @@ public class XmlSettingsDialog extends javax.swing.JDialog { gridBagConstraints.insets = new java.awt.Insets(0, 8, 0, 8); jPanel3.add(timestampExample, gridBagConstraints); - multipleRoots.setText("Single Object"); - multipleRoots.setToolTipText("Writes a single root/subject object with all its aggregated sub-objects.
The export process fails if more than one such object exists."); - multipleRoots.addItemListener(new java.awt.event.ItemListener() { + singleRoot.setText("Single Object"); + singleRoot.setToolTipText("Writes a single root/subject object with all its aggregated sub-objects.
The export process fails if more than one such object exists."); + singleRoot.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { - multipleRootsItemStateChanged(evt); + singleRootItemStateChanged(evt); + } + }); + singleRoot.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + singleRootActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); @@ -355,7 +360,7 @@ public class XmlSettingsDialog extends javax.swing.JDialog { gridBagConstraints.gridy = 20; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.insets = new java.awt.Insets(8, 0, 0, 0); - jPanel3.add(multipleRoots, gridBagConstraints); + jPanel3.add(singleRoot, gridBagConstraints); jPanel2.setLayout(new java.awt.GridBagLayout()); @@ -403,18 +408,18 @@ public class XmlSettingsDialog extends javax.swing.JDialog { gridBagConstraints.insets = new java.awt.Insets(8, 0, 0, 0); jPanel3.add(xmlRootTagLabel1, gridBagConstraints); - singleRoot.setText("Multiple Objects (Array)"); - singleRoot.setToolTipText("Writes an array/collection of all root/subject objects with all their aggregated sub-objects."); - singleRoot.addItemListener(new java.awt.event.ItemListener() { + multipleRoots.setText("Multiple Objects (Array)"); + multipleRoots.setToolTipText("Writes an array/collection of all root/subject objects with all their aggregated sub-objects."); + multipleRoots.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { - singleRootItemStateChanged(evt); + multipleRootsItemStateChanged(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 10; gridBagConstraints.gridy = 22; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; - jPanel3.add(singleRoot, gridBagConstraints); + jPanel3.add(multipleRoots, gridBagConstraints); include.setText("Include"); include.setToolTipText("Write out all objects that are not aggregated in any other object at root level."); @@ -508,14 +513,14 @@ public class XmlSettingsDialog extends javax.swing.JDialog { dispose(); }//GEN-LAST:event_cancelButtonActionPerformed - private void multipleRootsItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_multipleRootsItemStateChanged - rootTag.setEditable(!multipleRoots.isSelected()); - }//GEN-LAST:event_multipleRootsItemStateChanged - private void singleRootItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_singleRootItemStateChanged - // TODO add your handling code here: + rootTag.setEditable(!multipleRoots.isSelected()); }//GEN-LAST:event_singleRootItemStateChanged + private void multipleRootsItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_multipleRootsItemStateChanged + rootTag.setEditable(!multipleRoots.isSelected()); + }//GEN-LAST:event_multipleRootsItemStateChanged + private void includeItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_includeItemStateChanged // TODO add your handling code here: }//GEN-LAST:event_includeItemStateChanged @@ -527,6 +532,10 @@ public class XmlSettingsDialog extends javax.swing.JDialog { private void disallowItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_disallowItemStateChanged // TODO add your handling code here: }//GEN-LAST:event_disallowItemStateChanged + + private void singleRootActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_singleRootActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_singleRootActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton Ok; diff --git a/src/main/gui/net/sf/jailer/ui/XmlSketchBuilder.java b/src/main/gui/net/sf/jailer/ui/XmlSketchBuilder.java index 28b352bd6..b341cf6df 100644 --- a/src/main/gui/net/sf/jailer/ui/XmlSketchBuilder.java +++ b/src/main/gui/net/sf/jailer/ui/XmlSketchBuilder.java @@ -15,7 +15,11 @@ */ package net.sf.jailer.ui; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -31,11 +35,15 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; +import net.sf.jailer.ExecutionContext; import net.sf.jailer.datamodel.AggregationSchema; import net.sf.jailer.datamodel.Association; import net.sf.jailer.datamodel.Cardinality; import net.sf.jailer.datamodel.Table; +import net.sf.jailer.subsetting.ScriptFormat; +import net.sf.jailer.xml.XmlRowWriter; import net.sf.jailer.xml.XmlUtil; +import net.sf.jailer.xml.XmlUtil.ObjectNotationTransformerHandler; /** * Builds XML sketches. @@ -50,19 +58,53 @@ public class XmlSketchBuilder { * @param table the table * @return xml sketch for table */ - public static String buildSketch(Table table, int depth) throws Exception { + public static String buildSketch(Table table, int depth, ScriptFormat scriptFormat, ExecutionContext executionContext) throws Exception { if (table == null) { return ""; } Document sketch = table.getXmlTemplateAsDocument(null); if (sketch.getChildNodes().getLength() > 0 && sketch.getChildNodes().item(0) instanceof Element) { - insertAssociationSketch(sketch.getChildNodes().item(0), table, sketch, depth); + insertAssociationSketch(sketch.getChildNodes().item(0), table, sketch, scriptFormat, depth); } - return XmlUtil.buildOmitDeclaration(sketch); + String result = XmlUtil.buildOmitDeclaration(sketch); + if (scriptFormat != ScriptFormat.XML) { + OutputStream out = new ByteArrayOutputStream(); + ObjectNotationTransformerHandler th = XmlUtil.createObjectNotationTransformerHandler("", "", new OutputStreamWriter(out, Charset.defaultCharset()), true, scriptFormat, executionContext); + XmlRowWriter xmlRowWriter = new XmlRowWriter(out, null, null, null, null, scriptFormat, Charset.defaultCharset(), th, executionContext); + XmlUtil.visitDocumentNodes(XmlUtil.parse(result), xmlRowWriter.new XmlWritingNodeVisitor(null, null, table, null, null) { + @Override + protected Object toContent(String text, boolean returnNull) { + return "..."; + } + @Override + public void visitAssociationElement(String associationName, String name) { + if (name != null) { + table.associations.stream().filter(a -> associationName.equals(a.getName())).findAny() + .ifPresent(a -> { + th.associationSketch(a, associationName, name); + }); + } + } + }); + th.endDocument(); + result = out.toString(); + } + return result; } - private static void insertAssociationSketch(Node node, Table table, Document doc, int depth) throws DOMException, ParserConfigurationException, SAXException, IOException { + private static void insertAssociationSketch(Node node, Table table, Document doc, ScriptFormat scriptFormat, int depth) throws DOMException, ParserConfigurationException, SAXException, IOException { NodeList children = node.getChildNodes(); + if (node instanceof Element) { + Node a0 = ((Element) node).getAttributes().item(0); + if (a0 != null && a0.getNodeName() != null && a0.getNodeName().startsWith("j:")) { + ((Element) node).removeAttribute(a0.getNodeName()); + } + Node ch = ((Element) node).getFirstChild(); + if (ch != null && ch.getTextContent().trim().startsWith("SQL:")) { + ((Element) node).removeChild(ch); + ((Element) node).appendChild(doc.createTextNode("...")); + } + } int i = 0; while (i < children.getLength()) { if (children.item(i) instanceof Element) { @@ -77,17 +119,22 @@ public class XmlSketchBuilder { } } } - if (association != null && depth < 3) { - Node[] ae = insertAssociationSketch(association, doc, depth + 1); + if (association != null && depth < 5) { + Node[] ae = insertAssociationSketch(association, doc, scriptFormat, depth + 1); if (ae != null) { for (Node n: ae) { node.insertBefore(doc.importNode(n, true), e); ++i; } } - } - node.removeChild(e); + } + if (scriptFormat == ScriptFormat.XML) { + node.removeChild(e); + } else { + ++i; + } } else { + insertAssociationSketch(e, table, doc, scriptFormat, depth + 1); ++i; } } else { @@ -96,37 +143,39 @@ public class XmlSketchBuilder { } } - private static Node[] insertAssociationSketch(Association association, Document doc, int depth) throws ParserConfigurationException, SAXException, IOException { - if (association.getAggregationSchema() == AggregationSchema.EXPLICIT_LIST) { - Element e1 = doc.createElement(association.getAggregationTagName()); - Element e2 = doc.createElement(association.destination.getUnqualifiedName().toLowerCase(Locale.ENGLISH)); - e1.appendChild(e2); - if (association.getCardinality() != Cardinality.MANY_TO_ONE && association.getCardinality() != Cardinality.ONE_TO_ONE) { - e1.appendChild(doc.createComment("...")); + private static Node[] insertAssociationSketch(Association association, Document doc, ScriptFormat scriptFormat, int depth) throws ParserConfigurationException, SAXException, IOException { + if (scriptFormat == ScriptFormat.XML) { + if (association.getAggregationSchema() == AggregationSchema.EXPLICIT_LIST) { + Element e1 = doc.createElement(association.getAggregationTagName()); + Element e2 = doc.createElement(association.destination.getUnqualifiedName().toLowerCase(Locale.ENGLISH)); + e1.appendChild(e2); + // if (association.getCardinality() != Cardinality.MANY_TO_ONE && association.getCardinality() != Cardinality.ONE_TO_ONE) { + e1.appendChild(doc.createTextNode("...")); + // } + return new Node[] { e1 }; } - return new Node[] { e1 }; - } - else if (association.getAggregationSchema() == AggregationSchema.IMPLICIT_LIST) { - Element e1 = doc.createElement(association.getAggregationTagName()); - if (association.getCardinality() != Cardinality.MANY_TO_ONE && association.getCardinality() != Cardinality.ONE_TO_ONE) { - Node c = doc.createComment("..."); - return new Node[] { e1, c }; + else if (association.getAggregationSchema() == AggregationSchema.IMPLICIT_LIST) { + Element e1 = doc.createElement(association.getAggregationTagName()); + // if (association.getCardinality() != Cardinality.MANY_TO_ONE && association.getCardinality() != Cardinality.ONE_TO_ONE) { + e1.appendChild(doc.createTextNode("...")); + return new Node[] { e1 }; + // } + // return new Node[] { e1 }; + } else if (association.getAggregationSchema() == AggregationSchema.FLAT) { + Document sketch = association.destination.getXmlTemplateAsDocument(null); + List nodes = new ArrayList(); + if (sketch.getChildNodes().getLength() > 0 && sketch.getChildNodes().item(0) instanceof Element) { + insertAssociationSketch(sketch.getChildNodes().item(0), association.destination, sketch, scriptFormat, depth + 1); + } + NodeList children = sketch.getChildNodes().item(0).getChildNodes(); + for (int i = 0; i < children.getLength(); ++i) { + nodes.add(children.item(i)); + } + // if (association.getCardinality() != Cardinality.MANY_TO_ONE && association.getCardinality() != Cardinality.ONE_TO_ONE) { + // nodes.add(sketch.createTextNode("...")); + // } + return nodes.toArray(new Node[nodes.size()]); } - return new Node[] { e1 }; - } else if (association.getAggregationSchema() == AggregationSchema.FLAT) { - Document sketch = association.destination.getXmlTemplateAsDocument(null); - List nodes = new ArrayList(); - if (sketch.getChildNodes().getLength() > 0 && sketch.getChildNodes().item(0) instanceof Element) { - insertAssociationSketch(sketch.getChildNodes().item(0), association.destination, sketch, depth + 1); - } - NodeList children = sketch.getChildNodes().item(0).getChildNodes(); - for (int i = 0; i < children.getLength(); ++i) { - nodes.add(children.item(i)); - } - if (association.getCardinality() != Cardinality.MANY_TO_ONE && association.getCardinality() != Cardinality.ONE_TO_ONE) { - nodes.add(sketch.createComment("...")); - } - return nodes.toArray(new Node[nodes.size()]); } return null; }