/*
* Copyright (C) 2014 Indeed Inc.
*
* 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.
*/
package com.indeed.imhotep.ez;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Lists;
import org.apache.log4j.Logger;
import javax.annotation.Nullable;
import java.util.Iterator;
/**
* @author jplaisance
*/
public final class GroupKey<E> implements Iterable<E> {
private static final Logger log = Logger.getLogger(GroupKey.class);
private final @Nullable List<E> front;
private final @Nullable List<E> back;
private static final GroupKey EMPTY = new GroupKey(null, null);
public static <E> GroupKey<E> empty() {
return EMPTY;
}
public static <E> GroupKey<E> singleton(E e) {
return EMPTY.add(e);
}
private GroupKey(final @Nullable List<E> front, final @Nullable List<E> back) {
this.front = front;
this.back = back;
}
public Iterator<E> iterator() {
return new AbstractIterator<E>() {
GroupKey<E> current = GroupKey.this;
protected E computeNext() {
if (current.isEmpty()) return endOfData();
final E ret = current.head();
current = current.tail();
return ret;
}
};
}
private static final class List<E> {
private final E head;
private final List<E> tail;
private List(final E head, final @Nullable List<E> tail) {
this.head = head;
this.tail = tail;
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final List list = (List) o;
if (head != null ? !head.equals(list.head) : list.head != null) return false;
if (tail != null ? !tail.equals(list.tail) : list.tail != null) return false;
return true;
}
public int hashCode() {
int result = head != null ? head.hashCode() : 0;
result = 31 * result + (tail != null ? tail.hashCode() : 0);
return result;
}
}
public E head() {
if (front == null) {
if (back == null) throw new IllegalStateException("empty key has no head");
List<E> ptr = back;
while (ptr.tail != null) {
ptr = ptr.tail;
}
return ptr.head;
}
return front.head;
}
public GroupKey<E> tail() {
if (front == null) {
if (back == null) throw new IllegalStateException("empty key has no tail");
List<E> reversed = null;
List<E> current = back;
while (current.tail != null) {
reversed = new List<E>(current.head, reversed);
current = current.tail;
}
return new GroupKey<E>(reversed, null);
}
return new GroupKey<E>(front.tail, back);
}
public GroupKey<E> add(E e) {
return new GroupKey<E>(front, new List<E>(e, back));
}
public boolean isEmpty() {
return front == null && back == null;
}
public String toString() {
return Lists.newArrayList(this).toString();
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GroupKey groupKey = (GroupKey) o;
if (back != null ? !back.equals(groupKey.back) : groupKey.back != null) return false;
if (front != null ? !front.equals(groupKey.front) : groupKey.front != null) return false;
return true;
}
public int hashCode() {
int result = front != null ? front.hashCode() : 0;
result = 31 * result + (back != null ? back.hashCode() : 0);
return result;
}
}