1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 package pl.kernelpanic.dbmonster.generator;
48
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.sql.ResultSet;
52
53 import org.apache.commons.logging.Log;
54
55 import pl.kernelpanic.dbmonster.DBMonster;
56 import pl.kernelpanic.dbmonster.DBMonsterContext;
57 import pl.kernelpanic.dbmonster.connection.ConnectionProvider;
58 import pl.kernelpanic.dbmonster.connection.Transaction;
59 import pl.kernelpanic.dbmonster.schema.Column;
60 import pl.kernelpanic.dbmonster.schema.Key;
61
62 /***
63 * The string key generator uses sort of string magic known from Perl.
64 * in short it starts from i.e. "aaaaa" and increment it by one:
65 * "aaaab", "aaaac"....
66 *
67 * @author Piotr Maj <piotr.maj@kernelpanic.pl>
68 *
69 * @version $Id: StringKeyGenerator.html,v 1.1 2007/06/21 08:38:13 sbahloul Exp $
70 */
71 public class StringKeyGenerator implements KeyGenerator, Initializable {
72
73 /***
74 * Default value.
75 */
76 public static final String DEFAULT_VALUE = "aaaaaaaa";
77
78 /***
79 * The key.
80 */
81 private Key key = null;
82
83 /***
84 * Holds a list of columns.
85 */
86 private List columns = new ArrayList();
87
88 /***
89 * Initial value. A special value of 0 (zero) means that
90 * initial value should be taken from the database.
91 */
92 private String startValue = "0";
93
94 /***
95 * Context.
96 */
97 private DBMonsterContext context = null;
98
99 /***
100 * Next value.
101 */
102 private String nextValue = DEFAULT_VALUE;
103
104 /***
105 * Chars.
106 */
107 public static final char[] CHARS = new char[26];
108 static {
109 for (int i = 'a'; i <= 'z'; i++) {
110 CHARS[i - 'a'] = (char) i;
111 }
112 }
113
114 /***
115 * Sets the key.
116 *
117 * @param k the key.
118 */
119 public final void setKey(final Key k) {
120 key = k;
121 }
122
123 public Key getKey() {
124 return key;
125 }
126
127 /***
128 * Returns columns.
129 *
130 * @return a list of columns
131 */
132 public final List getColumns() {
133 return columns;
134 }
135
136 /***
137 * Generates a primary key.
138 *
139 * @return a list of generated <code>Column</code>s.
140 *
141 * @throws Exception if generation fails
142 */
143 public final List generate() throws Exception {
144 Column column = (Column) columns.get(0);
145 if (nextValue == null) {
146 throw new Exception("No more values available (generator: "
147 + getClass().getName() + ", table: "
148 + key.getTable().getName() + ").");
149 }
150 column.setValue(nextValue);
151 nextValue = incrementString(nextValue);
152 return columns;
153 }
154
155 /***
156 * @see Initializable#initialize(DBMonsterContext)
157 */
158 public final void initialize(final DBMonsterContext ctx) throws Exception {
159 context = ctx;
160
161 Log log = (Log) context.getProperty(DBMonster.LOGGER_KEY);
162 if (log.isDebugEnabled()) {
163 log.debug("Initializing generator <" + getClass().getName() + ">.");
164 }
165
166 if ("0".equals(startValue)) {
167 ConnectionProvider connProv =
168 (ConnectionProvider) context.getProperty(
169 DBMonster.CONNECTION_PROVIDER_KEY);
170 Transaction tx = new Transaction(connProv);
171 try {
172 tx.begin();
173 Column column = (Column) columns.get(0);
174 String sql = "SELECT max(" + column.getName() + ") FROM "
175 + key.getTable().getName();
176
177 if (log.isDebugEnabled()) {
178 log.debug(sql);
179 }
180
181 ResultSet rs = tx.executeQuery(sql);
182 if (rs.next()) {
183 nextValue = rs.getString(1);
184 if (rs.wasNull() || "".equals(nextValue)) {
185 nextValue = DEFAULT_VALUE;
186 }
187 nextValue = incrementString(nextValue);
188 } else {
189 nextValue = DEFAULT_VALUE;
190 }
191 tx.commit();
192 } catch (Exception e) {
193 if (log.isFatalEnabled()) {
194 log.fatal(e.getMessage());
195 }
196 tx.abort();
197 throw e;
198 } finally {
199 tx.close();
200 }
201 } else {
202 nextValue = startValue;
203 }
204
205 if (log.isDebugEnabled()) {
206 log.debug("Initialization of generator <"
207 + getClass().getName() + "> finished.");
208 }
209 }
210
211 /***
212 * Returns a column name.
213 *
214 * @return the name of the column.
215 */
216 public final String getColumnName() {
217 if (columns.size() == 1) {
218 Column column = (Column) columns.get(0);
219 if (column != null) {
220 return column.getName();
221 }
222 }
223 return null;
224 }
225
226 /***
227 * Sets the column name and sets up the column.
228 *
229 * @param name the name of the column.
230 */
231 public final void setColumnName(final String name) {
232 Column column = new Column();
233 column.setName(name);
234 columns = new ArrayList();
235 columns.add(column);
236 }
237
238 /***
239 * Increments a string.
240 *
241 * @param s string to increment
242 * @return incremented string
243 */
244 private final String incrementString(final String s) {
245 StringBuffer in = new StringBuffer(s);
246 int pos = in.length() - 1;
247 while (pos >= 0) {
248 char c = in.charAt(pos);
249 if (c != 'z') {
250 in.setCharAt(pos, CHARS[c - 'a' + 1]);
251 break;
252 } else {
253 in.setCharAt(pos, 'a');
254 }
255 --pos;
256 }
257 if (pos < 0) {
258 return null;
259 } else {
260 return in.toString();
261 }
262 }
263
264 /***
265 * Returns the start value.
266 *
267 * @return start value.
268 */
269 public final String getStartValue() {
270 return startValue;
271 }
272
273 /***
274 * Sets the start value
275 *
276 * @param sv The startValue to set.
277 */
278 public final void setStartValue(final String sv) {
279 startValue = sv;
280 }
281 }