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

Code
Comments
Other
Rev Date Author Line
5259 26 Feb 10 nicklas 1 /*
5259 26 Feb 10 nicklas 2   $Id $
5259 26 Feb 10 nicklas 3
5259 26 Feb 10 nicklas 4   Copyright (C) 2010 Nicklas Nordborg
5259 26 Feb 10 nicklas 5
5259 26 Feb 10 nicklas 6   This file is part of BASE - BioArray Software Environment.
5259 26 Feb 10 nicklas 7   Available at http://base.thep.lu.se/
5259 26 Feb 10 nicklas 8
5259 26 Feb 10 nicklas 9   BASE is free software; you can redistribute it and/or
5259 26 Feb 10 nicklas 10   modify it under the terms of the GNU General Public License
5259 26 Feb 10 nicklas 11   as published by the Free Software Foundation; either version 3
5259 26 Feb 10 nicklas 12   of the License, or (at your option) any later version.
5259 26 Feb 10 nicklas 13
5259 26 Feb 10 nicklas 14   BASE is distributed in the hope that it will be useful,
5259 26 Feb 10 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
5259 26 Feb 10 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
5259 26 Feb 10 nicklas 17   GNU General Public License for more details.
5259 26 Feb 10 nicklas 18
5259 26 Feb 10 nicklas 19   You should have received a copy of the GNU General Public License
5259 26 Feb 10 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
5259 26 Feb 10 nicklas 21 */
5259 26 Feb 10 nicklas 22 package net.sf.basedb.util.jep;
5259 26 Feb 10 nicklas 23
5384 13 Aug 10 nicklas 24 import java.io.IOException;
5384 13 Aug 10 nicklas 25 import java.io.NotSerializableException;
5384 13 Aug 10 nicklas 26 import java.io.ObjectInputStream;
5384 13 Aug 10 nicklas 27 import java.io.ObjectOutputStream;
5384 13 Aug 10 nicklas 28 import java.io.Serializable;
5259 26 Feb 10 nicklas 29 import java.util.Stack;
5259 26 Feb 10 nicklas 30
5259 26 Feb 10 nicklas 31 import org.nfunk.jep.FunctionTable;
5259 26 Feb 10 nicklas 32 import org.nfunk.jep.JEP;
5769 29 Sep 11 nicklas 33 import org.nfunk.jep.Node;
5259 26 Feb 10 nicklas 34 import org.nfunk.jep.ParseException;
5259 26 Feb 10 nicklas 35 import org.nfunk.jep.Token;
5259 26 Feb 10 nicklas 36 import org.nfunk.jep.function.PostfixMathCommandI;
5259 26 Feb 10 nicklas 37
5259 26 Feb 10 nicklas 38 /**
5259 26 Feb 10 nicklas 39   Extension to the JEP parser that provides better error handling for
5259 26 Feb 10 nicklas 40   unknown functions. The normal error message when JEP encounters
5259 26 Feb 10 nicklas 41   an unknown function is: Syntax Error (implicit multiplication not enabled).
5259 26 Feb 10 nicklas 42   This is not very informative so this class improves the error handling
5259 26 Feb 10 nicklas 43   by giving a message: Unknown function foo() instead.
5259 26 Feb 10 nicklas 44   
5259 26 Feb 10 nicklas 45   @author Nicklas
5259 26 Feb 10 nicklas 46   @since 2.15
5259 26 Feb 10 nicklas 47   @base.modified $Date $
5259 26 Feb 10 nicklas 48 */
5259 26 Feb 10 nicklas 49 public class FunctionSafeJep 
5259 26 Feb 10 nicklas 50   extends JEP 
5259 26 Feb 10 nicklas 51 {
5259 26 Feb 10 nicklas 52   
5259 26 Feb 10 nicklas 53   /**
5259 26 Feb 10 nicklas 54     Create a new function-safe JEP parser.
5259 26 Feb 10 nicklas 55   */
5259 26 Feb 10 nicklas 56   public FunctionSafeJep() 
5259 26 Feb 10 nicklas 57   {
5259 26 Feb 10 nicklas 58     super();
5259 26 Feb 10 nicklas 59   }
5259 26 Feb 10 nicklas 60   
5259 26 Feb 10 nicklas 61   /**
5259 26 Feb 10 nicklas 62     Overrides the regular function lookup table by replacing it with a
5259 26 Feb 10 nicklas 63     function table that pretends that all functions has been registered,
5259 26 Feb 10 nicklas 64     but then generates an error message if it doesn't exists.
5259 26 Feb 10 nicklas 65   */
5259 26 Feb 10 nicklas 66   @Override
5259 26 Feb 10 nicklas 67   public void initFunTab()
5259 26 Feb 10 nicklas 68   {
5259 26 Feb 10 nicklas 69     this.funTab = new SafeFunctionTable(this);
5259 26 Feb 10 nicklas 70   }
5259 26 Feb 10 nicklas 71   
5259 26 Feb 10 nicklas 72   /**
5259 26 Feb 10 nicklas 73     Adds an error message to the "real" JEP parser.
5259 26 Feb 10 nicklas 74   */
5259 26 Feb 10 nicklas 75   @SuppressWarnings("unchecked")
5259 26 Feb 10 nicklas 76   void addError(String error)
5259 26 Feb 10 nicklas 77   {
5259 26 Feb 10 nicklas 78     this.errorList.add(error);
5259 26 Feb 10 nicklas 79   }
5769 29 Sep 11 nicklas 80
5259 26 Feb 10 nicklas 81   /**
5769 29 Sep 11 nicklas 82     Override the superclass method so that exceptions are passed on
5769 29 Sep 11 nicklas 83     instead of swallowed.
5769 29 Sep 11 nicklas 84     @since 3.0
5769 29 Sep 11 nicklas 85   */
5769 29 Sep 11 nicklas 86   @Override
5769 29 Sep 11 nicklas 87   public Object getValueAsObject() 
5769 29 Sep 11 nicklas 88   {
5769 29 Sep 11 nicklas 89     Node topNode = getTopNode();
5769 29 Sep 11 nicklas 90     if (topNode == null || hasError()) return null;
5769 29 Sep 11 nicklas 91     try
5769 29 Sep 11 nicklas 92     {
5769 29 Sep 11 nicklas 93       return this.ev.getValue(topNode, this.symTab);
5769 29 Sep 11 nicklas 94     }
5769 29 Sep 11 nicklas 95     catch (ParseException ex)
5769 29 Sep 11 nicklas 96     {
5769 29 Sep 11 nicklas 97       throw new RuntimeException(ex);
5769 29 Sep 11 nicklas 98     }
5769 29 Sep 11 nicklas 99   }
5769 29 Sep 11 nicklas 100
5769 29 Sep 11 nicklas 101   /**
5259 26 Feb 10 nicklas 102     Function table implementation that pretends that all possible functions
5259 26 Feb 10 nicklas 103     exists, but sends an error message if someone tries to use a function that
5259 26 Feb 10 nicklas 104     doesn't really exists.
5259 26 Feb 10 nicklas 105   */
5259 26 Feb 10 nicklas 106   static class SafeFunctionTable
5259 26 Feb 10 nicklas 107     extends FunctionTable
5259 26 Feb 10 nicklas 108   {
5259 26 Feb 10 nicklas 109     
5259 26 Feb 10 nicklas 110     private static final long serialVersionUID = 4430148600636575401L;
5259 26 Feb 10 nicklas 111     private final FunctionSafeJep jep;
5259 26 Feb 10 nicklas 112     SafeFunctionTable(FunctionSafeJep jep) 
5259 26 Feb 10 nicklas 113     {
5259 26 Feb 10 nicklas 114       super();
5259 26 Feb 10 nicklas 115       this.jep = jep;
5259 26 Feb 10 nicklas 116     }
5259 26 Feb 10 nicklas 117     
5259 26 Feb 10 nicklas 118
5259 26 Feb 10 nicklas 119     /**
5259 26 Feb 10 nicklas 120       If the key is already registered, return TRUE. If the key is not
5259 26 Feb 10 nicklas 121       registered and is a function, register it as an unknown function,
5259 26 Feb 10 nicklas 122       set an error message and return TRUE. Otherwise return FALSE.
5259 26 Feb 10 nicklas 123     */
5259 26 Feb 10 nicklas 124     @Override
5259 26 Feb 10 nicklas 125     public boolean containsKey(Object key)
5259 26 Feb 10 nicklas 126     {
5259 26 Feb 10 nicklas 127       boolean exists = super.containsKey(key);
5259 26 Feb 10 nicklas 128       if (!exists && key instanceof String)
5259 26 Feb 10 nicklas 129       {
5259 26 Feb 10 nicklas 130         String name = (String)key;
5259 26 Feb 10 nicklas 131         if ("(".equals(nextToken(name)))
5259 26 Feb 10 nicklas 132         {
5259 26 Feb 10 nicklas 133           super.put(key, new UnknownFunction(name));
5259 26 Feb 10 nicklas 134           jep.addError("Unknown function: " + name + "()");
5259 26 Feb 10 nicklas 135           exists = true;
5259 26 Feb 10 nicklas 136         }
5259 26 Feb 10 nicklas 137       }
5259 26 Feb 10 nicklas 138       return exists;
5259 26 Feb 10 nicklas 139     }
5259 26 Feb 10 nicklas 140     
5259 26 Feb 10 nicklas 141     /**
5259 26 Feb 10 nicklas 142       Get the next token in the parser. If this is a "(", the 
5259 26 Feb 10 nicklas 143       parse is currently looking up a function name.
5259 26 Feb 10 nicklas 144       @param token The current token
5259 26 Feb 10 nicklas 145     */
5259 26 Feb 10 nicklas 146     private String nextToken(String token)
5259 26 Feb 10 nicklas 147     {
5259 26 Feb 10 nicklas 148       String next = null;
5259 26 Feb 10 nicklas 149       Token t = jep.parser.token;
5259 26 Feb 10 nicklas 150       while (t != null)
5259 26 Feb 10 nicklas 151       {
5259 26 Feb 10 nicklas 152         if (token.equals(t.image))
5259 26 Feb 10 nicklas 153         {
5259 26 Feb 10 nicklas 154           if (t.next != null) next = t.next.image;
5259 26 Feb 10 nicklas 155           break;
5259 26 Feb 10 nicklas 156         }
5259 26 Feb 10 nicklas 157         t = t.next;
5259 26 Feb 10 nicklas 158       }
5259 26 Feb 10 nicklas 159       return next;
5259 26 Feb 10 nicklas 160     }
5384 13 Aug 10 nicklas 161     
5384 13 Aug 10 nicklas 162     // This class is not serializable
5384 13 Aug 10 nicklas 163     private void readObject(ObjectInputStream ois)
5384 13 Aug 10 nicklas 164       throws ClassNotFoundException, IOException 
5384 13 Aug 10 nicklas 165     {
5384 13 Aug 10 nicklas 166       throw new NotSerializableException();
5384 13 Aug 10 nicklas 167     }
5384 13 Aug 10 nicklas 168     private void writeObject(ObjectOutputStream ois)
5384 13 Aug 10 nicklas 169       throws IOException 
5384 13 Aug 10 nicklas 170     {
5384 13 Aug 10 nicklas 171       throw new NotSerializableException();
5384 13 Aug 10 nicklas 172     }
5384 13 Aug 10 nicklas 173
5259 26 Feb 10 nicklas 174   }
5259 26 Feb 10 nicklas 175   
5259 26 Feb 10 nicklas 176   /**
5259 26 Feb 10 nicklas 177     Represents an unknown function that appears in an expression.
5259 26 Feb 10 nicklas 178     The error is usually catched at parse time, otherwise it will
5259 26 Feb 10 nicklas 179     be catched at evaluation time by this class.
5259 26 Feb 10 nicklas 180   */
5259 26 Feb 10 nicklas 181   static class UnknownFunction
5384 13 Aug 10 nicklas 182     implements PostfixMathCommandI, Serializable
5259 26 Feb 10 nicklas 183   {
5259 26 Feb 10 nicklas 184
5384 13 Aug 10 nicklas 185     private static final long serialVersionUID = 8761224046553039046L;
5259 26 Feb 10 nicklas 186     private final String name;
5259 26 Feb 10 nicklas 187     UnknownFunction(String name)
5259 26 Feb 10 nicklas 188     {
5259 26 Feb 10 nicklas 189       this.name = name;
5259 26 Feb 10 nicklas 190     }
5259 26 Feb 10 nicklas 191     
5259 26 Feb 10 nicklas 192     /*
5259 26 Feb 10 nicklas 193       From the PostfixMathCommandI interface
5259 26 Feb 10 nicklas 194       -------------------------------------------
5259 26 Feb 10 nicklas 195     */
5259 26 Feb 10 nicklas 196     @Override
5259 26 Feb 10 nicklas 197     public boolean checkNumberOfParameters(int numParameters) 
5259 26 Feb 10 nicklas 198     {
5259 26 Feb 10 nicklas 199       return true;
5259 26 Feb 10 nicklas 200     }
5259 26 Feb 10 nicklas 201     @Override
5259 26 Feb 10 nicklas 202     public int getNumberOfParameters() 
5259 26 Feb 10 nicklas 203     {
5259 26 Feb 10 nicklas 204       return -1;
5259 26 Feb 10 nicklas 205     }
5259 26 Feb 10 nicklas 206     @Override
6875 20 Apr 15 nicklas 207     @SuppressWarnings({"rawtypes"})
5259 26 Feb 10 nicklas 208     public void run(Stack paramStack) 
5259 26 Feb 10 nicklas 209       throws ParseException 
5259 26 Feb 10 nicklas 210     {
5259 26 Feb 10 nicklas 211       throw new ParseException("Unknown function: " + name + "()");
5259 26 Feb 10 nicklas 212     }
5259 26 Feb 10 nicklas 213     @Override
5259 26 Feb 10 nicklas 214     public void setCurNumberOfParameters(int numParameters) 
5259 26 Feb 10 nicklas 215     {}
5259 26 Feb 10 nicklas 216     // ---------------------------------------
5259 26 Feb 10 nicklas 217     
5259 26 Feb 10 nicklas 218   }
5259 26 Feb 10 nicklas 219   
5259 26 Feb 10 nicklas 220 }