View Javadoc
1   /*
2    * This file is a part of the SchemaSpy project (http://schemaspy.sourceforge.net).
3    * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 John Currier
4    *
5    * SchemaSpy is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *
10   * SchemaSpy is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18   */
19  package net.sourceforge.schemaspy.view;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Comparator;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  import java.util.TreeSet;
29  import net.sourceforge.schemaspy.model.Database;
30  import net.sourceforge.schemaspy.model.Table;
31  import net.sourceforge.schemaspy.model.TableColumn;
32  import net.sourceforge.schemaspy.model.TableIndex;
33  import net.sourceforge.schemaspy.util.LineWriter;
34  
35  /**
36   * The page that lists all of the columns in the schema,
37   * allowing the end user to sort by column's attributes.
38   *
39   * @author John Currier
40   */
41  public class HtmlColumnsPage extends HtmlFormatter {
42      private static HtmlColumnsPage instance = new HtmlColumnsPage();
43  
44      /**
45       * Singleton: Don't allow instantiation
46       */
47      private HtmlColumnsPage() {
48      }
49  
50      /**
51       * Singleton accessor
52       *
53       * @return the singleton instance
54       */
55      public static HtmlColumnsPage getInstance() {
56          return instance;
57      }
58  
59      /**
60       * Returns details about the columns that are displayed on this page.
61       *
62       * @return
63       */
64      public List<ColumnInfo> getColumnInfos()
65      {
66          List<ColumnInfo> columns = new ArrayList<ColumnInfo>();
67  
68          columns.add(new ColumnInfo("Table", new ByTableComparator()));
69          columns.add(new ColumnInfo("Column", new ByColumnComparator()));
70          columns.add(new ColumnInfo("Type", new ByTypeComparator()));
71          columns.add(new ColumnInfo("Size", new BySizeComparator()));
72          columns.add(new ColumnInfo("Nulls", new ByNullableComparator()));
73          columns.add(new ColumnInfo("Auto", new ByAutoUpdateComparator()));
74          columns.add(new ColumnInfo("Default", new ByDefaultValueComparator()));
75  
76          return columns;
77      }
78  
79      public class ColumnInfo
80      {
81          private final String columnName;
82          private final Comparator<TableColumn> comparator;
83  
84          private ColumnInfo(String columnName, Comparator<TableColumn> comparator)
85          {
86              this.columnName = columnName;
87              this.comparator = comparator;
88          }
89  
90          public String getColumnName() {
91              return columnName;
92          }
93  
94          public String getLocation() {
95              return getLocation(columnName);
96          }
97  
98          public String getLocation(String colName) {
99              return "columns.by" + colName + ".html";
100         }
101 
102         private Comparator<TableColumn> getComparator() {
103             return comparator;
104         }
105 
106         @Override
107         public String toString() {
108             return getLocation();
109         }
110     }
111 
112     public void write(Database database, Collection<Table> tables, ColumnInfo columnInfo, boolean showOrphansDiagram, LineWriter html) throws IOException {
113         Set<TableColumn> columns = new TreeSet<TableColumn>(columnInfo.getComparator());
114         Set<TableColumn> primaryColumns = new HashSet<TableColumn>();
115         Set<TableColumn> indexedColumns = new HashSet<TableColumn>();
116 
117         for (Table table : tables) {
118             columns.addAll(table.getColumns());
119 
120             primaryColumns.addAll(table.getPrimaryColumns());
121             for (TableIndex index : table.getIndexes()) {
122                 indexedColumns.addAll(index.getColumns());
123             }
124         }
125 
126         writeHeader(database, columns.size(), showOrphansDiagram, columnInfo, html);
127 
128         HtmlTablePage formatter = HtmlTablePage.getInstance();
129 
130         for (TableColumn column : columns) {
131             formatter.writeColumn(column, column.getTable().getName(), primaryColumns, indexedColumns, true, false, html);
132         }
133 
134         writeFooter(html);
135     }
136 
137     private void writeHeader(Database db, int numberOfColumns, boolean hasOrphans, ColumnInfo selectedColumn, LineWriter html) throws IOException {
138         writeHeader(db, null, "Columns", hasOrphans, html);
139 
140         html.writeln("<table width='100%' border='0'>");
141         html.writeln("<tr><td class='container'>");
142         writeGeneratedBy(db.getConnectTime(), html);
143         html.writeln("</td><td class='container' rowspan='2' align='right' valign='top'>");
144         writeLegend(false, false, html);
145         html.writeln("</td></tr>");
146         html.writeln("<tr valign='top'><td class='container' align='left' valign='top'>");
147         html.writeln("<p>");
148         html.writeln("<form name='options' action=''>");
149         html.writeln(" <label for='showComments'><input type=checkbox id='showComments'>Comments</label>");
150         html.writeln(" <label for='showLegend'><input type=checkbox checked id='showLegend'>Legend</label>");
151         html.writeln("</form>");
152         html.writeln("</table>");
153 
154         html.writeln("<div class='indent'>");
155         html.write("<b>");
156         html.write(db.getName());
157         if (db.getSchema() != null) {
158             html.write('.');
159             html.write(db.getSchema());
160         }
161         html.write(" contains ");
162         html.write(String.valueOf(numberOfColumns));
163         html.write(" columns</b> - click on heading to sort:");
164         Collection<Table> tables = db.getTables();
165         boolean hasTableIds = tables.size() > 0 && tables.iterator().next().getId() != null;
166         writeMainTableHeader(hasTableIds, selectedColumn, html);
167         html.writeln("<tbody valign='top'>");
168     }
169 
170     public void writeMainTableHeader(boolean hasTableIds, ColumnInfo selectedColumn, LineWriter out) throws IOException {
171         boolean onColumnsPage = selectedColumn != null;
172         out.writeln("<a name='columns'></a>");
173         out.writeln("<table id='columns' class='dataTable' border='1' rules='groups'>");
174         int numCols = 6;    // base number of columns
175         if (hasTableIds && !onColumnsPage)
176             ++numCols;      // for table id
177         if (onColumnsPage)
178             ++numCols;      // for table name
179         else
180             numCols += 2;   // for children and parents
181         for (int i = 0; i < numCols; ++i)
182             out.writeln("<colgroup>");
183         out.writeln("<colgroup class='comment'>");
184 
185         out.writeln("<thead align='left'>");
186         out.writeln("<tr>");
187         if (hasTableIds && !onColumnsPage)
188             out.writeln(getTH(selectedColumn, "ID", null, "right"));
189         if (onColumnsPage)
190             out.writeln(getTH(selectedColumn, "Table", null, null));
191         out.writeln(getTH(selectedColumn, "Column", null, null));
192         out.writeln(getTH(selectedColumn, "Type", null, null));
193         out.writeln(getTH(selectedColumn, "Size", null, null));
194         out.writeln(getTH(selectedColumn, "Nulls", "Are nulls allowed?", null));
195         out.writeln(getTH(selectedColumn, "Auto", "Is column automatically updated?", null));
196         out.writeln(getTH(selectedColumn, "Default", "Default value", null));
197         if (!onColumnsPage) {
198             out.write("  <th title='Columns in tables that reference this column'>");
199             out.writeln("<span class='notSortedByColumn'>Children</span></th>");
200             out.write("  <th title='Columns in tables that are referenced by this column'>");
201             out.writeln("<span class='notSortedByColumn'>Parents</span></th>");
202         }
203         out.writeln("  <th title='Comments' class='comment'><span class='notSortedByColumn'>Comments</span></th>");
204         out.writeln("</tr>");
205         out.writeln("</thead>");
206     }
207 
208     private String getTH(ColumnInfo selectedColumn, String columnName, String title, String align) {
209         StringBuilder buf = new StringBuilder("  <th");
210 
211         if (align != null) {
212             buf.append(" align='");
213             buf.append(align);
214             buf.append("'");
215         }
216 
217         if (title != null) {
218             buf.append(" title='");
219             buf.append(title);
220             buf.append("'");
221         }
222 
223         if (selectedColumn != null) {
224             if (selectedColumn.getColumnName().equals(columnName)) {
225                 buf.append(" class='sortedByColumn'>");
226                 buf.append(columnName);
227             } else {
228                 buf.append(" class='notSortedByColumn'>");
229                 buf.append("<a href='");
230                 buf.append(encodeHref(selectedColumn.getLocation(columnName)));
231                 buf.append("#columns'><span class='notSortedByColumn'>");
232                 buf.append(columnName);
233                 buf.append("</span></a>");
234             }
235         } else {
236             buf.append('>');
237             buf.append(columnName);
238         }
239         buf.append("</th>");
240 
241         return buf.toString();
242     }
243 
244     @Override
245     protected void writeFooter(LineWriter html) throws IOException {
246         html.writeln("</table>");
247         html.writeln("</div>");
248         super.writeFooter(html);
249     }
250 
251     @Override
252     protected boolean isColumnsPage() {
253         return true;
254     }
255 
256     private class ByColumnComparator implements Comparator<TableColumn> {
257         public int compare(TableColumn column1, TableColumn column2) {
258             int rc = column1.getName().compareToIgnoreCase(column2.getName());
259             if (rc == 0)
260                 rc = column1.getTable().compareTo(column2.getTable());
261             return rc;
262         }
263     }
264 
265     private class ByTableComparator implements Comparator<TableColumn> {
266         public int compare(TableColumn column1, TableColumn column2) {
267             int rc = column1.getTable().compareTo(column2.getTable());
268             if (rc == 0)
269                 rc = column1.getName().compareToIgnoreCase(column2.getName());
270             return rc;
271         }
272     }
273 
274     private class ByTypeComparator implements Comparator<TableColumn> {
275         private final Comparator<TableColumn> bySize = new BySizeComparator();
276 
277         public int compare(TableColumn column1, TableColumn column2) {
278             int rc = column1.getType().compareToIgnoreCase(column2.getType());
279             if (rc == 0) {
280                 rc = bySize.compare(column1, column2);
281             }
282             return rc;
283         }
284     }
285 
286     private class BySizeComparator implements Comparator<TableColumn> {
287         private final Comparator<TableColumn> byColumn = new ByColumnComparator();
288 
289         public int compare(TableColumn column1, TableColumn column2) {
290             int rc = column1.getLength() - column2.getLength();
291             if (rc == 0) {
292                 rc = column1.getDecimalDigits() - column2.getDecimalDigits();
293                 if (rc == 0)
294                     rc = byColumn.compare(column1, column2);
295             }
296             return rc;
297         }
298     }
299 
300     private class ByNullableComparator implements Comparator<TableColumn> {
301         private final Comparator<TableColumn> byColumn = new ByColumnComparator();
302 
303         public int compare(TableColumn column1, TableColumn column2) {
304             int rc = column1.isNullable() == column2.isNullable() ? 0 : column1.isNullable() ? -1 : 1;
305             if (rc == 0)
306                 rc = byColumn.compare(column1, column2);
307             return rc;
308         }
309     }
310 
311     private class ByAutoUpdateComparator implements Comparator<TableColumn> {
312         private final Comparator<TableColumn> byColumn = new ByColumnComparator();
313 
314         public int compare(TableColumn column1, TableColumn column2) {
315             int rc = column1.isAutoUpdated() == column2.isAutoUpdated() ? 0 : column1.isAutoUpdated() ? -1 : 1;
316             if (rc == 0)
317                 rc = byColumn.compare(column1, column2);
318             return rc;
319         }
320     }
321 
322     private class ByDefaultValueComparator implements Comparator<TableColumn> {
323         private final Comparator<TableColumn> byColumn = new ByColumnComparator();
324 
325         public int compare(TableColumn column1, TableColumn column2) {
326             int rc = String.valueOf(column1.getDefaultValue()).compareToIgnoreCase(String.valueOf(column2.getDefaultValue()));
327             if (rc == 0)
328                 rc = byColumn.compare(column1, column2);
329             return rc;
330         }
331     }
332 }