2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 package org
.libreoffice
.report
.pentaho
;
21 import com
.sun
.star
.lang
.XServiceInfo
;
22 import com
.sun
.star
.lib
.uno
.helper
.ComponentBase
;
23 import com
.sun
.star
.lib
.uno
.helper
.PropertySetMixin
;
24 import com
.sun
.star
.sheet
.FormulaLanguage
;
25 import com
.sun
.star
.sheet
.FormulaMapGroup
;
26 import com
.sun
.star
.sheet
.FormulaMapGroupSpecialOffset
;
27 import com
.sun
.star
.sheet
.FormulaOpCodeMapEntry
;
28 import com
.sun
.star
.sheet
.FormulaToken
;
29 import com
.sun
.star
.sheet
.XFormulaOpCodeMapper
;
30 import com
.sun
.star
.uno
.Any
;
31 import com
.sun
.star
.uno
.Exception
;
32 import com
.sun
.star
.uno
.Type
;
33 import com
.sun
.star
.uno
.UnoRuntime
;
34 import com
.sun
.star
.uno
.XComponentContext
;
36 import java
.io
.StringReader
;
38 import java
.util
.ArrayList
;
39 import java
.util
.HashMap
;
40 import java
.util
.Iterator
;
41 import java
.util
.List
;
44 import org
.pentaho
.reporting
.libraries
.base
.config
.Configuration
;
45 import org
.pentaho
.reporting
.libraries
.formula
.DefaultFormulaContext
;
46 import org
.pentaho
.reporting
.libraries
.formula
.function
.FunctionRegistry
;
47 import org
.pentaho
.reporting
.libraries
.formula
.parser
.FormulaParser
;
48 import org
.pentaho
.reporting
.libraries
.formula
.parser
.GeneratedFormulaParserConstants
;
49 import org
.pentaho
.reporting
.libraries
.formula
.parser
.GeneratedFormulaParserTokenManager
;
50 import org
.pentaho
.reporting
.libraries
.formula
.parser
.JavaCharStream
;
51 import org
.pentaho
.reporting
.libraries
.formula
.parser
.ParseException
;
52 import org
.pentaho
.reporting
.libraries
.formula
.parser
.Token
;
53 import org
.pentaho
.reporting
.libraries
.formula
.parser
.TokenMgrError
;
56 public final class SOFormulaParser
extends ComponentBase
57 implements com
.sun
.star
.report
.meta
.XFormulaParser
, XServiceInfo
59 /* Need this to get around generics array creation restriction */
60 private static class StringOpcodeMap
extends HashMap
<Integer
,FormulaOpCodeMapEntry
> {}
62 public static final int SEPARATORS
= 0;
63 public static final int ARRAY_SEPARATORS
= 1;
64 public static final int UNARY_OPERATORS
= 2;
65 public static final int BINARY_OPERATORS
= 3;
66 public static final int FUNCTIONS
= 4;
67 private final PropertySetMixin m_prophlp
;
68 private static final String __serviceName
= "com.sun.star.report.meta.FormulaParser";
69 private static final String OPERATORS
= "org.pentaho.reporting.libraries.formula.operators.";
71 final private List
<FormulaOpCodeMapEntry
> m_OpCodeMap
= new ArrayList
<FormulaOpCodeMapEntry
>();
72 private XFormulaOpCodeMapper formulaOpCodeMapper
= null;
73 private final Map
<Integer
,FormulaOpCodeMapEntry
> parserAllOpCodes
= new HashMap
<Integer
,FormulaOpCodeMapEntry
>();
74 private final Map
<String
,FormulaOpCodeMapEntry
> parserNames
= new HashMap
<String
,FormulaOpCodeMapEntry
>();
75 private final StringOpcodeMap
[] groupOpCodes
= new StringOpcodeMap
[5];
76 private final List
<FormulaOpCodeMapEntry
> specialOpCodes
= new ArrayList
<FormulaOpCodeMapEntry
>();
77 private int ownTokenCounter
= 1000;
78 private final FormulaOpCodeMapEntry opCodePush
;
79 private final FormulaParser parser
;
81 public SOFormulaParser(final XComponentContext context
)
84 final ClassLoader cl
= java
.lang
.Thread
.currentThread().getContextClassLoader();
85 Thread
.currentThread().setContextClassLoader(this.getClass().getClassLoader());
87 parser
= new FormulaParser();
90 final XFormulaOpCodeMapper mapper
= UnoRuntime
.queryInterface(XFormulaOpCodeMapper
.class, context
.getServiceManager().createInstanceWithContext("simple.formula.FormulaOpCodeMapperObj", context
));
91 FormulaOpCodeMapEntry
[] opCodes
= mapper
.getAvailableMappings(FormulaLanguage
.ODFF
, FormulaMapGroup
.FUNCTIONS
);
92 final DefaultFormulaContext defaultContext
= new DefaultFormulaContext();
93 final FunctionRegistry functionRegistry
= defaultContext
.getFunctionRegistry();
95 String
[] names
= functionRegistry
.getFunctionNames();
96 addOpCodes(names
, opCodes
, FUNCTIONS
);
97 names
= getOperators(defaultContext
, OPERATORS
);
98 opCodes
= mapper
.getAvailableMappings(FormulaLanguage
.ODFF
, FormulaMapGroup
.UNARY_OPERATORS
);
99 addOpCodes(names
, opCodes
, UNARY_OPERATORS
);
100 opCodes
= mapper
.getAvailableMappings(FormulaLanguage
.ODFF
, FormulaMapGroup
.BINARY_OPERATORS
);
101 addOpCodes(names
, opCodes
, BINARY_OPERATORS
);
103 names
= GeneratedFormulaParserConstants
.tokenImage
.clone();
104 for (int i
= 0; i
< names
.length
; i
++)
106 final String token
= names
[i
];
107 if (token
!= null && token
.length() > 0 && token
.charAt(0) == '"')
109 names
[i
] = token
.substring(1, token
.length() - 1);
112 opCodes
= mapper
.getAvailableMappings(FormulaLanguage
.ODFF
, FormulaMapGroup
.SEPARATORS
);
113 addOpCodes(names
, opCodes
, SEPARATORS
, false);
115 opCodes
= mapper
.getAvailableMappings(FormulaLanguage
.ODFF
, FormulaMapGroup
.ARRAY_SEPARATORS
);
116 addOpCodes(names
, opCodes
, ARRAY_SEPARATORS
, false);
118 opCodes
= mapper
.getAvailableMappings(FormulaLanguage
.ODFF
, FormulaMapGroup
.SPECIAL
);
120 for (int i
= 0; i
< opCodes
.length
; i
++)
122 final FormulaOpCodeMapEntry opCode
= opCodes
[i
];
123 parserAllOpCodes
.put(opCode
.Token
.OpCode
, opCode
);
124 specialOpCodes
.add(opCode
);
129 ex
.printStackTrace();
131 opCodePush
= specialOpCodes
.get(FormulaMapGroupSpecialOffset
.PUSH
);
132 Thread
.currentThread().setContextClassLoader(cl
);
133 // use the last parameter of the PropertySetMixin constructor
134 // for your optional attributes if necessary. See the documentation
135 // of the PropertySetMixin helper for further information.
136 // Ensure that your attributes are initialized correctly!
137 m_prophlp
= new PropertySetMixin(context
, this,
138 new Type(com
.sun
.star
.report
.meta
.XFormulaParser
.class), null);
141 // com.sun.star.sheet.XFormulaParser:
142 public com
.sun
.star
.sheet
.FormulaToken
[] parseFormula(String aFormula
, com
.sun
.star
.table
.CellAddress aReferencePos
)
144 final ArrayList
<FormulaToken
> tokens
= new ArrayList
<FormulaToken
>();
145 if (!"=".equals(aFormula
))
148 if (aFormula
.charAt(0) == '=')
150 formula
= aFormula
.substring(1);
156 final ArrayList
<String
> images
= new ArrayList
<String
>();
160 final GeneratedFormulaParserTokenManager tokenParser
= new GeneratedFormulaParserTokenManager(new JavaCharStream(new StringReader(formula
), 1, 1));
161 Token token
= tokenParser
.getNextToken();
162 while (token
.kind
!= GeneratedFormulaParserConstants
.EOF
)
164 final FormulaToken formulaToken
;
165 images
.add(token
.image
);
166 final String upper
= token
.image
.toUpperCase();
167 if (parserNames
.containsKey(upper
))
169 if ("(".equals(token
.image
))
173 else if (")".equals(token
.image
))
177 final FormulaOpCodeMapEntry opCode
= parserNames
.get(upper
);
178 formulaToken
= opCode
.Token
;
180 else if (token
.kind
== GeneratedFormulaParserConstants
.WHITESPACE
)
182 final FormulaOpCodeMapEntry opCode
= specialOpCodes
.get(FormulaMapGroupSpecialOffset
.SPACES
);
183 formulaToken
= opCode
.Token
;
187 formulaToken
= new FormulaToken();
188 formulaToken
.OpCode
= opCodePush
.Token
.OpCode
;
189 formulaToken
.Data
= new Any(Type
.STRING
, token
.image
);
192 tokens
.add(formulaToken
);
193 token
= tokenParser
.getNextToken();
197 final FormulaOpCodeMapEntry opCode
= parserNames
.get(")");
198 while (brackets
-- != 0)
200 formula
= formula
.concat(")");
202 tokens
.add(opCode
.Token
);
207 parser
.parse(formula
);
209 catch (ParseException ex
)
211 boolean found
= false;
212 // error occurred so all token must be bad
213 for (int i
= 0; i
< tokens
.size(); i
++)
215 if (!found
&& ex
.currentToken
!= null && images
.get(i
).equals(ex
.currentToken
.image
))
221 final FormulaToken dest
= new FormulaToken();
222 dest
.OpCode
= specialOpCodes
.get(FormulaMapGroupSpecialOffset
.BAD
).Token
.OpCode
;
223 dest
.Data
= new Any(Type
.STRING
, images
.get(i
));
229 catch (java
.lang
.Exception e
)
232 catch (TokenMgrError e
)
236 return tokens
.toArray(new FormulaToken
[tokens
.size()]);
239 public String
printFormula(com
.sun
.star
.sheet
.FormulaToken
[] aTokens
, com
.sun
.star
.table
.CellAddress aReferencePos
)
241 final StringBuffer ret
= new StringBuffer();
242 for (int i
= 0; i
< aTokens
.length
; i
++)
244 final FormulaToken formulaToken
= aTokens
[i
];
245 if (formulaToken
.OpCode
== opCodePush
.Token
.OpCode
&& !formulaToken
.Data
.equals(Any
.VOID
))
247 ret
.append(formulaToken
.Data
);
249 else if (parserAllOpCodes
.containsKey(formulaToken
.OpCode
))
251 final FormulaOpCodeMapEntry opCode
= parserAllOpCodes
.get(formulaToken
.OpCode
);
252 if (opCode
.Name
.length() > 0)
254 ret
.append(opCode
.Name
);
256 else if (!formulaToken
.Data
.equals(Any
.VOID
))
258 ret
.append(formulaToken
.Data
);
262 return ret
.toString();
265 // com.sun.star.beans.XPropertySet:
266 public com
.sun
.star
.beans
.XPropertySetInfo
getPropertySetInfo()
268 return m_prophlp
.getPropertySetInfo();
271 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
273 m_prophlp
.setPropertyValue(aPropertyName
, aValue
);
276 public Object
getPropertyValue(String aPropertyName
) throws com
.sun
.star
.beans
.UnknownPropertyException
, com
.sun
.star
.lang
.WrappedTargetException
278 return m_prophlp
.getPropertyValue(aPropertyName
);
281 public void addPropertyChangeListener(String aPropertyName
, com
.sun
.star
.beans
.XPropertyChangeListener xListener
) throws com
.sun
.star
.beans
.UnknownPropertyException
, com
.sun
.star
.lang
.WrappedTargetException
283 m_prophlp
.addPropertyChangeListener(aPropertyName
, xListener
);
286 public void removePropertyChangeListener(String aPropertyName
, com
.sun
.star
.beans
.XPropertyChangeListener xListener
) throws com
.sun
.star
.beans
.UnknownPropertyException
, com
.sun
.star
.lang
.WrappedTargetException
288 m_prophlp
.removePropertyChangeListener(aPropertyName
, xListener
);
291 public void addVetoableChangeListener(String aPropertyName
, com
.sun
.star
.beans
.XVetoableChangeListener xListener
) throws com
.sun
.star
.beans
.UnknownPropertyException
, com
.sun
.star
.lang
.WrappedTargetException
293 m_prophlp
.addVetoableChangeListener(aPropertyName
, xListener
);
296 public void removeVetoableChangeListener(String aPropertyName
, com
.sun
.star
.beans
.XVetoableChangeListener xListener
) throws com
.sun
.star
.beans
.UnknownPropertyException
, com
.sun
.star
.lang
.WrappedTargetException
298 m_prophlp
.removeVetoableChangeListener(aPropertyName
, xListener
);
301 // com.sun.star.report.meta.XFormulaParser:
302 public com
.sun
.star
.sheet
.FormulaOpCodeMapEntry
[] getOpCodeMap()
304 return m_OpCodeMap
.toArray(new FormulaOpCodeMapEntry
[m_OpCodeMap
.size()]);
307 public void setOpCodeMap(com
.sun
.star
.sheet
.FormulaOpCodeMapEntry
[] the_value
)
311 public String
getImplementationName()
313 return SOFormulaParser
.class.getName();
316 public boolean supportsService(String sServiceName
)
318 return sServiceName
.equals(__serviceName
);
321 public String
[] getSupportedServiceNames()
323 return getServiceNames();
327 * This method is a simple helper function to used in the static component initialisation functions as well as
328 * in getSupportedServiceNames.
330 public static String
[] getServiceNames()
338 public XFormulaOpCodeMapper
getFormulaOpCodeMapper()
340 if (formulaOpCodeMapper
== null)
342 formulaOpCodeMapper
= new SOFormulaOpCodeMapper(this);
345 return formulaOpCodeMapper
;
348 private void addOpCodes(String
[] names
, FormulaOpCodeMapEntry
[] opCodes
, int group
)
350 addOpCodes(names
, opCodes
, group
, true);
353 private void addOpCodes(String
[] names
, FormulaOpCodeMapEntry
[] opCodes
, int group
, boolean add
)
355 groupOpCodes
[group
] = new StringOpcodeMap();
356 for (int j
= 0; j
< names
.length
; j
++)
358 FormulaOpCodeMapEntry opCode
= null;
360 for (; i
< opCodes
.length
; i
++)
363 if (names
[j
].equals(opCode
.Name
))
368 if (i
>= opCodes
.length
)
374 final FormulaToken token
= new FormulaToken(ownTokenCounter
++, Any
.VOID
);
375 opCode
= new FormulaOpCodeMapEntry(names
[j
], token
);
377 parserNames
.put(names
[j
], opCode
);
378 parserAllOpCodes
.put(opCode
.Token
.OpCode
, opCode
);
379 groupOpCodes
[group
].put(opCode
.Token
.OpCode
, opCode
);
383 public Map
<String
,FormulaOpCodeMapEntry
> getNames()
388 public Map
<Integer
,FormulaOpCodeMapEntry
> getGroup(int group
)
390 return groupOpCodes
[group
];
393 private String
[] getOperators(DefaultFormulaContext defaultContext
, final String _kind
)
395 final ArrayList
<String
> ops
= new ArrayList
<String
>();
396 final Configuration configuration
= defaultContext
.getConfiguration();
397 final Iterator iter
= configuration
.findPropertyKeys(_kind
);
398 while (iter
.hasNext())
400 final String configKey
= (String
) iter
.next();
401 if (!configKey
.endsWith(".class"))
405 final String operatorClass
= configuration
.getConfigProperty(configKey
);
406 if (operatorClass
== null)
410 if (operatorClass
.length() == 0)
414 final String tokenKey
= configKey
.substring(0, configKey
.length() - ".class".length()) + ".token";
415 final String token
= configuration
.getConfigProperty(tokenKey
);
420 ops
.add(token
.trim());
422 return ops
.toArray(new String
[ops
.size()]);
425 public List
<FormulaOpCodeMapEntry
> getSpecialOpCodes()
427 return specialOpCodes
;