All Downloads are FREE. Search and download functionalities are using the official Maven repository.

schemacrawler.tools.text.schema.SchemaDotFormatter Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 16.24.2
Show newest version
/*
 * 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> foreignKeys) { for (final BaseForeignKey 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