001: /*
002: * (c) Copyright 2007 by Volker Bergmann. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, is permitted under the terms of the
006: * GNU General Public License.
007: *
008: * For redistributing this software or a derivative work under a license other
009: * than the GPL-compatible Free Software License as defined by the Free
010: * Software Foundation or approved by OSI, you must first obtain a commercial
011: * license to this software product from Volker Bergmann.
012: *
013: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
014: * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
015: * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
016: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
017: * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
018: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
019: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
020: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
021: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
022: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
023: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
024: * POSSIBILITY OF SUCH DAMAGE.
025: */
026:
027: package org.databene.benerator.primitive;
028:
029: import org.databene.benerator.*;
030: import org.databene.benerator.primitive.number.distribution.BitReverseNaturalNumberGenerator;
031: import org.databene.commons.ArrayUtil;
032: import org.databene.commons.CollectionUtil;
033: import org.databene.commons.ArrayFormat;
034: import org.databene.commons.CustomCounter;
035:
036: import java.util.Set;
037:
038: /**
039: * Generates unique strings of fixed length.<br/>
040: * <br/>
041: * Created: 15.11.2007 14:07:49
042: */
043: public class UniqueFixedLengthStringGenerator extends
044: LightweightGenerator<String> {
045:
046: public static final Set<Character> DEFAULT_CHAR_SET = ArrayUtil
047: .toSet('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
048: public static final Set<Character> ORDERED_CHAR_SET = ArrayUtil
049: .toSortedSet('0', '1', '2', '3', '4', '5', '6', '7', '8',
050: '9');
051: private static final int DEFAULT_LENGTH = 4;
052:
053: private int radix;
054: private int length;
055: private CustomCounter counter;
056: private char[] digitSymbols;
057: private int[] displayColumn;
058: private int[] seed;
059: private int cycleCounter;
060:
061: public UniqueFixedLengthStringGenerator() {
062: this (DEFAULT_LENGTH, DEFAULT_CHAR_SET);
063: }
064:
065: public UniqueFixedLengthStringGenerator(int length,
066: Set<Character> charSet) {
067: this (length, CollectionUtil.toArray(charSet));
068: }
069:
070: public UniqueFixedLengthStringGenerator(int length, char... chars) {
071: super (String.class);
072: radix = chars.length;
073: digitSymbols = chars;
074: this .length = length;
075: this .displayColumn = new int[length];
076: this .seed = new int[length];
077: Generator<Long> gen = new BitReverseNaturalNumberGenerator(
078: length - 1);
079: for (int i = 0; i < length; i++) {
080: this .displayColumn[i] = gen.generate().intValue();
081: this .seed[i] = SimpleRandom.randomInt(0, length - 1);
082: }
083: reset();
084: }
085:
086: // Generator interface ---------------------------------------------------------------------------------------------
087:
088: public void reset() {
089: super .reset();
090: this .counter = new CustomCounter(radix, length);
091: this .cycleCounter = 0;
092: }
093:
094: public boolean available() {
095: return counter != null;
096: }
097:
098: public String generate() {
099: if (counter == null)
100: throw new IllegalGeneratorStateException(
101: "Generator is not available any more. Check with available() before calling!");
102: int[] digits = counter.getDigits();
103: char[] tmp = new char[length];
104: for (int i = 0; i < digits.length; i++)
105: tmp[displayColumn[i]] = digitSymbols[(seed[i] + digits[i] + cycleCounter)
106: % radix];
107: String result = new String(tmp);
108: if (cycleCounter < radix - 1 && length > 0) {
109: cycleCounter++;
110: } else {
111: counter.increment();
112: cycleCounter = 0;
113: if (counter.hasOverrun() || radix == 1
114: || digits[length - 1] > 0) {
115: // counter + cycle have run through all combinations
116: counter = null;
117: }
118: }
119: return result;
120: }
121:
122: public String toString() {
123: return getClass().getSimpleName() + "[length=" + length
124: + ", charset="
125: + ArrayFormat.formatChars(",", digitSymbols) + ']';
126: }
127:
128: }
|