From: Aron Roberts Date: Sat, 20 Jun 2009 02:03:38 +0000 (+0000) Subject: NOJIRA Experiments with ID Parts and Generators, for potential use with the ID Service. X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=2efbb3ebfc57b27263483a34af5a3b279bd11b1d;p=tmp%2Fjakarta-migration.git NOJIRA Experiments with ID Parts and Generators, for potential use with the ID Service. --- diff --git a/sandbox/aron/id/pom.xml b/sandbox/aron/id/pom.xml new file mode 100644 index 000000000..60292caa1 --- /dev/null +++ b/sandbox/aron/id/pom.xml @@ -0,0 +1,35 @@ + + 4.0.0 + org.collectionspace.services + id + jar + 0.1-SNAPSHOT + id + http://maven.apache.org + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + + + + org.apache.commons + commons-id + 1.0-SNAPSHOT + + + junit + junit + 3.8.1 + test + + + diff --git a/sandbox/aron/id/src/main/java/org/collectionspace/services/id/AlphabeticGenerator.java b/sandbox/aron/id/src/main/java/org/collectionspace/services/id/AlphabeticGenerator.java new file mode 100644 index 000000000..0633ff100 --- /dev/null +++ b/sandbox/aron/id/src/main/java/org/collectionspace/services/id/AlphabeticGenerator.java @@ -0,0 +1,212 @@ + /* + * AlphabeticGenerator + * + *

An identifier generator that generates an incrementing ID from any composite + * of the USASCII character sequences 'A-Z' and 'a-z', as a String object.

+ * + *

The wrap property determines whether or not the sequence wraps + * when it reaches the largest value that can be represented in size. + * If wrap is false and the the maximum representable + * value is exceeded, an IllegalStateException is thrown

+ * + * Copyright 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * @author $Author$ + * @version $Revision$ + * $Date$ + */ + +// @TODO: The initial value determines the fixed number of characters. +// We may also need to model cases where the number of characters +// increases as values roll over, up to a specified maximum number of +// characters; e.g. "z" becomes "aa", and "ZZ" becomes "AAA". + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.collectionspace.services.id; + +import org.apache.commons.id.AbstractStringIdentifierGenerator; +import java.io.Serializable; + +public class AlphabeticGenerator extends AbstractStringIdentifierGenerator + implements Serializable { + + /** + * serialVersionUID is the serializable UID for the binary version of the class. + */ + // private static final long serialVersionUID = 20060120L; // @TODO ReplaceMe! + + /** + * Should the counter wrap. + */ + private boolean wrapping = true; + + /** + * The counter. + */ + private char[] count = null; + private char[] initialcount = null; + + /** + * 'Z' and 'z' chars + */ + private static final char LOWERCASE_Z_CHAR = 'z'; + private static final char UPPERCASE_Z_CHAR = 'Z'; + + /** + * Constructor with a default size for the alphanumeric identifier. + * + * @param wrap should the factory wrap when it reaches the maximum + * value (or throw an exception) + */ + public AlphabeticGenerator(boolean wrap) { + this(wrap, DEFAULT_ALPHANUMERIC_IDENTIFIER_SIZE); + } + + /** + * Constructor. + * + * @param wrap should the factory wrap when it reaches the maximum + * value (or throw an exception) + * @param size the size of the identifier + */ + public AlphabeticGenerator(boolean wrap, int size) { + super(); + this.wrapping = wrap; + if (size < 1) { + throw new IllegalArgumentException("The size must be at least one"); + } + this.count = new char[size]; + + // Initialize the contents of the identifier's character array + for (int i = 0; i < size; i++) { + count[i] = ' '; // space + } + } + + /** + * Construct with a counter, that will start at the specified + * alphanumeric value.

+ * + * @param wrap should the factory wrap when it reaches the maximum + * value (or throw an exception) + * @param initialValue the initial value to start at + */ + public AlphabeticGenerator(boolean wrap, String initialValue) { + super(); + this.wrapping = wrap; + + if ( initialValue == null ) { + throw new IllegalArgumentException("Initial value must not be null"); + } + + if ( initialValue == "" ) { + throw new IllegalArgumentException("Initial value must not be empty"); + } + + this.count = initialValue.toCharArray(); + + // Validate each of the characters in the initial value + // against ranges of valid values. + for (int i = 0; i < this.count.length; i++) { + char ch = this.count[i]; + if (ch >= 'A' && ch <= 'Z') continue; + if (ch >= 'a' && ch <= 'z') continue; + + throw new IllegalArgumentException( + "character " + this.count[i] + " is not valid"); + } + + // Store the initial character array + this.initialcount = this.count; + } + + public long maxLength() { + return this.count.length; + } + + public long minLength() { + return this.count.length; + } + + /** + * Getter for property wrap. + * + * @return true if this generator is set up to wrap. + * + */ + public boolean isWrap() { + return wrapping; + } + + /** + * Sets the wrap property. + * + * @param wrap value for the wrap property + * + */ + public void setWrap(boolean wrap) { + this.wrapping = wrap; + } + + /** + * Returns the (constant) size of the strings generated by this generator. + * + * @return the size of generated identifiers + */ + public int getSize() { + return this.count.length; + } + + public synchronized String nextStringIdentifier() { + + // Get next values for each character from right to left + for (int i = count.length - 1; i >= 0; i--) { + switch (count[i]) { + + case LOWERCASE_Z_CHAR: // z + if (i == 0 && !wrapping) { + throw new IllegalStateException + ("The maximum number of identifiers has been reached"); + } + count[i] = 'a'; + break; + + case UPPERCASE_Z_CHAR: // Z + if (i == 0 && !wrapping) { + throw new IllegalStateException + ("The maximum number of identifiers has been reached"); + } + count[i] = 'A'; + break; + + default: + count[i]++; + i = -1; + break; + } + } + return new String(count); + } +} diff --git a/sandbox/aron/id/src/main/java/org/collectionspace/services/id/AlphabeticSeriesIDPart.java b/sandbox/aron/id/src/main/java/org/collectionspace/services/id/AlphabeticSeriesIDPart.java new file mode 100644 index 000000000..26e32a4f1 --- /dev/null +++ b/sandbox/aron/id/src/main/java/org/collectionspace/services/id/AlphabeticSeriesIDPart.java @@ -0,0 +1,33 @@ + /* + * AlphabeticSeriesIDPart + * + * Models a part of an identifier (ID) whose value is alphabetic, + * and increments within a series of uppercase and/or lowercase values + * in the USASCII character sequence. + * + * Copyright 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * @author $Author$ + * @version $Revision$ + * $Date$ + */ + +package org.collectionspace.services.id; + +import org.apache.commons.id.StringIdentifierGenerator; + +public class AlphabeticSeriesIDPart extends SeriesIDPart { + + public AlphabeticSeriesIDPart(String baseVal) { + // Store the appropriate Alphabetic ID generator and the base value for this part + // Value 'false' refers to the NO_WRAP behavior of the StringIdentifierGenerator. + super(new AlphabeticGenerator(false, baseVal), baseVal); + }; + +} diff --git a/sandbox/aron/id/src/main/java/org/collectionspace/services/id/IDPart.java b/sandbox/aron/id/src/main/java/org/collectionspace/services/id/IDPart.java new file mode 100644 index 000000000..90ac7a45b --- /dev/null +++ b/sandbox/aron/id/src/main/java/org/collectionspace/services/id/IDPart.java @@ -0,0 +1,55 @@ + /* + * IDPart + * + * Models a part of an identifier (ID), such as (for instance) an incrementing + * numeric or alphabetic value, a date value, or a static separator. + * + * Copyright 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * @author $Author$ + * @version $Revision$ + * $Date$ + */ + +package org.collectionspace.services.id; + +import org.apache.commons.id.StringIdentifierGenerator; + +public abstract class IDPart { + + // Flags to identify whether series-based identifiers + // wrap to their initial values, after the last value + // in the series is reached. + final boolean WRAP = true; + final boolean NO_WRAP = false; + + // An identifier generator + protected StringIdentifierGenerator generator; + + // Constructor + public IDPart(StringIdentifierGenerator idGenerator) { + setGenerator(idGenerator); + } + + // Sets the identifier generator + protected void setGenerator(StringIdentifierGenerator idGenerator) { + if (idGenerator != null) { + generator = idGenerator; + } + } + + // Gets the next identifier + public String nextIdentifier() { + // @TODO: Add Exception-handling here ... + return generator.nextStringIdentifier(); + }; + + // public boolean validate() {}; + +} diff --git a/sandbox/aron/id/src/main/java/org/collectionspace/services/id/SeriesIDPart.java b/sandbox/aron/id/src/main/java/org/collectionspace/services/id/SeriesIDPart.java new file mode 100644 index 000000000..9c92f6c1d --- /dev/null +++ b/sandbox/aron/id/src/main/java/org/collectionspace/services/id/SeriesIDPart.java @@ -0,0 +1,71 @@ + /* + * SeriesIDPart + * + * Models a part of an identifier (ID) whose values are part of a series, + * such as (for instance) an incrementing numeric or alphabetic value. + * Values begin at an initial (base) value + * + * Copyright 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * @author $Author$ + * @version $Revision$ + * $Date$ + */ + + package org.collectionspace.services.id; + +import org.apache.commons.id.StringIdentifierGenerator; + +public abstract class SeriesIDPart extends IDPart { + + protected String baseValue = ""; + protected String lastIdGenerated = null; + + public SeriesIDPart(StringIdentifierGenerator idGenerator, String baseVal) { + // Store the identifier generator and base value, + // and set the current value to the base value + super(idGenerator); + setBaseValue(baseVal); + } + + // Store the base value + protected void setBaseValue(String baseVal) { + // @TODO: Throw an Exception if the base value is null. + if (baseVal != null) { + baseValue = baseVal; + } + }; + + // Get the base value + public String getBaseValue() { + return baseValue; + }; + + // Get the next identifier in series + public String nextIdentifier() { + // @TODO: Add Exception-handling here ... + // If no identifier has ever been generated, + // or if the value of the last identifier was reset, + // return the base value. + if (lastIdGenerated == null) { + lastIdGenerated = baseValue; + return lastIdGenerated; + // Otherwise, return the next value in the series. + } else { + lastIdGenerated = generator.nextStringIdentifier(); + return lastIdGenerated; + } + } + + // Reset the value of the last identifier generated. + public void reset() { + lastIdGenerated = null; + }; + +} diff --git a/sandbox/aron/id/src/test/java/org/collectionspace/services/id/AlphabeticGeneratorTest.java b/sandbox/aron/id/src/test/java/org/collectionspace/services/id/AlphabeticGeneratorTest.java new file mode 100644 index 000000000..34d297fb1 --- /dev/null +++ b/sandbox/aron/id/src/test/java/org/collectionspace/services/id/AlphabeticGeneratorTest.java @@ -0,0 +1,198 @@ + /* + * AlphabeticGeneratorTest + * + * Test class for AlphabeticGenerator. + * + * Copyright 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * @author $Author$ + * @version $Revision$ + * $Date$ + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// See +// for Exception handling in JUnit. + +package org.collectionspace.services.id; + +import org.apache.commons.id.StringIdentifierGenerator; +import static org.junit.Assert.fail; +import junit.framework.TestCase; + +public class AlphabeticGeneratorTest extends TestCase { + + final boolean NO_WRAP = false; + final boolean WRAP = true; + StringIdentifierGenerator generator; + + public void testNoWrapAndInitialValue() { + + // @TODO: Split this test into more focused individual tests + + // All lowercase initial values + + generator = new AlphabeticGenerator(NO_WRAP, "a"); + assertEquals("b", generator.nextStringIdentifier()); + assertEquals("c", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(NO_WRAP, "x"); + assertEquals("y", generator.nextStringIdentifier()); + assertEquals("z", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(NO_WRAP, "aa"); + assertEquals("ab", generator.nextStringIdentifier()); + assertEquals("ac", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(NO_WRAP, "ay"); + assertEquals("az", generator.nextStringIdentifier()); + assertEquals("ba", generator.nextStringIdentifier()); + assertEquals("bb", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(NO_WRAP, "zx"); + assertEquals("zy", generator.nextStringIdentifier()); + assertEquals("zz", generator.nextStringIdentifier()); + + // All uppercase initial values + + generator = new AlphabeticGenerator(NO_WRAP, "A"); + assertEquals("B", generator.nextStringIdentifier()); + assertEquals("C", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(NO_WRAP, "X"); + assertEquals("Y", generator.nextStringIdentifier()); + assertEquals("Z", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(NO_WRAP, "AA"); + assertEquals("AB", generator.nextStringIdentifier()); + assertEquals("AC", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(NO_WRAP, "AY"); + assertEquals("AZ", generator.nextStringIdentifier()); + assertEquals("BA", generator.nextStringIdentifier()); + assertEquals("BB", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(NO_WRAP, "ZX"); + assertEquals("ZY", generator.nextStringIdentifier()); + assertEquals("ZZ", generator.nextStringIdentifier()); + + } + + public void testWrapAndInitialLowercaseValue() { + + generator = new AlphabeticGenerator(WRAP, "x"); + assertEquals("y", generator.nextStringIdentifier()); + assertEquals("z", generator.nextStringIdentifier()); + assertEquals("a", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(WRAP, "zx"); + assertEquals("zy", generator.nextStringIdentifier()); + assertEquals("zz", generator.nextStringIdentifier()); + assertEquals("aa", generator.nextStringIdentifier()); + + } + + public void testOverflowWithNoWrapAndInitialLowercaseValue() + throws Exception { + + try { + generator = new AlphabeticGenerator(NO_WRAP, "zx"); + assertEquals("zy", generator.nextStringIdentifier()); + assertEquals("zz", generator.nextStringIdentifier()); + // Should throw IllegalStateException + assertNotNull(generator.nextStringIdentifier()); + fail("Should have thrown IllegalStateException here"); + } catch (IllegalStateException expected) { + // This Exception should be thrown, and thus the test should pass. + } + + } + + public void testWrapAndInitialUppercaseValue() { + + generator = new AlphabeticGenerator(WRAP, "X"); + assertEquals("Y", generator.nextStringIdentifier()); + assertEquals("Z", generator.nextStringIdentifier()); + assertEquals("A", generator.nextStringIdentifier()); + + generator = new AlphabeticGenerator(WRAP, "ZX"); + assertEquals("ZY", generator.nextStringIdentifier()); + assertEquals("ZZ", generator.nextStringIdentifier()); + assertEquals("AA", generator.nextStringIdentifier()); + + } + + public void testOverflowWithNoWrapAndInitialUppercaseValue() { + + try { + generator = new AlphabeticGenerator(NO_WRAP, "ZX"); + assertEquals("ZY", generator.nextStringIdentifier()); + assertEquals("ZZ", generator.nextStringIdentifier()); + // Should throw IllegalStateException + assertNotNull(generator.nextStringIdentifier()); + fail("Should have thrown IllegalStateException here"); + } catch (IllegalStateException expected) { + // This Exception should be thrown, and thus the test should pass. + } + + } + + public void testNonAlphabeticInitialValue() { + try { + generator = new AlphabeticGenerator(NO_WRAP, "&*432"); + fail("Should have thrown IllegalArgumentException here"); + } catch (IllegalArgumentException expected) { + // This Exception should be thrown, and thus the test should pass. + } + } + + public void testNullInitialValue() { + try { + generator = new AlphabeticGenerator(NO_WRAP, null); + fail("Should have thrown IllegalArgumentException here"); + } catch (IllegalArgumentException expected) { + // This Exception should be thrown, and thus the test should pass. + } + } + + public void testEmptyStringInitialValue() { + try { + generator = new AlphabeticGenerator(NO_WRAP, ""); + fail("Should have thrown IllegalArgumentException here"); + } catch (IllegalArgumentException expected) { + // This Exception should be thrown, and thus the test should pass. + } + } + + public void testAllSpaceCharsInitialValue() { + try { + generator = new AlphabeticGenerator(NO_WRAP, " "); + fail("Should have thrown IllegalArgumentException here"); + } catch (IllegalArgumentException expected) { + // This Exception should be thrown, and thus the test should pass. + } + } + +} diff --git a/sandbox/aron/id/src/test/java/org/collectionspace/services/id/AlphabeticSeriesIDPartTest.java b/sandbox/aron/id/src/test/java/org/collectionspace/services/id/AlphabeticSeriesIDPartTest.java new file mode 100644 index 000000000..4237ebad5 --- /dev/null +++ b/sandbox/aron/id/src/test/java/org/collectionspace/services/id/AlphabeticSeriesIDPartTest.java @@ -0,0 +1,44 @@ + /* + * AlphabeticSeriesIDPartTest + * + * Test class for AlphabeticSeriesIDPart. + * + * Copyright 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * @author $Author$ + * @version $Revision$ + * $Date$ + */ + +package org.collectionspace.services.id; + +import junit.framework.TestCase; + +public class AlphabeticSeriesIDPartTest extends TestCase { + + AlphabeticSeriesIDPart part; + + public void testInitialValue() { + + part = new AlphabeticSeriesIDPart("a"); + assertEquals("a", part.nextIdentifier()); + assertEquals("b", part.nextIdentifier()); + + part = new AlphabeticSeriesIDPart("x"); + assertEquals("x", part.nextIdentifier()); + assertEquals("y", part.nextIdentifier()); + assertEquals("z", part.nextIdentifier()); + part.reset(); + assertEquals("x", part.nextIdentifier()); + + } + + // Add tests of boundary conditions, exceptions ... + +} diff --git a/sandbox/aron/id/third-party/commons-id-1.0-SNAPSHOT.jar b/sandbox/aron/id/third-party/commons-id-1.0-SNAPSHOT.jar new file mode 100644 index 000000000..f60f41a06 Binary files /dev/null and b/sandbox/aron/id/third-party/commons-id-1.0-SNAPSHOT.jar differ diff --git a/sandbox/aron/id/third-party/install-third-party.sh b/sandbox/aron/id/third-party/install-third-party.sh new file mode 100644 index 000000000..4eca6cc11 --- /dev/null +++ b/sandbox/aron/id/third-party/install-third-party.sh @@ -0,0 +1,8 @@ +#! /bin/sh + +mvn install:install-file \ + -Dfile=./commons-id-1.0-SNAPSHOT.jar \ + -DgroupId=org.apache.commons \ + -DartifactId=commons-id \ + -Dversion=1.0-SNAPSHOT \ + -Dpackaging=jar