001/*
002 * Copyright (c) 2008-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2012-01-19 22:42:22 +0000 (Thu, 19 Jan 2012) $' 
007 * '$Revision: 29261 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.util.sql;
031
032import java.util.HashMap;
033import java.util.HashSet;
034import java.util.LinkedHashMap;
035import java.util.Map;
036import java.util.Set;
037
038/** 
039 *
040 * A generic representation of an SQL table. A Table can contain
041 * columns, constraints, and foreign keys.
042 *
043 * @author Daniel Crawl
044 * @version $Id: Table.java 29261 2012-01-19 22:42:22Z crawl $
045 *
046 */
047    
048public class Table
049{
050    /** Construct a new Table. */
051    Table(String name)
052    {
053        _tableName = name;
054
055        _columnMap = new LinkedHashMap<String,Column>();
056        _constraintMap = new LinkedHashMap<String,ColumnConstraint>();
057        _fkMap = new LinkedHashMap<String,ForeignKey>();
058
059        _columnConstraintsMap = new HashMap<String,Set<String>>();
060        _columnFKsMap = new HashMap<String,Set<String>>();
061    }
062    
063    /** Get the columns. */
064    public Set<Column> columns()
065    {
066        return new HashSet<Column>(_columnMap.values());
067    }
068
069    /** Get the column names. */
070    public Set<String> columnNames()
071    {
072        return new HashSet<String>(_columnMap.keySet()); 
073    }
074
075    /** Get the constraint names. */
076    public Set<String> constraintNames()
077    {
078        return _constraintMap.keySet();
079    }
080
081    /** Get the foreign key names. */
082    public Set<String> foreignKeyNames()
083    {
084        return _fkMap.keySet();
085    }
086
087    /** Get a specific column. */
088    public Column getColumn(String name)
089    {
090        return _columnMap.get(name);
091    }
092
093    /** Get a specific constraint. */
094    public ColumnConstraint getConstraint(String name)
095    {
096        return _constraintMap.get(name);
097    }
098
099    /** Get a specific foreign key. */
100    public ForeignKey getForeignKey(String name)
101    {
102        return _fkMap.get(name);
103    }
104    
105    /** Get the column(s) for an index. */
106    public String getIndexColumns(String indexName)
107    {
108        return _indexNameColumnsMap.get(indexName);
109    }
110    
111    /** Get the index type. */
112    public IndexType getIndexType(String name)
113    {
114        return _indexNameTypeMap.get(name);
115    }
116
117    /** Get the table name. */
118    public String getName()
119    {
120        return _tableName;
121    }
122    
123    /** Returns true if column references another table. */
124    public boolean isForeignKey(String columnName)
125    {
126        return _columnFKsMap.containsKey(columnName);
127    }
128    
129    /** Get the indexes. */
130    public Set<String> indexes()
131    {
132        return new HashSet<String>(_indexNameTypeMap.keySet());
133    }
134
135    /** Add a new or replace an existing column with no foreign key or index. */
136    public void putColumn(String name, Column column)
137    {
138        putColumn(name, column, null, null);
139    }
140    
141    /** Add a new or replace an existing column with a default value. */
142    public void putColumn(String name, Column column, String defaultValue)
143    {
144        putColumn(name, column, null, null, defaultValue);
145    }
146    
147    /** Add a new or replace an existing column.
148     *  @param name the column name
149     *  @param column the column type
150     *  @param fkTable the referenced table in foreign key
151     *  @param fkColumn the referenced column in foreign key
152     */
153    public void putColumn(String columnName, Column column, String fkTable, 
154        String fkColumn)
155    {
156        putColumn(columnName, column, fkTable, fkColumn, null);
157    }
158    
159    /** Add a new or replace an existing column.
160     *  @param name the column name
161     *  @param column the column type
162     *  @param fkTable the referenced table in foreign key
163     *  @param fkColumn the referenced column in foreign key
164     *  @param defaultValue the default value
165     */
166    public void putColumn(String columnName, Column column, String fkTable, 
167        String fkColumn, String defaultValue)
168    {
169        // NOTE: we make a copy of the column since we set the
170        // name and table, and don't want to overwrite these values
171        // in one of the static columns.
172        Column columnClone = new Column(column);
173        _columnMap.put(columnName, columnClone);
174        columnClone._setTable(this);
175        columnClone.setName(columnName);
176        if(defaultValue != null)
177        {
178            columnClone._setDefaultValue(defaultValue);
179        }
180
181        if(fkTable != null && fkColumn != null)
182        {
183            String fkName = _tableName + "_" + columnName + "_fk";
184            putForeignKey(fkName, columnName, fkTable, fkColumn);
185        }
186    }
187
188    /** Add a new or replace an existing constraint. */
189    public void putConstraint(String constraintName, String columnName,
190        String constraintValue)
191    {
192        ColumnConstraint constraint = new ColumnConstraint(columnName, constraintValue);
193        _constraintMap.put(constraintName, constraint);
194        _addColumnConstraint(columnName, constraintName);
195    }
196    
197    /** Add an index on one or more columns. */
198    public void putIndex(String columnName)
199    {
200        putIndex(columnName.replaceAll("[,\\s]", "_") + "_idx", columnName);
201    }
202    
203    /** Add a new or replace an existing index. */
204    public void putIndex(String indexName, String columnName)
205    {
206        putIndex(indexName, columnName, IndexType.Default);
207    }
208    
209    /** Add a new or replace an index index specifying the type of index. */
210    public void putIndex(String indexName, String columnName, IndexType type)
211    {
212        _indexNameTypeMap.put(indexName, type);
213        _indexNameColumnsMap.put(indexName, columnName);
214    }
215
216    /** Add a new or replace an existing foreign key. */
217    public void putForeignKey(String fkName, String columnName, String fkTable,
218        String fkColumn)
219    {
220        ForeignKey fk = new ForeignKey(columnName, fkTable, fkColumn);
221        _fkMap.put(fkName, fk);
222
223        _addColumnForeignKey(columnName, fkName);
224    }
225
226    /** Remove a column. */
227    public void removeColumn(String columnName)
228    {
229        _columnMap.remove(columnName);
230
231        // remove any constraints associated with this column
232        Set<String> constraintSet = _columnConstraintsMap.remove(columnName);
233        if(constraintSet != null)
234        {
235            for(String constraintName : constraintSet)
236            {
237                removeConstraint(constraintName);
238            }
239        }
240
241        // remove any foriegn keys associated with this column
242        Set<String> fkSet = _columnFKsMap.remove(columnName);
243        if(fkSet != null)
244        {
245            for(String fkName : fkSet)
246            {
247                _fkMap.remove(fkName);
248            }
249        }
250    }
251
252    /** Remove a constraint. */
253    public void removeConstraint(String name)
254    {
255        _constraintMap.remove(name);
256
257        //XXX update columnConstraintsMap?
258    }
259
260    /** Remove a foreign key. */
261    public void removeForeignKey(String name)
262    {
263        _fkMap.remove(name);
264
265        //XXX update columnFKsMap?
266    }
267    
268    /** Get a string representation of the table. */
269    public String toString()
270    {
271        StringBuilder retval = new StringBuilder("table ");
272        retval.append(_tableName);
273        retval.append("\n");
274        for(Column column : columns())
275        {
276            retval.append("{");
277            retval.append(column.toString());
278            retval.append("}\n");
279        }
280        return retval.toString();
281    }
282
283    ////////////////////////////////////////////////////////////////////////
284    //// public fields
285
286    /** Index types. */
287    public enum IndexType
288    {
289        Bitmap,
290        Default,
291    }
292
293    ////////////////////////////////////////////////////////////////////////
294    //// protected classes
295
296    protected static class ColumnConstraint
297    {
298        private ColumnConstraint(String columnName, String constraintValue)
299        {
300            _columnName = columnName;
301            _value = constraintValue;
302        }
303        
304        public String getColumnName()
305        {
306            return _columnName;
307        }
308        
309        public String getConstraintValue()
310        {
311            return _value;
312        }
313        
314        private String _columnName;
315        private String _value;
316    }
317    
318    /** A class for foreign keys. */
319    protected static class ForeignKey
320    {
321        /** Construct a new ForeignKey with a column name, and referent
322         *  table and column names. This is private since only Table
323         *  may create new ForeignKeys.
324         */
325        private ForeignKey(String columnName, String refTable, String refColumn)
326        {
327            _column = columnName;
328            _refColumn = refColumn;
329            _refTable = refTable;
330        }
331
332        /** Get the column name. */
333        protected String getColumn()
334        {
335            return _column;
336        }
337       
338        /** Get the referenced column name. */
339        protected String getRefColumn()
340        {
341            return _refColumn;
342        }
343       
344        /** Get the referenced table name. */
345        protected String getRefTable()
346        {
347            return _refTable;
348        }
349
350        /** The column name. */
351        private String _column;
352
353        /** The referenced column name. */
354        private String _refColumn;
355
356        /** The referenced table name. */
357        private String _refTable;
358    }
359
360    ////////////////////////////////////////////////////////////////////////
361    //// private methods
362
363    /** Add a constraint associated with a column. */
364    private void _addColumnConstraint(String columnName, String constraintName)
365    {
366        _updateMap(_columnConstraintsMap, columnName, constraintName);
367    }
368
369    /** Add a foreign key associated with a column. */
370    private void _addColumnForeignKey(String columnName, String fkName)
371    {
372        _updateMap(_columnFKsMap, columnName, fkName);
373    }
374
375    /** Update a map of string to hash set. */
376    private void _updateMap(Map<String,Set<String>> map, String key,
377        String value)
378    {
379        Set<String> valueSet = map.get(key);
380        if(valueSet == null)
381        {
382            valueSet = new HashSet<String>();
383            map.put(key, valueSet);
384        }
385        valueSet.add(value);
386    }
387
388    ////////////////////////////////////////////////////////////////////////
389    //// private variables
390
391    /** A mapping of column name to a set of its constraints. */
392    // XXX this is not used.
393    private Map<String,Set<String>> _columnConstraintsMap;
394
395    /** A mapping of columns name to a set of its foreign keys. */
396    private Map<String,Set<String>> _columnFKsMap;
397
398    /** A mapping of name to column. */
399    private Map<String,Column> _columnMap;
400    
401    /** A mapping of name to constraint. */
402    private Map<String,ColumnConstraint> _constraintMap;
403
404    /** A mapping of name to foreign keys. */
405    private Map<String,ForeignKey> _fkMap;
406
407    /** The name of this table. */
408    private String _tableName;
409    
410    /** A mapping of index name to index type. */
411    private Map<String,IndexType> _indexNameTypeMap = new HashMap<String,IndexType>();
412    
413    /** A mapping of index name to column(s). */
414    private Map<String,String> _indexNameColumnsMap = new HashMap<String,String>();    
415}