// Path: src/main/java/producerconsumer/Buffer.java
// public abstract class Buffer<E> {
// 
//     public abstract void putItem(E e);
// 
//     public abstract E popItem();
// 
//     public abstract int getSize();
// 
//     public abstract int getCapacity();
// }
// 
// Path: src/main/java/producerconsumer/Consumer.java
// public interface Consumer<E> {
//     //consume a element of type E
//     void consume(E e);
// }
// 
// Path: src/main/java/producerconsumer/Item.java
// @ThreadSafe
// public final class Item {
//     private final String itemId;
//     private final String itemName;
//     private static final Random random = new Random(System.nanoTime());
//     private static final String ITEM_NAME[] = {
//             "apple",
//             "banana",
//             "tomato",
//             "watermelon"
//     };
// 
//     private Item(String itemId, String itemName) {
//         this.itemId = itemId;
//         this.itemName = itemName;
//     }
// 
//     public static Item generate() {
//         String uuid = UUID.randomUUID().toString();
//         String name = ITEM_NAME[random.nextInt(ITEM_NAME.length)];
//         return new Item(uuid, name);
//     }
// 
//     @Override
//     public String toString() {
//         return MoreObjects.toStringHelper(this)
//                 .add("itemId", itemId)
//                 .add("itemName", itemName)
//                 .toString();
//     }
// 
// }


import net.jcip.annotations.GuardedBy;
import producerconsumer.Buffer;
import producerconsumer.Consumer;
import producerconsumer.Item;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
package producerconsumer.condition;



/**
 * Created by kennylbj on 16/9/10.
 * Consumer implemented by Condition
 */
public class ConditionConsumer implements Consumer<Item>, Runnable {
    @GuardedBy("lock")
    private final Buffer<Item> buffer;
    private final Lock lock;
    private final Condition full;
    private final Condition empty;
    private final Random random = new Random(System.nanoTime());

    public ConditionConsumer(Buffer<Item> buffer, Lock lock, Condition full, Condition empty) {
        this.buffer = buffer;
        this.lock = lock;
        this.full = full;
        this.empty = empty;
    }

    @Override
    public void consume(Item item) {
        try {
            Thread.sleep(random.nextInt(3) * 1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("[Condition] Consumer consumes item " + item.toString()
                + " by thread " + Thread.currentThread().getId());
    }

// Path: src/main/java/producerconsumer/Buffer.java
// public abstract class Buffer<E> {
// 
//     public abstract void putItem(E e);
// 
//     public abstract E popItem();
// 
//     public abstract int getSize();
// 
//     public abstract int getCapacity();
// }
// 
// Path: src/main/java/producerconsumer/Consumer.java
// public interface Consumer<E> {
//     //consume a element of type E
//     void consume(E e);
// }
// 
// Path: src/main/java/producerconsumer/Item.java
// @ThreadSafe
// public final class Item {
//     private final String itemId;
//     private final String itemName;
//     private static final Random random = new Random(System.nanoTime());
//     private static final String ITEM_NAME[] = {
//             "apple",
//             "banana",
//             "tomato",
//             "watermelon"
//     };
// 
//     private Item(String itemId, String itemName) {
//         this.itemId = itemId;
//         this.itemName = itemName;
//     }
// 
//     public static Item generate() {
//         String uuid = UUID.randomUUID().toString();
//         String name = ITEM_NAME[random.nextInt(ITEM_NAME.length)];
//         return new Item(uuid, name);
//     }
// 
//     @Override
//     public String toString() {
//         return MoreObjects.toStringHelper(this)
//                 .add("itemId", itemId)
//                 .add("itemName", itemName)
//                 .toString();
//     }
// 
// }


// Path: src/main/java/producerconsumer/condition/ConditionConsumer.java
import net.jcip.annotations.GuardedBy;
import producerconsumer.Buffer;
import producerconsumer.Consumer;
import producerconsumer.Item;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

package producerconsumer.condition;



/**
 * Created by kennylbj on 16/9/10.
 * Consumer implemented by Condition
 */
public class ConditionConsumer implements Consumer<Item>, Runnable {
    @GuardedBy("lock")
    private final Buffer<Item> buffer;
    private final Lock lock;
    private final Condition full;
    private final Condition empty;
    private final Random random = new Random(System.nanoTime());

    public ConditionConsumer(Buffer<Item> buffer, Lock lock, Condition full, Condition empty) {
        this.buffer = buffer;
        this.lock = lock;
        this.full = full;
        this.empty = empty;
    }

    @Override
    public void consume(Item item) {
        try {
            Thread.sleep(random.nextInt(3) * 1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("[Condition] Consumer consumes item " + item.toString()
                + " by thread " + Thread.currentThread().getId());
    }

    @Override