1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 package com.sun.star.report.pentaho;
24 
25 
26 import com.sun.star.lang.XServiceInfo;
27 import com.sun.star.lib.uno.helper.ComponentBase;
28 import com.sun.star.lib.uno.helper.PropertySetMixin;
29 import com.sun.star.sheet.FormulaLanguage;
30 import com.sun.star.sheet.FormulaMapGroup;
31 import com.sun.star.sheet.FormulaMapGroupSpecialOffset;
32 import com.sun.star.sheet.FormulaOpCodeMapEntry;
33 import com.sun.star.sheet.FormulaToken;
34 import com.sun.star.sheet.XFormulaOpCodeMapper;
35 import com.sun.star.uno.Any;
36 import com.sun.star.uno.Exception;
37 import com.sun.star.uno.Type;
38 import com.sun.star.uno.UnoRuntime;
39 import com.sun.star.uno.XComponentContext;
40 
41 import java.io.StringReader;
42 
43 import java.util.ArrayList;
44 import java.util.HashMap;
45 import java.util.Iterator;
46 import java.util.List;
47 import java.util.Map;
48 
49 import org.pentaho.reporting.libraries.base.config.Configuration;
50 import org.pentaho.reporting.libraries.formula.DefaultFormulaContext;
51 import org.pentaho.reporting.libraries.formula.function.FunctionRegistry;
52 import org.pentaho.reporting.libraries.formula.parser.FormulaParser;
53 import org.pentaho.reporting.libraries.formula.parser.GeneratedFormulaParserConstants;
54 import org.pentaho.reporting.libraries.formula.parser.GeneratedFormulaParserTokenManager;
55 import org.pentaho.reporting.libraries.formula.parser.JavaCharStream;
56 import org.pentaho.reporting.libraries.formula.parser.ParseException;
57 import org.pentaho.reporting.libraries.formula.parser.Token;
58 import org.pentaho.reporting.libraries.formula.parser.TokenMgrError;
59 
60 
61 public final class SOFormulaParser extends ComponentBase
62         implements com.sun.star.report.meta.XFormulaParser, XServiceInfo
63 {
64 
65     public static final int SEPARATORS = 0;
66     public static final int ARRAY_SEPARATORS = 1;
67     public static final int UNARY_OPERATORS = 2;
68     public static final int BINARY_OPERATORS = 3;
69     public static final int FUNCTIONS = 4;
70     private final XComponentContext m_xContext;
71     private final PropertySetMixin m_prophlp;
72     private static final String __serviceName = "com.sun.star.report.meta.FormulaParser";
73     private static final String OPERATORS = "org.pentaho.reporting.libraries.formula.operators.";
74     // attributes
75     final private List m_OpCodeMap = new ArrayList();
76     private XFormulaOpCodeMapper formulaOpCodeMapper = null;
77     private final Map parserAllOpCodes = new HashMap();
78     private final Map parserNames = new HashMap();
79     private final Map[] groupOpCodes = new HashMap[5];
80     private final List specialOpCodes = new ArrayList();
81 
getSpecialOpCodes()82     public List getSpecialOpCodes()
83     {
84         return specialOpCodes;
85     }
86     private int ownTokenCounter = 1000;
87     private final FormulaOpCodeMapEntry opCodePush;
88     private final FormulaParser parser;
89 
SOFormulaParser(final XComponentContext context)90     public SOFormulaParser(final XComponentContext context)
91     {
92 
93         m_xContext = context;
94         final ClassLoader cl = java.lang.Thread.currentThread().getContextClassLoader();
95         Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
96 
97         parser = new FormulaParser();
98         try
99         {
100             final XFormulaOpCodeMapper mapper = (XFormulaOpCodeMapper) UnoRuntime.queryInterface(XFormulaOpCodeMapper.class, m_xContext.getServiceManager().createInstanceWithContext("simple.formula.FormulaOpCodeMapperObj", m_xContext));
101             FormulaOpCodeMapEntry[] opCodes = mapper.getAvailableMappings(FormulaLanguage.ODFF, FormulaMapGroup.FUNCTIONS);
102             final DefaultFormulaContext defaultContext = new DefaultFormulaContext();
103             final FunctionRegistry functionRegistry = defaultContext.getFunctionRegistry();
104 
105             String[] names = functionRegistry.getFunctionNames();
106             addOpCodes(names, opCodes, FUNCTIONS);
107             names = getOperators(defaultContext, OPERATORS);
108             opCodes = mapper.getAvailableMappings(FormulaLanguage.ODFF, FormulaMapGroup.UNARY_OPERATORS);
109             addOpCodes(names, opCodes, UNARY_OPERATORS);
110             opCodes = mapper.getAvailableMappings(FormulaLanguage.ODFF, FormulaMapGroup.BINARY_OPERATORS);
111             addOpCodes(names, opCodes, BINARY_OPERATORS);
112 
113             names = GeneratedFormulaParserConstants.tokenImage.clone();
114             for (int i = 0; i < names.length; i++)
115             {
116                 final String token = names[i];
117                 if (token != null && token.length() > 0 && token.charAt(0) == '"')
118                 {
119                     names[i] = token.substring(1, token.length() - 1);
120                 }
121             }
122             opCodes = mapper.getAvailableMappings(FormulaLanguage.ODFF, FormulaMapGroup.SEPARATORS);
123             addOpCodes(names, opCodes, SEPARATORS, false);
124 
125             opCodes = mapper.getAvailableMappings(FormulaLanguage.ODFF, FormulaMapGroup.ARRAY_SEPARATORS);
126             addOpCodes(names, opCodes, ARRAY_SEPARATORS, false);
127 
128             opCodes = mapper.getAvailableMappings(FormulaLanguage.ODFF, FormulaMapGroup.SPECIAL);
129 
130             for (int i = 0; i < opCodes.length; i++)
131             {
132                 final FormulaOpCodeMapEntry opCode = opCodes[i];
133                 parserAllOpCodes.put(opCode.Token.OpCode, opCode);
134                 specialOpCodes.add(opCode);
135             }
136             // addOpCodes(names, opCodes,SPECIAL,false);
137         }
138         catch (Exception ex)
139         {
140             ex.printStackTrace();
141         }
142         opCodePush = (FormulaOpCodeMapEntry) specialOpCodes.get(FormulaMapGroupSpecialOffset.PUSH);
143         Thread.currentThread().setContextClassLoader(cl);
144         // use the last parameter of the PropertySetMixin constructor
145         // for your optional attributes if necessary. See the documentation
146         // of the PropertySetMixin helper for further information.
147         // Ensure that your attributes are initialized correctly!
148         m_prophlp = new PropertySetMixin(m_xContext, this,
149                 new Type(com.sun.star.report.meta.XFormulaParser.class), null);
150     }
151 
152     // com.sun.star.sheet.XFormulaParser:
parseFormula(String aFormula, com.sun.star.table.CellAddress aReferencePos)153     public com.sun.star.sheet.FormulaToken[] parseFormula(String aFormula, com.sun.star.table.CellAddress aReferencePos)
154     {
155         final ArrayList tokens = new ArrayList();
156         if (!"=".equals(aFormula))
157         {
158             String formula;
159             if (aFormula.charAt(0) == '=')
160             {
161                 formula = aFormula.substring(1);
162             }
163             else
164             {
165                 formula = aFormula;
166             }
167             final ArrayList images = new ArrayList();
168             try
169             {
170                 int brackets = 0;
171                 final GeneratedFormulaParserTokenManager tokenParser = new GeneratedFormulaParserTokenManager(new JavaCharStream(new StringReader(formula), 1, 1));
172                 Token token = tokenParser.getNextToken();
173                 while (token.kind != GeneratedFormulaParserConstants.EOF)
174                 {
175                     final FormulaToken formulaToken;
176                     images.add(token.image);
177                     final String upper = token.image.toUpperCase();
178                     if (parserNames.containsKey(upper))
179                     {
180                         if ("(".equals(token.image))
181                         {
182                             brackets++;
183                         }
184                         else if (")".equals(token.image))
185                         {
186                             --brackets;
187                         }
188                         final FormulaOpCodeMapEntry opCode = (FormulaOpCodeMapEntry) parserNames.get(upper);
189                         formulaToken = opCode.Token;
190                     }
191                     else if (token.kind == GeneratedFormulaParserConstants.WHITESPACE)
192                     {
193                         final FormulaOpCodeMapEntry opCode = (FormulaOpCodeMapEntry) specialOpCodes.get(FormulaMapGroupSpecialOffset.SPACES);
194                         formulaToken = opCode.Token;
195                     }
196                     else
197                     {
198                         formulaToken = new FormulaToken();
199                         formulaToken.OpCode = opCodePush.Token.OpCode;
200                         formulaToken.Data = new Any(Type.STRING, token.image);
201                     }
202 
203                     tokens.add(formulaToken);
204                     token = tokenParser.getNextToken();
205                 }
206                 if (brackets > 0)
207                 {
208                     final FormulaOpCodeMapEntry opCode = (FormulaOpCodeMapEntry) parserNames.get(")");
209                     while (brackets-- != 0)
210                     {
211                         formula = formula.concat(")");
212                         images.add(")");
213                         tokens.add(opCode.Token);
214                     }
215 
216                 }
217 
218                 parser.parse(formula);
219             }
220             catch (ParseException ex)
221             {
222                 boolean found = false;
223                 // error occured so all token must be bad
224                 for (int i = 0; i < tokens.size(); i++)
225                 {
226                     if (!found && ex.currentToken != null && images.get(i).equals(ex.currentToken.image))
227                     {
228                         found = true;
229                     }
230                     if (found)
231                     {
232                         final FormulaToken dest = new FormulaToken();
233                         dest.OpCode = ((FormulaOpCodeMapEntry) specialOpCodes.get(FormulaMapGroupSpecialOffset.BAD)).Token.OpCode;
234                         dest.Data = new Any(Type.STRING, images.get(i));
235                         tokens.remove(i);
236                         tokens.add(i, dest);
237                     }
238                 }
239             }
240             catch (java.lang.Exception e)
241             {
242             }
243             catch (TokenMgrError e)
244             {
245             }
246         }
247         return (FormulaToken[]) tokens.toArray(new FormulaToken[tokens.size()]);
248     }
249 
printFormula(com.sun.star.sheet.FormulaToken[] aTokens, com.sun.star.table.CellAddress aReferencePos)250     public String printFormula(com.sun.star.sheet.FormulaToken[] aTokens, com.sun.star.table.CellAddress aReferencePos)
251     {
252         final StringBuffer ret = new StringBuffer();
253         for (int i = 0; i < aTokens.length; i++)
254         {
255             final FormulaToken formulaToken = aTokens[i];
256             if (formulaToken.OpCode == opCodePush.Token.OpCode && !formulaToken.Data.equals(Any.VOID))
257             {
258                 ret.append(formulaToken.Data);
259             }
260             else if (parserAllOpCodes.containsKey(formulaToken.OpCode))
261             {
262                 final FormulaOpCodeMapEntry opCode = (FormulaOpCodeMapEntry) parserAllOpCodes.get(formulaToken.OpCode);
263                 if (opCode.Name.length() > 0)
264                 {
265                     ret.append(opCode.Name);
266                 }
267                 else if (!formulaToken.Data.equals(Any.VOID))
268                 {
269                     ret.append(formulaToken.Data);
270                 }
271             }
272         }
273         return ret.toString();
274     }
275 
276     // com.sun.star.beans.XPropertySet:
getPropertySetInfo()277     public com.sun.star.beans.XPropertySetInfo getPropertySetInfo()
278     {
279         return m_prophlp.getPropertySetInfo();
280     }
281 
setPropertyValue(String aPropertyName, Object aValue)282     public void setPropertyValue(String aPropertyName, Object aValue) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.beans.PropertyVetoException, com.sun.star.lang.IllegalArgumentException, com.sun.star.lang.WrappedTargetException
283     {
284         m_prophlp.setPropertyValue(aPropertyName, aValue);
285     }
286 
getPropertyValue(String aPropertyName)287     public Object getPropertyValue(String aPropertyName) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException
288     {
289         return m_prophlp.getPropertyValue(aPropertyName);
290     }
291 
addPropertyChangeListener(String aPropertyName, com.sun.star.beans.XPropertyChangeListener xListener)292     public void addPropertyChangeListener(String aPropertyName, com.sun.star.beans.XPropertyChangeListener xListener) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException
293     {
294         m_prophlp.addPropertyChangeListener(aPropertyName, xListener);
295     }
296 
removePropertyChangeListener(String aPropertyName, com.sun.star.beans.XPropertyChangeListener xListener)297     public void removePropertyChangeListener(String aPropertyName, com.sun.star.beans.XPropertyChangeListener xListener) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException
298     {
299         m_prophlp.removePropertyChangeListener(aPropertyName, xListener);
300     }
301 
addVetoableChangeListener(String aPropertyName, com.sun.star.beans.XVetoableChangeListener xListener)302     public void addVetoableChangeListener(String aPropertyName, com.sun.star.beans.XVetoableChangeListener xListener) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException
303     {
304         m_prophlp.addVetoableChangeListener(aPropertyName, xListener);
305     }
306 
removeVetoableChangeListener(String aPropertyName, com.sun.star.beans.XVetoableChangeListener xListener)307     public void removeVetoableChangeListener(String aPropertyName, com.sun.star.beans.XVetoableChangeListener xListener) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException
308     {
309         m_prophlp.removeVetoableChangeListener(aPropertyName, xListener);
310     }
311 
312     // com.sun.star.report.meta.XFormulaParser:
getOpCodeMap()313     public com.sun.star.sheet.FormulaOpCodeMapEntry[] getOpCodeMap()
314     {
315         return (com.sun.star.sheet.FormulaOpCodeMapEntry[]) m_OpCodeMap.toArray(new FormulaOpCodeMapEntry[m_OpCodeMap.size()]);
316     }
317 
setOpCodeMap(com.sun.star.sheet.FormulaOpCodeMapEntry[] the_value)318     public void setOpCodeMap(com.sun.star.sheet.FormulaOpCodeMapEntry[] the_value)
319     {
320 //        m_prophlp.prepareSet("OpCodeMap", null);
321 //        synchronized (this)
322 //        {
323 //            m_OpCodeMap.clear();
324 //        }
325     }
326 
getImplementationName()327     public String getImplementationName()
328     {
329         return SOFormulaParser.class.getName();
330     }
331 
supportsService(String sServiceName)332     public boolean supportsService(String sServiceName)
333     {
334         return sServiceName.equals(__serviceName);
335     }
336 
getSupportedServiceNames()337     public String[] getSupportedServiceNames()
338     {
339         return getServiceNames();
340     }
341 
342     /**
343      * This method is a simple helper function to used in the static component initialisation functions as well as
344      * in getSupportedServiceNames.
345      * @return
346      */
getServiceNames()347     public static String[] getServiceNames()
348     {
349         return new String[]
350                 {
351                     __serviceName
352                 };
353     }
354 
getFormulaOpCodeMapper()355     public XFormulaOpCodeMapper getFormulaOpCodeMapper()
356     {
357         if (formulaOpCodeMapper == null)
358         {
359             formulaOpCodeMapper = new SOFormulaOpCodeMapper(this);
360         }
361 
362         return formulaOpCodeMapper;
363     }
364 
addOpCodes(String[] names, FormulaOpCodeMapEntry[] opCodes, int group)365     private void addOpCodes(String[] names, FormulaOpCodeMapEntry[] opCodes, int group)
366     {
367         addOpCodes(names, opCodes, group, true);
368     }
369 
addOpCodes(String[] names, FormulaOpCodeMapEntry[] opCodes, int group, boolean add)370     private void addOpCodes(String[] names, FormulaOpCodeMapEntry[] opCodes, int group, boolean add)
371     {
372         groupOpCodes[group] = new HashMap();
373         for (int j = 0; j < names.length; j++)
374         {
375             FormulaOpCodeMapEntry opCode = null;
376             int i = 0;
377             for (; i < opCodes.length; i++)
378             {
379                 opCode = opCodes[i];
380                 if (names[j].equals(opCode.Name))
381                 {
382                     break;
383                 }
384             }
385             if (i >= opCodes.length)
386             {
387                 if (!add)
388                 {
389                     continue;
390                 }
391                 final FormulaToken token = new FormulaToken(ownTokenCounter++, Any.VOID);
392                 opCode = new FormulaOpCodeMapEntry(names[j], token);
393             }
394             parserNames.put(names[j], opCode);
395             parserAllOpCodes.put(opCode.Token.OpCode, opCode);
396             groupOpCodes[group].put(opCode.Token.OpCode, opCode);
397         }
398     }
399 
getNames()400     public Map getNames()
401     {
402         return parserNames;
403     }
404 
getGroup(int group)405     public Map getGroup(int group)
406     {
407         return groupOpCodes[group];
408     }
409 
getOperators(DefaultFormulaContext defaultContext, final String _kind)410     private String[] getOperators(DefaultFormulaContext defaultContext, final String _kind)
411     {
412         final ArrayList ops = new ArrayList();
413         final Configuration configuration = defaultContext.getConfiguration();
414         final Iterator iter = configuration.findPropertyKeys(_kind);
415         while (iter.hasNext())
416         {
417             final String configKey = (String) iter.next();
418             if (!configKey.endsWith(".class"))
419             {
420                 continue;
421             }
422             final String operatorClass = configuration.getConfigProperty(configKey);
423             if (operatorClass == null)
424             {
425                 continue;
426             }
427             if (operatorClass.length() == 0)
428             {
429                 continue;
430             }
431             final String tokenKey = configKey.substring(0, configKey.length() - ".class".length()) + ".token";
432             final String token = configuration.getConfigProperty(tokenKey);
433             if (token == null)
434             {
435                 continue;
436             }
437             ops.add(token.trim());
438         }
439         return (String[]) ops.toArray(new String[ops.size()]);
440     }
441 }
442 
443