/*
 * 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.commons.pool2.impl;

/**
 * This wrapper is used to track the additional information, such as state, for
 * the pooled objects.
 * <p>
 * This class is intended to be thread-safe.
 *
 * @param <T> the type of object in the pool
 * 
 * @version $Revision: $
 *
 * @since 2.0
 */
public class PooledObject<T> implements Comparable<PooledObject<T>> {

    private final T object;
    private volatile PooledObjectState state = PooledObjectState.IDLE; // @GuardedBy("this") to ensure transitions are valid
    private final long createTime = System.currentTimeMillis();
    private volatile long lastBorrowTime = createTime;
    private volatile long lastReturnTime = createTime;

    public PooledObject(T object) {
        this.object = object;
    }

    /**
     * Obtain the underlying object that is wrapped by this instance of
     * {@link PooledObject}.
     */
    public T getObject() {
        return object;
    }

    /**
     * Obtain the time (using the same basis as
     * {@link System#currentTimeMillis()}) that this object was created.
     */
    public long getCreateTime() {
        return createTime;
    }

    /**
     * Obtain the time in milliseconds that this object last spent in the the
     * active state (it may still be active in which case subsequent calls will
     * return an increased value).
     */
    public long getActiveTimeMillis() {
        // Take copies to avoid threading issues
        long rTime = lastReturnTime;
        long bTime = lastBorrowTime;

        if (rTime > bTime) {
            return rTime - bTime;
        } else {
            return System.currentTimeMillis() - bTime;
        }
    }

    /**
     * Obtain the time in milliseconds that this object last spend in the the
     * idle state (it may still be idle in which case subsequent calls will
     * return an increased value).
     */
    public long getIdleTimeMillis() {
        return System.currentTimeMillis() - lastReturnTime;
    }

    public long getLastBorrowTime() {
        return lastBorrowTime;
    }

    public long getLastReturnTime() {
        return lastReturnTime;
    }

    @Override
    public int compareTo(PooledObject<T> other) {
        final long lastActiveDiff =
                this.getLastReturnTime() - other.getLastReturnTime();
        if (lastActiveDiff == 0) {
            // make sure the natural ordering is consistent with equals
            // see java.lang.Comparable Javadocs
            return System.identityHashCode(this) - System.identityHashCode(other);
        }
        // handle int overflow
        return (int)Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE);
    }

    /**
     * Provides a String form of the wrapper for debug purposes. The format is
     * not fixed and may change at any time.
     */
    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        result.append("Object: ");
        result.append(object.toString());
        result.append(", State: ");
        result.append(state.toString());
        return result.toString();
        // TODO add other attributes
    }

    public synchronized boolean startEvictionTest() {
        if (state == PooledObjectState.IDLE) {
            state = PooledObjectState.MAINTAIN_EVICTION;
            return true;
        }

        return false;
    }

    public synchronized boolean endEvictionTest(
            LinkedBlockingDeque<PooledObject<T>> idleQueue) {
        if (state == PooledObjectState.MAINTAIN_EVICTION) {
            state = PooledObjectState.IDLE;
            return true;
        } else if (state == PooledObjectState.MAINTAIN_EVICTION_RETURN_TO_HEAD) {
            state = PooledObjectState.IDLE;
            if (!idleQueue.offerFirst(this)) {
                // TODO - Should never happen
            }
        }

        return false;
    }

    /**
     * Allocates the object.
     * 
     * @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE} 
     */
    public synchronized boolean allocate() {
        if (state == PooledObjectState.IDLE) {
            state = PooledObjectState.ALLOCATED;
            lastBorrowTime = System.currentTimeMillis();
            return true;
        } else if (state == PooledObjectState.MAINTAIN_EVICTION) {
            // TODO Allocate anyway and ignore eviction test
            state = PooledObjectState.MAINTAIN_EVICTION_RETURN_TO_HEAD;
            return false;
        }
        // TODO if validating and testOnBorrow == true then pre-allocate for
        // performance
        return false;
    }

    /**
     * Deallocates the object and sets it {@link PooledObjectState#IDLE IDLE}
     * if it is currently {@link PooledObjectState#ALLOCATED ALLOCATED}.
     * 
     * @return {@code true} if the state was {@link PooledObjectState#ALLOCATED ALLOCATED}
     */
    public synchronized boolean deallocate() {
        if (state == PooledObjectState.ALLOCATED) {
            state = PooledObjectState.IDLE;
            lastReturnTime = System.currentTimeMillis();
            return true;
        }

        return false;
    }

    /**
     * Sets the state to {@link PooledObjectState#INVALID INVALID}
     */
    public synchronized void invalidate() {
        state = PooledObjectState.INVALID;
    }
}

@@ -1045,18 +1045,17 @@
  pool%0A *
- 
 %0A
+
  * @vers
@@ -2926,24 +2926,357 @@
 ime;%0A    %7D%0A%0A
+    /**%0A     * Orders instances based on idle time - i.e. the length of time since the%0A     * instance was returned to the pool. Used by the GKOP idle object evictor.%0A     * %3Cp%3E%0A     * Note: This class has a natural ordering that is inconsistent with%0A     *       equals if distinct objects have the same identity hash code.%0A     */%0A
     @Overrid
@@ -3483,17 +3483,17 @@
      // 
-m
+M
 ake sure
@@ -3517,16 +3517,24 @@
 ring is 
+broadly 
 consiste
@@ -3548,16 +3548,130 @@
  equals%0A
+            // although this will break down if distinct objects have the same%0A            // identity hash code.%0A
         
@@ -5176,33 +5176,32 @@
 e object.%0A     *
- 
 %0A     * @return 
@@ -5274,17 +5274,16 @@
 LE IDLE%7D
- 
 %0A     */
@@ -6030,23 +6030,22 @@
 CATED%7D.%0A
+
      *
- 
 %0A     * 

Fix POOL-220. Update javadoc for compareTo