1 package org.apache.torque.map;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.lang.reflect.Method;
23 import java.text.MessageFormat;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.StringTokenizer;
29
30 import org.apache.commons.collections.map.ListOrderedMap;
31 import org.apache.commons.lang.StringUtils;
32 import org.apache.torque.TorqueException;
33 import org.apache.torque.adapter.IDMethod;
34 import org.apache.torque.oid.IDBroker;
35 import org.apache.torque.oid.IdGenerator;
36
37 /***
38 * DatabaseMap is used to model a database.
39 *
40 * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
41 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
42 * @author <a href="mailto:greg.monroe@dukece.com">Greg Monroe</a>
43 * @version $Id: DatabaseMap.java 476550 2006-11-18 16:08:37Z tfischer $
44 */
45 public class DatabaseMap implements java.io.Serializable
46 {
47 /***
48 * The character used by most implementations as the separator
49 * between name elements.
50 */
51 public static final char STD_SEPARATOR_CHAR = '_';
52
53 /***
54 * The character which separates the schema name from the table name.
55 */
56 public static final char SCHEMA_SEPARATOR_CHAR = '.';
57
58 /***
59 * Format used to create create the class name for initializing a DB
60 * specific map
61 */
62 public static final String INIT_CLASS_NAME_FORMAT =
63 "org.apache.torque.linkage.{0}MapInit";
64
65 /***
66 * Error Messages for initialisation.
67 */
68 protected static final String[] ERROR_MESSAGES_INIT = {
69 "Invalid Torque OM setup for Database \"{0}\".\n"
70 + "Database Map initialization class, \"{1}\"," + " "
71 + "could not be found in your classpath.",
72 "Invalid Torque OM setup for Database \"{0}\".\n"
73 + "A class that the Database Map initialization class, \"{1}\", "
74 + "depends on could not be found.",
75 "Invalid Torque OM setup for Database \"{0}\".\n"
76 + "Something unexpected happened doing Class.forName(\"{1}\"). "
77 + "See the nested exception for details.",
78 "Invalid Torque OM setup for Database \"{0}\".\n"
79 + "An error occured invoking the init() method in class, \"{1}\""
80 };
81
82 /*** The serialVersionUID for this class. */
83 private static final long serialVersionUID = 955251837095032274L;
84
85 /*** The initial size of the Id-Generators map. */
86 private static final int ID_GENERATORS_INITIAL_SIZE = 6;
87
88 /*** Name of the database. */
89 private String name;
90
91 /*** Name of the tables in the database. */
92 private Map tables;
93
94 /***
95 * A special table used to generate primary keys for the other
96 * tables.
97 */
98 private TableMap idTable = null;
99
100 /*** The IDBroker that goes with the idTable. */
101 private IDBroker idBroker = null;
102
103 /*** The IdGenerators, keyed by type of idMethod. */
104 private HashMap idGenerators;
105
106 /*** Flag indicating that all tables have been loaded via initialize() */
107 private boolean isInitialized = false;
108
109 /***
110 * Constructs a new DatabaseMap.
111 */
112 public DatabaseMap()
113 {
114 tables = Collections.synchronizedMap(new ListOrderedMap());
115 idGenerators = new HashMap(ID_GENERATORS_INITIAL_SIZE);
116 }
117
118 /***
119 * Constructor.
120 *
121 * @param name Name of the database.
122 * @param numberOfTables Number of tables in the database.
123 * @deprecated use DatabaseMap() instead. Will be removed
124 * in a future version of Torque.
125 */
126 public DatabaseMap(String name, int numberOfTables)
127 {
128 this.name = name;
129 tables = Collections.synchronizedMap(new ListOrderedMap());
130 idGenerators = new HashMap(ID_GENERATORS_INITIAL_SIZE);
131 }
132
133 /***
134 * Constructor.
135 *
136 * @param name Name of the database.
137 * @deprecated use DatabaseMap() instead. Will be removed
138 * in a future version of Torque.
139 */
140 public DatabaseMap(String name)
141 {
142 this.name = name;
143 tables = Collections.synchronizedMap(new ListOrderedMap());
144 idGenerators = new HashMap(ID_GENERATORS_INITIAL_SIZE);
145 }
146
147 /***
148 * Does this database contain this specific table?
149 *
150 * @param table The TableMap representation of the table.
151 * @return True if the database contains the table.
152 */
153 public boolean containsTable(TableMap table)
154 {
155 return containsTable(table.getName());
156 }
157
158 /***
159 * Does this database contain this specific table?
160 *
161 * @param name The String representation of the table.
162 * @return True if the database contains the table.
163 */
164 public boolean containsTable(String name)
165 {
166 if (name.indexOf('.') > 0)
167 {
168 name = name.substring(0, name.indexOf('.'));
169 }
170 return tables.containsKey(name);
171 }
172
173 /***
174 * Get the ID table for this database.
175 *
176 * @return A TableMap.
177 */
178 public TableMap getIdTable()
179 {
180 return idTable;
181 }
182
183 /***
184 * Get the IDBroker for this database.
185 *
186 * @return An IDBroker.
187 * @deprecated Will be removed in a future version of Torque.
188 * Use DatabaseInfo#getIdBroker() instead
189 * to access the IDBroker.
190 */
191 public IDBroker getIDBroker()
192 {
193 return idBroker;
194 }
195
196 /***
197 * Get the name of this database.
198 *
199 * @return A String.
200 * @deprecated Will be removed in a future version of Torque.
201 * Use the name of the corresponding database instead.
202 */
203 public String getName()
204 {
205 return name;
206 }
207
208 /***
209 * Get a TableMap for the table by name. <p>
210 *
211 * Note that by default Torque uses lazy initialization to minimize
212 * memory usage and startup time. However, if an OM or PEER class
213 * has not called the table's MapBuilder class, it will not be here.
214 * See the optional initialize method if you need full OM Mapping.<p>
215 *
216 * @param name Name of the table.
217 * @return A TableMap, null if the table was not found.
218 */
219 public TableMap getTable(String name)
220 {
221 return (TableMap) tables.get(name);
222 }
223
224 /***
225 * Get a TableMap[] of all of the tables in the database.<P>
226 *
227 * Note that by default Torque uses lazy initialization to minimize
228 * memory usage and startup time. However, if an OM or PEER class
229 * has not called the table's MapBuilder class, it will not be here.
230 * See the optional initialize method if you need full OM Mapping.<p>
231 *
232 * @return A TableMap[].
233 */
234 public TableMap[] getTables()
235 {
236 TableMap[] dbTables = new TableMap[tables.size()];
237 synchronized (tables)
238 {
239 Iterator it = tables.values().iterator();
240 int i = 0;
241 while (it.hasNext())
242 {
243 dbTables[i++] = (TableMap) it.next();
244 }
245 }
246 return dbTables;
247 }
248
249 /***
250 * Add a new table to the database by name. It creates an empty
251 * TableMap that you need to populate.
252 *
253 * @param tableName The name of the table.
254 */
255 public void addTable(String tableName)
256 {
257 TableMap tmap = new TableMap(tableName, this);
258 tables.put(tableName, tmap);
259 }
260
261 /***
262 * Add a new table to the database by name. It creates an empty
263 * TableMap that you need to populate.
264 *
265 * @param tableName The name of the table.
266 * @param numberOfColumns The number of columns in the table.
267 */
268 public void addTable(String tableName, int numberOfColumns)
269 {
270 TableMap tmap = new TableMap(tableName, numberOfColumns, this);
271 tables.put(tableName, tmap);
272 }
273
274 /***
275 * Add a new TableMap to the database.
276 *
277 * @param map The TableMap representation.
278 */
279 public void addTable(TableMap map)
280 {
281 tables.put(map.getName(), map);
282 }
283
284 /***
285 * Set the ID table for this database.
286 *
287 * @param idTable The TableMap representation for the ID table.
288 */
289 public void setIdTable(TableMap idTable)
290 {
291 this.idTable = idTable;
292 addTable(idTable);
293 }
294
295 /***
296 * Set the ID table for this database.
297 *
298 * @param tableName The name for the ID table.
299 */
300 public void setIdTable(String tableName)
301 {
302 TableMap tmap = new TableMap(tableName, this);
303 setIdTable(tmap);
304 }
305
306 /***
307 * Add a type of id generator for access by a TableMap.
308 *
309 * @param type a <code>String</code> value
310 * @param idGen an <code>IdGenerator</code> value
311 * @deprecated use DatabaseInfo.addGenerator() instead.
312 * Will be removed in a future version of Torque.
313 */
314 public void addIdGenerator(String type, IdGenerator idGen)
315 {
316 idGenerators.put(type, idGen);
317 }
318
319 /***
320 * Get a type of id generator. Valid values are listed in the
321 * {@link org.apache.torque.adapter.IDMethod} interface.
322 *
323 * @param type a <code>String</code> value
324 * @return an <code>IdGenerator</code> value
325 * @deprecated use DatabaseInfo.getIdGenerator() instead.
326 * Will be removed in a future version of Torque.
327 */
328 public IdGenerator getIdGenerator(String type)
329 {
330 return (IdGenerator) idGenerators.get(type);
331 }
332
333 /***
334 * Creates the Idbroker for this DatabaseMap.
335 * If an IDBroker already exists for the DatabaseMap, the method
336 * does nothing.
337 * @return true if a new IdBroker was created, false otherwise.
338 * @deprecated Will be removed in a future version of Torque.
339 * Use DatabaseInfo.startIdBroker() instead.
340 */
341 public synchronized boolean startIdBroker()
342 {
343 if (idBroker == null)
344 {
345 setIdTable("ID_TABLE");
346 TableMap tMap = getIdTable();
347 tMap.addPrimaryKey("ID_TABLE_ID", new Integer(0));
348 tMap.addColumn("TABLE_NAME", "");
349 tMap.addColumn("NEXT_ID", new Integer(0));
350 tMap.addColumn("QUANTITY", new Integer(0));
351 idBroker = new IDBroker(idTable);
352 addIdGenerator(IDMethod.ID_BROKER, idBroker);
353 return true;
354 }
355 return false;
356 }
357
358 /***
359 * Fully populate this DatabaseMap with all the TablesMaps. This
360 * is only needed if the application needs to use the complete OM
361 * mapping information. Otherwise, the OM Mapping information
362 * will be populated as needed by OM and Peer classes. An example
363 * of how to initialize the map info from the application:<p>
364 *
365 * <code>
366 * DatabaseMap dbMap = Torque.getDatabaseMap( dbName );
367 * try {
368 * dbMap.initialize();
369 * } catch ( TorqueException e ) {
370 * ... error handling
371 * }
372 * </code>
373 *
374 * Note that Torque database names are case sensitive and this DB
375 * map must be retrieved with the exact name used in the XML schema.<p>
376 *
377 * This uses Java reflection methods to locate and run the
378 * init() method of a class generated in the org.apache.torque.linkage
379 * package with a name based on the XML Database name value, e.g.
380 * org.apache.torque.linkage.DefaultMapInit<p>
381 *
382 * Some misconfiguration situations that could cause this method to fail
383 * are:<p>
384 *
385 * It was used with a Torque OM set of classes generated by V3.2 or older;
386 * <br>
387 * The class(es) in the org.apache.torque.linkage package were not included
388 * with the other generated class files (e.g. the jar file creation process
389 * only included com.* and not org.* files).<p>
390 *
391 * @throws TorqueException If an error is encountered locating and calling
392 * the init method.
393 */
394 public synchronized void initialize() throws TorqueException
395 {
396 if (isInitialized)
397 {
398 return;
399 }
400 String initClassName = MessageFormat.format(INIT_CLASS_NAME_FORMAT,
401 new Object[] {
402 javanameMethod(getName())
403 });
404
405 Class initClass = null;
406 try
407 {
408 initClass = Class.forName(initClassName);
409 }
410 catch (ClassNotFoundException e)
411 {
412 throw new TorqueException(MessageFormat.format(
413 ERROR_MESSAGES_INIT[0],
414 new Object[] {
415 getName(),
416 initClassName
417 }),
418 e);
419 }
420 catch (LinkageError e)
421 {
422 throw new TorqueException(MessageFormat.format(
423 ERROR_MESSAGES_INIT[1],
424 new Object[] {
425 getName(), initClassName
426 }),
427 e);
428 }
429 catch (Throwable e)
430 {
431 throw new TorqueException(MessageFormat.format(
432 ERROR_MESSAGES_INIT[2],
433 new Object[] {
434 getName(), initClassName
435 }),
436 e);
437 }
438 try
439 {
440 Method initMethod = initClass.getMethod("init", (Class []) null);
441 initMethod.invoke(null, (Object []) null);
442 }
443 catch (Exception e)
444 {
445 throw new TorqueException(MessageFormat.format(
446 ERROR_MESSAGES_INIT[3],
447 new Object[] {
448 getName(), initClassName
449 }),
450 e);
451 }
452 isInitialized = true;
453 }
454
455 /***
456 * Converts a database schema name to java object name. Operates
457 * same as underscoreMethod but does not convert anything to
458 * lowercase. This must match the javaNameMethod in the
459 * JavaNameGenerator class in Generator code.
460 *
461 * @param schemaName name to be converted.
462 * @return converted name.
463 *
464 * @see org.apache.torque.engine.database.model.NameGenerator
465 */
466 protected String javanameMethod(String schemaName)
467 {
468 StringBuffer result = new StringBuffer();
469 StringTokenizer tok = new StringTokenizer
470 (schemaName, String.valueOf(STD_SEPARATOR_CHAR));
471 while (tok.hasMoreTokens())
472 {
473 String namePart = (String) tok.nextElement();
474 result.append(StringUtils.capitalize(namePart));
475 }
476
477
478
479 schemaName = result.toString();
480 result = new StringBuffer();
481
482 tok = new StringTokenizer
483 (schemaName, String.valueOf(SCHEMA_SEPARATOR_CHAR));
484 while (tok.hasMoreTokens())
485 {
486 String namePart = (String) tok.nextElement();
487 result.append(StringUtils.capitalize(namePart));
488 }
489 return result.toString();
490 }
491 }