307 lines
9.6 KiB
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
|
|
);
|
|
}
|
|
}
|
|
|