/*
* Copyright 2008 (C) Tom Parker <thpr@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package pcgen.rules.persistence.util;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import pcgen.rules.persistence.token.CDOMSecondaryToken;
/**
* An Iterator that increments across the CDOMSecondaryTokens for a given Parent
* Token name and within a given TokenFamily. This is primarily used when items
* are "unparsed" (converted to LST), since it is necessary to increment across
* ALL tokens in order to be able to ensure that the object is fully written to
* a persistent format.
*
* Note that this Iterator "corrects" for the fact that parent classes can also
* be used, meaning if an LST Token is registered against CDOMObject.class it
* can be used on Race.class or Ability.class, etc.
*
* @param <C>
* The Class of object for which this TokenFamilySubIterator is
* iterating over tokens (will be the class of the object being
* loaded, such as Race or Ability)
*/
public class TokenFamilySubIterator<C> implements
Iterator<CDOMSecondaryToken<? super C>>
{
/**
* The Object class, "cached" due to common use
*/
private static final Class<Object> OBJECT_CLASS = Object.class;
/**
* The next token to be returned by this TokenFamilySubIterator (Note: is only
* validly containing the next token when needNewToken is false)
*/
private CDOMSecondaryToken<? super C> nextToken = null;
/**
* True if nextToken has been used and needs to be refreshed
*/
private boolean needNewToken = true;
/**
* The "acting" class for which tokens are being retrieved. This can be
* either the class being loaded (e.g. Race) or a parent Class (e.g.
* CDOMObject).
*/
private Class<? super C> actingClass;
/**
* The name of the parent token for which the CDOMSecondaryTokens are being
* returned.
*/
private final String parentToken;
/**
* The underlying Iterator that is incrementing across the
* CDOMSecondaryTokens in the actingClass.
*/
private Iterator<CDOMSecondaryToken<? super C>> subIterator;
/**
* The token names that have been used. This is necessary since a specific
* class (e.g. Race) can have a "local" version of a token. If that local
* version exists, then we MUST skip the version that is "more global" when
* we do this Iterator, otherwise this Iterator will provide tokens that are
* designed to be unreachable (And thus will produce errors)
*/
private final Set<String> used = new HashSet<>();
/**
* Constructs a new TokenFamilySubIterator for the given Class and parent
* token name.
*
* @param cl
* The Class for which this TokenFamilySubIterator will return
* CDOMSecondaryTokens.
* @param parentName
* The name of the parent token for which the CDOMSecondaryTokens
* will be returned
*/
public TokenFamilySubIterator(Class<C> cl, String parentName)
{
actingClass = cl;
parentToken = parentName;
subIterator = TokenFamily.CURRENT.getSubTokens(cl, parentToken)
.iterator();
}
/**
* Returns the next CDOMSecondaryToken
*
* @see java.util.Iterator#next()
*/
@Override
public CDOMSecondaryToken<? super C> next()
{
setNext();
if (nextToken == null)
{
throw new NoSuchElementException();
}
needNewToken = true;
return nextToken;
}
/**
* Sets nextToken to be "valid", in the sense of determining the next token
* that this TokenFamilySubIterator needs to return.
*/
private void setNext()
{
if (needNewToken)
{
nextToken = getNext();
if (nextToken != null)
{
String tokenName = nextToken.getTokenName();
if (used.contains(tokenName))
{
/*
* Don't use a super-class token in write
*/
needNewToken = true;
setNext();
}
else
{
used.add(nextToken.getTokenName());
}
}
}
}
/**
* Returns the next eligible CDOMSecondaryToken.
*
* Note the eligible token may not be valid for use if it is a token for a
* parent class and there was a more specific token already defined.
*
* @return The next eligible CDOMSecondaryToken
*/
private CDOMSecondaryToken<? super C> getNext()
{
needNewToken = false;
while (subIterator.hasNext())
{
CDOMSecondaryToken<? super C> tok = subIterator.next();
return tok;
}
if (OBJECT_CLASS.equals(actingClass))
{
return null;
}
actingClass = actingClass.getSuperclass();
Set<CDOMSecondaryToken<? super C>> st =
TokenFamily.CURRENT.getSubTokens(actingClass, parentToken);
subIterator = st.iterator();
return getNext();
}
/**
* @see java.util.Iterator#hasNext()
*/
@Override
public boolean hasNext()
{
setNext();
return nextToken != null;
}
/**
* Unsupported
*
* @see java.util.Iterator#remove()
*/
@Override
public void remove()
{
throw new UnsupportedOperationException(
"Iterator does not support remove");
}
}