001: package org.apache.torque.map;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.lang.reflect.Method;
023: import java.text.MessageFormat;
024: import java.util.Collections;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.Map;
028: import java.util.StringTokenizer;
029:
030: import org.apache.commons.collections.map.ListOrderedMap;
031: import org.apache.commons.lang.StringUtils;
032: import org.apache.torque.TorqueException;
033: import org.apache.torque.adapter.IDMethod;
034: import org.apache.torque.oid.IDBroker;
035: import org.apache.torque.oid.IdGenerator;
036:
037: /**
038: * DatabaseMap is used to model a database.
039: *
040: * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
041: * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
042: * @author <a href="mailto:greg.monroe@dukece.com">Greg Monroe</a>
043: * @version $Id: DatabaseMap.java 476550 2006-11-18 16:08:37Z tfischer $
044: */
045: public class DatabaseMap implements java.io.Serializable {
046: /**
047: * The character used by most implementations as the separator
048: * between name elements.
049: */
050: public static final char STD_SEPARATOR_CHAR = '_';
051:
052: /**
053: * The character which separates the schema name from the table name.
054: */
055: public static final char SCHEMA_SEPARATOR_CHAR = '.';
056:
057: /**
058: * Format used to create create the class name for initializing a DB
059: * specific map
060: */
061: public static final String INIT_CLASS_NAME_FORMAT = "org.apache.torque.linkage.{0}MapInit";
062:
063: /**
064: * Error Messages for initialisation.
065: */
066: protected static final String[] ERROR_MESSAGES_INIT = {
067: "Invalid Torque OM setup for Database \"{0}\".\n"
068: + "Database Map initialization class, \"{1}\","
069: + " " + "could not be found in your classpath.",
070: "Invalid Torque OM setup for Database \"{0}\".\n"
071: + "A class that the Database Map initialization class, \"{1}\", "
072: + "depends on could not be found.",
073: "Invalid Torque OM setup for Database \"{0}\".\n"
074: + "Something unexpected happened doing Class.forName(\"{1}\"). "
075: + "See the nested exception for details.",
076: "Invalid Torque OM setup for Database \"{0}\".\n"
077: + "An error occured invoking the init() method in class, \"{1}\"" };
078:
079: /** The serialVersionUID for this class. */
080: private static final long serialVersionUID = 955251837095032274L;
081:
082: /** The initial size of the Id-Generators map. */
083: private static final int ID_GENERATORS_INITIAL_SIZE = 6;
084:
085: /** Name of the database. */
086: private String name;
087:
088: /** Name of the tables in the database. */
089: private Map tables;
090:
091: /**
092: * A special table used to generate primary keys for the other
093: * tables.
094: */
095: private TableMap idTable = null;
096:
097: /** The IDBroker that goes with the idTable. */
098: private IDBroker idBroker = null;
099:
100: /** The IdGenerators, keyed by type of idMethod. */
101: private HashMap idGenerators;
102:
103: /** Flag indicating that all tables have been loaded via initialize() */
104: private boolean isInitialized = false;
105:
106: /**
107: * Constructs a new DatabaseMap.
108: */
109: public DatabaseMap() {
110: tables = Collections.synchronizedMap(new ListOrderedMap());
111: idGenerators = new HashMap(ID_GENERATORS_INITIAL_SIZE);
112: }
113:
114: /**
115: * Constructor.
116: *
117: * @param name Name of the database.
118: * @param numberOfTables Number of tables in the database.
119: * @deprecated use DatabaseMap() instead. Will be removed
120: * in a future version of Torque.
121: */
122: public DatabaseMap(String name, int numberOfTables) {
123: this .name = name;
124: tables = Collections.synchronizedMap(new ListOrderedMap());
125: idGenerators = new HashMap(ID_GENERATORS_INITIAL_SIZE);
126: }
127:
128: /**
129: * Constructor.
130: *
131: * @param name Name of the database.
132: * @deprecated use DatabaseMap() instead. Will be removed
133: * in a future version of Torque.
134: */
135: public DatabaseMap(String name) {
136: this .name = name;
137: tables = Collections.synchronizedMap(new ListOrderedMap());
138: idGenerators = new HashMap(ID_GENERATORS_INITIAL_SIZE);
139: }
140:
141: /**
142: * Does this database contain this specific table?
143: *
144: * @param table The TableMap representation of the table.
145: * @return True if the database contains the table.
146: */
147: public boolean containsTable(TableMap table) {
148: return containsTable(table.getName());
149: }
150:
151: /**
152: * Does this database contain this specific table?
153: *
154: * @param name The String representation of the table.
155: * @return True if the database contains the table.
156: */
157: public boolean containsTable(String name) {
158: if (name.indexOf('.') > 0) {
159: name = name.substring(0, name.indexOf('.'));
160: }
161: return tables.containsKey(name);
162: }
163:
164: /**
165: * Get the ID table for this database.
166: *
167: * @return A TableMap.
168: */
169: public TableMap getIdTable() {
170: return idTable;
171: }
172:
173: /**
174: * Get the IDBroker for this database.
175: *
176: * @return An IDBroker.
177: * @deprecated Will be removed in a future version of Torque.
178: * Use DatabaseInfo#getIdBroker() instead
179: * to access the IDBroker.
180: */
181: public IDBroker getIDBroker() {
182: return idBroker;
183: }
184:
185: /**
186: * Get the name of this database.
187: *
188: * @return A String.
189: * @deprecated Will be removed in a future version of Torque.
190: * Use the name of the corresponding database instead.
191: */
192: public String getName() {
193: return name;
194: }
195:
196: /**
197: * Get a TableMap for the table by name. <p>
198: *
199: * Note that by default Torque uses lazy initialization to minimize
200: * memory usage and startup time. However, if an OM or PEER class
201: * has not called the table's MapBuilder class, it will not be here.
202: * See the optional initialize method if you need full OM Mapping.<p>
203: *
204: * @param name Name of the table.
205: * @return A TableMap, null if the table was not found.
206: */
207: public TableMap getTable(String name) {
208: return (TableMap) tables.get(name);
209: }
210:
211: /**
212: * Get a TableMap[] of all of the tables in the database.<P>
213: *
214: * Note that by default Torque uses lazy initialization to minimize
215: * memory usage and startup time. However, if an OM or PEER class
216: * has not called the table's MapBuilder class, it will not be here.
217: * See the optional initialize method if you need full OM Mapping.<p>
218: *
219: * @return A TableMap[].
220: */
221: public TableMap[] getTables() {
222: TableMap[] dbTables = new TableMap[tables.size()];
223: synchronized (tables) {
224: Iterator it = tables.values().iterator();
225: int i = 0;
226: while (it.hasNext()) {
227: dbTables[i++] = (TableMap) it.next();
228: }
229: }
230: return dbTables;
231: }
232:
233: /**
234: * Add a new table to the database by name. It creates an empty
235: * TableMap that you need to populate.
236: *
237: * @param tableName The name of the table.
238: */
239: public void addTable(String tableName) {
240: TableMap tmap = new TableMap(tableName, this );
241: tables.put(tableName, tmap);
242: }
243:
244: /**
245: * Add a new table to the database by name. It creates an empty
246: * TableMap that you need to populate.
247: *
248: * @param tableName The name of the table.
249: * @param numberOfColumns The number of columns in the table.
250: */
251: public void addTable(String tableName, int numberOfColumns) {
252: TableMap tmap = new TableMap(tableName, numberOfColumns, this );
253: tables.put(tableName, tmap);
254: }
255:
256: /**
257: * Add a new TableMap to the database.
258: *
259: * @param map The TableMap representation.
260: */
261: public void addTable(TableMap map) {
262: tables.put(map.getName(), map);
263: }
264:
265: /**
266: * Set the ID table for this database.
267: *
268: * @param idTable The TableMap representation for the ID table.
269: */
270: public void setIdTable(TableMap idTable) {
271: this .idTable = idTable;
272: addTable(idTable);
273: }
274:
275: /**
276: * Set the ID table for this database.
277: *
278: * @param tableName The name for the ID table.
279: */
280: public void setIdTable(String tableName) {
281: TableMap tmap = new TableMap(tableName, this );
282: setIdTable(tmap);
283: }
284:
285: /**
286: * Add a type of id generator for access by a TableMap.
287: *
288: * @param type a <code>String</code> value
289: * @param idGen an <code>IdGenerator</code> value
290: * @deprecated use DatabaseInfo.addGenerator() instead.
291: * Will be removed in a future version of Torque.
292: */
293: public void addIdGenerator(String type, IdGenerator idGen) {
294: idGenerators.put(type, idGen);
295: }
296:
297: /**
298: * Get a type of id generator. Valid values are listed in the
299: * {@link org.apache.torque.adapter.IDMethod} interface.
300: *
301: * @param type a <code>String</code> value
302: * @return an <code>IdGenerator</code> value
303: * @deprecated use DatabaseInfo.getIdGenerator() instead.
304: * Will be removed in a future version of Torque.
305: */
306: public IdGenerator getIdGenerator(String type) {
307: return (IdGenerator) idGenerators.get(type);
308: }
309:
310: /**
311: * Creates the Idbroker for this DatabaseMap.
312: * If an IDBroker already exists for the DatabaseMap, the method
313: * does nothing.
314: * @return true if a new IdBroker was created, false otherwise.
315: * @deprecated Will be removed in a future version of Torque.
316: * Use DatabaseInfo.startIdBroker() instead.
317: */
318: public synchronized boolean startIdBroker() {
319: if (idBroker == null) {
320: setIdTable("ID_TABLE");
321: TableMap tMap = getIdTable();
322: tMap.addPrimaryKey("ID_TABLE_ID", new Integer(0));
323: tMap.addColumn("TABLE_NAME", "");
324: tMap.addColumn("NEXT_ID", new Integer(0));
325: tMap.addColumn("QUANTITY", new Integer(0));
326: idBroker = new IDBroker(idTable);
327: addIdGenerator(IDMethod.ID_BROKER, idBroker);
328: return true;
329: }
330: return false;
331: }
332:
333: /**
334: * Fully populate this DatabaseMap with all the TablesMaps. This
335: * is only needed if the application needs to use the complete OM
336: * mapping information. Otherwise, the OM Mapping information
337: * will be populated as needed by OM and Peer classes. An example
338: * of how to initialize the map info from the application:<p>
339: *
340: * <code>
341: * DatabaseMap dbMap = Torque.getDatabaseMap( dbName );
342: * try {
343: * dbMap.initialize();
344: * } catch ( TorqueException e ) {
345: * ... error handling
346: * }
347: * </code>
348: *
349: * Note that Torque database names are case sensitive and this DB
350: * map must be retrieved with the exact name used in the XML schema.<p>
351: *
352: * This uses Java reflection methods to locate and run the
353: * init() method of a class generated in the org.apache.torque.linkage
354: * package with a name based on the XML Database name value, e.g.
355: * org.apache.torque.linkage.DefaultMapInit<p>
356: *
357: * Some misconfiguration situations that could cause this method to fail
358: * are:<p>
359: *
360: * It was used with a Torque OM set of classes generated by V3.2 or older;
361: * <br>
362: * The class(es) in the org.apache.torque.linkage package were not included
363: * with the other generated class files (e.g. the jar file creation process
364: * only included com.* and not org.* files).<p>
365: *
366: * @throws TorqueException If an error is encountered locating and calling
367: * the init method.
368: */
369: public synchronized void initialize() throws TorqueException {
370: if (isInitialized) {
371: return;
372: }
373: String initClassName = MessageFormat.format(
374: INIT_CLASS_NAME_FORMAT,
375: new Object[] { javanameMethod(getName()) });
376:
377: Class initClass = null;
378: try {
379: initClass = Class.forName(initClassName);
380: } catch (ClassNotFoundException e) {
381: throw new TorqueException(MessageFormat.format(
382: ERROR_MESSAGES_INIT[0], new Object[] { getName(),
383: initClassName }), e);
384: } catch (LinkageError e) {
385: throw new TorqueException(MessageFormat.format(
386: ERROR_MESSAGES_INIT[1], new Object[] { getName(),
387: initClassName }), e);
388: } catch (Throwable e) {
389: throw new TorqueException(MessageFormat.format(
390: ERROR_MESSAGES_INIT[2], new Object[] { getName(),
391: initClassName }), e);
392: }
393: try {
394: Method initMethod = initClass.getMethod("init",
395: (Class[]) null);
396: initMethod.invoke(null, (Object[]) null);
397: } catch (Exception e) {
398: throw new TorqueException(MessageFormat.format(
399: ERROR_MESSAGES_INIT[3], new Object[] { getName(),
400: initClassName }), e);
401: }
402: isInitialized = true;
403: }
404:
405: /**
406: * Converts a database schema name to java object name. Operates
407: * same as underscoreMethod but does not convert anything to
408: * lowercase. This must match the javaNameMethod in the
409: * JavaNameGenerator class in Generator code.
410: *
411: * @param schemaName name to be converted.
412: * @return converted name.
413: *
414: * @see org.apache.torque.engine.database.model.NameGenerator
415: */
416: protected String javanameMethod(String schemaName) {
417: StringBuffer result = new StringBuffer();
418: StringTokenizer tok = new StringTokenizer(schemaName, String
419: .valueOf(STD_SEPARATOR_CHAR));
420: while (tok.hasMoreTokens()) {
421: String namePart = (String) tok.nextElement();
422: result.append(StringUtils.capitalize(namePart));
423: }
424:
425: // remove the SCHEMA_SEPARATOR_CHARs and capitalize
426: // the tokens
427: schemaName = result.toString();
428: result = new StringBuffer();
429:
430: tok = new StringTokenizer(schemaName, String
431: .valueOf(SCHEMA_SEPARATOR_CHAR));
432: while (tok.hasMoreTokens()) {
433: String namePart = (String) tok.nextElement();
434: result.append(StringUtils.capitalize(namePart));
435: }
436: return result.toString();
437: }
438: }
|