View Javadoc

1   package org.apache.torque.map;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.LinkedHashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.StringTokenizer;
33  
34  import org.apache.commons.lang.StringUtils;
35  import org.apache.torque.Database;
36  import org.apache.torque.Torque;
37  import org.apache.torque.TorqueException;
38  import org.apache.torque.adapter.IDMethod;
39  
40  /**
41   * TableMap is used to model a table in a database.
42   *
43   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
44   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
45   * @author <a href="mailto:greg.monroe@dukece.com">Greg Monroe</a>
46   * @version $Id: TableMap.java 1375888 2012-08-22 03:51:00Z tfischer $
47   */
48  public class TableMap implements Serializable
49  {
50      /**
51       * Serial version.
52       */
53      private static final long serialVersionUID = 1L;
54  
55      /** The columns in the table. XML Order is preserved. */
56      private final Map<String, ColumnMap> columns
57              = Collections.synchronizedMap(
58                  new LinkedHashMap<String, ColumnMap>());
59  
60      /** The foreign keys in the table. XML Order is preserved. */
61      private final List<ForeignKeyMap> foreignKeys = new ArrayList<ForeignKeyMap>();
62  
63      /** The database this table belongs to. */
64      private DatabaseMap dbMap;
65  
66      /** The name of the table. */
67      private String tableName;
68  
69      /**
70       * The name of the schema to which this table belongs,
71       * or null for the default schema.
72       */
73      private String schemaName;
74  
75      /** The JavaName of the table as defined in XML */
76      private String javaName;
77  
78      /** The prefix on the table name. */
79      private String prefix;
80  
81      /** The primary key generation method. */
82      private IDMethod primaryKeyMethod = IDMethod.NO_ID_METHOD;
83  
84      /** The table description info. */
85      private String description = "";
86  
87      /** The Peer Class for this table. */
88      private Class<?> peerClass;
89  
90      /** The OM Root Class for this table. */
91      private Class<?> omClass;
92  
93      /** Whether any column uses Inheritance. */
94      private boolean useInheritance = false;
95  
96      /** Whether cache managers are used. */
97      private boolean useManager = false;
98  
99      /** The associated cache manager class. */
100     private Class<?> managerClass;
101 
102     /** Overrides the information stored in the pkInfoMap for all id methods. */
103     private Object pkInfoOverride;
104 
105     /**
106      * Stores information that is needed for generating primary keys.
107      * The information is keyed by the idMethodType because it might be
108      * different for different id methods.
109      */
110     private final Map<IDMethod, Object> pkInfoMap
111             = new HashMap<IDMethod, Object>();
112 
113     /** Associated options. */
114     private final Map<String, String> optionsMap
115             = Collections.synchronizedMap(new LinkedHashMap<String, String>());
116 
117     /**
118      * Constructor.
119      *
120      * @param tableName The name of the table, may be prefixed with a
121      *        schema name, not null.
122      * @param containingDB A DatabaseMap that this table belongs to.
123      */
124     public TableMap(String tableName, DatabaseMap containingDB)
125     {
126         setTableName(tableName);
127         dbMap = containingDB;
128     }
129 
130     /**
131      * Constructor.
132      *
133      * @param tableName The name of the table, may be prefixed with a
134      *        schema name, not null.
135      * @param prefix The prefix for the table name (ie: SCARAB for
136      *        SCARAB_PROJECT).
137      * @param containingDB A DatabaseMap that this table belongs to.
138      */
139     public TableMap(String tableName,
140                     String prefix,
141                     DatabaseMap containingDB)
142     {
143         setTableName(tableName);
144         this.prefix = prefix;
145         dbMap = containingDB;
146     }
147 
148     private void setTableName(String tableName)
149     {
150         if (tableName == null)
151         {
152             throw new NullPointerException("tableName must not be null");
153         }
154         int dotIndex = tableName.indexOf(".");
155         if (dotIndex != -1)
156         {
157             this.schemaName = tableName.substring(0, dotIndex);
158             this.tableName = tableName.substring(dotIndex + 1);
159         }
160         else
161         {
162             this.tableName = tableName;
163         }
164     }
165 
166     /**
167      * Sets the database map this table belongs to.
168      * @param databaseMap
169      */
170     void setDatabaseMap(DatabaseMap databaseMap)
171     {
172         dbMap = databaseMap;
173     }
174 
175     /**
176      * Does this table contain the specified column?
177      *
178      * @param column A ColumnMap.
179      * @return True if the table contains the column.
180      */
181     public boolean containsColumn(ColumnMap column)
182     {
183         return containsColumn(column.getColumnName());
184     }
185 
186     /**
187      * Does this table contain the specified column?
188      *
189      * @param name A String with the name of the column.
190      * @return True if the table contains the column.
191      */
192     public boolean containsColumn(String name)
193     {
194         if (name.indexOf('.') > 0)
195         {
196             name = name.substring(name.indexOf('.') + 1);
197         }
198         return columns.containsKey(name);
199     }
200 
201     /**
202      * Get the DatabaseMap containing this TableMap.
203      *
204      * @return A DatabaseMap.
205      */
206     public DatabaseMap getDatabaseMap()
207     {
208         return dbMap;
209     }
210 
211     /**
212      * Returns true if this tableMap contains a column with object
213      * data.  If the type of the column is not a string, a number or a
214      * date, it is assumed that it is object data.
215      *
216      * @return True if map contains a column with object data.
217      */
218     public boolean containsObjectColumn()
219     {
220         synchronized (columns)
221         {
222             Iterator<ColumnMap> it = columns.values().iterator();
223             while (it.hasNext())
224             {
225                 Object theType = it.next().getType();
226                 if (!(theType instanceof String || theType instanceof Number
227                         || theType instanceof java.util.Date))
228                 {
229                     return true;
230                 }
231             }
232         }
233         return false;
234     }
235 
236     /**
237      * Get the name of the Table, not prefixed by a possible schema name
238      *
239      * @return A String with the name of the table, not null.
240      */
241     public String getName()
242     {
243         return tableName;
244     }
245 
246     /**
247      * Get the schema to which the table belongs to.
248      *
249      * @return the schema name, or null if the default schema should be used.
250      */
251     public String getSchemaName()
252     {
253         return schemaName;
254     }
255 
256     /**
257      * Get the Java name of the table as defined in XML.
258      *
259      * @return A String with the Java name of the table.
260      */
261     public String getJavaName()
262     {
263         return javaName;
264     }
265 
266     /**
267      * Set the Java name of the table as defined by generator/XML.
268      *
269      * @param value A String with the Java name of the table.
270      */
271     public void setJavaName(String value)
272     {
273         this.javaName = value;
274     }
275 
276     /**
277      * Get table prefix name.
278      *
279      * @return A String with the prefix.
280      */
281     public String getPrefix()
282     {
283         return this.prefix;
284     }
285 
286     /**
287      * Set table prefix name.
288      *
289      * @param prefix The prefix for the table name (ie: SCARAB for
290      * SCARAB_PROJECT).
291      */
292     public void setPrefix(String prefix)
293     {
294         this.prefix = prefix;
295     }
296 
297     /**
298      * Get the method used to generate primary keys for this table.
299      *
300      * @return A String with the method.
301      */
302     public IDMethod getPrimaryKeyMethod()
303     {
304         return primaryKeyMethod;
305     }
306 
307     /**
308      * Get the information used to generate a primary key
309      *
310      * @return An Object.
311      */
312     public Object getPrimaryKeyMethodInfo(IDMethod idMethod)
313     {
314         if (pkInfoOverride != null)
315         {
316             return pkInfoOverride;
317         }
318         return pkInfoMap.get(idMethod);
319     }
320 
321     /**
322      * Get a ColumnMap[] of the columns in this table.
323      *
324      * @return A ColumnMap[].
325      */
326     public ColumnMap[] getColumns()
327     {
328         ColumnMap[] tableColumns = new ColumnMap[columns.size()];
329         synchronized (columns)
330         {
331             Iterator<ColumnMap> it = columns.values().iterator();
332             int i = 0;
333             while (it.hasNext())
334             {
335                 tableColumns[i++] = it.next();
336             }
337         }
338         return tableColumns;
339     }
340 
341     /**
342      * Get all foreign keys in the table..
343      *
344      * @return All foreign keys, not null.
345      */
346     public List<ForeignKeyMap> getForeignKeys()
347     {
348         return Collections.unmodifiableList(foreignKeys);
349     }
350 
351     /**
352      * Get a ColumnMap for the named table.
353      *
354      * @param name A String with the name of the table.
355      * @return A ColumnMap.
356      */
357     public ColumnMap getColumn(String name)
358     {
359         try
360         {
361             return columns.get(name);
362         }
363         catch (Exception e)
364         {
365             return null;
366         }
367     }
368 
369     /**
370      * Add a pre-created column to this table.  It will replace any
371      * existing column.
372      *
373      * @param cmap A ColumnMap.
374      */
375     public void addColumn(ColumnMap cmap)
376     {
377         columns.put(cmap.getColumnName(), cmap);
378     }
379 
380     /**
381      * Add a foreign key to this table.
382      *
383      * @param foreignKey the foreign key map, not null
384      */
385     public void addForeignKey(ForeignKeyMap foreignKey)
386     {
387         foreignKeys.add(foreignKey);
388     }
389 
390     /**
391      * Sets the method used to generate a key for this table.  Valid
392      * values are as specified in the {@link
393      * org.apache.torque.adapter.IDMethod} interface.
394      *
395      * @param method The ID generation method type, not null.
396      */
397     public void setPrimaryKeyMethod(IDMethod method)
398     {
399         if (method == null)
400         {
401             throw new NullPointerException("method must not be null");
402         }
403         primaryKeyMethod = method;
404         if (IDMethod.ID_BROKER == method)
405         {
406             Database database = Torque.getOrCreateDatabase(
407                     getDatabaseMap().getName());
408             database.createAndRegisterIdBroker();
409         }
410     }
411 
412     /**
413      * Sets the pk information needed to generate a key.
414      * This overrides all information set by
415      * <code>setPrimaryKeyMethodInfo(String, Object)</code>.
416      *
417      * @param pkInfo information needed to generate a key
418      */
419     public void setPrimaryKeyMethodInfo(Object pkInfo)
420     {
421         pkInfoOverride = pkInfo;
422     }
423 
424     /**
425      * Sets the pk information needed to generate a key.
426      *
427      * @param idMethod the id method for which this information is stored.
428      * @param pkInfo information needed to generate a key.
429      */
430     public void setPrimaryKeyMethodInfo(IDMethod idMethod, Object pkInfo)
431     {
432         pkInfoMap.put(idMethod, pkInfo);
433     }
434 
435     //---Utility methods for doing intelligent lookup of table names
436 
437     /**
438      * Tell me if i have PREFIX in my string.
439      *
440      * @param data A String.
441      * @return True if prefix is contained in data.
442      */
443     private boolean hasPrefix(String data)
444     {
445         return (data.indexOf(getPrefix()) != -1);
446     }
447 
448     /**
449      * Removes the PREFIX.
450      *
451      * @param data A String.
452      * @return A String with data, but with prefix removed.
453      */
454     private String removePrefix(String data)
455     {
456         return data.substring(getPrefix().length());
457     }
458 
459     /**
460      * Removes the PREFIX, removes the underscores and makes
461      * first letter caps.
462      *
463      * SCARAB_FOO_BAR becomes FooBar.
464      *
465      * @param data A String.
466      * @return A String with data processed.
467      */
468     public final String removeUnderScores(String data)
469     {
470         String tmp = null;
471         StringBuffer out = new StringBuffer();
472         if (hasPrefix(data))
473         {
474             tmp = removePrefix(data);
475         }
476         else
477         {
478             tmp = data;
479         }
480 
481         StringTokenizer st = new StringTokenizer(tmp, "_");
482         while (st.hasMoreTokens())
483         {
484             String element = ((String) st.nextElement()).toLowerCase();
485             out.append(StringUtils.capitalize(element));
486         }
487         return out.toString();
488     }
489 
490     /**
491      * Returns the table description info.
492      *
493      * @return Returns the description.
494      */
495     public String getDescription()
496     {
497         return description;
498     }
499 
500     /**
501      * Sets the table description.
502      *
503      * @param description The description to set.
504      */
505     public void setDescription(String description)
506     {
507         this.description = description;
508     }
509 
510     /**
511      * Returns the OM class for this table.
512      *
513      * @return the OM class.
514      */
515     public Class<?> getOMClass()
516     {
517         return omClass;
518     }
519 
520     /**
521      * Sets the OM root class for this table.
522      *
523      * @param omClass The OM root class for this table.
524      */
525     public void setOMClass(Class<?> omClass)
526     {
527         this.omClass = omClass;
528     }
529 
530     /**
531      * Returns the Peer Class for this table.
532      *
533      * @return The peerClass for this table.
534      */
535     public Class<?> getPeerClass()
536     {
537         return peerClass;
538     }
539 
540     /**
541      * Sets the Peer class for this table.
542      *
543      * @param peerClass The peerClass to set.
544      */
545     public void setPeerClass(Class<?> peerClass)
546     {
547         this.peerClass = peerClass;
548     }
549 
550     /**
551      * Returns the database map for this table.
552      *
553      * @return the database map for this table.
554      */
555     public DatabaseMap getDbMap()
556     {
557         return dbMap;
558     }
559 
560     /**
561      * Returns whether this table uses inheritance.
562      *
563      * @return whether inheritance is used.
564      */
565     public boolean isUseInheritance()
566     {
567         return useInheritance;
568     }
569 
570     /**
571      * Sets whether this table uses inheritance.
572      *
573      * @param useInheritance whether this table uses inheritance.
574      */
575     public void setUseInheritance(boolean useInheritance)
576     {
577         this.useInheritance = useInheritance;
578     }
579 
580     /**
581      * Returns whether managers are used for this table.
582      *
583      * @return whether managers are used for this table.
584      */
585     public boolean isUseManager()
586     {
587         return useManager;
588     }
589 
590     /**
591      * Sets whether managers are used for this table.
592      *
593      * @param useManager whether managers are used for this table.
594      */
595     public void setUseManager(boolean useManager)
596     {
597         this.useManager = useManager;
598     }
599 
600     /**
601      * Returns the manager class for this table.
602      *
603      * @return the managerClass.
604      */
605     public Class<?> getManagerClass()
606     {
607         return managerClass;
608     }
609 
610     /**
611      * Sets the manager class for this table.
612      *
613      * @param managerClass the manager class for this table.
614      */
615     public void setManagerClass(Class<?> managerClass)
616     {
617         this.managerClass = managerClass;
618     }
619 
620     /**
621      * Returns an unmodifiable map of all options.
622      *
623      * @return A map containing all options, not null.
624      */
625     public Map<String, String> getOptions()
626     {
627         return Collections.unmodifiableMap(optionsMap);
628     }
629 
630     /**
631      * Sets an option.
632      *
633      * @param key the key of the option
634      * @param value the value of the option.
635      */
636     public void setOption(String key, String value)
637     {
638         optionsMap.put(key, value);
639     }
640 
641     /**
642      * Returns the value of an option.
643      *
644      * @param key the key of the option.
645      *
646      * @return the value of the option, or null if not set.
647      */
648     public String getOption(String key)
649     {
650         return optionsMap.get(key);
651     }
652 
653     /**
654      * Returns the single primary key of this table, if it exists
655      *
656      * @return the single primary key column.
657      *
658      * @throws TorqueException If the table has no primary key
659      *         or if the table has multiple primary keys.
660      */
661     public ColumnMap getPrimaryKey()
662             throws TorqueException
663     {
664         Set<ColumnMap> result = new HashSet<ColumnMap>();
665 
666         for (ColumnMap column : columns.values())
667         {
668             if (column.isPrimaryKey())
669             {
670                 result.add(column);
671             }
672         }
673         if (result.isEmpty())
674         {
675             throw new TorqueException("getPrimaryKey(): Table " + tableName
676                     + "has no primary key.");
677         }
678         if (result.size() > 1)
679         {
680             throw new TorqueException("getPrimaryKey(): Table " + tableName
681                     + "has more than one primary key.");
682         }
683         return result.iterator().next();
684     }
685 
686     @Override
687     public String toString()
688     {
689         return "TableMap[" + tableName + "]";
690     }
691 }