// generated IDs can grow, likely as an additional parameter to be
// passed to a constructor, with a default value hard-coded in the class.
-// @TODO: Handle escaped characters or sequences which represent Unicode code points,
-// both in the start and end characters of the sequence, and in the initial value.
+// @TODO: Consider handling escaped characters or sequences which represent Unicode
+// code points, both in the start and end characters of the sequence, and in the initial value.
// (Example: '\u0072' for the USASCII 'r' character; see
// http://www.fileformat.info/info/unicode/char/0072/index.htm)
//
// http://www.velocityreviews.com/forums/t367758-unescaping-unicode-code-points-in-a-java-string.html
// We might also look into the (protected) source code for java.util.Properties.load()
// which reads escaped Unicode values.
+//
+// Note also that, if the goal is to cycle through a series of alphabetic identifiers,
+// such as the sequence of characters used in a particular human language, it may or may not
+// be the case that any contiguous Unicode code point sequence reflects such a character sequence.
// NOTE: This class currently hard-codes the assumption that the values in
// alphabetic identifiers are ordered in significance from left-to-right;
// use Arrays.asList() to copy the initial array to a Vector.)
char[] chars = initial.toCharArray();
char ch;
- for (int i=0; i < chars.length; i++) {
+ for (int i = 0; i < chars.length; i++) {
// If the character falls within the range bounded by the start and end
// characters, copy it to the Vector.
public synchronized String getCurrentID() {
return getIDString(this.currentValue);
}
+
+ // Sets the current value.
+ public synchronized void setCurrentID(String value) throws IllegalArgumentException {
+
+ // @TODO Much of this code is copied from the main constructor,
+ // and may be ripe for refactoring.
+
+ if (value == null || value.equals("")) {
+ throw new IllegalArgumentException("Initial value must not be null or empty");
+ }
+
+ // @TODO: Add a check for maximum length of the value here.
+
+ // Store the chars in the value as Characters in a Vector,
+ // validating each character to identify whether it falls within
+ // the provided series.
+ //
+ // (Since we're performing casts from char to Character, we can't just
+ // use Arrays.asList() to copy the initial array to a Vector.)
+ char[] chars = value.toCharArray();
+ char ch;
+ Vector v = new Vector<Character>();
+ for (int i = 0; i < chars.length; i++) {
+
+ // If the character falls within the range bounded by the start and end
+ // characters, copy it to the Vector.
+ ch = chars[i];
+ if (ch >= this.startChar && ch <= this.endChar) {
+ v.add(new Character(ch));
+ // Otherwise, we've detected a character not in the series.
+ } else {
+ throw new IllegalArgumentException("character " + "\'" + ch + "\'" + " is not valid");
+ }
+
+ }
+
+ // Set the current value.
+ this.currentValue = new Vector<Character>(v);
+ }
// Returns the next alphabetic ID in the series.
//
public String getInitialID();
public String getCurrentID();
+
+ public void setCurrentID(String value);
public String getNextID();
return generator.getCurrentID();
}
+ // Sets the current value of this ID.
+ public synchronized void setCurrentID(String value) {
+ generator.setCurrentID(value);
+ }
+
// Returns the next value of this ID.
public synchronized String getNextID() throws IllegalStateException {
return generator.getNextID();
// @TODO: Add Javadoc comments
+// @TODO: Catch Exceptions thrown by IDPart, then
+// reflect this in the corresponding IDPatternTest class.
+
package org.collectionspace.services.id;
import java.util.Vector;
}
// Returns the next value of this ID.
+ //
+ // @TODO: Throws IllegalArgumentException
public synchronized String getNextID() {
+ // Obtain the last (least significant) IDPart,
+ // and call its getNextID() method, which will
+ // concurrently set the current value of that ID
+ // to the next ID.
+ int last = this.parts.size() - 1;
+ this.parts.get(last).getNextID();
+ // Then call the getCurrentID() method on all of the IDParts
StringBuffer sb = new StringBuffer(MAX_ID_LENGTH);
+ for (IDPart part : this.parts) {
+ sb.append(part.getCurrentID());
+ }
+ return sb.toString();
+ }
+
+ // Returns the next value of this ID, given a
+ // supplied ID that entirely matches the pattern.
+ //
+ // @TODO: Throws IllegalArgumentException
+ public synchronized String getNextID(String value) {
+
+ if (value == null) return value;
+
+ Pattern pattern = Pattern.compile(getRegex());
+ Matcher matcher = pattern.matcher(value);
+
+ // If the supplied ID doesn't entirely match the pattern,
+ // return that same ID as the next ID.
+ //
+ // @TODO: We may wish to handle this differently,
+ // such as by throwing an Exception.
+ if (! matcher.matches()) {
+ return value;
+ }
+
+ // Otherwise, if the supplied ID entirely matches the pattern,
+ // split the ID into its components and store those values in
+ // each of the pattern's IDparts.
+ IDPart currentPart;
+ for (int i = 1; i <= (matcher.groupCount() - 1); i++) {
+ currentPart = this.parts.get(i - 1);
+ currentPart.setCurrentID(matcher.group(i));
+ }
+
// Obtain the last (least significant) IDPart,
// and call its getNextID() method, which will
// concurrently set the current value of that ID
// to the next ID.
+ //
+ // @TODO: This code is duplicated in getNextID(), above,
+ // and thus we may want to refactor this.
int last = this.parts.size() - 1;
this.parts.get(last).getNextID();
// Then call the getCurrentID() method on all of the IDParts
+ StringBuffer sb = new StringBuffer();
for (IDPart part : this.parts) {
sb.append(part.getCurrentID());
}
}
// Validates a provided ID against the pattern.
+ //
+ // @TODO May potentially throw at least one pattern-related exception.
public synchronized boolean isValidID(String value) {
+ if (value == null) return false;
+
Pattern pattern = Pattern.compile(getRegex());
Matcher matcher = pattern.matcher(value);
if (matcher.matches()) {
public synchronized String getCurrentID() {
return Long.toString(this.currentValue);
}
+
+ // Sets the current value.
+ public synchronized void setCurrentID(String value) throws IllegalArgumentException {
+
+ // @TODO Much of this code is copied from the main constructor,
+ // and may be ripe for refactoring.
+ try {
+ long l = Long.parseLong(value.trim());
+ if ( l < 0 ) {
+ throw new IllegalArgumentException("Initial ID value should be zero (0) or greater");
+ }
+ this.currentValue = l;
+ this.initialValue = l;
+ } catch (NullPointerException e) {
+ throw new IllegalArgumentException("ID value should not be null");
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("ID value must be parseable as a number");
+ }
+
+ // @TODO An expedient; we may need to check the String length of the
+ // provided ID and calculate a maximum length here.
+ this.maxLength = DEFAULT_MAX_LENGTH;
+ }
public synchronized String getNextID() throws IllegalStateException {
this.currentValue++;
public synchronized String getCurrentID() {
return this.currentValue;
}
+
+ public synchronized void setCurrentID(String value) throws IllegalArgumentException {
+ if ( initialValue == null || initialValue == "") {
+ throw new IllegalArgumentException("ID value must not be null or empty");
+ }
+ this.currentValue = value;
+ }
public synchronized String getNextID() {
return this.currentValue;
throw new IllegalArgumentException("Initial ID value must not be null or empty");
}
- // @TODO: Add regex-based validation here, by calling a
- // to-be-added validate() method. Consider implications
- // for Internationalization when doing so.
+ // @TODO: Add regex-based validation here, by calling isValidID().
+ // Consider implications for Internationalization when doing so.
this.initialValue = initialValue;
this.currentValue = initialValue;
public synchronized String getCurrentID() {
return this.currentValue;
}
+
+ // Sets the current value.
+ public synchronized void setCurrentID(String value) throws IllegalArgumentException {
+
+ // @TODO This code is copied from the main constructor,
+ // and thus there may be an opportunity for refactoring.
+
+ if ( value == null || value == "") {
+ throw new IllegalArgumentException("ID value must not be null or empty");
+ }
+
+ // @TODO: Add regex-based validation here, by calling isValidID().
+ // Consider implications for Internationalization when doing so.
+
+ this.currentValue = value;
+
+ }
// @TODO: We'll need to decide what a "next" ID means in the context of:
// - An initially supplied value.
assertEquals("2009.1-", pattern.getNextID());
}
+
+ public void testNextIDWithSuppliedID() {
+
+ pattern = new IDPattern();
+ pattern.add(new YearIDPart("2009"));
+ pattern.add(new StringIDPart("."));
+ pattern.add(new NumericIDPart("1"));
+ assertEquals("2009.2", pattern.getNextID("2009.1"));
+ assertEquals("2009.3", pattern.getNextID("2009.2"));
+
+ pattern = new IDPattern();
+ pattern.add(new YearIDPart("2009"));
+ pattern.add(new StringIDPart("."));
+ pattern.add(new NumericIDPart("1"));
+ pattern.add(new StringIDPart("-"));
+ pattern.add(new AlphabeticIDPart("a"));
+ assertEquals("2009.1-b", pattern.getNextID("2009.1-a"));
+ assertEquals("2009.3-c", pattern.getNextID("2009.3-b"));
+
+ }
public void testEmptyPartsListCurrentID() {