/* Copyright (c) 2011-2022 Reliancy LLC Licensed under the GNU LESSER GENERAL PUBLIC LICENSE Version 3. You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html. You may not use this file except in compliance with the License. */ package com.reliancy.dbo; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.ListIterator; import java.util.Observable; /** * Observable collection wrapper for monitoring data modifications. * *

A {@code Bag} is a {@link Collection} implementation that extends {@link Observable} * to notify observers of add/remove operations. It's designed as a holder for result sets * and provides a foundation for creating virtual collections backed by external data sources. * *

Features:

* * *

Event Types:

* * *

Usage Example:

*
{@code
 * Bag people = new Bag<>();
 * 
 * // Add observer
 * people.addObserver((observable, event) -> {
 *     BagChanged change = (BagChanged) event;
 *     switch (change.getOperation()) {
 *         case BagChanged.ADD:
 *             System.out.println("Added: " + Arrays.toString(change.getArguments()));
 *             break;
 *         case BagChanged.REMOVE:
 *             System.out.println("Removed: " + Arrays.toString(change.getArguments()));
 *             break;
 *     }
 * });
 * 
 * // Modifications trigger events
 * people.add(person1);     // Observer notified
 * people.remove(person1);  // Observer notified
 * }
* *

Fluent API:

*

The {@link #append(Object)} method provides a chainable alternative to {@link #add(Object)}: *

{@code
 * Bag names = new Bag<>()
 *     .append("Alice")
 *     .append("Bob")
 *     .append("Charlie");
 * }
* *

Construction:

*
{@code
 * // Empty bag
 * Bag bag1 = new Bag<>();
 * 
 * // From iterable
 * Bag bag2 = new Bag<>(someList);
 * 
 * // From iterator
 * Bag bag3 = new Bag<>(resultIterator);
 * }
* *

Extension for Virtual Collections:

*

Subclasses can override methods to implement lazy loading or database-backed storage: *

{@code
 * public class LazyBag extends Bag {
 *     @Override
 *     public Iterator iterator() {
 *         notifyObservers(new BagChanged<>(this, BagChanged.ACCESS));
 *         // Load from database on first access
 *         return super.iterator();
 *     }
 * }
 * }
* * @param the type of elements in this bag * @see Observable * @see Collection * @see BagChanged */ public class Bag extends Observable implements Collection{ /** event to send to observers. */ public static final class BagChanged{ public static final int ADD=0; public static final int REMOVE=1; public static final int ACCESS=2; public static final int POST_LOAD=3; public static final int PRE_SAVE=4; final Bag bag; final int operation; final Object[] arguments; public BagChanged(Bag p,int op,Object ... args){ bag=p; operation=op; arguments=args; } public Bag getBag() { return bag; } public int getOperation() { return operation; } public Object[] getArguments() { return arguments; } } final ArrayList items=new ArrayList<>(); public Bag(){ } public Bag(Iterable o){ this(o.iterator()); } public Bag(Iterator o){ while(o.hasNext()) add(o.next()); } @Override public int size() { return items.size(); } @Override public boolean isEmpty() { return size()==0; } @Override public boolean contains(Object o) { final Iterator it=iterator(); while(it.hasNext()){ final E e=it.next(); if(e!=null && o!=null && e.equals(o)) return true; else if(e==o) return true; } return false; } @Override public boolean containsAll(Collection c) { for (Object e : c) if (!contains(e)) return false; return true; } public ListIterator listIterator(){ return listIterator(0); } public ListIterator listIterator(int offset){ return items.listIterator(offset); } @Override public Iterator iterator() { return items.iterator(); } @Override public Object[] toArray() { return toArray(new Object[size()]); } @Override public T[] toArray(T[] a) { return items.toArray(a); } @Override public boolean add(E e) { if(items.contains(e)) return true; if(countObservers()>0){ BagChanged evt=new Bag.BagChanged<>(this,BagChanged.ADD,e); setChanged(); notifyObservers(evt); } return items.add(e); } public Bag append(E e){ add(e); return this; } @Override public boolean remove(Object o) { if(!contains(o)) return false; if(countObservers()>0){ BagChanged evt=new Bag.BagChanged<>(this,BagChanged.REMOVE,o); setChanged(); notifyObservers(evt); } return items.remove(o); } @Override public boolean addAll(Collection c) { if(countObservers()>0){ BagChanged evt=new Bag.BagChanged<>(this,BagChanged.ADD,c.toArray()); setChanged(); notifyObservers(evt); } if(c==null || c.size()==0) return false; c.forEach(e->{this.append(e);}); return true; } @Override public boolean removeAll(Collection c) { if(countObservers()>0){ BagChanged evt=new Bag.BagChanged<>(this,BagChanged.REMOVE,c!=null?c.toArray():null); setChanged(); notifyObservers(evt); } if(c!=null){ return items.removeAll(c); }else{ items.clear(); return true; } } @Override public boolean retainAll(Collection c) { return items.retainAll(c); } @Override public void clear() { removeAll(null); } }