001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.util;
018
019/**
020 * Provides a base class for you to extend when you want object to maintain a
021 * doubly linked list to other objects without using a collection class.
022 * 
023 * @author chirino
024 */
025public class LinkedNode {
026
027    protected LinkedNode next = this;
028    protected LinkedNode prev = this;
029    protected boolean tail = true;
030
031    public LinkedNode getHeadNode() {
032        if (isHeadNode()) {
033            return this;
034        }
035        if (isTailNode()) {
036            return next;
037        }
038        LinkedNode rc = prev;
039        while (!rc.isHeadNode()) {
040            rc = rc.prev;
041        }
042        return rc;
043    }
044
045    public LinkedNode getTailNode() {
046        if (isTailNode()) {
047            return this;
048        }
049        if (isHeadNode()) {
050            return prev;
051        }
052        LinkedNode rc = next;
053        while (!rc.isTailNode()) {
054            rc = rc.next;
055        }
056        return rc;
057    }
058
059    public LinkedNode getNext() {
060        return tail ? null : next;
061    }
062
063    public LinkedNode getPrevious() {
064        return prev.tail ? null : prev;
065    }
066
067    public boolean isHeadNode() {
068        return prev.isTailNode();
069    }
070
071    public boolean isTailNode() {
072        return tail;
073    }
074
075    /**
076     * @param rightHead the node to link after this node.
077     * @return this
078     */
079    public LinkedNode linkAfter(LinkedNode rightHead) {
080
081        if (rightHead == this) {
082            throw new IllegalArgumentException("You cannot link to yourself");
083        }
084        if (!rightHead.isHeadNode()) {
085            throw new IllegalArgumentException("You only insert nodes that are the first in a list");
086        }
087
088        LinkedNode rightTail = rightHead.prev;
089
090        if (tail) {
091            tail = false;
092        } else {
093            rightTail.tail = false;
094        }
095
096        rightHead.prev = this; // link the head of the right side.
097        rightTail.next = next; // link the tail of the right side
098        next.prev = rightTail; // link the head of the left side
099        next = rightHead; // link the tail of the left side.
100
101        return this;
102    }
103
104    /**
105     * @param leftHead the node to link after this node.
106     * @return
107     * @return this
108     */
109    public LinkedNode linkBefore(LinkedNode leftHead) {
110
111        if (leftHead == this) {
112            throw new IllegalArgumentException("You cannot link to yourself");
113        }
114        if (!leftHead.isHeadNode()) {
115            throw new IllegalArgumentException("You only insert nodes that are the first in a list");
116        }
117
118        // The left side is no longer going to be a tail..
119        LinkedNode leftTail = leftHead.prev;
120        leftTail.tail = false;
121
122        leftTail.next = this; // link the tail of the left side.
123        leftHead.prev = prev; // link the head of the left side.
124        prev.next = leftHead; // link the tail of the right side.
125        prev = leftTail; // link the head of the right side.
126
127        return leftHead;
128    }
129
130    /**
131     * Removes this node out of the linked list it is chained in.
132     */
133    public void unlink() {
134        // If we are allready unlinked...
135        if (prev == this) {
136            reset();
137            return;
138        }
139
140        if (tail) {
141            prev.tail = true;
142        }
143
144        // Update the peers links..
145        next.prev = prev;
146        prev.next = next;
147
148        // Update our links..
149        reset();
150    }
151    
152    public void reset() {
153        next = this;
154        prev = this;
155        tail = true;
156    }
157
158}