CharStar.java

/*-
 * #%L
 * io.earcam.utilitarian.charstar
 * %%
 * Copyright (C) 2017 - 2018 earcam
 * %%
 * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
 *
 * You <b>must</b> choose to accept, in full - any individual or combination of
 * the following licenses:
 * <ul>
 * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
 * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
 * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
 * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
 * </ul>
 * #L%
 */
package io.earcam.utilitarian.charstar;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;

final class CharStar implements CharSequence, Comparable<CharSequence>, Externalizable {

	private static final long serialVersionUID = 42L;

	private char[] sequence;
	private int offset;
	private int length;


	public CharStar()
	{}


	private CharStar(char[] sequence)
	{
		this(sequence, 0, sequence.length);
	}


	private CharStar(char[] sequence, int offset, int length)
	{
		this.sequence = sequence;
		this.offset = offset;
		this.length = length;
	}


	static CharStar backedCharSequence(char[] sequence)
	{
		return new CharStar(sequence);
	}


	static CharStar charSequence(char[] sequence)
	{
		char[] encapsulated = Arrays.copyOf(sequence, sequence.length);
		return new CharStar(encapsulated);
	}


	@Override
	public boolean equals(Object other)
	{
		return other instanceof CharStar && equals((CharStar) other);
	}


	public boolean equals(CharStar that)
	{
		return that != null && CharSequences.same(this, that);
	}


	@Override
	public int hashCode()
	{
		return CharSequences.hashCode(this);
	}


	@Override
	public String toString()
	{
		return String.valueOf(sequence, offset, length);
	}


	@Override
	public int length()
	{
		return length;
	}


	@Override
	public char charAt(int index)
	{
		return sequence[offset + index];
	}


	@Override
	public CharStar subSequence(int start, int end)
	{
		requireValidIndices(start, end);
		return new CharStar(sequence, start, end - start);
	}


	private void requireValidIndices(int start, int end)
	{
		requireNonNegative("start", start);
		requireNonNegative("end", end);
		requireLessThanOrEqualTo("end", end, "length", length);
		requireLessThanOrEqualTo("start", start, "end", end);
	}


	private void requireNonNegative(String name, int value)
	{
		if(value < 0) {
			throw new IndexOutOfBoundsException(name + " is less than zero: " + value);
		}
	}


	private void requireLessThanOrEqualTo(String lhs, int lhsValue, String rhs, int rhsValue)
	{
		if(lhsValue > rhsValue) {
			throw new IndexOutOfBoundsException(lhs + " > " + rhs + ": " + lhsValue + " > " + rhsValue);
		}
	}


	public int compareTo(CharSequence that)
	{
		int max = Math.min(this.length, that.length());
		int i = 0;
		while(i < max) {
			char c1 = sequence[offset + i];
			char c2 = that.charAt(i);
			if(c1 != c2) {
				return c1 - c2;
			}
			i++;
		}
		return this.length - that.length();
	}


	@Override
	public void writeExternal(ObjectOutput out) throws IOException
	{
		out.writeInt(length);
		for(int i = offset; i < offset + length; i++) {
			out.writeChar(sequence[i]);
		}
	}


	@Override
	public void readExternal(ObjectInput in) throws IOException
	{
		length = in.readInt();
		sequence = new char[length];
		for(int i = 0; i < length; i++) {
			sequence[i] = in.readChar();
		}
	}


	public char[] toArray()
	{
		char[] dest = new char[length];
		System.arraycopy(sequence, offset, dest, 0, length);
		return dest;
	}
}