View Javadoc
1   /*
2   Copyright (c) 2008 Health Market Science, Inc.
3   
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  */
16  
17  package com.healthmarketscience.jackcess;
18  
19  import java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import com.healthmarketscience.jackcess.impl.DatabaseImpl;
30  import com.healthmarketscience.jackcess.impl.PropertyMapImpl;
31  import com.healthmarketscience.jackcess.impl.TableCreator;
32  
33  /**
34   * Builder style class for constructing a {@link Table}.
35   * <p/>
36   * Example:
37   * <pre>
38   *   Table table = new TableBuilder("Test")
39   *     .addColumn(new ColumnBuilder("ID", DataType.LONG)
40   *                .setAutoNumber(true))
41   *     .addColumn(new ColumnBuilder("Name", DataType.TEXT))
42   *     .addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
43   *               .addColumns("ID").setPrimaryKey())
44   *     .toTable(db);
45   * </pre>
46   *
47   * @author James Ahlborn
48   * @see ColumnBuilder
49   * @see IndexBuilder
50   * @see RelationshipBuilder
51   * @usage _general_class_
52   */
53  public class TableBuilder {
54  
55    /** Prefix for column or table names that are reserved words */
56    private static final String ESCAPE_PREFIX = "x";
57  
58    /* nested class for lazy loading */
59    private static final class ReservedWords {
60      /**
61       * All of the reserved words in Access that should be escaped when creating
62       * table or column names
63       */
64      private static final Set<String> VALUES = 
65        new HashSet<String>(Arrays.asList(
66         "add", "all", "alphanumeric", "alter", "and", "any", "application", "as",
67         "asc", "assistant", "autoincrement", "avg", "between", "binary", "bit",
68         "boolean", "by", "byte", "char", "character", "column", "compactdatabase",
69         "constraint", "container", "count", "counter", "create", "createdatabase",
70         "createfield", "creategroup", "createindex", "createobject", "createproperty",
71         "createrelation", "createtabledef", "createuser", "createworkspace",
72         "currency", "currentuser", "database", "date", "datetime", "delete",
73         "desc", "description", "disallow", "distinct", "distinctrow", "document",
74         "double", "drop", "echo", "else", "end", "eqv", "error", "exists", "exit",
75         "false", "field", "fields", "fillcache", "float", "float4", "float8",
76         "foreign", "form", "forms", "from", "full", "function", "general",
77         "getobject", "getoption", "gotopage", "group", "group by", "guid", "having",
78         "idle", "ieeedouble", "ieeesingle", "if", "ignore", "imp", "in", "index",
79         "indexes", "inner", "insert", "inserttext", "int", "integer", "integer1",
80         "integer2", "integer4", "into", "is", "join", "key", "lastmodified", "left",
81         "level", "like", "logical", "logical1", "long", "longbinary", "longtext",
82         "macro", "match", "max", "min", "mod", "memo", "module", "money", "move",
83         "name", "newpassword", "no", "not", "null", "number", "numeric", "object",
84         "oleobject", "off", "on", "openrecordset", "option", "or", "order", "outer",
85         "owneraccess", "parameter", "parameters", "partial", "percent", "pivot",
86         "primary", "procedure", "property", "queries", "query", "quit", "real",
87         "recalc", "recordset", "references", "refresh", "refreshlink",
88         "registerdatabase", "relation", "repaint", "repairdatabase", "report",
89         "reports", "requery", "right", "screen", "section", "select", "set",
90         "setfocus", "setoption", "short", "single", "smallint", "some", "sql",
91         "stdev", "stdevp", "string", "sum", "table", "tabledef", "tabledefs",
92         "tableid", "text", "time", "timestamp", "top", "transform", "true", "type",
93         "union", "unique", "update", "user", "value", "values", "var", "varp",
94         "varbinary", "varchar", "where", "with", "workspace", "xor", "year", "yes",
95         "yesno"));
96    }
97  
98  
99    /** name of the new table */
100   private String _name;
101   /** columns for the new table */
102   private List<ColumnBuilder> _columns = new ArrayList<ColumnBuilder>();
103   /** indexes for the new table */
104   private List<IndexBuilder> _indexes = new ArrayList<IndexBuilder>();
105   /** whether or not table/column/index names are automatically escaped */
106   private boolean _escapeIdentifiers;
107   /** table properties (if any) */
108   private Map<String,PropertyMap.Property> _props;
109 
110   
111   public TableBuilder(String name) {
112     this(name, false);
113   }
114   
115   public TableBuilder(String name, boolean escapeIdentifiers) {
116     _name = name;
117     _escapeIdentifiers = escapeIdentifiers;
118     if(_escapeIdentifiers) {
119       _name = escapeIdentifier(_name);
120     }
121   }
122 
123   public String getName() {
124     return _name;
125   }
126 
127   /**
128    * Adds a Column to the new table.
129    */
130   public TableBuilder addColumn(ColumnBuilder column) {
131     if(_escapeIdentifiers) {
132       column.escapeName();
133     }
134     _columns.add(column);
135     return this;
136   }
137 
138   /**
139    * Adds the Columns to the new table.
140    */
141   public TableBuilder addColumns(Collection<? extends ColumnBuilder> columns) {
142     if(columns != null) {
143       for(ColumnBuilder col : columns) {
144         addColumn(col);
145       }
146     }
147     return this;
148   }
149 
150   public List<ColumnBuilder> getColumns() {
151     return _columns;
152   }
153   
154   /**
155    * Adds an IndexBuilder to the new table.
156    */
157   public TableBuilder addIndex(IndexBuilder index) {
158     if(_escapeIdentifiers) {
159       index.setName(escapeIdentifier(index.getName()));
160       for(IndexBuilder.Column col : index.getColumns()) {
161         col.setName(escapeIdentifier(col.getName()));
162       }
163     }
164     _indexes.add(index);
165     return this;
166   }
167 
168   /**
169    * Adds the Indexes to the new table.
170    */
171   public TableBuilder addIndexes(Collection<? extends IndexBuilder> indexes) {
172     if(indexes != null) {
173       for(IndexBuilder col : indexes) {
174         addIndex(col);
175       }
176     }
177     return this;
178   }
179 
180   public List<IndexBuilder> getIndexes() {
181     return _indexes;
182   }
183   
184   /**
185    * Sets whether or not subsequently added columns will have their names
186    * automatically escaped
187    */
188   public TableBuilder setEscapeIdentifiers(boolean escapeIdentifiers) {
189     _escapeIdentifiers = escapeIdentifiers;
190     return this;
191   }
192 
193   /**
194    * Sets the names of the primary key columns for this table.  Convenience
195    * method for creating a primary key index on a table.
196    */
197   public TableBuilder setPrimaryKey(String... colNames) {
198     addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
199              .addColumns(colNames).setPrimaryKey());
200     return this;
201   }
202   
203   /**
204    * Escapes the new table's name using {@link TableBuilder#escapeIdentifier}.
205    */
206   public TableBuilder escapeName() {
207     _name = escapeIdentifier(_name);
208     return this;
209   }
210 
211   /**
212    * Sets the table property with the given name to the given value.  Attempts
213    * to determine the type of the property (see
214    * {@link PropertyMap#put(String,Object)} for details on determining the
215    * property type).
216    */
217   public TableBuilder putProperty(String name, Object value) {
218     return putProperty(name, null, value);
219   }
220   
221   /**
222    * Sets the table property with the given name and type to the given value.
223    */
224   public TableBuilder putProperty(String name, DataType type, Object value) {
225     if(_props == null) {
226       _props = new HashMap<String,PropertyMap.Property>();
227     }
228     _props.put(name, PropertyMapImpl.createProperty(name, type, value));
229     return this;
230   }
231 
232   public Map<String,PropertyMap.Property> getProperties() {
233     return _props;
234   }
235 
236   /**
237    * Creates a new Table in the given Database with the currently configured
238    * attributes.
239    */
240   public Table toTable(Database db) throws IOException {
241     return new TableCreator(((DatabaseImpl)db)).createTable(this);
242   }
243 
244   /**
245    * @return A table or column name escaped for Access
246    * @usage _general_method_
247    */
248   public static String escapeIdentifier(String s) {
249     if (isReservedWord(s)) {
250       return ESCAPE_PREFIX + s; 
251     }
252     return s;
253   }
254 
255   /**
256    * @return {@code true} if the given string is a reserved word,
257    *         {@code false} otherwise
258    * @usage _general_method_
259    */
260   public static boolean isReservedWord(String s) {
261     return ReservedWords.VALUES.contains(s.toLowerCase());
262   }
263 
264 }