]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-234,CSPACE-364: Ongoing work on revising interfaces for ID Parts, still in...
authorAron Roberts <aron@socrates.berkeley.edu>
Thu, 19 Nov 2009 03:04:11 +0000 (03:04 +0000)
committerAron Roberts <aron@socrates.berkeley.edu>
Thu, 19 Nov 2009 03:04:11 +0000 (03:04 +0000)
services/id/service/src/main/java/org/collectionspace/services/id/part/GregorianDateIDPart.java
services/id/service/src/main/java/org/collectionspace/services/id/part/GregorianDateIDPartOutputFormatter.java
services/id/service/src/main/java/org/collectionspace/services/id/part/IDPartOutputFormatter.java
services/id/service/src/main/java/org/collectionspace/services/id/part/JavaPrintfIDPartOutputFormatter.java
services/id/service/src/main/java/org/collectionspace/services/id/part/NoOpIDPartOutputFormatter.java
services/id/service/src/main/java/org/collectionspace/services/id/part/NumericIDPartOutputFormatter.java [new file with mode: 0644]
services/id/service/src/main/java/org/collectionspace/services/id/part/NumericSequenceIDPart.java
services/id/service/src/main/java/org/collectionspace/services/id/part/SequenceIDPart.java
services/id/service/src/test/java/org/collectionspace/services/id/part/test/NumericIDPartRegexValidatorTest.java
services/id/service/src/test/java/org/collectionspace/services/id/part/test/NumericSequenceIDPartTest.java

index 725ae9ea4d596d43204ea7d5981122f87ec34f62..a3cba0703a4a2d0ab0c97c487aa24494514e1f48 100644 (file)
@@ -8,17 +8,28 @@ public class GregorianDateIDPart extends DateIDPart {
     private GregorianDateIDPartOutputFormatter formatter;
     private IDPartValidator validator = IDPart.DEFAULT_VALIDATOR;
 
+    // Because the output from newID() is dependent on a
+    // format pattern having been set in the outputFormatter,
+    // constructors for this class all require that pattern.
+
     public GregorianDateIDPart(String formatPattern) {
-        setFormatter(new GregorianDateIDPartOutputFormatter(formatPattern));
+        setOutputFormatter(new GregorianDateIDPartOutputFormatter(formatPattern));
+    }
+
+    public GregorianDateIDPart(String formatPattern, IDPartValidator validator) {
+        setOutputFormatter(new GregorianDateIDPartOutputFormatter(formatPattern));
+        setValidator(validator);
     }
 
     public GregorianDateIDPart(String formatPattern, String languageCode) {
-        setFormatter(
+        setOutputFormatter(
             new GregorianDateIDPartOutputFormatter(formatPattern, languageCode));
     }
 
-    public GregorianDateIDPart(String formatPattern, IDPartValidator validator) {
-        setFormatter(new GregorianDateIDPartOutputFormatter(formatPattern));
+    public GregorianDateIDPart(String formatPattern, String languageCode,
+        IDPartValidator validator) {
+        setOutputFormatter(
+            new GregorianDateIDPartOutputFormatter(formatPattern, languageCode));
         setValidator(validator);
     }
 
@@ -27,11 +38,19 @@ public class GregorianDateIDPart extends DateIDPart {
        return this.formatter;
     }
 
+    private void setOutputFormatter(GregorianDateIDPartOutputFormatter formatter) {
+        this.formatter = formatter;
+    }
+
     @Override
     public IDPartValidator getValidator() {
         return this.validator;
     }
 
+    private void setValidator(IDPartValidator validator) {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
     @Override
     public String newID() {
         
@@ -56,13 +75,5 @@ public class GregorianDateIDPart extends DateIDPart {
         return getOutputFormatter().format(Long.toString(cal.getTime().getTime()));
     }
 
-    private void setFormatter(GregorianDateIDPartOutputFormatter formatter) {
-        this.formatter = formatter;
-    }
-
-    private void setValidator(IDPartValidator validator) {
-        throw new UnsupportedOperationException("Not yet implemented");
-    }
-
 }
 
index 83ccf3d50539f8c7e0a2395813c92d803c9635d7..d2c89aaa6311318b09416f2e17a8c5386d8ece71 100644 (file)
@@ -1,11 +1,11 @@
 package org.collectionspace.services.id.part;
 
-import java.util.Date;
-import java.util.Locale;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
-
 import java.util.Arrays;
+import java.util.Date;
+import java.util.Locale;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -18,9 +18,17 @@ public class GregorianDateIDPartOutputFormatter implements IDPartOutputFormatter
         LoggerFactory.getLogger(GregorianDateIDPartOutputFormatter.class);
 
     private int maxOutputLength = DEFAULT_MAX_OUTPUT_LENGTH;
-    private Locale locale = null;
-    private String language;
     private String formatPattern;
+    private final String DEFAULT_ISO_8601_FORMAT_PATTERN =
+        "yyyy-MM-dd"; // Note: 'floating date' without time zone.
+    private final String DEFAULT_GREGORIAN_DATE_FORMAT_PATTERN =
+        DEFAULT_ISO_8601_FORMAT_PATTERN;
+    private Locale locale = null;
+    
+    public GregorianDateIDPartOutputFormatter() {
+        setFormatPattern(DEFAULT_GREGORIAN_DATE_FORMAT_PATTERN);
+        setLocale(Locale.getDefault());
+    }
 
     public GregorianDateIDPartOutputFormatter(String formatPattern) {
         setFormatPattern(formatPattern);
@@ -38,6 +46,7 @@ public class GregorianDateIDPartOutputFormatter implements IDPartOutputFormatter
         return this.maxOutputLength;
     }
 
+    @Override
     public void setMaxOutputLength (int length) {
         this.maxOutputLength = length;
     }
@@ -47,6 +56,7 @@ public class GregorianDateIDPartOutputFormatter implements IDPartOutputFormatter
         return this.formatPattern;
     }
 
+    @Override
     public void setFormatPattern(String pattern) {
         if (pattern == null || pattern.trim().isEmpty()) {
             logger.error("Format pattern cannot be null or empty.");
@@ -58,6 +68,14 @@ public class GregorianDateIDPartOutputFormatter implements IDPartOutputFormatter
     @Override
     public String format(String id) {
 
+        String pattern = getFormatPattern();
+        // If the formatting pattern is empty, output using
+        // a default formatting pattern.
+        if (pattern == null || pattern.trim().isEmpty()) {
+            pattern = DEFAULT_GREGORIAN_DATE_FORMAT_PATTERN;
+        }
+
+        // Convert milliseconds in Epoch to Date.
         Long millisecondsInEpoch = 0L;
         try {
             millisecondsInEpoch = (Long.parseLong(id));
@@ -65,14 +83,22 @@ public class GregorianDateIDPartOutputFormatter implements IDPartOutputFormatter
             logger.error("Could not parse date milliseconds as a number.", e);
             return "";
         }
-        
-        String formattedID = "";
-        if (millisecondsInEpoch > 0) {
-          Date d = new Date(millisecondsInEpoch);
-          formattedID = formatDate(d);
-
-          // @TODO Check for exceeding maximum length before
-          // returning formatted date value.
+
+        if (millisecondsInEpoch <= 0) {
+            return "";
+
+        }
+
+        // Format the date using the specified format pattern.
+        Date d = new Date(millisecondsInEpoch);
+        String formattedID = formatDate(d);
+
+        if (! isValidLength(formattedID)) {
+            logger.error(
+                "Formatted ID '" + formattedID +
+                "' exceeds maximum length of " +
+                getMaxOutputLength() + " characters.");
+            return "";
         }
 
         return formattedID;
@@ -88,6 +114,14 @@ public class GregorianDateIDPartOutputFormatter implements IDPartOutputFormatter
         return dateformatter.format(date);
     }
 
+    private boolean isValidLength(String id) {
+        if (id.length() <= getMaxOutputLength()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     // @TODO Consider generalizing locale-specific operations
     // in a utility class outside of ID package.
 
@@ -109,23 +143,25 @@ public class GregorianDateIDPartOutputFormatter implements IDPartOutputFormatter
         if (languageCode.length() != 2) {
             logger.error(
                 "Locale language code '" + languageCode +
-                "' must be a two-letter ISO-639 language code.");
+                "' must be a two-letter ISO 639-1 language code.");
             return;
         }
 
-        // Although language codes are documented as required to be
-        // in lowercase, and they are output in that way in
+        // Although it is documented in Sun's Javadocs that
+        // the language code parameter when initializing a
+        // Locale is required to be in lowercase, and as well,
+        // language codes are output only in lowercase in
         // DateFormat.getAvailableLocales(), they appear to be
         // matched - within Locales - in a case-insensitive manner.
-        /*
-        if (! languageCode.equals(languageCode.toLowerCase())) {
-            logger.error("Locale language code must be in lower case.");
-            return;
-        }
-        */
+        // If that ever changes, uncomment the following block.
+        //
+        // if (! languageCode.equals(languageCode.toLowerCase())) {
+        //    logger.error("Locale language code must be in lower case.");
+        //    return;
+        // }
 
         Locale l = new Locale(languageCode, "");
-        if (isValidLocaleForDateFormatter(l)) {
+        if (isValidLocaleForFormatter(l)) {
             setLocale(l);
         } else {
             logger.error("Locale language code '" + languageCode +
@@ -134,18 +170,10 @@ public class GregorianDateIDPartOutputFormatter implements IDPartOutputFormatter
         }
     }
     
-    private boolean isValidLocaleForDateFormatter(Locale l) {
+    private boolean isValidLocaleForFormatter(Locale l) {
         Locale[] locales = DateFormat.getAvailableLocales();
         return (Arrays.asList(locales).contains(l)) ? true : false;
     }
 
-    private boolean isValidLength(String id) {
-        if (id.length() > getMaxOutputLength()) {
-            return false;
-        } else {
-            return true;
-        }
-    }
-
 }
 
index 301025168f6923a1cd7e5713b4194d4812a70f80..5e3afaeaf5a7e3d873eb9d45db32a840cbc820ac 100644 (file)
@@ -6,8 +6,13 @@ public interface IDPartOutputFormatter {
 
     public int getMaxOutputLength();
 
+    public void setMaxOutputLength(int length);
+
     public String getFormatPattern();
 
+    public void setFormatPattern(String pattern);
+
+    // @TODO Consider throwing IllegalStateException from this method.
     public String format(String id);
 
 }
index ac06702c73a32f3c1050a152b26189cce8f0d208..5e2f96870296e4f2c3ec4545ecf0e546ecb650fc 100644 (file)
@@ -1,39 +1,56 @@
 package org.collectionspace.services.id.part;
 
-import java.util.IllegalFormatException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.IllegalFormatException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 // Uses Java's interpreter for printf-style format strings.
 // http://java.sun.com/javase/1.6/docs/api/java/util/Formatter.html
 
 public class JavaPrintfIDPartOutputFormatter implements IDPartOutputFormatter {
 
+    final Logger logger =
+        LoggerFactory.getLogger(JavaPrintfIDPartOutputFormatter.class);
+
     StringWriter stringwriter = new StringWriter();
     private int maxOutputLength = DEFAULT_MAX_OUTPUT_LENGTH;
     private String formatPattern;
 
-    public JavaPrintfIDPartOutputFormatter () {
+    public JavaPrintfIDPartOutputFormatter() {
+    }
+
+    public JavaPrintfIDPartOutputFormatter(String formatPattern) {
+        setFormatPattern(formatPattern);
     }
 
     @Override
-    public int getMaxOutputLength () {
+    public int getMaxOutputLength() {
         return this.maxOutputLength;
     }
 
-    public void setMaxOutputLength (int length) {
+    @Override
+    public void setMaxOutputLength(int length) {
         this.maxOutputLength = length;
     }
 
     @Override
-    public String getFormatPattern () {
+    public String getFormatPattern() {
         return this.formatPattern;
     }
 
+    @Override
     public void setFormatPattern(String pattern) {
-        this.formatPattern = pattern;
+        if (pattern == null || pattern.trim().isEmpty()) {
+            logger.error("Format pattern cannot be null or empty.");
+        } else {
+            this.formatPattern = pattern;
+        }
     }
 
+
     @Override
     public String format(String id) {
 
@@ -43,7 +60,15 @@ public class JavaPrintfIDPartOutputFormatter implements IDPartOutputFormatter {
 
         // If the formatting pattern is empty, just check length.
         if (pattern == null || pattern.trim().isEmpty()) {
-            isValidLength(formattedID);
+
+            if (! isValidLength(formattedID)) {
+                logger.error(
+                    "Formatted ID '" + formattedID +
+                    "' exceeds maximum length of " +
+                    getMaxOutputLength() + " characters." +
+                    "Returning ID without formatting.");
+                return id;
+            }
 
         // Otherwise, format the ID using the pattern, then check length.
         } else {
@@ -66,11 +91,12 @@ public class JavaPrintfIDPartOutputFormatter implements IDPartOutputFormatter {
 
     // Check whether the formatted ID exceeds the specified maximum length.
 
-    private void isValidLength(String id) {
-        if (id.length() > getMaxOutputLength()) {
-            // @TODO Log error, possibly throw exception.
+    private boolean isValidLength(String id) {
+        if (id.length() <= getMaxOutputLength()) {
+            return true;
+        } else {
+            return false;
         }
-
     }
 
 }
index 57f4c2b264010330283c03c61b78c76419b6f994..3bdfc01b860c70489047f70e0d54f7022adcc0c4 100644 (file)
@@ -2,23 +2,29 @@ package org.collectionspace.services.id.part;
 
 public class NoOpIDPartOutputFormatter implements IDPartOutputFormatter {
 
-    public NoOpIDPartOutputFormatter () {
+    public NoOpIDPartOutputFormatter() {
+    }
+
+    public NoOpIDPartOutputFormatter(String formatPattern) {
+        // Do nothing.
     }
 
     @Override
-    public int getMaxOutputLength () {
+    public int getMaxOutputLength() {
         return Integer.MAX_VALUE;
     }
 
-    public void setMaxOutputLength (int length) {
+    @Override
+    public void setMaxOutputLength(int length) {
         // Do nothing.
     }
 
     @Override
-    public String getFormatPattern () {
+    public String getFormatPattern() {
         return "";
     }
 
+    @Override
     public void setFormatPattern(String pattern) {
         // Do nothing.
     }
diff --git a/services/id/service/src/main/java/org/collectionspace/services/id/part/NumericIDPartOutputFormatter.java b/services/id/service/src/main/java/org/collectionspace/services/id/part/NumericIDPartOutputFormatter.java
new file mode 100644 (file)
index 0000000..7600727
--- /dev/null
@@ -0,0 +1,136 @@
+package org.collectionspace.services.id.part;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// For symbols used in format patterns, see:
+// http://java.sun.com/docs/books/tutorial/i18n/format/decimalFormat.html#numberpattern
+
+public class NumericIDPartOutputFormatter implements IDPartOutputFormatter {
+
+    final Logger logger =
+        LoggerFactory.getLogger(NumericIDPartOutputFormatter.class);
+
+    private int maxOutputLength = DEFAULT_MAX_OUTPUT_LENGTH;
+    private String formatPattern;
+    private NumberFormat numberFormat;
+
+    public NumericIDPartOutputFormatter() {
+        setNumberFormat(formatPattern);
+    }
+
+    public NumericIDPartOutputFormatter(String formatPattern) {
+        setFormatPattern(formatPattern);
+        setNumberFormat(formatPattern);
+    }
+
+    @Override
+    public int getMaxOutputLength () {
+        return this.maxOutputLength;
+    }
+
+    @Override
+    public void setMaxOutputLength (int length) {
+        this.maxOutputLength = length;
+    }
+
+    @Override
+    public String getFormatPattern () {
+        return this.formatPattern;
+    }
+
+    @Override
+    public void setFormatPattern(String pattern) {
+        if (pattern == null || pattern.trim().isEmpty()) {
+            logger.error("Format pattern cannot be null or empty.");
+        } else {
+            this.formatPattern = pattern;
+        }
+    }
+
+    @Override
+    public String format(String id) {
+
+        Long l;
+        try {
+            l = (Long.parseLong(id));
+        } catch (NumberFormatException e) {
+            logger.error("Could not parse id '" + id + "' as a number.", e);
+            return id;
+        }
+
+        // Format the number using the specified format pattern.
+        String formattedID = formatLong(l);
+
+        if (! isValidLength(formattedID)) {
+            logger.error(
+                "Formatted ID '" + formattedID +
+                "' exceeds maximum length of " +
+                getMaxOutputLength() + " characters." +
+                "Returning ID without formatting.");
+            return id;
+        }
+
+        return formattedID;
+    }
+
+    public String formatLong(Long l) {
+        NumberFormat nf = getNumberFormat();
+        return nf.format(l);
+    }
+
+    public long parseAsLong(String id) {
+        Long l = 0L;
+        try {
+            Number n = getNumberFormat().parse(id);
+            l = n.longValue();
+        } catch (ParseException e) {
+            // @TODO Handle this exception
+        }
+        return l;
+    }
+
+    private NumberFormat getNumberFormat() {
+        return this.numberFormat;
+    }
+
+    private void setNumberFormat(String pattern) {
+
+        NumberFormat nf = new DecimalFormat();
+
+        // If there is no pattern specified, use a general purpose pattern.
+        if (pattern == null || pattern.trim().isEmpty()) {
+            nf = NumberFormat.getIntegerInstance();
+            // In this general purpose pattern, turn off grouping
+            // of numbers via grouping separators (such as "," or ".").
+            nf.setGroupingUsed(false);
+        // Otherwise, use the specified pattern.
+        } else {
+             if (nf instanceof DecimalFormat) {
+                 try {
+                      ((DecimalFormat) nf).applyPattern(pattern);
+                 } catch (IllegalArgumentException e) {
+                    // @TODO Handle this exception;
+                 }
+            }
+
+        }
+
+        this.numberFormat = nf;
+    }
+
+    private boolean isValidLength(String id) {
+        if (id.length() <= getMaxOutputLength()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+}
+
index 88f69e84565a8941bd51c06fb88af6c714777099..ffbe79ccc7a75a1a88a9672e7f0aa16fbec6305c 100644 (file)
@@ -17,14 +17,16 @@ public class NumericSequenceIDPart extends SequenceIDPart {
     final static private long DEFAULT_INCREMENT_BY_VALUE = 1;
     private long incrementBy = DEFAULT_INCREMENT_BY_VALUE;
 
-    // @TODO Replace the NoOp formatter with a printf formatter.
-    private IDPartOutputFormatter formatter = new NoOpIDPartOutputFormatter();
+    private IDPartOutputFormatter formatter = new NumericIDPartOutputFormatter();
     private IDPartValidator validator = new NumericIDPartRegexValidator();
 
-
     public NumericSequenceIDPart() {
     }
 
+    public NumericSequenceIDPart(String formatPattern) {
+        setOutputFormatter(new NumericIDPartOutputFormatter(formatPattern));
+    }
+
     public NumericSequenceIDPart(long initial) {
         setInitialID(initial);
     }
@@ -34,12 +36,19 @@ public class NumericSequenceIDPart extends SequenceIDPart {
         setIncrementBy(incrementBy);
     }
 
+    public NumericSequenceIDPart(String formatPattern, long initial,
+        long incrementBy) {
+        setOutputFormatter(new NumericIDPartOutputFormatter(formatPattern));
+        setInitialID(initial);
+        setIncrementBy(incrementBy);
+    }
+
     @Override
     public IDPartOutputFormatter getOutputFormatter() {
         return this.formatter;
     }
 
-    public void setOutputFormatter (IDPartOutputFormatter formatter) {
+    public void setOutputFormatter(IDPartOutputFormatter formatter) {
         this.formatter = formatter;
     }
 
@@ -48,7 +57,11 @@ public class NumericSequenceIDPart extends SequenceIDPart {
         return this.validator;
     }
 
-    // newID() is implemented in superclass, SequenceIDPart.
+    @Override
+    public String newID() {
+        String newID = super.newID();
+        return getOutputFormatter().format(newID);
+    }
 
     @Override
     public boolean hasCurrentID() {
@@ -70,11 +83,17 @@ public class NumericSequenceIDPart extends SequenceIDPart {
     }
 
     @Override
-    public void setCurrentID(String str) {
-        try {
-            setCurrentID(Long.parseLong(str));
-        } catch (NumberFormatException e) {
-            logger.error("Could not parse current ID value as a number.", e);
+    public void setCurrentID(String id) {
+        String formatPattern = getOutputFormatter().getFormatPattern();
+        if (formatPattern == null || formatPattern.trim().isEmpty()) {
+            try {
+                setCurrentID(Long.parseLong(id));
+            } catch (NumberFormatException e) {
+                logger.error("Could not parse current ID value as a number.", e);
+            }
+        } else {
+            setCurrentID(((NumericIDPartOutputFormatter)
+                getOutputFormatter()).parseAsLong(id));
         }
     }
 
@@ -94,11 +113,7 @@ public class NumericSequenceIDPart extends SequenceIDPart {
 
     @Override
     public String nextID() {
-        // @TODO Rethink this approach soon, as we may not want
-        // to change the current value for IDs that have been
-        // provisionally issued.
-        this.currentValue = this.currentValue + this.incrementBy;
-        return getCurrentID();
+        return Long.toString(this.currentValue + this.incrementBy);
     }
 
     public String getIncrementBy() {
index 882fd6b3ad1681321e437fedc6a8d3692b9e8d19..767765d0eadfa680a61b2d5593cfa5b1e5ad025d 100644 (file)
@@ -14,9 +14,9 @@ public abstract class SequenceIDPart implements IDPart, DynamicValueIDPart {
     @Override
     public String newID() {
         if (hasCurrentID()) {
-            return getOutputFormatter().format(nextID());
+            return nextID();
         } else {
-            return getOutputFormatter().format(getInitialID());
+            return getInitialID();
         }
     }
 
index 85e542567ef758c7682d5f46a72e5ce2385ceca2..5c02974fff8553bbc11c6f4e65eeedae999183c1 100644 (file)
@@ -24,4 +24,9 @@ public class NumericIDPartRegexValidatorTest {
         Assert.assertFalse(validator.isValid("-1"));
     }
 
+    @Test
+    public void format() {
+
+    }
+
 }
index 1e53bf9f8300900e54b09aa6f65c15c1f9cbe823..500d2cd0fa762d550053a8d40630b60430e10ba3 100644 (file)
@@ -38,7 +38,8 @@ public class NumericSequenceIDPartTest {
 
     @Test
     public void newIDWithSuppliedInitialValue() {
-         part = new NumericSequenceIDPart(100);
+
+        part = new NumericSequenceIDPart(100);
         id = part.newID();
         Assert.assertEquals(id, "100");
         part.setCurrentID(id);
@@ -49,6 +50,18 @@ public class NumericSequenceIDPartTest {
 
         id = part.newID();
         Assert.assertEquals(id, "102");
+
+        // Tests whether default formatting has disabled grouping.
+
+        part = new NumericSequenceIDPart(12345);
+        id = part.newID();
+        Assert.assertEquals(id, "12345"); // No grouping separator; e.g. 12,345
+        part.setCurrentID(id);
+
+        id = part.newID();
+        Assert.assertEquals(id, "12346");
+        part.setCurrentID(id);
+
     }
 
     @Test
@@ -67,7 +80,49 @@ public class NumericSequenceIDPartTest {
     }
 
     @Test
-    public void format() {
+    public void formatWithLeadingZeros() {
+        
+        // Pad at left with leading zeros up to width specified.
+        part = new NumericSequenceIDPart("000");
+        id = part.newID();
+        Assert.assertEquals(id, "001");
+        part.setCurrentID(id);
+
+        id = part.newID();
+        Assert.assertEquals(id, "002");
+        part.setCurrentID(id);
+
+        part = new NumericSequenceIDPart("000", 20, 5);
+        id = part.newID();
+        Assert.assertEquals(id, "020");
+        part.setCurrentID(id);
+
+        id = part.newID();
+        Assert.assertEquals(id, "025");
+        part.setCurrentID(id);
+
+        // Numerals with more digits than pattern do not receive padding.
+        part = new NumericSequenceIDPart("000", 5000, 1);
+        id = part.newID();
+        Assert.assertEquals(id, "5000");
+        part.setCurrentID(id);
+
+        id = part.newID();
+        Assert.assertEquals(id, "5001");
+        part.setCurrentID(id);
+    }
+
+    @Test
+    public void formatWithSeparators() {
+
+        part = new NumericSequenceIDPart("#,###", 1234567, 1);
+        id = part.newID();
+        Assert.assertEquals(id, "1,234,567");
+        part.setCurrentID(id);
+
+        id = part.newID();
+        Assert.assertEquals(id, "1,234,568");
+        part.setCurrentID(id);
     }
 
     @Test