Files
bstore-j/src/main/java/com/reliancy/rec/Slots.java

307 lines
9.6 KiB
Java

/*
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.rec;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import com.reliancy.util.Arrays;
/**
* Iterator and Iterable for traversing {@link Slot} collections.
*
* <p>This class provides a unified interface for iterating over slots,
* supporting both traditional iteration and Java 8+ Stream operations.
*
* Each header is iterated using local scope so you need to explictly specify an array in cases of inheritance.
*
* <h2>Type Parameters:</h2>
* <ul>
* <li>{@code S} - The slot type (must extend {@link Slot})</li>
* <li>{@code H} - The header type (must extend {@link Hdr})</li>
* </ul>
*
* <h2>Usage:</h2>
* <pre>{@code
* // For basic slots
* Slots<Slot, Hdr> slots = new Slots<>(header);
*
* // For fields (Field extends Slot, Entity extends Hdr)
* Slots<Field, Entity> fields = new Slots<>(entity);
*
* // Traditional iteration
* for (Slot slot : slots) {
* System.out.println(slot.getName());
* }
*
* // Stream operations
* fields.stream()
* .filter(f -> f.getName().startsWith("user_"))
* .forEach(f -> System.out.println(f.getName()));
* }</pre>
*
* <h2>Utility Methods:</h2>
* <p>The {@link #current()}, {@link #currentHeader()}, and {@link #currentIndex()} methods
* provide context about the current iteration state. Each refers to the element returned
* by the most recent call to {@link #next()} (or {@code null}/-1 before any call).
* These are useful for inspection or conditional logic during iteration.
*
* @param <S> The slot type extending {@link Slot}
* @param <H> The header type extending {@link Hdr}
* @see Slot
* @see Hdr
* @see Iterable
* @see Iterator
* @see Stream
*/
public class Slots<S extends Slot> implements Iterator<S>, Iterable<S> {
public static Slots<Slot> of(Hdr... headers){
return new Slots<>(headers);
}
public static interface Selector{
boolean select(Slot s);
}
public static final Selector SELECT_ALL=new Selector(){
public boolean select(Slot slot){
return true;
}
};
public static class SELECT_INCLUDING implements Selector{
int flags;
public SELECT_INCLUDING(int flags){
this.flags=flags;
}
public boolean select(Slot slot){
return (slot.getFlags() & flags) != 0;
}
}
public static class SELECT_EXCLUDING implements Selector{
int flags;
public SELECT_EXCLUDING(int flags){
this.flags=flags;
}
public boolean select(Slot slot){
return (slot.getFlags() & flags) == 0;
}
}
Hdr[] headers;
Selector selector;
// iterator state
int h_next;
List<Slot> h_slots;
int s_next;
int s_current_index;
S s_current_item;
Hdr s_current_header;
/**
* Creates a new Slots iterator.
*
* @param headers The headers containing slots to iterate over
*/
public Slots(Hdr... headers) {
this.headers=headers;
rewind();
}
public Slots<S> considering(Hdr... headers){
this.headers=headers;
rewind();
return this;
}
public Slots<S> selectBy(Selector selector){
this.selector=selector;
rewind();
return this;
}
public Slots<S> clone(){
Slots<S> cloned =new Slots<>(headers);
if(selector!=null){
cloned.selectBy(selector);
}
return cloned;
}
/**
* Filters fields to include only those with the specified flags set.
*
* @param flags The flag mask to match
* @return This Fields instance for method chaining
*/
public Slots<S> including(int flags) {
selectBy(new Slots.SELECT_INCLUDING(flags));
return this;
}
/**
* Filters fields to exclude those with the specified flags set.
*
* @param flags The flag mask to exclude
* @return This Fields instance for method chaining
*/
public Slots<S> excluding(int flags) {
selectBy(new Slots.SELECT_EXCLUDING(flags));
return this;
}
public Slots<S> rewind(){
h_next=0;
h_slots=headers.length>0?headers[h_next].getOwnSlots():new ArrayList<Slot>();
s_next=-1;
seekNext();
// above seeknext is not consuming so we clear the current state
s_current_index=-1;
s_current_item=null;
s_current_header=(0<=h_next && h_next<headers.length)?headers[h_next]:null;
return this;
}
@SuppressWarnings("unchecked")
protected void seekNext(){
// Iterate through slots in current header, then move to next header if needed
S last_item=(0<=s_next && s_next<h_slots.size())?(S)h_slots.get(s_next):null;
Hdr last_header=(0<=h_next && h_next<headers.length)?headers[h_next]:null;
while(h_next < headers.length){
// Try to find next matching slot in current header
while(s_next + 1 < h_slots.size()){
s_next+=1;
Slot slot = (Slot)h_slots.get(s_next);
if(selector == null || selector.select(slot)){
// we found next item - lock in current values
s_current_item=last_item;
s_current_header=last_header;
s_current_index+=1;
return;
}
}
// Current header's slots exhausted, move to next header
h_next++;
if(h_next < headers.length){
h_slots = headers[h_next].getOwnSlots();
s_next = -1;
}else{
h_slots = new ArrayList<Slot>();
s_next = -1;
// we are done -lock in last item
s_current_item=last_item;
s_current_header=last_header;
s_current_index+=1;
}
}
}
// ========================================================================
// Iterator Implementation
// ========================================================================
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws java.util.NoSuchElementException if the iteration has no more elements
*/
@Override
public S next() {
if(!hasNext()) throw new java.util.NoSuchElementException();
seekNext();
return s_current_item;
}
/**
* Returns {@code true} if the iteration has more elements.
*
* @return {@code true} if the iteration has more elements
*/
@Override
public boolean hasNext() {
return 0<=s_next && s_next < h_slots.size();
}
public S current(){
return s_current_item;
}
public Hdr currentHeader(){
return s_current_header;
}
/**
* Returns the index of the last element returned by {@link #next()}.
*
* <p>Returns -1 if {@link #next()} has not been called yet (i.e., before the first
* call to {@link #next()}). After each call to {@link #next()}, this value is
* incremented to reflect the index of the element that was just returned.
*
* @return the index of the last returned element, or -1 if no elements have been returned yet
*/
public int currentIndex(){
return s_current_index;
}
/**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation).
*
* @throws UnsupportedOperationException if the {@code remove} operation
* is not supported by this iterator
*/
@Override
public void remove() {
throw new UnsupportedOperationException("forward only immutable view");
}
// ========================================================================
// Iterable Implementation
// ========================================================================
/**
* Returns an iterator over elements of type {@code S}.
*
* @return an Iterator
*/
@Override
public Iterator<S> iterator() {
return clone();
}
// ========================================================================
// Stream Support
// ========================================================================
/**
* Returns a sequential {@code Stream} with this collection as its source.
*
* <p>This method allows the slots to be processed using Java 8+ Stream API:
* <pre>{@code
* slots.stream()
* .filter(s -> s.getName().startsWith("user_"))
* .map(Slot::getName)
* .collect(Collectors.toList());
* }</pre>
*
* @return a sequential Stream over the slots
*/
public Stream<S> stream() {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(this, 0),
false
);
}
/**
* Returns a possibly parallel {@code Stream} with this collection as its source.
*
* <p>It is allowable for this method to return a sequential stream.
*
* @return a possibly parallel Stream over the slots
*/
public Stream<S> parallelStream() {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(this, 0),
true
);
}
}