/* * ListMatchingReference.java * Copyright James Dempsey, 2011 * * 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 * * Created on 23/05/2011 7:44:55 PM * * $Id$ */ package pcgen.cdom.reference; import java.util.ArrayList; import java.util.Collection; import java.util.List; import pcgen.cdom.base.CDOMObject; import pcgen.cdom.base.CDOMReference; import pcgen.cdom.enumeration.GroupingState; import pcgen.cdom.enumeration.ListKey; /** * A ListMatchingReference is a CDOMReference that matches objects based on a * a ListKey and an expected value. * * An underlying start list is provided during construction of the * ListMatchingReference. Generally, this will be the CDOMAllRef for the class * of object underlying this ListMatchingReference. * * @param <T> * The class of object underlying this ListMatchingReference. * @param <V> * The class of object stored by the List Key used by this * ListMatchingReference. * <br> * * @author James Dempsey <jdempsey@users.sourceforge.net> */ public class ListMatchingReference<T extends CDOMObject, V> extends CDOMReference<T> { /** * The CDOMGroupRef containing the underlying list of objects from which * this ListMatchingReference will draw. */ private final CDOMGroupRef<T> all; private final ListKey<V> key; private final V value; /* * CONSIDER is it necessary/useful to cache the results of the pattern * match? If that is done, under what conditions does the cache need to be * invalidated (how can the underlying CDOMGroupRef be known to not have * been modified)? */ /** * Constructs a new ListMatchingReference * * @param objClass * The Class of the underlying objects contained by this * reference. * @param startingGroup * The underlying list of objects from which this * ListMatchingReference will draw. * @throws IllegalArgumentException * if the starting group is null or the provided pattern does * not end with the PCGen pattern characters */ public ListMatchingReference(String unparse, Class<T> objClass, CDOMGroupRef<T> startingGroup, ListKey<V> targetKey, V expectedValue) { super(objClass, unparse); if (startingGroup == null) { throw new IllegalArgumentException( "Starting Group cannot be null in ListMatchingReference"); } if (targetKey == null) { throw new IllegalArgumentException( "Target Key cannot be null in ListMatchingReference"); } all = startingGroup; key = targetKey; value = expectedValue; } /** * Throws an exception. This method may not be called because a * ListMatchingReference is resolved based on the pattern provided at * construction. * * @param item * ignored * @throws IllegalStateException * because a ListMatchingReference is resolved based on the * key/value pair provided at construction. */ @Override public void addResolution(T item) { throw new IllegalStateException( "Cannot add resolution to ListMatchingReference"); } /** * Returns true if the given Object is included in the Collection of Objects * to which this ListMatchingReference refers. * * Note that the behavior of this class is undefined if the CDOMGroupRef * underlying this ListMatchingReference has not yet been resolved. * * @param item * The object to be tested to see if it is referred to by this * ListMatchingReference. * @return true if the given Object is included in the Collection of Objects * to which this ListMatchingReference refers; false otherwise. */ @Override public boolean contains(T item) { if (!all.contains(item)) { return false; } List<V> actualList = item.getListFor(key); if (actualList != null) { for (V actual : actualList) { if (value != null && value.equals(actual)) { return true; } } } return false; } /** * Returns a Collection containing the Objects to which this * ListMatchingReference refers. * * This method is reference-semantic, meaning that ownership of the * Collection returned by this method is transferred to the calling object. * Modification of the returned Collection should not result in modifying * the ListMatchingReference, and modifying the ListMatchingReference * after the Collection is returned should not modify the Collection. * * Note that the behavior of this class is undefined if the CDOMGroupRef * underlying this ListMatchingReference has not yet been resolved. * * @return A Collection containing the Objects to which this * ListMatchingReference refers. */ @Override public Collection<T> getContainedObjects() { List<T> list = new ArrayList<>(); for (T obj : all.getContainedObjects()) { List<V> actualList = obj.getListFor(key); if (actualList != null) { for (V actual : actualList) { if (value != null && value.equals(actual)) { list.add(obj); continue; } } } } return list; } /** * Returns a representation of this ListMatchingReference, suitable for * storing in an LST file. * * Note that this will return the pattern String provided during * construction of the ListMatchingReference. * * @return A representation of this ListMatchingReference, suitable for * storing in an LST file. * @see pcgen.cdom.base.CDOMReference#getLSTformat(boolean) */ @Override public String getLSTformat(boolean useAny) { return getName(); } /** * Returns the count of the number of objects included in the Collection of * Objects to which this ListMatchingReference refers. * * Note that the behavior of this class is undefined if the CDOMGroupRef * underlying this ListMatchingReference has not yet been resolved. * * @return The count of the number of objects included in the Collection of * Objects to which this ListMatchingReference refers. */ @Override public int getObjectCount() { int count = 0; for (T obj : all.getContainedObjects()) { List<V> actualList = obj.getListFor(key); if (actualList != null) { for (V actual : actualList) { if (value != null && value.equals(actual)) { count++; continue; } } } } return count; } /** * Returns true if this ListMatchingReference is equal to the given * Object. Equality is defined as being another ListMatchingReference * object with equal Class represented by the reference, an equal staring * CDOMGroupRef and an equal pattern. This may or may not be a deep .equals, * depending on the behaviour of the underlying CDOMGroupRef. You should * check the documentation for the .equals(Object) method of that class to * establish the actual behaviour of this method. * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj instanceof ListMatchingReference) { ListMatchingReference<?, ?> other = (ListMatchingReference<?, ?>) obj; if (getReferenceClass().equals(other.getReferenceClass()) && all.equals(other.all) && key.equals(other.key)) { if (value == null) { return other.value == null; } return value.equals(other.value); } } return false; } /** * Returns the consistent-with-equals hashCode for this * ListMatchingReference * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return getReferenceClass().hashCode() ^ key.hashCode() + (value == null ? -1 : value.hashCode()); } /** * Returns the GroupingState for this ListMatchingReference. The * GroupingState indicates how this ListMatchingReference can be combined * with other PrimitiveChoiceFilters. * * @return The GroupingState for this ListMatchingReference. */ @Override public GroupingState getGroupingState() { return GroupingState.ANY; } @Override public String getChoice() { return null; } }