View Javadoc

1   /* Version 1.0 based on Apache Software License 1.1
2    *
3    * Copyright (c) 2003 Piotr Maj and DBMonster developers. All rights
4    * reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions are
8    * met:
9    *
10   * 1. Redistributions of source code must retain the above copyright
11   *    notice, this list of conditions and the following disclaimer.
12   *
13   * 2. Redistributions in binary form must reproduce the above copyright
14   *    notice, this list of conditions and the following disclaimer in the
15   *    documentation and/or other materials provided with the distribution.
16   *
17   * 3. The end-user documentation included with the redistribution, if any,
18   *    must include the following acknowledgment:
19   *
20   *    "This product includes software developed by DBMonster developers
21   *    (http://dbmonster.kernelpanic.pl/)."
22   *
23   *  Alternately, this acknowledgment may appear in the software itself,
24   *  if and wherever such third-party acknowledgments normally appear.
25   *
26   * 4. The name "DBMonster" must not be used to endorse or promote products
27   *    derived from this software without prior written permission. For
28   *    written permission, please contact piotr.maj@kernelpanic.pl.
29   *
30   * 5. Products derived from this software may not be called "DBMonster",
31   *    nor may "DBMonster" appear in their name, without prior written
32   *    permission of Piotr Maj.
33   *
34   * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
35   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
37   * IN NO EVENT SHALL THE DBMONSTER DEVELOPERS BE LIABLE FOR ANY DIRECT,
38   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
40   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
43   * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
44   * POSSIBILITY OF SUCH DAMAGE.
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; // no more strings
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 }