content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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.apache.activemq.broker.region.cursors;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.activemq.broker.region.MessageReference;
  import org.apache.activemq.command.MessageId;
  import org.apache.activemq.management.SizeStatisticImpl;
  
  public class OrderedPendingList implements PendingList {
  
      private PendingNode root = null;
      private PendingNode tail = null;
      private final Map<MessageId, PendingNode> map = new HashMap<MessageId, PendingNode>();
      private final SizeStatisticImpl messageSize;
      private final PendingMessageHelper pendingMessageHelper;
  
      public OrderedPendingList() {
          messageSize = new SizeStatisticImpl("messageSize", "The size in bytes of the pending messages");
          messageSize.setEnabled(true);
          pendingMessageHelper = new PendingMessageHelper(map, messageSize);
      }
  
      @Override
      public PendingNode addMessageFirst(MessageReference message) {
          PendingNode node = new PendingNode(this, message);
          if (root == null) {
              root = node;
              tail = node;
          } else {
              root.linkBefore(node);
              root = node;
          }
          pendingMessageHelper.addToMap(message, node);
          return node;
      }
  
      @Override
      public PendingNode addMessageLast(MessageReference message) {
          PendingNode node = new PendingNode(this, message);
          if (root == null) {
              root = node;
          } else {
              tail.linkAfter(node);
          }
          tail = node;
          pendingMessageHelper.addToMap(message, node);
          return node;
      }
  
      @Override
      public void clear() {
          this.root = null;
          this.tail = null;
          this.map.clear();
          this.messageSize.reset();
      }
  
      @Override
      public boolean isEmpty() {
          return this.map.isEmpty();
      }
  
      @Override
      public Iterator<MessageReference> iterator() {
          return new Iterator<MessageReference>() {
              private PendingNode current = null;
              private PendingNode next = root;
  
              @Override
              public boolean hasNext() {
                  return next != null;
              }
  
              @Override
              public MessageReference next() {
                  MessageReference result = null;
                  this.current = this.next;
                  result = this.current.getMessage();
                  this.next = (PendingNode) this.next.getNext();
                  return result;
              }
  
              @Override
              public void remove() {
                  if (this.current != null && this.current.getMessage() != null) {
                      pendingMessageHelper.removeFromMap(this.current.getMessage());
                  }
                  removeNode(this.current);
              }
          };
      }
  
      @Override
      public PendingNode remove(MessageReference message) {
          PendingNode node = null;
          if (message != null) {
              node = pendingMessageHelper.removeFromMap(message);
              removeNode(node);
          }
          return node;
      }
  
      @Override
      public int size() {
          return this.map.size();
      }
  
      @Override
      public long messageSize() {
          return this.messageSize.getTotalSize();
      }
  
      void removeNode(PendingNode node) {
          if (node != null) {
              pendingMessageHelper.removeFromMap(node.getMessage());
              if (root == node) {
                  root = (PendingNode) node.getNext();
              }
              if (tail == node) {
                  tail = (PendingNode) node.getPrevious();
              }
              node.unlink();
          }
      }
  
      List<PendingNode> getAsList() {
          List<PendingNode> result = new ArrayList<PendingNode>(size());
          PendingNode node = root;
          while (node != null) {
              result.add(node);
              node = (PendingNode) node.getNext();
          }
          return result;
      }
  
      @Override
      public String toString() {
          return "OrderedPendingList(" + System.identityHashCode(this) + ")";
      }
  
      @Override
      public boolean contains(MessageReference message) {
          if (message != null) {
              return this.map.containsKey(message.getMessageId());
          }
          return false;
      }
  
      @Override
      public Collection<MessageReference> values() {
          return getValues(this);
      }
  
      public static Collection<MessageReference> getValues(final PendingList pendingList) {
          List<MessageReference> messageReferences = new ArrayList<MessageReference>();
          Iterator<MessageReference> iterator = pendingList.iterator();
          while (iterator.hasNext()) {
              messageReferences.add(iterator.next());
          }
          return messageReferences;
      }
  
      @Override
      public void addAll(PendingList pendingList) {
          if (pendingList != null) {
              for(MessageReference messageReference : pendingList) {
                  addMessageLast(messageReference);
              }
          }
      }
  
      @Override
      public MessageReference get(MessageId messageId) {
          PendingNode node = map.get(messageId);
          if (node != null) {
              return node.getMessage();
          }
          return null;
      }
  
      public void insertAtHead(List<MessageReference> list) {
          if (list != null && !list.isEmpty()) {
              PendingNode newHead = null;
              PendingNode appendNode = null;
              for (MessageReference ref : list) {
                  PendingNode node = new PendingNode(this, ref);
                  pendingMessageHelper.addToMap(ref, node);
                  if (newHead == null) {
                      newHead = node;
                      appendNode = node;
                      continue;
                  }
                  appendNode.linkAfter(node);
                  appendNode = node;
              }
              // insert this new list at root
              if (root == null) {
                  root = newHead;
                  tail = appendNode;
              } else {
                  appendNode.linkAfter(root);
                  root = newHead;
              }
          }
      }
  
  }
  