/** * */ package querqy; import java.util.List; /** * * @author rene * */ public class CompoundCharSequence implements ComparableCharSequence { final CharSequence[] parts; public CompoundCharSequence(final List<? extends CharSequence> parts) { this(null, parts); } public CompoundCharSequence(final CharSequence separator, final List<? extends CharSequence> parts) { this(separator, parts.toArray(new CharSequence[parts.size()])); } /** * * @param separator * @param parts * The parts to combine. */ public CompoundCharSequence(final CharSequence separator, final CharSequence... parts) { if (parts == null || parts.length == 0) { throw new IllegalArgumentException("Excpectinig one or more parts"); } if (parts.length == 1 || separator == null || separator.length() == 0) { this.parts = parts; } else { this.parts = new CharSequence[parts.length * 2 - 1]; for (int i = 0; i < parts.length; i++) { int pos = i * 2; this.parts[pos] = parts[i]; if (i < parts.length - 1) { this.parts[pos + 1] = separator; } } } } /* * (non-Javadoc) * * @see java.lang.CharSequence#length() */ @Override public int length() { int length = parts[0].length(); if (parts.length > 1) { for (int i = 1; i < parts.length; i++) { length += parts[i].length(); } } return length; } /* * (non-Javadoc) * * @see java.lang.CharSequence#charAt(int) */ @Override public char charAt(final int index) { if (parts.length == 1) { return parts[0].charAt(index); } final PartInfo partInfo = getPartInfoForCharIndex(index); return parts[partInfo.partIndex].charAt(index - partInfo.globalStart); } PartInfo getPartInfoForCharIndex(final int index) { int globalEnd = 0; for (int i = 0, last = parts.length - 1; i <= last; i++) { int globalStart = globalEnd; globalEnd = globalStart + parts[i].length(); if (globalEnd > index) { return new PartInfo(i, globalStart); } } throw new ArrayIndexOutOfBoundsException(index); } /* * (non-Javadoc) * * @see java.lang.CharSequence#subSequence(int, int) */ @Override public ComparableCharSequence subSequence(final int start, final int end) { if (parts.length == 1) { // TODO: do subsequence as view in wrapper return new ComparableCharSequenceWrapper(parts[0].subSequence(start, end)); } if (start == end) { if (start <= length()) { return ComparableCharSequenceWrapper.EMPTY_SEQUENCE; } else { throw new ArrayIndexOutOfBoundsException(start); } } final PartInfo partInfoStart = getPartInfoForCharIndex(start); final PartInfo partInfoEnd = getPartInfoForCharIndex(end - 1); // end is exclusive if (partInfoStart.partIndex == partInfoEnd.partIndex) { // TODO: do subsequence as view in wrapper return new ComparableCharSequenceWrapper( parts[partInfoStart.partIndex] .subSequence(start - partInfoStart.globalStart, end - partInfoStart.globalStart)); } final CharSequence[] resParts = new CharSequence[partInfoEnd.partIndex - partInfoStart.partIndex + 1]; resParts[0] = parts[partInfoStart.partIndex] .subSequence(start - partInfoStart.globalStart, parts[partInfoStart.partIndex].length()); for (int i = partInfoStart.partIndex + 1, j = 1; i < partInfoEnd.partIndex; i++) { resParts[j++] = parts[i]; } resParts[resParts.length - 1] = parts[partInfoEnd.partIndex].subSequence(0, end - partInfoEnd.globalStart); return new CompoundCharSequence(null, resParts); } class PartInfo { final int partIndex; final int globalStart; public PartInfo(final int partIndex, final int globalStart) { this.partIndex = partIndex; this.globalStart = globalStart; } } @Override public int compareTo(final CharSequence other) { // TODO: avoid calls to this.charAt(i) to make comparison faster final int length = length(); for (int i = 0, len = Math.min(length, other.length()); i < len; i++) { final char ch1 = charAt(i); final char ch2 = other.charAt(i); if (ch1 != ch2) { return ch1 - ch2; } } return length - other.length(); } @Override public int hashCode() { return CharSequenceUtil.hashCode(this); } @Override public boolean equals(final Object obj) { return CharSequenceUtil.equals(this, obj); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); for (int i = 0; i < parts.length; i++) { buf.append(parts[i].toString()); } return buf.toString(); } }