schemacrawler.tools.text.schema.SchemaDotFormatter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of schemacrawler Show documentation
Show all versions of schemacrawler Show documentation
SchemaCrawler is an open-source Java API that makes working with database metadata as easy as working with plain old Java objects. SchemaCrawler is also a database schema discovery and comprehension, and schema documentation tool. You can search for database schema objects using regular expressions, and output the schema and data in a readable text format. The output is designed to be diff-ed against other database schemas.
/*
* SchemaCrawler
* http://www.schemacrawler.com
* Copyright (c) 2000-2016, Sualeh Fatehi.
* This library is free software; you can redistribute it and/or modify it under
* the terms
* of the GNU Lesser General Public License as published by the Free Software
* Foundation;
* either version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
package schemacrawler.tools.text.schema;
import static schemacrawler.tools.analysis.counts.CountsUtility.getRowCountMessage;
import static schemacrawler.tools.analysis.counts.CountsUtility.hasRowCount;
import static schemacrawler.utility.MetaDataUtility.findForeignKeyCardinality;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import schemacrawler.schema.BaseForeignKey;
import schemacrawler.schema.Column;
import schemacrawler.schema.ColumnDataType;
import schemacrawler.schema.ColumnReference;
import schemacrawler.schema.ForeignKeyColumnReference;
import schemacrawler.schema.Routine;
import schemacrawler.schema.Sequence;
import schemacrawler.schema.Synonym;
import schemacrawler.schema.Table;
import schemacrawler.schemacrawler.SchemaCrawlerException;
import schemacrawler.tools.analysis.associations.WeakAssociationForeignKey;
import schemacrawler.tools.analysis.associations.WeakAssociationsUtility;
import schemacrawler.tools.integration.graph.GraphOptions;
import schemacrawler.tools.options.OutputOptions;
import schemacrawler.tools.options.TextOutputFormat;
import schemacrawler.tools.text.base.BaseDotFormatter;
import schemacrawler.tools.text.utility.html.Alignment;
import schemacrawler.tools.text.utility.html.TableRow;
import schemacrawler.tools.traversal.SchemaTraversalHandler;
import schemacrawler.utility.MetaDataUtility.ForeignKeyCardinality;
import schemacrawler.utility.NamedObjectSort;
import sf.util.Color;
/**
* GraphViz DOT formatting of schema.
*
* @author Sualeh Fatehi
*/
public final class SchemaDotFormatter
extends BaseDotFormatter
implements SchemaTraversalHandler
{
private final boolean isVerbose;
private final boolean isBrief;
/**
* Text formatting of schema.
*
* @param schemaTextDetailType
* Types for text formatting of schema
* @param options
* Options for text formatting of schema
* @param outputOptions
* Options for text formatting of schema
* @throws SchemaCrawlerException
* On an exception
*/
public SchemaDotFormatter(final SchemaTextDetailType schemaTextDetailType,
final SchemaTextOptions options,
final OutputOptions outputOptions)
throws SchemaCrawlerException
{
super(options,
schemaTextDetailType == SchemaTextDetailType.details,
outputOptions);
isVerbose = schemaTextDetailType == SchemaTextDetailType.details;
isBrief = schemaTextDetailType == SchemaTextDetailType.brief;
}
@Override
public void handle(final ColumnDataType columnDataType)
throws SchemaCrawlerException
{
}
/**
* Provides information on the database schema.
*
* @param routine
* Routine metadata.
*/
@Override
public void handle(final Routine routine)
{
}
/**
* Provides information on the database schema.
*
* @param sequence
* Sequence metadata.
*/
@Override
public void handle(final Sequence sequence)
{
}
/**
* Provides information on the database schema.
*
* @param synonym
* Synonym metadata.
*/
@Override
public void handle(final Synonym synonym)
{
}
@Override
public void handle(final Table table)
{
final String tableName;
if (options.isShowUnqualifiedNames())
{
tableName = table.getName();
}
else
{
tableName = table.getFullName();
}
final String tableType = "[" + table.getTableType() + "]";
final Color tableNameBgColor = colorMap.getColor(table);
final int colspan = options.isShowOrdinalNumbers()? 3: 2;
formattingHelper.append(" /* ").append(table.getFullName())
.append(" -=-=-=-=-=-=-=-=-=-=-=-=-=- */").println();
formattingHelper.append(" \"").append(nodeId(table)).append("\" [")
.println();
formattingHelper.append(" label=<").println();
formattingHelper
.append(" ")
.println();
formattingHelper.append(new TableRow(TextOutputFormat.html)
.add(newTableCell(tableName,
Alignment.left,
true,
tableNameBgColor,
colspan))
.add(newTableCell(tableType, Alignment.right, false, tableNameBgColor, 1))
.toString()).println();
printTableRemarks(table);
final List columns = table.getColumns();
printTableColumns(columns, false);
printTableRowCount(table);
formattingHelper.append("
").println();
formattingHelper.append(" >").println();
formattingHelper.append(" ];").println();
formattingHelper.println();
printForeignKeys(table);
if (isVerbose)
{
printWeakAssociations(table);
}
formattingHelper.println();
formattingHelper.println();
}
@Override
public void handleColumnDataTypesEnd()
{
}
@Override
public void handleColumnDataTypesStart()
{
}
@Override
public void handleRoutinesEnd()
throws SchemaCrawlerException
{
}
@Override
public void handleRoutinesStart()
throws SchemaCrawlerException
{
}
@Override
public void handleSequencesEnd()
throws SchemaCrawlerException
{
}
@Override
public void handleSequencesStart()
throws SchemaCrawlerException
{
}
@Override
public void handleSynonymsEnd()
throws SchemaCrawlerException
{
}
@Override
public void handleSynonymsStart()
throws SchemaCrawlerException
{
}
@Override
public void handleTablesEnd()
throws SchemaCrawlerException
{
}
@Override
public void handleTablesStart()
throws SchemaCrawlerException
{
}
private String arrowhead(final ForeignKeyCardinality connectivity)
{
switch (connectivity)
{
case unknown:
return "box";
case zero_one:
return "teeodot";
case zero_many:
return "crowodot";
case one_one:
return "teetee";
default:
return "box";
}
}
private String[] getPortIds(final Column column, final boolean isNewNode)
{
final String portIds[] = new String[2];
if (!isNewNode)
{
portIds[0] = String.format("\"%s\":\"%s.start\"",
nodeId(column.getParent()),
nodeId(column));
portIds[1] = String.format("\"%s\":\"%s.end\"",
nodeId(column.getParent()),
nodeId(column));
}
else
{
// Create new node
final String nodeId = printNewNode(column);
//
portIds[0] = nodeId;
portIds[1] = nodeId;
}
return portIds;
}
private String printColumnReference(final String fkName,
final ColumnReference columnRef,
final ForeignKeyCardinality fkCardinality,
final boolean isFkColumnFiltered)
{
final boolean isForeignKey = columnRef instanceof ForeignKeyColumnReference;
final Column primaryKeyColumn = columnRef.getPrimaryKeyColumn();
final Column foreignKeyColumn = columnRef.getForeignKeyColumn();
final String[] pkPortIds = getPortIds(primaryKeyColumn, false);
final String[] fkPortIds = getPortIds(foreignKeyColumn, isFkColumnFiltered);
final GraphOptions graphOptions = (GraphOptions) options;
final String pkSymbol;
if (graphOptions.isShowPrimaryKeyCardinality())
{
pkSymbol = "teetee";
}
else
{
pkSymbol = "none";
}
final String fkSymbol;
if (graphOptions.isShowForeignKeyCardinality())
{
fkSymbol = arrowhead(fkCardinality);
}
else
{
fkSymbol = "none";
}
final String style;
if (isForeignKey)
{
style = "solid";
}
else
{
style = "dashed";
}
final String associationName;
if (options.isHideForeignKeyNames() || !isForeignKey)
{
associationName = "";
}
else
{
associationName = fkName;
}
return String
.format(" %s:w -> %s:e [label=<%s> style=\"%s\" dir=\"both\" arrowhead=\"%s\" arrowtail=\"%s\"];%n",
fkPortIds[0],
pkPortIds[1],
associationName,
style,
pkSymbol,
fkSymbol);
}
private void printForeignKeys(final Table table)
{
printForeignKeys(table, table.getForeignKeys());
}
private void printForeignKeys(final Table table,
final Collection extends BaseForeignKey>> foreignKeys)
{
for (final BaseForeignKey extends ColumnReference> foreignKey: foreignKeys)
{
final ForeignKeyCardinality fkCardinality = findForeignKeyCardinality(foreignKey);
for (final ColumnReference columnRef: foreignKey)
{
final Table referencedTable = columnRef.getForeignKeyColumn()
.getParent();
final boolean isForeignKeyFiltered = referencedTable
.getAttribute("schemacrawler.table.no_grep_match", false);
if (isForeignKeyFiltered)
{
continue;
}
final boolean isFkColumnFiltered = referencedTable
.getAttribute("schemacrawler.table.filtered_out", false);
if (table.equals(columnRef.getPrimaryKeyColumn().getParent()))
{
formattingHelper
.append(printColumnReference(foreignKey.getName(),
columnRef,
fkCardinality,
isFkColumnFiltered));
}
}
}
}
private String printNewNode(final Column column)
{
final String nodeId = "\"" + nodeId(column) + "\"";
final String columnName;
if (options.isShowUnqualifiedNames())
{
columnName = column.getShortName();
}
else
{
columnName = column.getFullName();
}
final String columnNode = String.format(" %s [label=<%s>];%n",
nodeId,
columnName);
formattingHelper.append(columnNode);
return nodeId;
}
private void printTableColumnAutoIncremented(final Column column)
{
if (column == null || !column.isAutoIncremented())
{
return;
}
final TableRow autoIncrementedRow = new TableRow(TextOutputFormat.html);
if (options.isShowOrdinalNumbers())
{
autoIncrementedRow
.add(newTableCell("", Alignment.right, false, Color.white, 1));
}
autoIncrementedRow
.add(newTableCell("", Alignment.left, false, Color.white, 1))
.add(newTableCell(" ", Alignment.left, false, Color.white, 1))
.add(newTableCell("auto-incremented",
Alignment.left,
false,
Color.white,
1));
formattingHelper.append(autoIncrementedRow.toString()).println();
}
private void printTableColumnRemarks(final Column column)
{
if (column == null || !column.hasRemarks() || options.isHideRemarks())
{
return;
}
final TableRow remarksRow = new TableRow(TextOutputFormat.html);
if (options.isShowOrdinalNumbers())
{
remarksRow.add(newTableCell("", Alignment.right, false, Color.white, 1));
}
remarksRow.add(newTableCell("", Alignment.left, false, Color.white, 1))
.add(newTableCell(" ", Alignment.left, false, Color.white, 1))
.add(newTableCell(column.getRemarks(),
Alignment.left,
false,
Color.white,
1));
formattingHelper.append(remarksRow.toString()).println();
}
private void printTableColumns(final List columns,
final boolean showHidden)
{
if (columns.isEmpty())
{
return;
}
Collections
.sort(columns,
NamedObjectSort
.getNamedObjectSort(options.isAlphabeticalSortForTableColumns()));
for (final Column column: columns)
{
if (!showHidden && column.isHidden())
{
continue;
}
if (isBrief && !isColumnSignificant(column))
{
continue;
}
final String columnTypeName;
if (options.isShowStandardColumnTypeNames())
{
columnTypeName = column.getColumnDataType().getJavaSqlType()
.getJavaSqlTypeName();
}
else
{
columnTypeName = column.getColumnDataType()
.getDatabaseSpecificTypeName();
}
final String columnType = columnTypeName + column.getWidth();
final String nullable = columnNullable(columnTypeName,
column.isNullable());
final String columnDetails = columnType + nullable;
final boolean emphasize = column.isPartOfPrimaryKey();
final TableRow row = new TableRow(TextOutputFormat.html);
if (options.isShowOrdinalNumbers())
{
final String ordinalNumberString = String
.valueOf(column.getOrdinalPosition());
row.add(newTableCell(ordinalNumberString,
Alignment.right,
false,
Color.white,
1));
}
row
.add(newTableCell(column.getName(),
Alignment.left,
emphasize,
Color.white,
1))
.add(newTableCell(" ", Alignment.left, false, Color.white, 1))
.add(newTableCell(columnDetails,
Alignment.left,
false,
Color.white,
1));
row.firstCell().addAttribute("port", nodeId(column) + ".start");
row.lastCell().addAttribute("port", nodeId(column) + ".end");
formattingHelper.append(row.toString()).println();
printTableColumnAutoIncremented(column);
printTableColumnRemarks(column);
}
}
private void printTableRemarks(final Table table)
{
if (table == null || !table.hasRemarks() || options.isHideRemarks())
{
return;
}
formattingHelper
.append(new TableRow(TextOutputFormat.html)
.add(newTableCell(table.getRemarks(),
Alignment.left,
false,
Color.white,
3))
.toString())
.println();
}
private void printTableRowCount(final Table table)
{
if (!options.isShowRowCounts() || table == null || !hasRowCount(table))
{
return;
}
formattingHelper
.append(new TableRow(TextOutputFormat.html)
.add(newTableCell(getRowCountMessage(table),
Alignment.right,
false,
Color.white,
3))
.toString())
.println();
}
private void printWeakAssociations(final Table table)
{
final Collection weakFks = WeakAssociationsUtility
.getWeakAssociations(table);
printForeignKeys(table, weakFks);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy