View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.id.serial;
19  
20  import org.apache.commons.id.AbstractStringIdentifierGenerator;
21  
22  import java.io.Serializable;
23  
24  /**
25   * <code>AlphanumericGenerator</code> is an identifier generator
26   * that generates an incrementing number in base 36 as a String
27   * object.
28   *
29   * <p>All generated ids have the same length (padding with 0's on the left),
30   * which is determined by the <code>size</code> parameter passed to the constructor.<p>
31   *
32   * <p>The <code>wrap</code> property determines whether or not the sequence wraps
33   * when it reaches the largest value that can be represented in <code>size</code>
34   * base 36 digits. If <code>wrap</code> is false and the the maximum representable
35   * value is exceeded, an IllegalStateException is thrown</p>
36   *
37   * @author Commons-Id team
38   * @version $Id: AlphanumericGenerator.java 480488 2006-11-29 08:57:26Z bayard $
39   */
40  public class AlphanumericGenerator extends AbstractStringIdentifierGenerator implements Serializable {
41  
42      /**
43       * <code>serialVersionUID</code> is the serializable UID for the binary version of the class.
44       */
45      private static final long serialVersionUID = 20060120L;
46  
47      /**
48       * Should the counter wrap.
49       */
50      private boolean wrapping = true;
51  
52      /**
53       * The counter.
54       */
55      private char[] count = null;
56  
57      /**
58       * 'z' char
59       */
60      private static final char Z_CHAR = 'z';
61  
62      /**
63       * '9' char
64       */
65      private static final char NINE_CHAR = '9';
66  
67      /**
68       * Constructor with a default size for the alphanumeric identifier.
69       *
70       * @param wrap should the factory wrap when it reaches the maximum
71       *  long value (or throw an exception)
72       */
73      public AlphanumericGenerator(boolean wrap) {
74          this(wrap, DEFAULT_ALPHANUMERIC_IDENTIFIER_SIZE);
75      }
76  
77      /**
78       * Constructor.
79       *
80       * @param wrap should the factory wrap when it reaches the maximum
81       *  long value (or throw an exception)
82       * @param size  the size of the identifier
83       */
84      public AlphanumericGenerator(boolean wrap, int size) {
85          super();
86          this.wrapping = wrap;
87          if (size < 1) {
88              throw new IllegalArgumentException("The size must be at least one");
89          }
90          this.count = new char[size];
91          for (int i = 0; i < size; i++) {
92              count[i] = '0';  // zero
93          }
94      }
95  
96      /**
97       * Construct with a counter, that will start at the specified
98       * alphanumeric value.</p>
99       *
100      * @param wrap should the factory wrap when it reaches the maximum
101      * value (or throw an exception)
102      * @param initialValue the initial value to start at
103      */
104     public AlphanumericGenerator(boolean wrap, String initialValue) {
105         super();
106         this.wrapping = wrap;
107         this.count = initialValue.toCharArray();
108 
109         for (int i = 0; i < this.count.length; i++) {
110             char ch = this.count[i];
111             if (ch >= '0' && ch <= '9') continue;
112             if (ch >= 'a' && ch <= 'z') continue;
113             
114             throw new IllegalArgumentException(
115                     "character " + this.count[i] + " is not valid");
116         }
117     }
118     
119     public long maxLength() {
120         return this.count.length;
121     }
122 
123     public long minLength() {
124         return this.count.length;
125     }
126 
127     /**
128      * Getter for property wrap.
129      *
130      * @return <code>true</code> if this generator is set up to wrap.
131      *
132      */
133     public boolean isWrap() {
134         return wrapping;
135     }
136 
137     /**
138      * Sets the wrap property.
139      *
140      * @param wrap value for the wrap property
141      *
142      */
143     public void setWrap(boolean wrap) {
144         this.wrapping = wrap;
145     }
146 
147     /**
148      * Returns the (constant) size of the strings generated by this generator.
149      *
150      * @return the size of generated identifiers
151      */
152     public int getSize() {
153         return this.count.length;
154     }
155 
156     public synchronized String nextStringIdentifier() {
157         for (int i = count.length - 1; i >= 0; i--) {
158             switch (count[i]) {
159                 case Z_CHAR:  // z
160                     if (i == 0 && !wrapping) {
161                         throw new IllegalStateException
162                         ("The maximum number of identifiers has been reached");
163                     }
164                     count[i] = '0';
165                     break;
166 
167                 case NINE_CHAR:  // 9
168                     count[i] = 'a';
169                     i = -1;
170                     break;
171 
172                 default:
173                     count[i]++;
174                     i = -1;
175                     break;
176             }
177         }
178         return new String(count);
179     }
180 }