/* * Copyright 2004-2015 the Seasar Foundation and the Others. * * 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 org.seasar.framework.util; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.NoSuchElementException; /** * Seasar2用の連結リストです。 * * @author higa * */ public class SLinkedList implements Cloneable, Externalizable { static final long serialVersionUID = 1L; private transient Entry header = new Entry(null, null, null); private transient int size = 0; /** * {@link SLinkedList}を作成します。 */ public SLinkedList() { header._next = header._previous = header; } /** * 最初のエントリを返します。 * * @return 最初のエントリ */ public Entry getFirstEntry() { if (isEmpty()) { return null; } return header._next; } /** * 最初の要素を返します。 * * @return 最初の要素 */ public Object getFirst() { if (isEmpty()) { throw new NoSuchElementException(); } return getFirstEntry()._element; } /** * 最後のエントリを返します。 * * @return 最後のエントリ */ public Entry getLastEntry() { if (isEmpty()) { return null; } return header._previous; } /** * 最後の要素を返します。 * * @return 最後の要素 */ public Object getLast() { if (isEmpty()) { throw new NoSuchElementException(); } return getLastEntry()._element; } /** * 最初の要素を削除します。 * * @return 最初の要素 */ public Object removeFirst() { if (isEmpty()) { throw new NoSuchElementException(); } Object first = header._next._element; header._next.remove(); return first; } /** * 最後の要素を削除します。 * * @return 最後の要素 */ public Object removeLast() { if (isEmpty()) { throw new NoSuchElementException(); } Object last = header._previous._element; header._previous.remove(); return last; } /** * 先頭に追加します。 * * @param o * 追加するオブジェクト */ public void addFirst(final Object o) { header._next.addBefore(o); } /** * 最後に追加します。 * * @param o * 追加するオブジェクト */ public void addLast(final Object o) { header.addBefore(o); } /** * 指定した位置にオブジェクトを追加します。 * * @param index * 位置 * @param element * 要素 */ public void add(final int index, final Object element) { getEntry(index).addBefore(element); } /** * 要素の数を返します。 * * @return 要素の数 */ public int size() { return size; } /** * 空かどうかを返します。 * * @return 空かどうか */ public boolean isEmpty() { return size == 0; } /** * 要素が含まれているかどうかを返します。 * * @param o * 要素 * @return 要素が含まれているかどうか */ public boolean contains(final Object o) { return indexOf(o) != -1; } /** * 要素を削除します。 * * @param o * @return 削除されたかどうか */ public boolean remove(final Object o) { if (o == null) { for (Entry e = header._next; e != header; e = e._next) { if (e._element == null) { e.remove(); return true; } } } else { for (Entry e = header._next; e != header; e = e._next) { if (o.equals(e._element)) { e.remove(); return true; } } } return false; } /** * 指定した位置の要素を削除します。 * * @param index * 位置 * @return 削除された要素 */ public Object remove(final int index) { Entry e = getEntry(index); e.remove(); return e._element; } /** * 要素を空にします。 */ public void clear() { header._next = header._previous = header; size = 0; } /** * エントリを返します。 * * @param index * @return エントリ */ public Entry getEntry(final int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); } Entry e = header; if (index < size / 2) { for (int i = 0; i <= index; i++) { e = e._next; } } else { for (int i = size; i > index; i--) { e = e._previous; } } return e; } /** * 要素を返します。 * * @param index * 位置 * @return 要素 */ public Object get(final int index) { return getEntry(index)._element; } /** * 要素を設定します。 * * @param index * @param element * @return 元の要素 */ public Object set(final int index, final Object element) { Entry e = getEntry(index); Object oldVal = e._element; e._element = element; return oldVal; } /** * 位置を返します。 * * @param o * 要素 * @return 位置 */ public int indexOf(final Object o) { int index = 0; if (o == null) { for (Entry e = header._next; e != header; e = e._next) { if (e._element == null) { return index; } index++; } } else { for (Entry e = header._next; e != header; e = e._next) { if (o.equals(e._element)) { return index; } index++; } } return -1; } public void writeExternal(final ObjectOutput s) throws IOException { s.writeInt(size); for (Entry e = header._next; e != header; e = e._next) { s.writeObject(e._element); } } public void readExternal(ObjectInput s) throws IOException, ClassNotFoundException { int size = s.readInt(); header = new Entry(null, null, null); header._next = header._previous = header; for (int i = 0; i < size; i++) { addLast(s.readObject()); } } public Object clone() { SLinkedList copy = new SLinkedList(); for (Entry e = header._next; e != header; e = e._next) { copy.addLast(e._element); } return copy; } /** * 配列に変換します。 * * @return 配列 */ public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Entry e = header._next; e != header; e = e._next) { result[i++] = e._element; } return result; } /** * 要素を格納するエントリです。 * */ public class Entry { private Object _element; private Entry _next; private Entry _previous; Entry(final Object element, final Entry next, final Entry previous) { _element = element; _next = next; _previous = previous; } /** * 要素を返します。 * * @return 要素 */ public Object getElement() { return _element; } /** * 次のエントリを返します。 * * @return 次のエントリ */ public Entry getNext() { if (_next != SLinkedList.this.header) { return _next; } return null; } /** * 前のエントリを返します。 * * @return 前のエントリ */ public Entry getPrevious() { if (_previous != SLinkedList.this.header) { return _previous; } return null; } /** * 要素を削除します。 */ public void remove() { _previous._next = _next; _next._previous = _previous; SLinkedList.this.size--; } /** * 前に追加します。 * * @param o * 要素 * @return 追加されたエントリ */ public Entry addBefore(final Object o) { Entry newEntry = new Entry(o, this, _previous); _previous._next = newEntry; _previous = newEntry; SLinkedList.this.size++; return newEntry; } } }