summaryrefslogtreecommitdiffstats
path: root/src/com/iciql/DbInspector.java
blob: 12124b855fccddc04f6f523273ff8beaf58ee7f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*
 * Copyright 2004-2011 H2 Group.
 * Copyright 2011 James Moger.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.iciql;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import com.iciql.Iciql.IQTable;
import com.iciql.util.JdbcUtils;
import com.iciql.util.StringUtils;
import com.iciql.util.Utils;

/**
 * Class to inspect a model and a database for the purposes of model validation
 * and automatic model generation. This class finds the available schemas and
 * tables and serves as the entry point for model generation and validation.
 */
public class DbInspector {

	private Db db;
	private DatabaseMetaData metaData;
	private Class<? extends java.util.Date> dateTimeClass = java.util.Date.class;

	public DbInspector(Db db) {
		this.db = db;
		setPreferredDateTimeClass(db.getDialect().getDateTimeClass());
	}

	/**
	 * Set the preferred class to store date and time. Possible values are:
	 * java.util.Date (default) and java.sql.Timestamp.
	 * 
	 * @param dateTimeClass
	 *            the new class
	 */
	public void setPreferredDateTimeClass(Class<? extends java.util.Date> dateTimeClass) {
		this.dateTimeClass = dateTimeClass;
	}

	/**
	 * Generates models class skeletons for schemas and tables. If the table
	 * name is undefined, models will be generated for every table within the
	 * specified schema. Additionally, if no schema is defined, models will be
	 * generated for all schemas and all tables.
	 * 
	 * @param schema
	 *            the schema name (optional)
	 * @param table
	 *            the table name (optional)
	 * @param packageName
	 *            the package name (optional)
	 * @param annotateSchema
	 *            (includes schema name in annotation)
	 * @param trimStrings
	 *            (trims strings to maxLength of column)
	 * @return a list of complete model classes as strings, each element a class
	 */
	public List<String> generateModel(String schema, String table, String packageName,
			boolean annotateSchema, boolean trimStrings) {
		try {
			List<String> models = Utils.newArrayList();
			List<TableInspector> tables = getTables(schema, table);
			for (TableInspector t : tables) {
				t.read(metaData);
				String model = t.generateModel(packageName, annotateSchema, trimStrings);
				models.add(model);
			}
			return models;
		} catch (SQLException s) {
			throw new IciqlException(s);
		}
	}

	/**
	 * Validates a model.
	 * 
	 * @param model
	 *            an instance of the model class
	 * @param throwOnError
	 *            if errors should cause validation to fail
	 * @return a list of validation remarks
	 */
	public <T> List<ValidationRemark> validateModel(T model, boolean throwOnError) {
		try {
			TableInspector inspector = getTable(model);
			inspector.read(metaData);
			@SuppressWarnings("unchecked")
			Class<T> clazz = (Class<T>) model.getClass();
			TableDefinition<T> def = db.define(clazz);
			return inspector.validate(def, throwOnError);
		} catch (SQLException s) {
			throw new IciqlException(s);
		}
	}

	private DatabaseMetaData getMetaData() throws SQLException {
		if (metaData == null) {
			metaData = db.getConnection().getMetaData();
		}
		return metaData;
	}

	/**
	 * Get the table in the database based on the model definition.
	 * 
	 * @param model
	 *            an instance of the model class
	 * @return the table inspector
	 */
	private <T> TableInspector getTable(T model) throws SQLException {
		@SuppressWarnings("unchecked")
		Class<T> clazz = (Class<T>) model.getClass();
		TableDefinition<T> def = db.define(clazz);
		boolean forceUpperCase = getMetaData().storesUpperCaseIdentifiers();
		String schema = (forceUpperCase && def.schemaName != null) ? def.schemaName.toUpperCase()
				: def.schemaName;
		String table = forceUpperCase ? def.tableName.toUpperCase() : def.tableName;
		List<TableInspector> tables = getTables(schema, table);
		return tables.get(0);
	}

	/**
	 * Returns a list of tables. This method always returns at least one
	 * element. If no table is found, an exception is thrown.
	 * 
	 * @param schema
	 *            the schema name
	 * @param table
	 *            the table name
	 * @return a list of table inspectors (always contains at least one element)
	 */
	private List<TableInspector> getTables(String schema, String table) throws SQLException {
		ResultSet rs = null;
		try {
			rs = getMetaData().getSchemas();
			ArrayList<String> schemaList = Utils.newArrayList();
			while (rs.next()) {
				schemaList.add(rs.getString("TABLE_SCHEM"));
			}
			JdbcUtils.closeSilently(rs);

			String iciqlTables = DbVersion.class.getAnnotation(IQTable.class).name();

			List<TableInspector> tables = Utils.newArrayList();
			if (schemaList.size() == 0) {
				schemaList.add(null);
			}
			for (String s : schemaList) {
				rs = getMetaData().getTables(null, s, null, new String[] { "TABLE" });
				while (rs.next()) {
					String t = rs.getString("TABLE_NAME");
					if (!t.equalsIgnoreCase(iciqlTables)) {
						tables.add(new TableInspector(s, t, dateTimeClass));
					}
				}
			}

			if (StringUtils.isNullOrEmpty(schema) && StringUtils.isNullOrEmpty(table)) {
				// all schemas and tables
				return tables;
			}
			// schema subset OR table subset OR exact match
			List<TableInspector> matches = Utils.newArrayList();
			for (TableInspector t : tables) {
				if (t.matches(schema, table)) {
					matches.add(t);
				}
			}
			if (matches.size() == 0) {
				throw new IciqlException(MessageFormat.format("Failed to find schema={0} table={1}",
						schema == null ? "" : schema, table == null ? "" : table));
			}
			return matches;
		} finally {
			JdbcUtils.closeSilently(rs);
		}
	}

}