Author: ms1279
Date: Tue Apr 14 09:35:52 2009
New Revision: 2981
Added:
trunk/src/ca/sqlpower/architect/swingui/action/ExportHTMLReportAction.java
trunk/src/xsltStylesheets/
trunk/src/xsltStylesheets/architect2html.xslt
Modified:
trunk/src/ca/sqlpower/architect/swingui/ArchitectFrame.java
trunk/src/ca/sqlpower/architect/swingui/messages.properties
Log:
Added an ExportHTMLReportAction which generates a report in html off of the
content in the playpen using a specific stylesheet.
The Action can be found under the File menu.
Made some changes to the architect2html.xslt stylesheet provided to us
courtesy of forum users bronius and castorp and added it to the folder
xsltStylesheets in the src package.
Modified: trunk/src/ca/sqlpower/architect/swingui/ArchitectFrame.java
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/ArchitectFrame.java (original)
+++ trunk/src/ca/sqlpower/architect/swingui/ArchitectFrame.java Tue Apr 14
09:35:52 2009
@@ -90,6 +90,7 @@
import ca.sqlpower.architect.swingui.action.ExportCSVAction;
import ca.sqlpower.architect.swingui.action.ExportDDLAction;
import ca.sqlpower.architect.swingui.action.ExportPlaypenToPDFAction;
+import ca.sqlpower.architect.swingui.action.ExportHTMLReportAction;
import
ca.sqlpower.architect.swingui.action.FocusToChildOrParentTableAction;
import ca.sqlpower.architect.swingui.action.HelpAction;
import ca.sqlpower.architect.swingui.action.InsertColumnAction;
@@ -512,7 +513,7 @@
Action exportCSVAction = new ExportCSVAction(this, session);
Action mappingReportAction = new VisualMappingReportAction(this,
session);
Action kettleETL = new KettleJobAction(session);
-
+ Action exportHTMLReportAction = new
ExportHTMLReportAction(session);
menuBar = new JMenuBar();
JMenu fileMenu = new
JMenu(Messages.getString("ArchitectFrame.fileMenu")); //$NON-NLS-1$
@@ -526,6 +527,7 @@
fileMenu.add(saveProjectAsAction);
fileMenu.add(printAction);
fileMenu.add(exportPlaypenToPDFAction);
+ fileMenu.add(exportHTMLReportAction);
fileMenu.addSeparator();
if (!context.isMacOSX()) {
fileMenu.add(prefAction);
Added:
trunk/src/ca/sqlpower/architect/swingui/action/ExportHTMLReportAction.java
==============================================================================
--- (empty file)
+++
trunk/src/ca/sqlpower/architect/swingui/action/ExportHTMLReportAction.java
Tue Apr 14 09:35:52 2009
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2009, SQL Power Group Inc.
+ *
+ * This file is part of Power*Architect.
+ *
+ * Power*Architect is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Power*Architect 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package ca.sqlpower.architect.swingui.action;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+import ca.sqlpower.architect.swingui.ArchitectSwingSession;
+import ca.sqlpower.architect.swingui.Messages;
+import ca.sqlpower.sqlobject.SQLObjectException;
+import ca.sqlpower.swingui.SPSUtils;
+import ca.sqlpower.util.BrowserUtil;
+import ca.sqlpower.util.XsltTransformation;
+
+/**
+ * The xslt stylesheet used is architect2html.xslt.
+ * An xml OutputStream(using a {...@link PipedOutputStream}) is generated,
based on the
+ * current playPen content and is read by a {...@link PipedInputStream} which
is used as the xml source. <br>
+ * The stylesheet and the xml source are passed as parameters to the
+ * {...@link XsltTransformation} methods to generate an HTML report off the
content
+ * to a location specified by the user.
+ */
+public class ExportHTMLReportAction extends AbstractArchitectAction {
+
+ private static final String ENCODING = "UTF-8";
+
+ private PipedOutputStream xmlOutputStream;
+
+ private FileOutputStream result;
+
+ public ExportHTMLReportAction(ArchitectSwingSession session) {
+ super(session, "Export to HTML", "Generates an HTML report of all
the tables in the playpen ");
+ }
+
+ public void actionPerformed(ActionEvent e) {
+
+ XsltTransformation xsltTransform = new
XsltTransformation("/xsltStylesheets/architect2html.xslt");
+ PipedInputStream xmlInputStream = new PipedInputStream();
+ try {
+ xmlOutputStream = new PipedOutputStream(xmlInputStream);
+ new Thread(
+ new Runnable(){
+ public void run(){
+ try {
+ session.getProject().save(xmlOutputStream,
ENCODING);
+ } catch (IOException e2) {
+
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(),"You got
an error", e2);
+ } catch (SQLObjectException e2) {
+
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(),"You got
an error", e2);
+ }
+ }
+ }
+ ).start();
+ xmlOutputStream.flush();
+
+ } catch (IOException e2) {
+
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(),"You got
an error", e2);
+ }
+
+
+ JFileChooser chooser = new
JFileChooser(session.getProject().getFile());
+ chooser.addChoosableFileFilter(SPSUtils.HTML_FILE_FILTER);
+ int response = chooser.showSaveDialog(session.getArchitectFrame());
+ chooser.setDialogTitle("Save HTML Report As");
+ if (response != JFileChooser.APPROVE_OPTION) {
+ return;
+ }
+ File file = chooser.getSelectedFile();
+ if (!file.getPath().endsWith(".html")) { //$NON-NLS-1$
+ file = new File(file.getPath() + ".html"); //$NON-NLS-1$
+ }
+ if (file.exists()) {
+ response =
JOptionPane.showConfirmDialog(session.getArchitectFrame(),
Messages.getString(
+ "ExportHTMLReportAction.fileAlreadyExists",
file.getPath()), //$NON-NLS-1$
+
Messages.getString("ExportHTMLReportAction.fileAlreadyExistsDialogTitle"),
JOptionPane.YES_NO_OPTION); //$NON-NLS-1$
+ if (response == JOptionPane.NO_OPTION) {
+ return;
+ }
+ }
+ try {
+ result = new FileOutputStream(file);
+ } catch (FileNotFoundException e2) {
+
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(),"You got
an error", e2);
+ }
+
+
+ try {
+ xsltTransform.transform(xmlInputStream, result);
+ result.close();
+ xmlInputStream.close();
+ xmlOutputStream.close();
+
+ //Opens up the html file in the default browser
+ BrowserUtil.launch(file.toURI().toString());
+ } catch (Exception e1) {
+
SPSUtils.showExceptionDialogNoReport(session.getArchitectFrame(),"You got
an error", e1);
+ }
+ }
+
+}
Modified: trunk/src/ca/sqlpower/architect/swingui/messages.properties
==============================================================================
--- trunk/src/ca/sqlpower/architect/swingui/messages.properties (original)
+++ trunk/src/ca/sqlpower/architect/swingui/messages.properties Tue Apr 14
09:35:52 2009
@@ -47,7 +47,7 @@
ArchitectSwingSessionImpl.couldNotCreateFile=Could not create file
ArchitectSwingSessionImpl.doNotSaveOption=Don't Save
ArchitectSwingSessionImpl.nullOlapSession=Null OLAPSession can not have an
OLAPEditSession\!
-ArchitectSwingSessionImpl.fileAlreadyExists=The file\n\n{0}\n\nalready
exists. Do you want to overwrite it?
+ArchitectSwingSessionImpl.fileAlreadyExists=The file\n{0}\nalready exists.
Do you want to overwrite it?
ArchitectSwingSessionImpl.fileAlreadyExistsDialogTitle=File Exists
ArchitectSwingSessionImpl.forceQuiteOption=Force Quit
ArchitectSwingSessionImpl.mainFrameTitle={0} - Power*Architect
@@ -184,6 +184,8 @@
DefaultColumnPanel.remarks=Column Reamrks By Default
DefaultColumnPanel.scale=Column Scale By Default
DefaultColumnPanel.type=Column Type By Default
+ExportHTMLReportAction.fileAlreadyExists=The file\n{0}\nalready exists. Do
you want to overwrite it?
+ExportHTMLReportAction.fileAlreadyExistsDialogTitle=File Exists
IndexColumnTable.ascendingDescendingTableColumnName=Asc/Des
IndexColumnTable.columnTableColumnName=Column
IndexColumnTable.indexTableColumnName=In Index
Added: trunk/src/xsltStylesheets/architect2html.xslt
==============================================================================
--- (empty file)
+++ trunk/src/xsltStylesheets/architect2html.xslt Tue Apr 14 09:35:52 2009
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<xsl:transform
+ version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:output
+ encoding="iso-8859-15"
+ method="html"
+ indent="yes"
+ standalone="yes"
+ omit-xml-declaration="yes"
+ doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"
+/>
+
+<xsl:strip-space elements="*"/>
+
+<xsl:template match="/">
+ <html>
+ <head>
+ <style>
+ .table {
+ background-color:#F5F5FF;
+ border-left:4px solid gray;
+ border-bottom:4px solid gray;
+ border-top:4px solid gray;
+ border-right:4px solid gray;
+ margin-bottom:3em;
+ margin-left:2em;
+ margin-right:2em;
+ padding:1em;
+ }
+ .tableNameHeading h1 {
+ font-family: Segoe UI, Arial, sans-serif;
+ font-size:125%;
+ font-weight:bold;
+ }
+
+ .tableComment {
+ background-color:#e4efff; margin-bottom:20px;
+ }
+
+ .tableDefinition {
+ padding:2px;
+ border-collapse:collapse;
+ margin-top:1em;
+ }
+
+ .tdTableDefinition {
+ padding-right:10px;
+ vertical-align:top;
+ border-top:1px solid #C0C0C0;
+ }
+
+ .tdLogicalColName {
+ width:20em;
+ }
+
+ .tdPhysicalColName {
+ width:20em;
+ }
+
+ .tdDataType {
+ width:10em;
+ }
+
+ .tdPkFlag {
+ width:4em;
+ }
+
+ .tdNullFlag {
+ width:9em;
+ }
+
+ .tdTableHeading {
+ padding:2px;
+ font-family: Segoe UI, Arial, sans-serif;
+ font-weight: bold;
+ vertical-align:top;
+ border-bottom: 1px solid #C0C0C0;
+ background-color: rgb(240,240,240);
+ }
+
+ .subTitle {
+ font-family: Segoe UI, Arial, sans-serif;
+ font-weight: bold;
+ }
+
+ .references {
+ }
+ .comment {
+ color:#666666;
+ margin-left:3em;
+ padding:0.25em;
+ }
+ </style>
+
+ <title><xsl:call-template name="write-name"/></title>
+ </head>
+ <body>
+ <xsl:call-template name="create-toc"/>
+ <xsl:call-template name="table-definitions"/>
+ </body>
+ </html>
+</xsl:template>
+
+<xsl:template name="write-name">
+ <xsl:variable name="title" select="//architect-project/project-name"/>
+ <xsl:choose>
+ <xsl:when test="string-length($title) > 0">
+ <xsl:value-of select="$title"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'Power*Architect Datamodel'"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="create-toc">
+ <center><h2><xsl:call-template name="write-name"/></h2></center>
+ <h3><xsl:text>List of tables</xsl:text></h3>
+
+ <ul>
+ <xsl:for-each select="/architect-project/target-database/table">
+ <xsl:sort select="@name"/>
+ <li>
+ <xsl:variable name="table" select="@name"/>
+ <a href="#{$table}"><xsl:value-of select="$table"/></a>
+ </li>
+ </xsl:for-each>
+ </ul>
+
+</xsl:template>
+
+
+<xsl:template name="table-definitions">
+
+ <xsl:for-each select="/architect-project/target-database/table">
+
+ <xsl:sort select="@name"/>
+ <xsl:variable name="table" select="@name"/>
+ <xsl:variable name="table-id" select="@id"/>
+
+ <div class="tableNameHeading">
+ <h1>
+ <xsl:value-of select="$table"/>
+ <a name="{$table}"></a>
+ </h1>
+ <xsl:if test="string-length(@remarks) > 0">
+ <p class="comment">
+ <xsl:apply-templates select="@remarks" />
+ </p>
+ </xsl:if>
+ </div>
+
+ <div class="table">
+ <table class="tableDefinition" width="100%">
+ <tr>
+ <td class="tdTableHeading tdLogicalColName"><xsl:text>Logical
Column Name</xsl:text></td>
+ <td class="tdTableHeading tdPhysicalColName"><xsl:text>Physical
Column Name</xsl:text></td>
+ <td class="tdTableHeading
tdDataType"><xsl:text>Type</xsl:text></td>
+ <td class="tdTableHeading tdPkFlag"><xsl:text>PK</xsl:text></td>
+ <td class="tdTableHeading
tdNullFlag"><xsl:text>Nullable</xsl:text></td>
+ </tr>
+
+ <xsl:for-each select="folder//column">
+ <xsl:sort select="@id"/>
+ <xsl:variable name="col-id" select="@id"/>
+ <tr valign="top">
+ <td class="tdTableDefinition"><xsl:value-of select="@name"/>
+ <xsl:if test="string-length(@primaryKeySeq) > 0">
+ <xsl:text> (PK)</xsl:text>
+ </xsl:if>
+ <xsl:for-each
select="/architect-project/target-database/relationships/relationsh...@fk-table-ref=$table-id]/column-mappi...@fk-column-ref=$col-id]">
+ <xsl:variable name="pk-id" select="../@pk-table-ref"/>
+ <xsl:variable name="targetTable"
select="/architect-project/target-database/tab...@id=$pk-id]/@name"/>
+  (<a href="#{$targetTable}"><xsl:value-of
select="'FK'"/></a>)
+ </xsl:for-each>
+ </td>
+ <td class="tdTableDefinition"><xsl:value-of
select="@physicalName"/></td>
+ <td class="tdTableDefinition">
+ <xsl:call-template name="write-data-type">
+ <xsl:with-param name="type-id" select="@type"/>
+ <xsl:with-param name="precision" select="@precision"/>
+ <xsl:with-param name="scale" select="@scale"/>
+ </xsl:call-template>
+ </td>
+ <td class="tdTableDefinition" nowrap="nowrap">
+ <xsl:if test="string-length(@primaryKeySeq) > 0">
+ <xsl:text>PK</xsl:text>
+ </xsl:if>
+ </td>
+ <td class="tdTableDefinition" nowrap="nowrap">
+ <xsl:if test="@nullable='0'">
+ <xsl:text>NOT NULL</xsl:text>
+ </xsl:if>
+ </td>
+ </tr>
+ <xsl:if test="string-length(@remarks) > 0">
+ <tr>
+ <td colspan="4">
+ <div class="comment"><xsl:apply-templates select="@remarks"
/></div>
+ </td>
+ </tr>
+ </xsl:if>
+ </xsl:for-each>
+ </table>
+
+ <div class="references">
+ <xsl:if
test="count(/architect-project/target-database/relationships/relationsh...@fk-table-ref=$table-id])
> 0">
+ <p class="subTitle"><xsl:text>References</xsl:text></p>
+ <ul>
+ <xsl:for-each
select="/architect-project/target-database/relationships/relationsh...@fk-table-ref=$table-id]">
+ <xsl:variable name="pk-id" select="@pk-table-ref"/>
+ <xsl:variable name="targetTable"
select="/architect-project/target-database/tab...@id=$pk-id]/@name"/>
+ <li>
+ <a href="#{$targetTable}"><xsl:value-of
select="$targetTable"/></a><xsl:text> through (</xsl:text>
+ <xsl:for-each select="column-mapping">
+ <xsl:variable name="fk-col-id" select="@fk-column-ref"/>
+ <xsl:variable name="fk-col-name"
select="//colu...@id=$fk-col-id]/@name"/>
+ <xsl:value-of select="$fk-col-name"/>
+ <xsl:if test="position() < last()"><xsl:text>,
</xsl:text></xsl:if>
+ </xsl:for-each><xsl:text>)</xsl:text>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:if>
+
+ <xsl:if
test="count(/architect-project/target-database/relationships/relationsh...@pk-table-ref=$table-id])
> 0">
+ <p class="subTitle"><xsl:text>Referenced By</xsl:text></p>
+ <ul>
+ <xsl:for-each
select="/architect-project/target-database/relationships/relationsh...@pk-table-ref=$table-id]">
+ <xsl:variable name="fk-id" select="@fk-table-ref"/>
+ <xsl:variable name="targetTable"
select="/architect-project/target-database/tab...@id=$fk-id]/@name"/>
+ <li><a href="#{$targetTable}"><xsl:value-of
select="$targetTable"/></a><xsl:text> referencing (</xsl:text>
+ <xsl:for-each select="column-mapping">
+ <xsl:variable name="pk-col-id" select="@pk-column-ref"/>
+ <xsl:variable name="pk-col-name"
select="//colu...@id=$pk-col-id]/@name"/>
+ <xsl:value-of select="$pk-col-name"/>
+ <xsl:if test="position() < last()"><xsl:text>,
</xsl:text></xsl:if>
+ </xsl:for-each><xsl:text>)</xsl:text>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:if>
+ </div>
+ </div>
+ </xsl:for-each>
+
+</xsl:template>
+
+<xsl:template name="write-data-type">
+ <xsl:param name="type-id"/>
+ <xsl:param name="precision"/>
+ <xsl:param name="scale"/>
+ <xsl:choose>
+ <xsl:when test="$type-id = 2005">
+ <xsl:text>CLOB</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 2011">
+ <xsl:text>NCLOB</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 2004">
+ <xsl:text>BLOB</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = -3">
+ <xsl:text>VARBINARY</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = -4">
+ <xsl:text>LONGVARBINARY</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = -1">
+ <xsl:text>LONGVARCHAR</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 93">
+ <xsl:text>TIMESTAMP</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 92">
+ <xsl:text>TIME</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 91">
+ <xsl:text>DATE</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 1">
+ <xsl:text>CHAR</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = -15">
+ <xsl:text>NCHAR</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 4">
+ <xsl:text>INTEGER</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 5">
+ <xsl:text>SMALLINT</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = -6">
+ <xsl:text>TINYINT</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 8">
+ <xsl:text>DOUBLE</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 7">
+ <xsl:text>REAL</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 6">
+ <xsl:text>FLOAT</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 16">
+ <xsl:text>BOOLEAN</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = -7">
+ <xsl:text>BIT</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 2">
+ <xsl:text>NUMERIC(</xsl:text><xsl:value-of
select="$precision"/><xsl:text>,</xsl:text><xsl:value-of
select="$scale"/><xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 3">
+ <xsl:text>DECIMAL(</xsl:text><xsl:value-of
select="$precision"/><xsl:text>,</xsl:text><xsl:value-of
select="$scale"/><xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:when test="$type-id = 12">
+ <xsl:text>VARCHAR(</xsl:text><xsl:value-of
select="$precision"/><xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>[</xsl:text><xsl:value-of
select="$type-id"/><xsl:text>]</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="makelinebreak" match="@remarks">
+ <xsl:param name="text" select="."/>
+ <xsl:choose>
+ <xsl:when test="contains($text, '
')">
+ <xsl:value-of select="substring-before($text, '
')"/>
+ <br/>
+ <xsl:call-template name="makelinebreak">
+ <xsl:with-param name="text"
select="substring-after($text,'
')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="contains($text, ' ')">
+ <xsl:value-of select="substring-before($text, ' ')"/>
+ <br/>
+ <xsl:call-template name="makelinebreak">
+ <xsl:with-param name="text"
select="substring-after($text,' ')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$text"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:transform>
\ No newline at end of file