All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jagg.msd.ChainedDiscriminator Maven / Gradle / Ivy

Go to download

jAgg is a Java 5.0 API that supports “group by” operations on Lists of Java objects: aggregate operations such as count, sum, max, min, avg, and many more. It also allows custom aggregate operations.

The newest version!
package net.sf.jagg.msd;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * A ChainedDiscriminator relies on another
 * Discriminator to perform its work in a separate step, after
 * this discriminator's work is done.
 *
 * @author Randy Gettman
 * @since 0.5.0
 */
public abstract class ChainedDiscriminator extends AbstractDiscriminator
{
   private static final boolean DEBUG = false;

   /**
    * Partitions the given List of elements into another
    * List, in which all of the elements from the given list exist
    * in the new list, and all elements that compare equal are adjacent to each
    * other, according to the given Extractor.
    * @param elements A List of elements.
    * @param extractor An Extractor that gives labels for
    *    each element.
    * @param workspace The MsdWorkspace used in the discrimination
    *    process.
    * @return A List of Lists containing all
    *    equivalence classes.  Each equivalence class list contains all elements
    *    that compare equal to each other.  If for any reason, this is unable
    *    to discriminate the List, e.g. the elements are non-
    *    Discriminable objects, then this returns
    *    null.
    */
   @SuppressWarnings({"unchecked","ForLoopReplaceableByForEach"})
   public  List> discriminate(List elements, Extractor extractor, MsdWorkspace workspace)
   {
      if (DEBUG)
         System.err.println(getClass().getName() + " elements: " + Arrays.deepToString(elements.toArray()));

      if (elements.size() == 0)
         return new ArrayList>(0);

      List> curr = new ArrayList>(1);
      curr.add(elements);
      List> equivClasses;
      List> results = new ArrayList>();
      ChainedExtractor/**/ chainedExtractor = getChainedExtractor(elements, extractor);

      // Index loop.
      int index = 0;
      // Cannot get a wildcard for the extractor -- which could possibly
      // be any type -- to line up with the wildcard for the
      // discriminator.  That is, one  cannot equal another
      // .  That's okay, the type might vary from one loop
      // iteration to the next.
      while (!curr.isEmpty())
      {
         Discriminator/**/ discr = getDiscriminator(elements, chainedExtractor, index);
         if (discr == null)
         {
            // Failed to get a Discriminator; can't discriminate.
            return null;
         }

         List> next = new ArrayList>();
         if (DEBUG)
            System.err.println("  index: " + index);

         chainedExtractor.setIndex(index);

         // Loop through each equivalence class to create sub-equivalence
         // classes.
         for (int i = 0; i < curr.size(); i++)
         {
            List currElements = curr.get(i);
            List finished = new ArrayList();
            List remaining = new ArrayList();
            // Take out those elements that are complete according to the given extractor.
            for (int j = 0; j < currElements.size(); j++)
            {
               E element = currElements.get(j);
               if (chainedExtractor.isComplete(element))
                  finished.add(element);
               else
                  remaining.add(element);
            }
            // The discriminator will mark the "complete" property in portionExtractor.
            equivClasses = (List>) discr.discriminate(remaining, chainedExtractor, workspace);
            if (DEBUG)
            {
               System.err.println("    " + getClass().getName() + " equivClasses:");
               for (List equivClass : equivClasses)
                  System.err.println("      " + Arrays.deepToString(equivClass.toArray()));
               if (!finished.isEmpty())
               {
                  System.err.println("      finished:" + Arrays.deepToString(finished.toArray()));
               }
            }

            // Add in finished results in their own equivalence class, if any.
            if (!finished.isEmpty())
               results.add(finished);
            // If the condition says for all of the elements that they all
            // terminated, then they are all equivalent, in one class.
            if (equivClasses.size() == 1 && chainedExtractor.isAllComplete())
            {
               results.add(equivClasses.get(0));
            }
            else
            {
               // Store the sub-equivalence classes.
               for ( int j = 0; j < equivClasses.size(); j++)
               {
                  List equivClass = equivClasses.get(j);
                  // Don't bother with classes of size 1, which won't change in
                  // subsequent loops.
                  if (equivClass.size() > 1)
                  {
                     next.add(equivClass);
                     if (DEBUG)
                        System.err.println("    Sending equivClass to next loop: " + Arrays.deepToString(equivClass.toArray()));
                  }
                  else
                     results.add(equivClass);
               }
            }
         }

         // Prepare for next loop.
         curr = next;
         index++;
      }

      // Add the last iteration in.
      int size = curr.size();
      // Avoid creating an Iterator in a call to "addAll".
      for (int i = 0; i < size; i++)
      {
         results.add(curr.get(i));
      }

      if (DEBUG)
      {
         System.err.println("  " + getClass().getName() + " results: ");
         for (List result : results)
            System.err.println("    " + Arrays.deepToString(result.toArray()));
      }

      return results;
   }

   /**
    * Returns an Extractor that extracts a label of unknown type
    * from a label of type T, using the given
    * Extractor, which supplies labels of type T.
    * For the same index, the returned Extractor must return
    * labels of the same type as the type discriminated by the
    * Discriminator returned by getDiscriminator.
    * @param elements The List of elements.
    * @param extractor The Extractor that extracts a label of type
    *     T from the element.
    * @param  The type of element.
    * @return A ChainedExtractor that extracts a label of unknown
    *    type from a label of type T.
    * @see #getDiscriminator
    */
   protected abstract  ChainedExtractor getChainedExtractor(List elements, Extractor extractor);

   /**
    * Returns a Discriminator that discriminates on an unknown
    * type.  For the same index, the returned Discriminator must
    * discriminate on the same type as the labels that are returned by the
    * ChainedExtractor that is returned by
    * getChainedExtractor.  If it is known that no more loops are
    * necessary, then the returned Discriminator may be
    * null.
    * @param elements The list of elements.
    * @param extractor The ChainedExtractor that was obtained from
    *    getChainedExtractor.
    * @param index The index of the loop.
    * @return A Discriminator, or null if it is known
    *    for sure that no more loops are necessary.
    * @see #getChainedExtractor
    */
   protected abstract  Discriminator getDiscriminator(List elements, ChainedExtractor extractor,
      int index);

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy