Advertisement
pavadeli

GroovySupport.java

Jan 30th, 2012
880
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5 16.33 KB | None | 0 0
  1. package amis.examples.groovy;
  2.  
  3. import java.math.BigDecimal;
  4.  
  5. import java.util.ArrayList;
  6. import java.util.BitSet;
  7. import java.util.HashSet;
  8. import java.util.List;
  9. import java.util.Set;
  10.  
  11. import oracle.jbo.ExprEval;
  12. import oracle.jbo.Row;
  13. import oracle.jbo.RowIterator;
  14. import oracle.jbo.RowSet;
  15. import oracle.jbo.RowSetIterator;
  16.  
  17.  
  18. /**
  19.  * GroovySupport delivers basic aggregate functions to RowSets and RowIterators using the
  20.  * built-in-ADF Groovy support. To instantiate a GroovySupport object use the <code>get</code>
  21.  * functions.
  22.  *
  23.  * @see #get(RowIterator) GroovySupport.get(RowIterator)
  24.  * @see #get(RowSet) GroovySupport.get(RowSet)
  25.  */
  26. public abstract class GroovySupport {
  27.     /*
  28.      * Some simple aggregate functions
  29.      */
  30.  
  31.     /**
  32.      * Execute the provided <code>groovy</code> expression in the context of each row and return the
  33.      * biggest result (based on the <code>Comparable</code> interface).
  34.      *
  35.      * @param groovy a groovy expression to be executed in the context of a single row
  36.      * @return the biggest value
  37.      */
  38.     public Comparable max(String groovy) {
  39.         return max(groovy, null);
  40.     }
  41.  
  42.     /**
  43.      * Execute the provided <code>groovy</code> expression in the context of each row and return the
  44.      * biggest result (based on the <code>Comparable</code> interface). Only rows on which
  45.      * <code>groovyWhere</code> evaluates to <code>true</code> are taken into account.
  46.      *
  47.      * @param groovy a groovy expression to be executed in the context of a single row
  48.      * @param groovyWhere a (boolean) groovy expression that defines which rows are taken into account
  49.      * @return the biggest value
  50.      */
  51.     public Comparable max(String groovy, String groovyWhere) {
  52.         return aggregate(groovy, groovyWhere, new MaxAggregator()).result;
  53.     }
  54.  
  55.     private static final class MaxAggregator extends Aggregator<Comparable> {
  56.         Comparable result = null;
  57.  
  58.         boolean add(Comparable value) {
  59.             if (result == null || (value != null && value.compareTo(result) > 0)) {
  60.                 result = value;
  61.             }
  62.             return true;
  63.         }
  64.     }
  65.  
  66.     /**
  67.      * Execute the provided <code>groovy</code> expression in the context of each row and return the
  68.      * sum of these results.
  69.      *
  70.      * @param groovy a groovy expression to be executed in the context of a single row
  71.      * @return the sum
  72.      */
  73.     public BigDecimal sum(String groovy) {
  74.         return sum(groovy, null);
  75.     }
  76.  
  77.     /**
  78.      * Execute the provided <code>groovy</code> expression in the context of each row and return the
  79.      * sum of these results. Only rows on which <code>groovyWhere</code> evaluates to <code>true</code>
  80.      * are taken into account.
  81.      *
  82.      * @param groovy a groovy expression to be executed in the context of a single row
  83.      * @param groovyWhere a (boolean) groovy expression that defines which rows are taken into account
  84.      * @return the sum
  85.      */
  86.     public BigDecimal sum(String groovy, String groovyWhere) {
  87.         return aggregate(groovy, groovyWhere, new SumAggregator()).result;
  88.     }
  89.  
  90.     private static final class SumAggregator extends Aggregator<Number> {
  91.         BigDecimal result = null;
  92.  
  93.         boolean add(Number value) {
  94.             // Convert value to BigDecimal, we don't want to lose precision.
  95.             BigDecimal bd =
  96.                 (value == null || value instanceof BigDecimal) ? (BigDecimal)value : new BigDecimal(value.toString());
  97.             if (result == null) {
  98.                 result = bd;
  99.             } else if (bd != null) {
  100.                 result = result.add(bd);
  101.             }
  102.             return true;
  103.         }
  104.     }
  105.  
  106.     /**
  107.      * Returns the number of rows for which <code>groovyWhere</code> evaluates to <code>true</code>.
  108.      *
  109.      * @param groovyWhere a groovy expression to be executed in the context of a single row
  110.      * @return the number of rows for which groovyWhere evaluates to true
  111.      */
  112.     public int count(String groovyWhere) {
  113.         return aggregate(null, groovyWhere, new CountAggregator()).result;
  114.     }
  115.  
  116.     private static final class CountAggregator extends Aggregator<Object> {
  117.         int result = 0;
  118.  
  119.         boolean add(Object value) {
  120.             ++result;
  121.             return true;
  122.         }
  123.     }
  124.  
  125.     /**
  126.      * Returns the number of unique values that the provided <code>groovy</code> expression delivers
  127.      * in the context of individual rows.
  128.      *
  129.      * @param groovy a groovy expression to be executed in the context of a single row
  130.      * @return the number of unique values
  131.      */
  132.     public int distinctCount(String groovy) {
  133.         return distinctCount(groovy, null);
  134.     }
  135.  
  136.     /**
  137.      * Returns the number of unique values that the provided <code>groovy</code> expression delivers
  138.      * in the context of individual rows. Only rows on which <code>groovyWhere</code> evaluates to
  139.      * <code>true</code> are taken into account.
  140.      *
  141.      * @param groovy a groovy expression to be executed in the context of a single row
  142.      * @param groovyWhere a (boolean) groovy expression that defines which rows are taken into account
  143.      * @return the number of unique values
  144.      */
  145.     public int distinctCount(String groovy, String groovyWhere) {
  146.         return aggregate(groovy, groovyWhere, new DistinctValuesAggregator()).resultSet.size();
  147.     }
  148.  
  149.     private static final class DistinctValuesAggregator extends Aggregator<Object> {
  150.         Set<Object> resultSet = new HashSet<Object>();
  151.  
  152.         boolean add(Object value) {
  153.             resultSet.add(value);
  154.             return true;
  155.         }
  156.     }
  157.  
  158.     /**
  159.      * Returns a row for which <code>groovyWhere</code> evaluates to <code>true</code>, if found.
  160.      *
  161.      * @param groovyWhere a (boolean) groovy expression that defines when a row qualifies
  162.      * @return a row for which groovyWhere evaluates to true or null
  163.      */
  164.     public Row find(String groovyWhere) {
  165.         return aggregate("adf.object", groovyWhere, new FindAggregator<Row>()).result;
  166.     }
  167.  
  168.     /**
  169.      * Returns the value of the <code>groovy</code> expression in the context of a row for which
  170.      * <code>groovyWhere</code> evaluates to <code>true</code>, if found.
  171.      *
  172.      * @param groovy a groovy expression to be executed in the context of a single row
  173.      * @param groovyWhere a (boolean) groovy expression that defines when a row qualifies
  174.      * @return the value of the groovy expression in the context of a row for which groovyWhere
  175.      * evaluates to true
  176.      */
  177.     public Object find(String groovy, String groovyWhere) {
  178.         return aggregate(groovy, groovyWhere, new FindAggregator<Object>()).result;
  179.     }
  180.  
  181.     private static final class FindAggregator<T> extends Aggregator<T> {
  182.         T result = null;
  183.  
  184.         boolean add(T value) {
  185.             result = value;
  186.             return false;
  187.         }
  188.     }
  189.  
  190.     /**
  191.      * Returns all rows for which <code>groovyWhere</code> evaluates to <code>true</code>.
  192.      *
  193.      * @param groovyWhere a (boolean) groovy expression that defines when a row qualifies
  194.      * @return all rows for which groovyWhere evaluates to true
  195.      */
  196.     public List<Row> findAll(String groovyWhere) {
  197.         return aggregate("adf.object", groovyWhere, new FindAllAggregator<Row>()).result;
  198.     }
  199.  
  200.     /**
  201.      * Returns the value of the <code>groovy</code> expression in the context of all row for which
  202.      * <code>groovyWhere</code> evaluates to <code>true</code>.
  203.      *
  204.      * @param groovy a groovy expression to be executed in the context of a single row
  205.      * @param groovyWhere a (boolean) groovy expression that defines when a single row qualifies
  206.      * @return the value of the groovy expression in the context of all row for which groovyWhere
  207.      * evaluates to true
  208.      */
  209.     public <T> List<T> findAll(String groovy, String groovyWhere) {
  210.         return aggregate(groovy, groovyWhere, new FindAllAggregator<T>()).result;
  211.     }
  212.  
  213.     private static final class FindAllAggregator<T> extends Aggregator<T> {
  214.         List<T> result = new ArrayList<T>();
  215.  
  216.         boolean add(T value) {
  217.             result.add(value);
  218.             return true;
  219.         }
  220.     }
  221.  
  222.     /**
  223.      * Execute the given <code>groovy</code> statement for each row. For example, to delete all
  224.      * rows, one could call: <code>doForAllRows("adf.object.remove()");</code>
  225.      *
  226.      * @param groovy the groovy statement that should be executed in the context of every single row
  227.      * @return the number of rows for which the statement was executed
  228.      */
  229.     public int doForAllRows(String groovy) {
  230.         return doForAllRows(groovy, null);
  231.     }
  232.  
  233.     /**
  234.      * Execute the given <code>groovy</code> statement for each row for which
  235.      * <code>groovyWhere</code> evaluates to <code>true</code>. For example, to delete expensive
  236.      * employees, one could call: <code>doForAllRows("adf.object.remove()", "Salary > 1000");</code>
  237.      *
  238.      * @param groovy the groovy statement that should be executed in the context of every single row
  239.      * @param groovyWhere a (boolean) groovy expression that defines when a single row qualifies
  240.      * @return the number of rows for which the statement was executed
  241.      */
  242.     public int doForAllRows(String groovy, String groovyWhere) {
  243.         return aggregate(groovy, groovyWhere, new CountAggregator()).result;
  244.     }
  245.  
  246.     /**
  247.      * Check whether the <code>groovy</code> expression evaluates to a unique value for every row.
  248.      *
  249.      * @param groovy a groovy expression to be executed in the context of a single row
  250.      * @return <code>true</code> iff all values are unique
  251.      */
  252.     public boolean unique(String groovy) {
  253.         return unique(groovy, null);
  254.     }
  255.  
  256.     /**
  257.      * Check whether the <code>groovy</code> expression evaluates to a unique value for every row.
  258.      * Only rows on which <code>groovyWhere</code> evaluates to <code>true</code> are taken into
  259.      * account.
  260.      *
  261.      * @param groovy a groovy expression to be executed in the context of a single row
  262.      * @param groovyWhere a (boolean) groovy expression that defines when a single row qualifies
  263.      * @return <code>true</code> iff all values are unique
  264.      */
  265.     public boolean unique(String groovy, String groovyWhere) {
  266.         return aggregate(groovy, groovyWhere, new UniqueAggregator()).result;
  267.     }
  268.  
  269.     private static final class UniqueAggregator extends Aggregator<Object> {
  270.         Set<Object> set = new HashSet<Object>();
  271.         boolean result = true;
  272.  
  273.         boolean add(Object value) {
  274.             result = set.add(value);
  275.             return result;
  276.         }
  277.     }
  278.  
  279.     /*
  280.      * An example of a more interesting aggregate function
  281.      */
  282.  
  283.     /**
  284.      * Return the lowest integer in the range <code>startWith..Integer.MAX_VALUE</code> that is
  285.      * available. An integer is "available" if there is no row for which the given
  286.      * <code>groovy</code> expression evaluates to it.
  287.      *
  288.      * @param groovy
  289.      * @param startWith
  290.      * @return
  291.      */
  292.     public int firstFreeInteger(String groovy, int startWith) {
  293.         return firstFreeInteger(groovy, null, startWith);
  294.     }
  295.  
  296.     /**
  297.      * Return the lowest integer in the range <code>startWith..Integer.MAX_VALUE</code> that is
  298.      * available. An integer is "available" if there is no row for which the given
  299.      * <code>groovy</code> expression evaluates to it.
  300.      *
  301.      * @param groovy
  302.      * @param groovyWhere a (boolean) groovy expression that defines which rows are taken into account
  303.      * @param startWith
  304.      * @return
  305.      */
  306.     public int firstFreeInteger(String groovy, String groovyWhere, int startWith) {
  307.         return aggregate(groovy, groovyWhere,
  308.                          new FirstFreeIntegerAggregator(startWith)).firstFree();
  309.     }
  310.  
  311.     private static final class FirstFreeIntegerAggregator extends Aggregator<Number> {
  312.         int last;
  313.         BitSet free = new BitSet();
  314.  
  315.         public FirstFreeIntegerAggregator(int startWith) {
  316.             last = startWith;
  317.         }
  318.  
  319.         boolean add(Number value) {
  320.             if (value == null) {
  321.                 return true;
  322.             }
  323.             int i = value.intValue();
  324.             if (i == last) {
  325.                 ++last;
  326.             } else if (i > last) {
  327.                 free.set(last, i);
  328.                 last = i + 1;
  329.             } else {
  330.                 free.clear(i);
  331.             }
  332.             return true;
  333.         }
  334.  
  335.         int firstFree() {
  336.             return free.isEmpty() ? last : free.nextSetBit(0);
  337.         }
  338.     }
  339.  
  340.  
  341.     /*
  342.      * The important part... the actual aggregation
  343.      */
  344.  
  345.     /**
  346.      * The aggregator does the actual aggregation, the <code>{@link #aggregate}</code> function
  347.      * feeds all values (after evaluating the Groovy expression) to the aggregator.
  348.      *
  349.      * @param <T> the expected aggregate type
  350.      */
  351.     private static abstract class Aggregator<T> {
  352.         abstract boolean add(T value);
  353.     }
  354.  
  355.     /**
  356.      * Execute the <code>groovy</code> expression and feed the calculated values to the aggregator.
  357.      *
  358.      * @param <A> the type of aggregator
  359.      * @param <T> the aggregate type
  360.      * @param groovy the expression that will be evaluated for all qualifying rows
  361.      * @param groovyWhere the expression that indicates which rows qualify
  362.      * @param aggregator the aggregator
  363.      * @return the same aggregator
  364.      */
  365.     private <T, A extends Aggregator<T>> A aggregate(String groovy, String groovyWhere,
  366.                                                      A aggregator) {
  367.         // The ExprEval is the magic class that provides all ADF-BC specific Groovy features, so
  368.         // we can use the same code as we would use in business rules, default values, etc.
  369.         ExprEval eval = null;
  370.         if (groovy != null) {
  371.             eval = new ExprEval(groovy, ExprEval.EXPR_STYLE_GROOVY);
  372.         }
  373.  
  374.         ExprEval where = null;
  375.         if (groovyWhere != null) {
  376.             where = new ExprEval("(" + groovyWhere + ") as boolean", ExprEval.EXPR_STYLE_GROOVY);
  377.         }
  378.  
  379.         RowIterator iter = createRowIterator();
  380.         try {
  381.             Row row = iter.first();
  382.             while (row != null) {
  383.  
  384.                 // Evaluate the where clause.
  385.                 boolean rowQualifies = true;
  386.                 if (where != null) {
  387.                     Boolean b = (Boolean)where.evaluateForRow(row);
  388.                     rowQualifies = b != null ? b : false;
  389.                 }
  390.  
  391.                 // Evaluate the value expression and feed it to the aggregator.
  392.                 if (rowQualifies) {
  393.                     boolean cont;
  394.                     if (eval == null) {
  395.                         cont = aggregator.add(null);
  396.                     } else {
  397.                         cont = aggregator.add((T)eval.evaluateForRow(row));
  398.                     }
  399.                     if (!cont) {
  400.                         break;
  401.                     }
  402.                 }
  403.  
  404.                 row = iter.next();
  405.             }
  406.  
  407.             // Finally return the aggregator, for convenience.
  408.             return aggregator;
  409.  
  410.         } finally {
  411.             closeRowIterator(iter);
  412.         }
  413.     }
  414.  
  415.     protected abstract RowIterator createRowIterator();
  416.  
  417.     protected void closeRowIterator(RowIterator iter) {
  418.     }
  419.  
  420.     /**
  421.      * Get GroovySupport on a RowIterator.
  422.      *
  423.      * @param rowIterator
  424.      * @return a new GroovySupport object
  425.      */
  426.     public static GroovySupport get(final RowIterator rowIterator) {
  427.         return new GroovySupport() {
  428.             protected RowIterator createRowIterator() {
  429.                 return rowIterator;
  430.             }
  431.         };
  432.     }
  433.  
  434.     /**
  435.      * Get GroovySupport on a RowSet (or a ViewObject). This will create a secondary RowSetIterator
  436.      * everytime an aggregate function is called, so you will not lose row currency.
  437.      *
  438.      * @param rowSet
  439.      * @return a new GroovySupport object
  440.      */
  441.     public static GroovySupport get(final RowSet rowSet) {
  442.         return new GroovySupport() {
  443.             protected RowIterator createRowIterator() {
  444.                 return rowSet.createRowSetIterator(null);
  445.             }
  446.  
  447.             @Override
  448.             protected void closeRowIterator(RowIterator iter) {
  449.                 ((RowSetIterator)iter).closeRowSetIterator();
  450.             }
  451.         };
  452.     }
  453. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement