1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sourceforge.schemaspy.view;
20
21 import java.util.Collection;
22 import java.util.Comparator;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.TreeSet;
26 import java.util.regex.Pattern;
27 import net.sourceforge.schemaspy.model.ForeignKeyConstraint;
28 import net.sourceforge.schemaspy.model.Table;
29 import net.sourceforge.schemaspy.model.TableColumn;
30 import net.sourceforge.schemaspy.model.TableIndex;
31 import net.sourceforge.schemaspy.util.DOMUtil;
32 import org.w3c.dom.Document;
33 import org.w3c.dom.Element;
34 import org.w3c.dom.Node;
35
36
37
38
39
40
41 public class XmlTableFormatter {
42 private static final XmlTableFormatter instance = new XmlTableFormatter();
43
44
45
46
47 private static final Pattern validXmlChars =
48 Pattern.compile("^[ -\uD7FF\uE000-\uFFFD\\p{L}\\p{M}\\p{Z}\\p{S}\\p{N}\\p{P}]*$");
49
50
51
52
53 private XmlTableFormatter() {}
54
55
56
57
58
59
60 public static XmlTableFormatter getInstance() {
61 return instance;
62 }
63
64
65
66
67
68
69
70 public void appendTables(Element schemaNode, Collection<Table> tables) {
71 Set<Table> byName = new TreeSet<Table>(new Comparator<Table>() {
72 public int compare(Table table1, Table table2) {
73 return table1.getName().compareToIgnoreCase(table2.getName());
74 }
75 });
76 byName.addAll(tables);
77
78 Document document = schemaNode.getOwnerDocument();
79 Element tablesNode = document.createElement("tables");
80 schemaNode.appendChild(tablesNode);
81 for (Table table : byName)
82 appendTable(tablesNode, table);
83 }
84
85
86
87
88
89
90
91 private void appendTable(Element tablesNode, Table table) {
92 Document document = tablesNode.getOwnerDocument();
93 Element tableNode = document.createElement("table");
94 tablesNode.appendChild(tableNode);
95 if (table.getId() != null)
96 DOMUtil.appendAttribute(tableNode, "id", String.valueOf(table.getId()));
97 if (table.getSchema() != null)
98 DOMUtil.appendAttribute(tableNode, "schema", table.getSchema());
99 DOMUtil.appendAttribute(tableNode, "name", table.getName());
100 if (table.getNumRows() != -1)
101 DOMUtil.appendAttribute(tableNode, "numRows", String.valueOf(table.getNumRows()));
102 DOMUtil.appendAttribute(tableNode, "type", table.isView() ? "VIEW" : "TABLE");
103 DOMUtil.appendAttribute(tableNode, "remarks", table.getComments() == null ? "" : table.getComments());
104 appendColumns(tableNode, table);
105 appendPrimaryKeys(tableNode, table);
106 appendIndexes(tableNode, table);
107 appendCheckConstraints(tableNode, table);
108 appendView(tableNode, table);
109 }
110
111
112
113
114
115
116
117 private void appendColumns(Element tableNode, Table table) {
118 for (TableColumn column : table.getColumns()) {
119 appendColumn(tableNode, column);
120 }
121 }
122
123
124
125
126
127
128
129
130 private Node appendColumn(Node tableNode, TableColumn column) {
131 Document document = tableNode.getOwnerDocument();
132 Node columnNode = document.createElement("column");
133 tableNode.appendChild(columnNode);
134
135 DOMUtil.appendAttribute(columnNode, "id", String.valueOf(column.getId()));
136 DOMUtil.appendAttribute(columnNode, "name", column.getName());
137 DOMUtil.appendAttribute(columnNode, "type", column.getType());
138 DOMUtil.appendAttribute(columnNode, "size", String.valueOf(column.getLength()));
139 DOMUtil.appendAttribute(columnNode, "digits", String.valueOf(column.getDecimalDigits()));
140 DOMUtil.appendAttribute(columnNode, "nullable", String.valueOf(column.isNullable()));
141 DOMUtil.appendAttribute(columnNode, "autoUpdated", String.valueOf(column.isAutoUpdated()));
142 if (column.getDefaultValue() != null) {
143 String defaultValue = column.getDefaultValue().toString();
144 if (isBinary(defaultValue)) {
145
146 defaultValue = asBinary(defaultValue);
147
148 DOMUtil.appendAttribute(columnNode, "defaultValueIsBinary", "true");
149 }
150 DOMUtil.appendAttribute(columnNode, "defaultValue", defaultValue);
151 }
152 DOMUtil.appendAttribute(columnNode, "remarks", column.getComments() == null ? "" : column.getComments());
153
154 for (TableColumn childColumn : column.getChildren()) {
155 Node childNode = document.createElement("child");
156 columnNode.appendChild(childNode);
157 ForeignKeyConstraint constraint = column.getChildConstraint(childColumn);
158 DOMUtil.appendAttribute(childNode, "foreignKey", constraint.getName());
159 DOMUtil.appendAttribute(childNode, "table", childColumn.getTable().getName());
160 DOMUtil.appendAttribute(childNode, "column", childColumn.getName());
161 DOMUtil.appendAttribute(childNode, "implied", String.valueOf(constraint.isImplied()));
162 DOMUtil.appendAttribute(childNode, "onDeleteCascade", String.valueOf(constraint.isCascadeOnDelete()));
163 }
164
165 for (TableColumn parentColumn : column.getParents()) {
166 Node parentNode = document.createElement("parent");
167 columnNode.appendChild(parentNode);
168 ForeignKeyConstraint constraint = column.getParentConstraint(parentColumn);
169 DOMUtil.appendAttribute(parentNode, "foreignKey", constraint.getName());
170 DOMUtil.appendAttribute(parentNode, "table", parentColumn.getTable().getName());
171 DOMUtil.appendAttribute(parentNode, "column", parentColumn.getName());
172 DOMUtil.appendAttribute(parentNode, "implied", String.valueOf(constraint.isImplied()));
173 DOMUtil.appendAttribute(parentNode, "onDeleteCascade", String.valueOf(constraint.isCascadeOnDelete()));
174 }
175
176 return columnNode;
177 }
178
179
180
181
182
183
184
185 private void appendPrimaryKeys(Element tableNode, Table table) {
186 Document document = tableNode.getOwnerDocument();
187 int index = 1;
188
189 for (TableColumn primaryKeyColumn : table.getPrimaryColumns()) {
190 Node primaryKeyNode = document.createElement("primaryKey");
191 tableNode.appendChild(primaryKeyNode);
192
193 DOMUtil.appendAttribute(primaryKeyNode, "column", primaryKeyColumn.getName());
194 DOMUtil.appendAttribute(primaryKeyNode, "sequenceNumberInPK", String.valueOf(index++));
195 }
196 }
197
198
199
200
201
202
203
204 private void appendCheckConstraints(Element tableNode, Table table) {
205 Document document = tableNode.getOwnerDocument();
206 Map<String, String> constraints = table.getCheckConstraints();
207 if (constraints != null && !constraints.isEmpty()) {
208 for (String name : constraints.keySet()) {
209 Node constraintNode = document.createElement("checkConstraint");
210 tableNode.appendChild(constraintNode);
211
212 DOMUtil.appendAttribute(constraintNode, "name", name);
213 DOMUtil.appendAttribute(constraintNode, "constraint", constraints.get(name).toString());
214 }
215 }
216 }
217
218
219
220
221
222
223
224 private void appendIndexes(Node tableNode, Table table) {
225 boolean showId = table.getId() != null;
226 Set<TableIndex> indexes = table.getIndexes();
227 if (indexes != null && !indexes.isEmpty()) {
228 indexes = new TreeSet<TableIndex>(indexes);
229 Document document = tableNode.getOwnerDocument();
230
231 for (TableIndex index : indexes) {
232 Node indexNode = document.createElement("index");
233
234 if (showId)
235 DOMUtil.appendAttribute(indexNode, "id", String.valueOf(index.getId()));
236 DOMUtil.appendAttribute(indexNode, "name", index.getName());
237 DOMUtil.appendAttribute(indexNode, "unique", String.valueOf(index.isUnique()));
238
239 for (TableColumn column : index.getColumns()) {
240 Node columnNode = document.createElement("column");
241
242 DOMUtil.appendAttribute(columnNode, "name", column.getName());
243 DOMUtil.appendAttribute(columnNode, "ascending", String.valueOf(index.isAscending(column)));
244 indexNode.appendChild(columnNode);
245 }
246 tableNode.appendChild(indexNode);
247 }
248 }
249 }
250
251
252
253
254
255
256
257 private void appendView(Element tableNode, Table table) {
258 String sql;
259 if (table.isView() && (sql = table.getViewSql()) != null) {
260 DOMUtil.appendAttribute(tableNode, "viewSql", sql);
261 }
262 }
263
264
265
266
267
268
269
270
271 private static boolean isBinary(String str) {
272 return !validXmlChars.matcher(str).matches();
273 }
274
275
276
277
278
279
280
281
282 private String asBinary(String str) {
283 byte[] bytes = str.getBytes();
284 StringBuilder buf = new StringBuilder(bytes.length * 2);
285 for (int i = 0; i < bytes.length; ++i) {
286 buf.append(String.format("%02X", bytes[i]));
287 }
288 return buf.toString();
289 }
290 }