From: Aron Roberts Date: Tue, 1 Dec 2009 21:25:30 +0000 (+0000) Subject: CSPACE-234: Fixed broken Gregorian date ID Part test; modified log level for reportin... X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=46f4f23673e7d87b0153f83b404f3ee920103adb;p=tmp%2Fjakarta-migration.git CSPACE-234: Fixed broken Gregorian date ID Part test; modified log level for reporting errors in input to random number ID part. --- diff --git a/services/id/service/src/main/java/org/collectionspace/services/id/part/AlphabeticSequenceIDPart.java b/services/id/service/src/main/java/org/collectionspace/services/id/part/AlphabeticSequenceIDPart.java index 6974535d4..8a10cc96e 100644 --- a/services/id/service/src/main/java/org/collectionspace/services/id/part/AlphabeticSequenceIDPart.java +++ b/services/id/service/src/main/java/org/collectionspace/services/id/part/AlphabeticSequenceIDPart.java @@ -1,19 +1,38 @@ package org.collectionspace.services.id.part; -// @TODO Largely unimplemented at present. -// Corresponding test class has not yet been created. +import java.util.ArrayList; +import java.util.LinkedHashSet; + +// @TODO Largely unimplemented at present. Much code is non-working. public class AlphabeticSequenceIDPart extends SequenceIDPart { + private IDPartOutputFormatter formatter = new NoOpIDPartOutputFormatter(); + private IDPartValidator validator = new NoOpIDPartValidator(); + // @TODO Externalize character sequences to their own class. private AlphabeticSequenceIDPart.AlphabeticCharSequence charsInSequence; - private IDPartOutputFormatter formatter; - private IDPartValidator validator; - private char initialValue; + + LinkedHashSet alphabeticSequence = new LinkedHashSet(); + private ArrayList initialValue = new ArrayList(); + private ArrayList currentValue = new ArrayList(); + + private static final char NULL_CHAR = '\u0000'; + + private char startChar = NULL_CHAR; + private char endChar = NULL_CHAR; public AlphabeticSequenceIDPart () { } + public AlphabeticSequenceIDPart(LinkedHashSet sequence) { + this.alphabeticSequence = sequence; + Character[] chars = (Character[]) alphabeticSequence.toArray(); + this.startChar = chars[0].charValue(); + // initialValue.add(new Character(start.char)); + this.endChar = chars[chars.length - 1].charValue(); + } + @Override public IDPartOutputFormatter getOutputFormatter () { return formatter; @@ -50,15 +69,57 @@ public class AlphabeticSequenceIDPart extends SequenceIDPart { @Override public String nextID() { - throw new UnsupportedOperationException("Not supported yet."); + + // Get next values for each character, from right to left + // (least significant to most significant). + boolean expandIdentifier = false; + int size = this.currentValue.size(); + char ch; + for (int i = (size - 1); i >= 0; i--) { + + ch = this.currentValue.get(i).charValue(); + + // When we reach the maximum value for any character, + // 'roll over' to the minimum value in our character range. + if (ch == this.endChar) { + this.currentValue.set(i, Character.valueOf(this.startChar)); + // If this rollover occurs in the most significant value, + // set a flag to later expand the size of the identifier. + // + // @TODO: Set another flag to enable or disable this behavior, + // as well as a mechanism for setting the maximum expansion + // permitted. + if (i == 0) { + expandIdentifier = true; + } + // When we reach the most significant character whose value + // doesn't roll over, increment that character and exit the loop. + } else { + ch++; + this.currentValue.set(i, Character.valueOf(ch)); + i = -1; + break; + } + + } + + // If we are expanding the size of the identifier, insert a new + // value at the most significant (leftmost) character position, + // sliding other values to the right. + if (expandIdentifier) { + this.currentValue.add(0, Character.valueOf(this.startChar)); + } + + return toIDString(this.currentValue); + } public char getInitialValue () { - return initialValue; + throw new UnsupportedOperationException("Not supported yet."); } public void setInitialValue (char val) { - this.initialValue = val; + throw new UnsupportedOperationException("Not supported yet."); } public AlphabeticSequenceIDPart.AlphabeticCharSequence getCharsInSequence () { @@ -74,5 +135,12 @@ public class AlphabeticSequenceIDPart extends SequenceIDPart { ; } + public String toIDString(ArrayList characters) { + StringBuffer sb = new StringBuffer(); + for ( Character ch : characters ) { + sb.append(ch.toString()); + } + return sb.toString(); + } } diff --git a/services/id/service/src/main/java/org/collectionspace/services/id/part/JavaRandomNumberIDPartAlgorithm.java b/services/id/service/src/main/java/org/collectionspace/services/id/part/JavaRandomNumberIDPartAlgorithm.java index cd72ec135..7f1112f88 100644 --- a/services/id/service/src/main/java/org/collectionspace/services/id/part/JavaRandomNumberIDPartAlgorithm.java +++ b/services/id/service/src/main/java/org/collectionspace/services/id/part/JavaRandomNumberIDPartAlgorithm.java @@ -32,7 +32,7 @@ public class JavaRandomNumberIDPartAlgorithm implements IDPartAlgorithm { // other invocation of this constructor." private static Random r = new Random(); - public final static int DEFAULT_MAX_VALUE = Integer.MAX_VALUE - 1; + public final static int DEFAULT_MAX_VALUE = Integer.MAX_VALUE - 2; public final static int DEFAULT_MIN_VALUE = 0; private int maxValue = DEFAULT_MAX_VALUE; @@ -57,14 +57,17 @@ public class JavaRandomNumberIDPartAlgorithm implements IDPartAlgorithm { } private void setMaxValue(int maxVal) { - if (0 < maxVal && maxVal < DEFAULT_MAX_VALUE) { + if (0 < maxVal && maxVal <= DEFAULT_MAX_VALUE) { this.maxValue = maxVal; } else { String msg = - "Invalid maximum value for random number. " + + "Invalid maximum value '" + + Integer.toString(maxVal) + + "' for random number. " + "Must be between 1 and " + - Integer.toString(DEFAULT_MAX_VALUE - 1) + "."; - logger.error(msg); + Integer.toString(DEFAULT_MAX_VALUE) + + ", inclusive."; + logger.info(msg); throw new IllegalArgumentException(msg); } } @@ -74,10 +77,14 @@ public class JavaRandomNumberIDPartAlgorithm implements IDPartAlgorithm { this.minValue = minVal; } else { String msg = - "Invalid minimum value for random number. " + + "Invalid minimum value '" + + Integer.toString(minVal) + + "' for random number. " + "Must be between 0 and " + - Integer.toString(this.maxValue - 1) + "."; - logger.error(msg); + Integer.toString(this.maxValue - 1) + + ", inclusive (i.e. less than the supplied maximum value " + + "of " + Integer.toString(this.maxValue) + ")."; + logger.info(msg); throw new IllegalArgumentException(msg); } } @@ -85,11 +92,18 @@ public class JavaRandomNumberIDPartAlgorithm implements IDPartAlgorithm { @Override public String generateID(){ // Returns an evenly distributed random value between 0 - // and the maximum value. + // and the maximum value. An even distribution decreases + // randomness but is more likely to meet end user requirements + // and expectations. + // // See http://mindprod.com/jgloss/pseudorandom.html // // Note: Random.nextInt() returns a pseudorandom number // between 0 and n-1 inclusive, not a number between 0 and n. + + // @TODO Consider adding code to ensure the uniqueness of + // each generated pseudorandom number, until all possible + // values within the inclusive set have been generated. return Integer.toString(r.nextInt( this.maxValue - this.minValue + 1) + this.minValue); diff --git a/services/id/service/src/test/java/org/collectionspace/services/id/part/test/GregorianDateIDPartTest.java b/services/id/service/src/test/java/org/collectionspace/services/id/part/test/GregorianDateIDPartTest.java index 9f1f40533..f341b0e18 100644 --- a/services/id/service/src/test/java/org/collectionspace/services/id/part/test/GregorianDateIDPartTest.java +++ b/services/id/service/src/test/java/org/collectionspace/services/id/part/test/GregorianDateIDPartTest.java @@ -2,6 +2,12 @@ package org.collectionspace.services.id.part.test; import org.collectionspace.services.id.part.GregorianDateIDPart; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; + import org.testng.Assert; import org.testng.annotations.Test; @@ -15,24 +21,27 @@ public class GregorianDateIDPartTest { GregorianDateIDPart part; + final static String MONTH_FULL_NAME_PATTERN = "MMMM"; + final static String FRENCH_LANGUAGE_ISO_CODE = "fr"; + final static Locale FRENCH_LANGUAGE_LOCALE = + new Locale(FRENCH_LANGUAGE_ISO_CODE, ""); + @Test public void newID() { - - // @TODO Replace these hard-coded expedients, which will all fail - // when the current month or year doesn't match these asserted values. part = new GregorianDateIDPart("yyyy"); - Assert.assertEquals(part.newID(), "2009"); + Assert.assertEquals(part.newID(), currentYearAsString()); part = new GregorianDateIDPart("M"); - Assert.assertEquals(part.newID(), "11"); + Assert.assertEquals(part.newID(), currentMonthNumberAsString()); - part = new GregorianDateIDPart("MMMM"); - Assert.assertEquals(part.newID(), "November"); + part = new GregorianDateIDPart(MONTH_FULL_NAME_PATTERN); + Assert.assertEquals(part.newID(), currentMonthFullName()); - part = new GregorianDateIDPart("MMMM", "fr"); - // Month names are not capitalized in French. - Assert.assertEquals(part.newID(), "novembre"); + part = new GregorianDateIDPart(MONTH_FULL_NAME_PATTERN, + FRENCH_LANGUAGE_ISO_CODE); + Assert.assertEquals(part.newID(), + currentMonthFullNameLocalized(FRENCH_LANGUAGE_LOCALE)); } @@ -47,4 +56,27 @@ public class GregorianDateIDPartTest { Assert.assertTrue(part.getValidator().isValid(part.newID())); } + public String currentYearAsString() { + int y = GregorianCalendar.getInstance().get(Calendar.YEAR); + return Integer.toString(y); + } + + public String currentMonthNumberAsString() { + // Calendar.MONTH numbers begin with 0; hence the need to add 1. + int m = GregorianCalendar.getInstance().get(Calendar.MONTH) + 1; + return Integer.toString(m); + } + + public String currentMonthFullName() { + SimpleDateFormat df = + new SimpleDateFormat(MONTH_FULL_NAME_PATTERN, + Locale.getDefault()); + return df.format(GregorianCalendar.getInstance().getTime()); + } + + public String currentMonthFullNameLocalized(Locale locale) { + SimpleDateFormat df = + new SimpleDateFormat(MONTH_FULL_NAME_PATTERN, locale); + return df.format(GregorianCalendar.getInstance().getTime()); + } } diff --git a/services/id/service/src/test/java/org/collectionspace/services/id/part/test/RandomNumberIDPartTest.java b/services/id/service/src/test/java/org/collectionspace/services/id/part/test/RandomNumberIDPartTest.java index bfb3f204b..3349b9ecb 100644 --- a/services/id/service/src/test/java/org/collectionspace/services/id/part/test/RandomNumberIDPartTest.java +++ b/services/id/service/src/test/java/org/collectionspace/services/id/part/test/RandomNumberIDPartTest.java @@ -86,16 +86,32 @@ public class RandomNumberIDPartTest { } } - @Test(expectedExceptions = IllegalArgumentException.class) - public void minValueTooLow() { - int minValue = -1; + @Test + public void defaultMaxValue() { + part = new RandomNumberIDPart( + JavaRandomNumberIDPartAlgorithm.DEFAULT_MAX_VALUE); + part.newID(); + } + + @Test(dependsOnMethods = {"defaultMaxValue"}) + public void defaultMinValue() { part = new RandomNumberIDPart( - JavaRandomNumberIDPartAlgorithm.DEFAULT_MAX_VALUE, minValue); + JavaRandomNumberIDPartAlgorithm.DEFAULT_MAX_VALUE, + JavaRandomNumberIDPartAlgorithm.DEFAULT_MIN_VALUE); + part.newID(); } @Test(expectedExceptions = IllegalArgumentException.class) public void maxValueTooHigh() { - part = new RandomNumberIDPart(Integer.MAX_VALUE); + int maxValue = Integer.MAX_VALUE; // Value too high + part = new RandomNumberIDPart(maxValue); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void minValueTooLow() { + int maxValue = 10; + int minValue = -1; // Value too low + part = new RandomNumberIDPart(maxValue, minValue); } @Test