/** * Copyright 2008 - CommonCrawl Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * **/ package org.commoncrawl.util; /* * Copyright 2010 - CommonCrawl Foundation * * 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. */ import java.util.Iterator; import junit.framework.TestCase; import org.junit.Test; /** * Inheritance based Linked List class * * * @author rana * */ public class IntrusiveList<ClassType extends IntrusiveList.IntrusiveListElement<ClassType>> implements Iterable<ClassType> { private ClassType _head = null; private ClassType _tail = null; private int _itemCount = 0; public IntrusiveList() { } public ClassType getHead() { return (ClassType) _head; } public ClassType getTail() { return (ClassType) _tail; } public int size() { return _itemCount; } public void addHead(ClassType element) { element._prev = null; element._next = _head; if (_head != null) { _head.setPrev(element); } _head = element; if (_tail == null) _tail = _head; ++_itemCount; } public void addTail(ClassType element) { element._next = null; element.setPrev(_tail); if (_tail != null) { _tail.setNext(element); } _tail = element; if (_head == null) { _head = _tail; } ++_itemCount; } public void insertAfter(ClassType elementToInsertAfter, ClassType elementToInsert) { if (elementToInsertAfter == _tail) { _tail = elementToInsert; elementToInsert.setNext(null); } else { elementToInsert.setNext(elementToInsertAfter.getNext()); elementToInsert.getNext().setPrev(elementToInsert); } elementToInsertAfter.setNext(elementToInsert); elementToInsert.setPrev(elementToInsertAfter); ++_itemCount; } public ClassType removeHead() { ClassType elementRemoved = _head; removeElement(elementRemoved); return elementRemoved; } public ClassType removeTail() { ClassType elementRemoved = _tail; removeElement(elementRemoved); return elementRemoved; } public void removeElement(ClassType element) { if (element == _head) { _head = _head.getNext(); if (_head != null) { _head.setPrev(null); } if (_tail == element) { _tail = _head; } } else if (element == _tail) { _tail = _tail.getPrev(); _tail.setNext(null); } else { element.getPrev().setNext(element.getNext()); element.getNext().setPrev(element.getPrev()); } --_itemCount; element._next = null; element._prev = null; } public void removeAll() { ClassType element = _head; while (element != null) { ClassType next = element._next; element._prev = null; element._next = null; element = next; } _head = null; _tail = null; _itemCount = 0; } /** remove all elements starting at target element to a new list **/ public IntrusiveList<ClassType> detach(ClassType targetElement) { IntrusiveList<ClassType> newList = new IntrusiveList<ClassType>(); // set NEW lists' head tail pointers... newList._head = targetElement; newList._tail = this._tail; // adjust THIS list's head / tail pointers ... if (targetElement._prev != null) { this._tail = targetElement._prev; targetElement._prev = null; this._tail._next = null; } else { this._head = null; this._tail = null; } // calculate number of elements in new list... int newListItemCount = 0; ClassType element = targetElement; while (element != null) { ++newListItemCount; element = element._next; } // set NEW list item count ... newList._itemCount = newListItemCount; // and adjust THIS list's item cont this._itemCount -= newListItemCount; return newList; } /** append all elements from passed in list to THIS list **/ public void attach(IntrusiveList<ClassType> listToAppendFrom) { if (listToAppendFrom.size() != 0) { if (this._tail != null) { this._tail._next = listToAppendFrom._head; listToAppendFrom._head._prev = this._tail; } else { this._head = listToAppendFrom._head; } this._tail = listToAppendFrom._tail; this._itemCount += listToAppendFrom._itemCount; } listToAppendFrom._head = null; listToAppendFrom._tail = null; listToAppendFrom._itemCount = 0; } public static class IntrusiveListElement<ClassType> { ClassType _prev = null; ClassType _next = null; public IntrusiveListElement() { } public ClassType getPrev() { return _prev; } public ClassType getNext() { return _next; } public void setPrev(ClassType prev) { _prev = prev; } public void setNext(ClassType next) { _next = next; } ClassType getObject() { return (ClassType) this; } } private class IntrusiveListIterator<ClassType extends IntrusiveList.IntrusiveListElement<ClassType>> implements Iterator<ClassType> { private IntrusiveList<ClassType> _list = null; private ClassType _current = null; private ClassType _next = null; public IntrusiveListIterator() { _current = null; _next = (ClassType) _head; } public boolean hasNext() { return _next != null; } public ClassType next() { _current = _next; if (_next != null) { _next = _next.getNext(); } return _current; } public void remove() { _list.removeElement(_current); _current = null; } } public Iterator<ClassType> iterator() { return new IntrusiveListIterator<ClassType>(); } private static class UnitTestElement extends IntrusiveList.IntrusiveListElement<UnitTestElement> { private int _id; public UnitTestElement(int id) { _id = id; } public int getId() { return _id; } @Override public boolean equals(Object obj) { if (obj instanceof UnitTestElement) { return (_id == ((UnitTestElement) obj)._id); } return false; } } public static class LinkedListTest extends TestCase { private IntrusiveList<UnitTestElement> empty; private IntrusiveList<UnitTestElement> one; private IntrusiveList<UnitTestElement> several; public LinkedListTest(String s) { super(s); } public void setUp() { empty = new IntrusiveList<UnitTestElement>(); one = new IntrusiveList<UnitTestElement>(); one.addHead(new UnitTestElement(0)); several = new IntrusiveList<UnitTestElement>(); several.addHead(new UnitTestElement(2)); several.addHead(new UnitTestElement(1)); several.addHead(new UnitTestElement(0)); } public void testGetHead() { assertEquals("Check 0", new UnitTestElement(0), one.getHead()); assertEquals("Check 0", new UnitTestElement(0), several.getHead()); assertEquals("Check 2", new UnitTestElement(2), several.getTail()); assertEquals("Check 1", new UnitTestElement(1), several.getTail() .getPrev()); } public void testAddRemoveAdd() { one.removeTail(); assertTrue("one empty", one.size() == 0); one.addHead(new UnitTestElement(0)); assertEquals("Check remove then add 0", new UnitTestElement(0), one .getHead()); } public void testIterator() { int counter = 0; for (Iterator iterator = empty.iterator(); iterator.hasNext();) { fail("Iterating empty list and found element"); } counter = 0; for (Iterator iterator = several.iterator(); iterator.hasNext();) { assertEquals("Check several iteration", new UnitTestElement(counter++), (UnitTestElement) iterator.next()); } } } @Test public void classUnitTest() throws Exception { LinkedListTest test = new LinkedListTest("Test"); test.setUp(); test.testGetHead(); test.testAddRemoveAdd(); test.testIterator(); UnitTestElement element1 = new UnitTestElement(1); UnitTestElement element2 = new UnitTestElement(2); UnitTestElement element3 = new UnitTestElement(3); UnitTestElement element4 = new UnitTestElement(4); UnitTestElement element5 = new UnitTestElement(5); IntrusiveList<UnitTestElement> list = new IntrusiveList<UnitTestElement>(); list.addHead(element1); list.addHead(element2); list.addHead(element3); list.addHead(element4); list.addHead(element5); list.removeHead(); list.removeTail(); for (UnitTestElement element : list) { System.out.println("ID:" + element.getId()); } while (list.getHead() != null) list.removeHead(); for (UnitTestElement element : list) { System.out.println("ID:" + element.getId()); } list.addHead(element1); list.addHead(element2); list.addHead(element3); list.addHead(element4); list.addHead(element5); while (list.getTail() != null) list.removeTail(); list.addHead(element1); list.addHead(element2); list.addHead(element3); list.addHead(element4); list.addHead(element5); IntrusiveList<UnitTestElement> detached = list.detach(list.getHead() .getNext()); System.out.println("Original:"); for (UnitTestElement element : list) { System.out.println("ID:" + element.getId()); } System.out.println("Detached:"); for (UnitTestElement element : detached) { System.out.println("ID:" + element.getId()); } list.attach(detached); System.out.println("Original Post Attach:"); for (UnitTestElement element : list) { System.out.println("ID:" + element.getId()); } System.out.println("Detacehd Post Attach:"); for (UnitTestElement element : detached) { System.out.println("ID:" + element.getId()); } } }