// 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/Producer.java
// public interface Producer<E> {
//     //produce a element of type E
//     E produce();
// }
// 
// 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.Producer;
import producerconsumer.Item;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
        this.buffer = buffer;
        this.lock = lock;
        this.full = full;
        this.empty = empty;
    }

    @Override
    public Item produce() {
        try {
            Thread.sleep(random.nextInt(3) * 1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Item item = Item.generate();
        System.out.println("[Condition] Producer produces item " + item.toString()
                + " by thread " + Thread.currentThread().getId());
        return item;
    }

    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                //outside critical section
                Item item = produce();

                lock.lock();
                try {
                    //buffer is full, so wait until no full
                    while (buffer.getSize() == buffer.getCapacity()) {
// 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/Producer.java
// public interface Producer<E> {
//     //produce a element of type E
//     E produce();
// }
// 
// 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/ConditionProducer.java
import net.jcip.annotations.GuardedBy;
import producerconsumer.Buffer;
import producerconsumer.Producer;
import producerconsumer.Item;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

        this.buffer = buffer;
        this.lock = lock;
        this.full = full;
        this.empty = empty;
    }

    @Override
    public Item produce() {
        try {
            Thread.sleep(random.nextInt(3) * 1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Item item = Item.generate();
        System.out.println("[Condition] Producer produces item " + item.toString()
                + " by thread " + Thread.currentThread().getId());
        return item;
    }

    @Override
    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                //outside critical section
                Item item = produce();

                lock.lock();
                try {
                    //buffer is full, so wait until no full
                    while (buffer.getSize() == buffer.getCapacity()) {
                        full.await();