/*
* Copyright (c) 2005-2011 Grameen Foundation USA
* All rights reserved.
*
* Licensed 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 also http://www.apache.org/licenses/LICENSE-2.0.html for an
* explanation of the license and how it is applied.
*/
package org.mifos.framework.util.helpers;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Contains a representation of a "chapter number", like "4.1.3", "1.2.10",
* "1.2.20", etc. The chapter number is a comparable <a
* href="http://en.wikipedia.org/wiki/Tuple">tuple</a> (or group) of integers.
* Negative numbers must not be added to this tuple, although no explicit
* runtime checks exist to ensure this. If {@link #fromString(String)} is used,
* negative numbers will be filtered out. This is the preferred method for
* instantiating new objects of this class.
*
* <p>
* We don't need to override {@link ArrayList#equals(Object)}, the parent
* version works for us too.
*/
public class ChapterNum extends ArrayList<Integer> implements Comparable<List<Integer>> {
private static final long serialVersionUID = 1L;
@Override
public int compareTo(List<Integer> o) {
int i = 0, z = 1;
// lists are equal length, and all elements are equal.
if (this.equals(o)) {
return 0;
}
// must compare every element one by one
while (true) {
// the first number we encounter that is unequal immediately
// indicates sort order
if (this.get(i) < o.get(i)) {
return -1;
}
if (this.get(i) > o.get(i)) {
return 1;
}
// at end of both arrays and we're equal. Both must be equal.
// This case is covered by the above call to the parent class
// .equals() method
// if (this.size() == atElemNum && this.size() == o.size())
// return 0;
// we're at the end of this array and we're equal so far. Other
// must have more digits, hence, must be greater.
if (this.size() == z) {
return -1;
}
// we're at the end of the other array and we're equal so far.
// Other must have less digits, hence, must be less.
if (o.size() == z) {
return 1;
}
// we're equal so far, and both lists have more numbers to
// compare. increment and continue.
i++;
z++;
}
}
/**
* Returns a dotted numeric "chapter number", like "1.12.7".
*/
@Override
public String toString() {
String s = "";
Iterator<Integer> numiter = this.iterator();
while (numiter.hasNext()) {
s += numiter.next();
if (numiter.hasNext()) {
s += '.';
}
}
return s;
}
/**
* Instantiates a new <code>ChapterNum</code> object based on information
* found in the given string.
*
* <p>
* Dots are discarded and numbers are then parsed as <code>int</code>s.
*
* <p>
* Strings that don't exactly match the form <code>^\d+(\.\d+)*$</code> will
* result in a <code>null</code> return value.
*
* @param s
* {@link String} to transform into a <code>ChapterNum</code>.
* @return May be null if input does not match expected pattern of a chapter
* number.
*/
public static ChapterNum fromString(String s) {
if (null == s || s.length() < 1 || !s.matches("^\\d+(\\.\\d+)*$")) {
return null;
}
// extract digits
String[] ints = s.split("\\.");
ChapterNum c = new ChapterNum();
for (String intStr : ints) {
c.add(Integer.parseInt(intStr));
}
return c;
}
/**
* Exactly follows specification of
* {@link java.util.Comparator#compare(Object, Object)}.
*/
public static int compare(ChapterNum c1, ChapterNum c2) {
return c1.compareTo(c2);
}
/**
* Exactly follows specification of
* {@link java.util.Comparator#compare(Object, Object)}.
*/
public static int compare(String s1, String s2) {
return fromString(s1).compareTo(fromString(s2));
}
}