package org.apache.commons.collections;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

public class SequencedHashMap implements Map, Cloneable, Externalizable {
  private Entry sentinel = createSentinel();
  
  private HashMap entries = new HashMap();
  
  private transient long modCount = 0L;
  
  private static final int KEY = 0;
  
  private static final int VALUE = 1;
  
  private static final int ENTRY = 2;
  
  private static final int REMOVED_MASK = -2147483648;
  
  private static final long serialVersionUID = 3380552487888102930L;
  
  public SequencedHashMap() {}
  
  public SequencedHashMap(int paramInt) {}
  
  public SequencedHashMap(int paramInt, float paramFloat) {}
  
  public SequencedHashMap(Map paramMap) {
    this();
    putAll(paramMap);
  }
  
  public void clear() {
    this.modCount++;
    this.entries.clear();
    this.sentinel.next = this.sentinel;
    this.sentinel.prev = this.sentinel;
  }
  
  public Object clone() throws CloneNotSupportedException {
    SequencedHashMap sequencedHashMap = (SequencedHashMap)super.clone();
    sequencedHashMap.sentinel = createSentinel();
    sequencedHashMap.entries = new HashMap();
    sequencedHashMap.putAll(this);
    return sequencedHashMap;
  }
  
  public boolean containsKey(Object paramObject) {
    return this.entries.containsKey(paramObject);
  }
  
  public boolean containsValue(Object paramObject) {
    if (paramObject == null) {
      for (Entry entry = this.sentinel.next; entry != this.sentinel; entry = entry.next) {
        if (entry.getValue() == null)
          return true; 
      } 
    } else {
      for (Entry entry = this.sentinel.next; entry != this.sentinel; entry = entry.next) {
        if (paramObject.equals(entry.getValue()))
          return true; 
      } 
    } 
    return false;
  }
  
  private static final Entry createSentinel() {
    Entry entry = new Entry(null, null);
    entry.prev = entry;
    entry.next = entry;
    return entry;
  }
  
  public Set entrySet() {
    return new AbstractSet(this) {
        private final SequencedHashMap this$0;
        
        public void clear() {
          this.this$0.clear();
        }
        
        public boolean contains(Object param1Object) {
          return !(findEntry(param1Object) == null);
        }
        
        private SequencedHashMap.Entry findEntry(Object param1Object) {
          if (param1Object == null)
            return null; 
          if (!(param1Object instanceof Map.Entry))
            return null; 
          Map.Entry entry = (Map.Entry)param1Object;
          SequencedHashMap.Entry entry1 = (SequencedHashMap.Entry)this.this$0.entries.get(entry.getKey());
          return (entry1 != null && entry1.equals(entry)) ? entry1 : null;
        }
        
        public boolean isEmpty() {
          return this.this$0.isEmpty();
        }
        
        public Iterator iterator() {
          return new SequencedHashMap.OrderedIterator(this.this$0, 2);
        }
        
        public boolean remove(Object param1Object) {
          SequencedHashMap.Entry entry = findEntry(param1Object);
          return (entry == null) ? false : (!(this.this$0.removeImpl(entry.getKey()) == null));
        }
        
        public int size() {
          return this.this$0.size();
        }
      };
  }
  
  public boolean equals(Object paramObject) {
    return (paramObject == null) ? false : ((paramObject == this) ? true : (!(paramObject instanceof Map) ? false : entrySet().equals(((Map)paramObject).entrySet())));
  }
  
  public Object get(int paramInt) {
    return getEntry(paramInt).getKey();
  }
  
  public Object get(Object paramObject) {
    Entry entry = (Entry)this.entries.get(paramObject);
    return (entry == null) ? null : entry.getValue();
  }
  
  private Map.Entry getEntry(int paramInt) {
    Entry entry = this.sentinel;
    if (paramInt < 0)
      throw new ArrayIndexOutOfBoundsException(String.valueOf(paramInt) + " < 0"); 
    byte b = -1;
    while (b < paramInt - 1 && entry.next != this.sentinel) {
      b++;
      entry = entry.next;
    } 
    if (entry.next == this.sentinel)
      throw new ArrayIndexOutOfBoundsException(String.valueOf(paramInt) + " >= " + (b + 1)); 
    return entry.next;
  }
  
  public Map.Entry getFirst() {
    return isEmpty() ? null : this.sentinel.next;
  }
  
  public Object getFirstKey() {
    return this.sentinel.next.getKey();
  }
  
  public Object getFirstValue() {
    return this.sentinel.next.getValue();
  }
  
  public Map.Entry getLast() {
    return isEmpty() ? null : this.sentinel.prev;
  }
  
  public Object getLastKey() {
    return this.sentinel.prev.getKey();
  }
  
  public Object getLastValue() {
    return this.sentinel.prev.getValue();
  }
  
  public Object getValue(int paramInt) {
    return getEntry(paramInt).getValue();
  }
  
  public int hashCode() {
    return entrySet().hashCode();
  }
  
  public int indexOf(Object paramObject) {
    Entry entry = (Entry)this.entries.get(paramObject);
    byte b = 0;
    while (entry.prev != this.sentinel) {
      b++;
      entry = entry.prev;
    } 
    return b;
  }
  
  private void insertEntry(Entry paramEntry) {
    paramEntry.next = this.sentinel;
    paramEntry.prev = this.sentinel.prev;
    this.sentinel.prev.next = paramEntry;
    this.sentinel.prev = paramEntry;
  }
  
  public boolean isEmpty() {
    return !(this.sentinel.next != this.sentinel);
  }
  
  public Iterator iterator() {
    return keySet().iterator();
  }
  
  public Set keySet() {
    return new AbstractSet(this) {
        private final SequencedHashMap this$0;
        
        public void clear() {
          this.this$0.clear();
        }
        
        public boolean contains(Object param1Object) {
          return this.this$0.containsKey(param1Object);
        }
        
        public boolean isEmpty() {
          return this.this$0.isEmpty();
        }
        
        public Iterator iterator() {
          return new SequencedHashMap.OrderedIterator(this.this$0, 0);
        }
        
        public boolean remove(Object param1Object) {
          SequencedHashMap.Entry entry = this.this$0.removeImpl(param1Object);
          return !(entry == null);
        }
        
        public int size() {
          return this.this$0.size();
        }
      };
  }
  
  public int lastIndexOf(Object paramObject) {
    return indexOf(paramObject);
  }
  
  public Object put(Object paramObject1, Object paramObject2) {
    this.modCount++;
    Object object = null;
    Entry entry = (Entry)this.entries.get(paramObject1);
    if (entry != null) {
      removeEntry(entry);
      object = entry.setValue(paramObject2);
    } else {
      entry = new Entry(paramObject1, paramObject2);
      this.entries.put(paramObject1, entry);
    } 
    insertEntry(entry);
    return object;
  }
  
  public void putAll(Map paramMap) {
    for (Map.Entry entry : paramMap.entrySet())
      put(entry.getKey(), entry.getValue()); 
  }
  
  public void readExternal(ObjectInput paramObjectInput) throws IOException, ClassNotFoundException {
    int i = paramObjectInput.readInt();
    for (byte b = 0; b < i; b++) {
      Object object1 = paramObjectInput.readObject();
      Object object2 = paramObjectInput.readObject();
      put(object1, object2);
    } 
  }
  
  public Object remove(int paramInt) {
    return remove(get(paramInt));
  }
  
  public Object remove(Object paramObject) {
    Entry entry = removeImpl(paramObject);
    return (entry == null) ? null : entry.getValue();
  }
  
  private void removeEntry(Entry paramEntry) {
    paramEntry.next.prev = paramEntry.prev;
    paramEntry.prev.next = paramEntry.next;
  }
  
  private Entry removeImpl(Object paramObject) {
    Entry entry = (Entry)this.entries.remove(paramObject);
    if (entry == null)
      return null; 
    this.modCount++;
    removeEntry(entry);
    return entry;
  }
  
  public List sequence() {
    ArrayList arrayList = new ArrayList(size());
    Iterator iterator = keySet().iterator();
    while (iterator.hasNext())
      arrayList.add(iterator.next()); 
    return Collections.unmodifiableList(arrayList);
  }
  
  public int size() {
    return this.entries.size();
  }
  
  public String toString() {
    StringBuffer stringBuffer = new StringBuffer();
    stringBuffer.append('[');
    for (Entry entry = this.sentinel.next; entry != this.sentinel; entry = entry.next) {
      stringBuffer.append(entry.getKey());
      stringBuffer.append('=');
      stringBuffer.append(entry.getValue());
      if (entry.next != this.sentinel)
        stringBuffer.append(','); 
    } 
    stringBuffer.append(']');
    return stringBuffer.toString();
  }
  
  public Collection values() {
    return new AbstractCollection(this) {
        private final SequencedHashMap this$0;
        
        public void clear() {
          this.this$0.clear();
        }
        
        public boolean contains(Object param1Object) {
          return this.this$0.containsValue(param1Object);
        }
        
        public boolean isEmpty() {
          return this.this$0.isEmpty();
        }
        
        public Iterator iterator() {
          return new SequencedHashMap.OrderedIterator(this.this$0, 1);
        }
        
        public boolean remove(Object param1Object) {
          if (param1Object == null) {
            for (SequencedHashMap.Entry entry = this.this$0.sentinel.next; entry != this.this$0.sentinel; entry = entry.next) {
              if (entry.getValue() == null) {
                this.this$0.removeImpl(entry.getKey());
                return true;
              } 
            } 
          } else {
            for (SequencedHashMap.Entry entry = this.this$0.sentinel.next; entry != this.this$0.sentinel; entry = entry.next) {
              if (param1Object.equals(entry.getValue())) {
                this.this$0.removeImpl(entry.getKey());
                return true;
              } 
            } 
          } 
          return false;
        }
        
        public int size() {
          return this.this$0.size();
        }
      };
  }
  
  public void writeExternal(ObjectOutput paramObjectOutput) throws IOException {
    paramObjectOutput.writeInt(size());
    for (Entry entry = this.sentinel.next; entry != this.sentinel; entry = entry.next) {
      paramObjectOutput.writeObject(entry.getKey());
      paramObjectOutput.writeObject(entry.getValue());
    } 
  }
  
  private static class Entry implements Map.Entry {
    private final Object key;
    
    private Object value;
    
    Entry next = null;
    
    Entry prev = null;
    
    public Entry(Object param1Object1, Object param1Object2) {
      this.key = param1Object1;
      this.value = param1Object2;
    }
    
    public boolean equals(Object param1Object) {
      if (param1Object == null)
        return false; 
      if (param1Object == this)
        return true; 
      if (!(param1Object instanceof Map.Entry))
        return false; 
      Map.Entry entry = (Map.Entry)param1Object;
      return !(!((getKey() == null) ? (entry.getKey() == null) : getKey().equals(entry.getKey())) || !((getValue() == null) ? (entry.getValue() == null) : getValue().equals(entry.getValue())));
    }
    
    public Object getKey() {
      return this.key;
    }
    
    public Object getValue() {
      return this.value;
    }
    
    public int hashCode() {
      return ((getKey() == null) ? 0 : getKey().hashCode()) ^ ((getValue() == null) ? 0 : getValue().hashCode());
    }
    
    public Object setValue(Object param1Object) {
      Object object = this.value;
      this.value = param1Object;
      return object;
    }
    
    public String toString() {
      return "[" + getKey() + "=" + getValue() + "]";
    }
  }
  
  private class OrderedIterator implements Iterator {
    private final SequencedHashMap this$0;
    
    private int returnType;
    
    private SequencedHashMap.Entry pos;
    
    private transient long expectedModCount;
    
    public OrderedIterator(SequencedHashMap this$0, int param1Int) {
      this.this$0 = this$0;
      this.pos = this.this$0.sentinel;
      this.expectedModCount = this.this$0.modCount;
      this.returnType = param1Int | Integer.MIN_VALUE;
    }
    
    public boolean hasNext() {
      return !(this.pos.next == this.this$0.sentinel);
    }
    
    public Object next() {
      if (this.this$0.modCount != this.expectedModCount)
        throw new ConcurrentModificationException(); 
      if (this.pos.next == this.this$0.sentinel)
        throw new NoSuchElementException(); 
      this.returnType &= Integer.MAX_VALUE;
      this.pos = this.pos.next;
      switch (this.returnType) {
        case 0:
          return this.pos.getKey();
        case 1:
          return this.pos.getValue();
        case 2:
          return this.pos;
      } 
      throw new Error("bad iterator type: " + this.returnType);
    }
    
    public void remove() {
      if ((this.returnType & Integer.MIN_VALUE) != 0)
        throw new IllegalStateException("remove() must follow next()"); 
      if (this.this$0.modCount != this.expectedModCount)
        throw new ConcurrentModificationException(); 
      this.this$0.removeImpl(this.pos.getKey());
      this.expectedModCount++;
      this.returnType |= Integer.MIN_VALUE;
    }
  }
}
