/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* 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 org.jkiss.dbeaver.tools.compare;
import org.jkiss.dbeaver.model.navigator.DBNDatabaseFolder;
import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.xml.XMLBuilder;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
public class CompareReportRenderer {
private CompareReport report;
private XMLBuilder xml;
private CompareObjectsSettings settings;
public void renderReport(DBRProgressMonitor monitor, CompareReport report, CompareObjectsSettings settings, OutputStream outputStream) throws IOException
{
this.report = report;
this.settings = settings;
this.xml = new XMLBuilder(outputStream, GeneralUtils.UTF8_ENCODING, true);
this.xml.setButify(true);
xml.addContent(
"<!DOCTYPE html \n" +
" PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" +
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
if (settings.isShowOnlyDifferences()) {
// Mark differences on tree nodes
List<CompareReportLine> reportLines = report.getReportLines();
int reportLinesSize = reportLines.size();
for (int i = 0; i < reportLinesSize; i++) {
if (reportLines.get(i).hasDifference) {
int depth = reportLines.get(i).depth;
for (int k = i - 1; k >= 0; k--) {
CompareReportLine prevNode = reportLines.get(k);
if (prevNode.depth < depth) {
if (prevNode.hasDifference) {
// Already set
break;
}
depth = prevNode.depth;
prevNode.hasDifference = true;
}
}
}
}
}
xml.startElement("html");
xml.startElement("head");
xml.startElement("meta");
xml.addAttribute("http-equiv", "Content-type");
xml.addAttribute("content", "text/html; charset=utf-8");
xml.endElement();
xml.startElement("title");
xml.addText("Compare report");
xml.endElement();
xml.endElement();
xml.startElement("body");
renderHeader();
xml.startElement("table");
xml.addAttribute("width", "100%");
//xml.addAttribute("border", "1");
xml.addAttribute("cellspacing", 0);
xml.addAttribute("cellpadding", 0);
renderBody(monitor);
xml.endElement();
xml.endElement();
xml.endElement();
this.xml.flush();
}
private void renderHeader() throws IOException
{
int maxLevel = 0;
for (CompareReportLine line : report.getReportLines()) {
if (line.depth > maxLevel) {
maxLevel = line.depth;
}
}
maxLevel++;
xml.startElement("style");
StringBuilder styles = new StringBuilder();
styles.append("table {font-family:\"Lucida Sans Unicode\", \"Lucida Grande\", Sans-Serif;font-size:12px;text-align:left;} ");
styles.append(".missing {color:red;} .differs {color:red;} ");
styles.append(".object td,th {border-top:solid 1px; border-right:solid 1px; border-color: black; white-space:nowrap;} ");
styles.append(".property td,th {border-right:solid 1px; border-color: black; white-space:nowrap; } ");
styles.append(".struct {border-top:none; !important } ");
// styles.append(".object:first-child {border:none; } ");
// styles.append(".property:first-child {border:none; } ");
for (int i = 1; i <= maxLevel; i++) {
styles.append(".level").append(i).append(" td,th { text-align:left; padding-left:").append(20 * i).append("px; } ");
}
xml.addText(styles.toString(), false);
xml.endElement();
}
private void renderBody(DBRProgressMonitor monitor) throws IOException
{
// Table head
xml.startElement("tr");
xml.startElement("th");
xml.addText("Structure");
xml.endElement();
for (DBNDatabaseNode node : report.getNodes()) {
xml.startElement("th");
xml.addText(node.getNodeFullName());
xml.endElement();
}
xml.endElement();
// Table body
boolean showOnlyDifferences = settings.isShowOnlyDifferences();
int objectCount = report.getNodes().size();
List<CompareReportLine> reportLines = report.getReportLines();
int reportLinesSize = reportLines.size();
for (int i = 0; i < reportLinesSize; i++) {
monitor.worked(1);
CompareReportLine line = reportLines.get(i);
if (showOnlyDifferences && !line.hasDifference) {
continue;
}
boolean onlyStructure = line.structure instanceof DBNDatabaseFolder && !line.hasDifference;
// Skip empty folders
if (onlyStructure && (i >= reportLinesSize - 1 || reportLines.get(i + 1).depth <= line.depth)) {
continue;
}
xml.startElement("tr");
xml.addAttribute("class", "object level" + line.depth);
xml.startElement("td");
xml.addText(line.structure.getNodeType());
xml.endElement();
if (onlyStructure) {
xml.startElement("td");
xml.addAttribute("colspan", line.nodes.length);
xml.addText(" ", false);
xml.endElement();
} else {
for (int k = 0; k < objectCount; k++) {
xml.startElement("td");
if (line.nodes[k] == null) {
xml.addAttribute("class", "missing");
xml.addText("N/A");
} else {
xml.addText(line.nodes[k].getName());
}
xml.endElement();
}
}
xml.endElement();
if (line.properties != null) {
for (CompareReportProperty reportProperty : line.properties) {
boolean differs = false;
Object firstValue = null;
boolean hasValue = false;
for (int k = 0; k < reportProperty.values.length; k++) {
if (line.nodes[k] == null) {
// Ignore properties of missing objects
continue;
}
Object value = reportProperty.values[k];
if (value != null) {
hasValue = true;
if (firstValue == null) {
firstValue = value;
}
}
if (!CompareUtils.equalPropertyValues(value, firstValue)) {
differs = true;
break;
}
}
if (!hasValue) {
// Skip[ properties when nobody have it's value
continue;
}
if (showOnlyDifferences && !differs) {
continue;
}
xml.startElement("tr");
xml.addAttribute("class", "property level" + (line.depth + 1) + (differs ? " differs" : ""));
xml.startElement("td");
xml.addText(reportProperty.property.getDisplayName());
xml.endElement();
for (int k = 0; k < objectCount; k++) {
xml.startElement("td");
String stringValue = "";
if (reportProperty.values[k] != null) {
stringValue = reportProperty.values[k].toString();
}
if (CommonUtils.isEmpty(stringValue)) {
xml.addText(" ", false);
} else {
xml.addText(stringValue);
}
xml.endElement();
}
xml.endElement();
}
}
}
// Table footer
xml.startElement("tr");
xml.addAttribute("class", "object");
xml.startElement("td");
xml.addAttribute("colspan", report.getNodes().size() + 1);
xml.addText("" + reportLines.size() + " objects compared");
xml.endElement();
xml.endElement();
}
}