src/core/net/sf/basedb/util/jep/ChannelFunction.java

Code
Comments
Other
Rev Date Author Line
2086 17 Mar 06 nicklas 1 /*
2086 17 Mar 06 nicklas 2   $Id$
2086 17 Mar 06 nicklas 3
4889 06 Apr 09 nicklas 4   Copyright (C) 2006 Jari Häkkinen, Nicklas Nordborg
2086 17 Mar 06 nicklas 5
2304 22 May 06 jari 6   This file is part of BASE - BioArray Software Environment.
2304 22 May 06 jari 7   Available at http://base.thep.lu.se/
2086 17 Mar 06 nicklas 8
2086 17 Mar 06 nicklas 9   BASE is free software; you can redistribute it and/or
2086 17 Mar 06 nicklas 10   modify it under the terms of the GNU General Public License
4479 05 Sep 08 jari 11   as published by the Free Software Foundation; either version 3
2086 17 Mar 06 nicklas 12   of the License, or (at your option) any later version.
2086 17 Mar 06 nicklas 13
2086 17 Mar 06 nicklas 14   BASE is distributed in the hope that it will be useful,
2086 17 Mar 06 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
2086 17 Mar 06 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2086 17 Mar 06 nicklas 17   GNU General Public License for more details.
2086 17 Mar 06 nicklas 18
2086 17 Mar 06 nicklas 19   You should have received a copy of the GNU General Public License
4515 11 Sep 08 jari 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
2086 17 Mar 06 nicklas 21 */
2086 17 Mar 06 nicklas 22 package net.sf.basedb.util.jep;
2086 17 Mar 06 nicklas 23
2086 17 Mar 06 nicklas 24 import net.sf.basedb.core.BaseException;
2981 30 Nov 06 nicklas 25 import net.sf.basedb.core.DbControl;
5319 20 Apr 10 nicklas 26 import net.sf.basedb.core.DynamicQuery;
4912 29 Apr 09 nicklas 27 import net.sf.basedb.core.IntensityTransform;
2086 17 Mar 06 nicklas 28 import net.sf.basedb.core.VirtualColumn;
2086 17 Mar 06 nicklas 29 import net.sf.basedb.core.query.Dynamic;
2086 17 Mar 06 nicklas 30 import net.sf.basedb.core.query.Expression;
2086 17 Mar 06 nicklas 31 import net.sf.basedb.core.query.SqlResult;
2981 30 Nov 06 nicklas 32 import net.sf.basedb.util.BioAssaySetUtil;
2086 17 Mar 06 nicklas 33
2086 17 Mar 06 nicklas 34 import java.sql.SQLException;
2086 17 Mar 06 nicklas 35 import java.util.Stack;
2086 17 Mar 06 nicklas 36
2086 17 Mar 06 nicklas 37 import org.nfunk.jep.Node;
2086 17 Mar 06 nicklas 38 import org.nfunk.jep.ParseException;
2086 17 Mar 06 nicklas 39
2086 17 Mar 06 nicklas 40 /**
2086 17 Mar 06 nicklas 41   A JEP function class that adds a <code>ch(int)</code> function to a 
2086 17 Mar 06 nicklas 42   JEP expression parser. The function will look up the intensity value for the
4912 29 Apr 09 nicklas 43   given channel. For example: <code>ch(1) / ch(2)</code>
2086 17 Mar 06 nicklas 44   <p>
4912 29 Apr 09 nicklas 45   Note! If the current bioassay set has stored transformed intensity values,
4912 29 Apr 09 nicklas 46   the values are un-transformed before returned by the this function.
4912 29 Apr 09 nicklas 47   Use {@link RawChannelFunction} if you need the raw values exactly 
4912 29 Apr 09 nicklas 48   as they are stored in the database.  
4912 29 Apr 09 nicklas 49   <p>
2086 17 Mar 06 nicklas 50   To be able to use this function it must be registered with the JEP
2302 22 May 06 nicklas 51   parser and, before the expression is evaluated, a {@link SqlResult} object must be set.
2086 17 Mar 06 nicklas 52   For example we can evaluate an expression for every position in a bioassay:
2086 17 Mar 06 nicklas 53   
2086 17 Mar 06 nicklas 54   <pre class="code">
2086 17 Mar 06 nicklas 55 DbControl dc = ...
2086 17 Mar 06 nicklas 56 BioAssay assay = ...
2086 17 Mar 06 nicklas 57 String expression = "ch(1) / ch(2)";
2086 17 Mar 06 nicklas 58 ChannelFunction ch = new ChannelFunction({3, 4});
2086 17 Mar 06 nicklas 59 JEP jep = JepUtil.newJep(expression, ch);
2086 17 Mar 06 nicklas 60 DynamicResultIterator result = 
2086 17 Mar 06 nicklas 61    assay.getSpotData().iterate(dc);
2086 17 Mar 06 nicklas 62 while (result.hasNext())
2086 17 Mar 06 nicklas 63 {
2086 17 Mar 06 nicklas 64    ch.setSqlResult(result.next());
2086 17 Mar 06 nicklas 65    double value = jep.getValue();
2086 17 Mar 06 nicklas 66    // Do something with the value
2086 17 Mar 06 nicklas 67 }
2086 17 Mar 06 nicklas 68 result.close();
2086 17 Mar 06 nicklas 69 </pre>
2086 17 Mar 06 nicklas 70
2086 17 Mar 06 nicklas 71   @author Nicklas
2086 17 Mar 06 nicklas 72   @version 2.0
2086 17 Mar 06 nicklas 73   @base.modified $Date$
2086 17 Mar 06 nicklas 74   @see Jep
5319 20 Apr 10 nicklas 75   @see BioAssaySetUtil#createJepExpression(DbControl, String, DynamicQuery)
2086 17 Mar 06 nicklas 76 */
2086 17 Mar 06 nicklas 77 public class ChannelFunction
2086 17 Mar 06 nicklas 78   implements JepExpressionFunction
2086 17 Mar 06 nicklas 79 {
2086 17 Mar 06 nicklas 80
2086 17 Mar 06 nicklas 81   private int[] channelToIndex;
4912 29 Apr 09 nicklas 82   private IntensityTransform transform;
2086 17 Mar 06 nicklas 83   private SqlResult result;
2086 17 Mar 06 nicklas 84   private int numParameters;
2086 17 Mar 06 nicklas 85
2086 17 Mar 06 nicklas 86   /**
2086 17 Mar 06 nicklas 87     Create a new instance of this function. The new instance cannot be used
2086 17 Mar 06 nicklas 88     to dynamically evaluate expressions. It should only be used for converting
2086 17 Mar 06 nicklas 89     JEP formulas to {@link Expression}:s.
2086 17 Mar 06 nicklas 90     @see Jep#formulaToExpression(String, JepFunction[])  
2086 17 Mar 06 nicklas 91   */
2086 17 Mar 06 nicklas 92   public ChannelFunction()
2086 17 Mar 06 nicklas 93   {}
2086 17 Mar 06 nicklas 94   
2086 17 Mar 06 nicklas 95   /**
2086 17 Mar 06 nicklas 96     Create a new instance of this function which can be used
2086 17 Mar 06 nicklas 97     to dynamically evaluate expressions.
2086 17 Mar 06 nicklas 98     @param channelToIndex An array that maps channel numbers to column indexes in
2086 17 Mar 06 nicklas 99       the SqlResult, array position 0 maps the index of channel number 1, etc.
2086 17 Mar 06 nicklas 100     @see #setSqlResult(SqlResult)
2086 17 Mar 06 nicklas 101   */
4912 29 Apr 09 nicklas 102   public ChannelFunction(int[] channelToIndex, IntensityTransform transform)
2086 17 Mar 06 nicklas 103   {
2086 17 Mar 06 nicklas 104     this.channelToIndex = channelToIndex;
4912 29 Apr 09 nicklas 105     this.transform = transform;
2086 17 Mar 06 nicklas 106   }
2086 17 Mar 06 nicklas 107   
2086 17 Mar 06 nicklas 108   /*
2086 17 Mar 06 nicklas 109     From the JepFunction interface
2086 17 Mar 06 nicklas 110     -------------------------------------------
2086 17 Mar 06 nicklas 111   */
2086 17 Mar 06 nicklas 112   /**
2086 17 Mar 06 nicklas 113     @return The string "ch"
2086 17 Mar 06 nicklas 114   */
6127 14 Sep 12 nicklas 115   @Override
2086 17 Mar 06 nicklas 116   public String getFunctionName()
2086 17 Mar 06 nicklas 117   {
2086 17 Mar 06 nicklas 118     return "ch";
2086 17 Mar 06 nicklas 119   }
2086 17 Mar 06 nicklas 120   // -------------------------------------------
2086 17 Mar 06 nicklas 121   /*
2086 17 Mar 06 nicklas 122     From the JepExpressionFunction interface
2086 17 Mar 06 nicklas 123     -------------------------------------------
2086 17 Mar 06 nicklas 124   */
2086 17 Mar 06 nicklas 125   /**
2086 17 Mar 06 nicklas 126     Use the {@link Dynamic#column(VirtualColumn)} method to create an
2086 17 Mar 06 nicklas 127     expression referencing a channel intensity.
2086 17 Mar 06 nicklas 128   */
6127 14 Sep 12 nicklas 129   @Override
2086 17 Mar 06 nicklas 130   public Expression toExpression(Node node)
2086 17 Mar 06 nicklas 131   {
2086 17 Mar 06 nicklas 132     int numChildren = node.jjtGetNumChildren();
2086 17 Mar 06 nicklas 133     if (numChildren != 1)
2086 17 Mar 06 nicklas 134     {
2086 17 Mar 06 nicklas 135       throw new BaseException("Invalid number of expressions for 'ch' function: " + numChildren);
2086 17 Mar 06 nicklas 136     }
4912 29 Apr 09 nicklas 137     return Dynamic.column(VirtualColumn.channelIntensity(Jep.nodeToInt(node.jjtGetChild(0))));
2086 17 Mar 06 nicklas 138   }
2086 17 Mar 06 nicklas 139   // -------------------------------------------
2086 17 Mar 06 nicklas 140   /*
2086 17 Mar 06 nicklas 141     From the PostfixMathCommandI interface
2086 17 Mar 06 nicklas 142     -------------------------------------------
2086 17 Mar 06 nicklas 143   */
2086 17 Mar 06 nicklas 144   /**
2086 17 Mar 06 nicklas 145     @return Always 1
2086 17 Mar 06 nicklas 146   */
6127 14 Sep 12 nicklas 147   @Override
2086 17 Mar 06 nicklas 148   public int getNumberOfParameters()
2086 17 Mar 06 nicklas 149   {
2086 17 Mar 06 nicklas 150     return 1;
2086 17 Mar 06 nicklas 151   }
6127 14 Sep 12 nicklas 152   @Override
2086 17 Mar 06 nicklas 153   public void setCurNumberOfParameters(int n)
2086 17 Mar 06 nicklas 154   {
2086 17 Mar 06 nicklas 155     this.numParameters = n;
2086 17 Mar 06 nicklas 156   }
6127 14 Sep 12 nicklas 157   @Override
2474 31 Jul 06 nicklas 158   public boolean checkNumberOfParameters(int n)
2474 31 Jul 06 nicklas 159   {
2474 31 Jul 06 nicklas 160     return n == 1;
2474 31 Jul 06 nicklas 161   }
6127 14 Sep 12 nicklas 162   @Override
6875 20 Apr 15 nicklas 163   @SuppressWarnings({"unchecked", "rawtypes"})
2086 17 Mar 06 nicklas 164   public void run(Stack stack)
2086 17 Mar 06 nicklas 165     throws ParseException
2086 17 Mar 06 nicklas 166   {
2086 17 Mar 06 nicklas 167     if (stack == null || stack.empty()) 
2086 17 Mar 06 nicklas 168     {
2086 17 Mar 06 nicklas 169       throw new ParseException("Stack is empty");
2086 17 Mar 06 nicklas 170     }
2086 17 Mar 06 nicklas 171     Object channel = stack.pop();
2086 17 Mar 06 nicklas 172     if (channel instanceof Number)
2086 17 Mar 06 nicklas 173     {
2086 17 Mar 06 nicklas 174       stack.push(channel(((Number)channel).intValue()));
2086 17 Mar 06 nicklas 175     }
2086 17 Mar 06 nicklas 176     else
2086 17 Mar 06 nicklas 177     {
2086 17 Mar 06 nicklas 178       throw new ParseException("Invalid parameter type: " + channel + "; expected integer");
2086 17 Mar 06 nicklas 179     }
2086 17 Mar 06 nicklas 180   }
2086 17 Mar 06 nicklas 181   // -------------------------------------------
2086 17 Mar 06 nicklas 182
2086 17 Mar 06 nicklas 183   /**
2086 17 Mar 06 nicklas 184     Set a new {@link SqlResult} object that will be used the next time the
2086 17 Mar 06 nicklas 185     JEP expression is evaluated.
2086 17 Mar 06 nicklas 186     @param result The result object
2086 17 Mar 06 nicklas 187   */
2086 17 Mar 06 nicklas 188   public void setSqlResult(SqlResult result)
2086 17 Mar 06 nicklas 189   {
2086 17 Mar 06 nicklas 190     this.result = result;
2086 17 Mar 06 nicklas 191   }
2086 17 Mar 06 nicklas 192   /**
2086 17 Mar 06 nicklas 193     Get the value of the specified channel of the current sql result.
2086 17 Mar 06 nicklas 194     @param channel The channel number
4017 28 Nov 07 martin 195      @throws ParseException Either if it's an invalid channel number or 
4017 28 Nov 07 martin 196        if no result object has been specified.
2086 17 Mar 06 nicklas 197   */
2086 17 Mar 06 nicklas 198   public Object channel(int channel)
2086 17 Mar 06 nicklas 199     throws ParseException
2086 17 Mar 06 nicklas 200   {
2086 17 Mar 06 nicklas 201     if (result == null)
2086 17 Mar 06 nicklas 202     {
2086 17 Mar 06 nicklas 203       throw new ParseException("No result object has been specified for function ch("+channel+")");
2086 17 Mar 06 nicklas 204     }
2086 17 Mar 06 nicklas 205     if (channelToIndex == null || channel < 1 || channel > channelToIndex.length)
2086 17 Mar 06 nicklas 206     {
2086 17 Mar 06 nicklas 207       throw new ParseException("Invalid channel number for function ch("+channel+")");
2086 17 Mar 06 nicklas 208     }
2086 17 Mar 06 nicklas 209     try
2086 17 Mar 06 nicklas 210     {
4912 29 Apr 09 nicklas 211       Number n = (Number)result.getObject(channelToIndex[channel-1]);
4912 29 Apr 09 nicklas 212       if (transform != null) n = transform.unTransform(n.doubleValue());
4912 29 Apr 09 nicklas 213       return n;
2086 17 Mar 06 nicklas 214     }
2086 17 Mar 06 nicklas 215     catch (SQLException ex)
2086 17 Mar 06 nicklas 216     {
2086 17 Mar 06 nicklas 217       throw new ParseException(ex.getMessage());
2086 17 Mar 06 nicklas 218     }
2086 17 Mar 06 nicklas 219   }
2086 17 Mar 06 nicklas 220 }