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