Package org.openntf.formula

Source Code of org.openntf.formula.FormulaParser$FormulaCache$FCMapEntryComparator

/*
* © Copyright FOCONIS AG, 2014
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*/
package org.openntf.formula;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.openntf.formula.parse.AtFormulaParserImpl;
import org.openntf.formula.parse.ParseException;
import org.openntf.formula.parse.TokenMgrError;

/**
* This class adds additonal functionality to the AtFormulaParserImpl (which is auto generated).
*
* @author Roland Praml, Foconis AG
*
*/
public abstract class FormulaParser {

  /** the formatter to format/parse date time values while parsing the formula */
  protected Formatter formatter;

  /** the functionFactory */
  protected FunctionFactory functionFactory;

  /** the includeProvider for {@literal @}include function */
  protected FormulaProvider<ASTNode> includeProvider;

  protected Map<String, Function> customFunc;
  protected boolean parsing = false;

  public static final int MAX_FORMULA_CACHESIZE = 512;
  public static final int REDUCE_FORMULA_CACHESIZE_TO = 256;

  protected class FormulaCache {
    class FormulaCacheEntry {
      ASTNode node;
      int usageCount;

      FormulaCacheEntry(final ASTNode n) {
        node = n;
        usageCount = 1;
      }
    }

    class FCMapEntryComparator implements Comparator<Object> {
      public int compare(final Object ent1, final Object ent2) {
        if (!(ent1 instanceof Map.Entry) || !(ent2 instanceof Map.Entry))
          throw new IllegalArgumentException("FCMapEntryComparator");
        @SuppressWarnings("unchecked")
        int us1 = ((Map.Entry<String, FormulaCacheEntry>) ent1).getValue().usageCount;
        @SuppressWarnings("unchecked")
        int us2 = ((Map.Entry<String, FormulaCacheEntry>) ent2).getValue().usageCount;
        return (us1 > us2) ? 1 : (us1 < us2) ? -1 : 0;
      }
    }

    private Map<String, FormulaCacheEntry> cacheMap = new HashMap<String, FormulaCacheEntry>();

    ASTNode get(final String key) {
      FormulaCacheEntry fce = cacheMap.get(key);
      if (fce == null)
        return null;
      fce.usageCount++;
      return fce.node;
    }

    void reset() {
      cacheMap.clear();
    }

    @SuppressWarnings("unchecked")
    void put(final String key, final ASTNode node) {
      if (cacheMap.size() > MAX_FORMULA_CACHESIZE) {
        Object[] arr = cacheMap.entrySet().toArray();
        Arrays.sort(arr, new FCMapEntryComparator());
        int numToThrow = cacheMap.size() - REDUCE_FORMULA_CACHESIZE_TO;
        String[] toThrow = new String[numToThrow];
        for (int i = 0; i < numToThrow; i++)
          toThrow[i] = ((Map.Entry<String, FormulaCacheEntry>) arr[i]).getKey();
        for (int i = 0; i < numToThrow; i++)
          cacheMap.remove(toThrow[i]);
        Set<Map.Entry<String, FormulaCacheEntry>> cacheSet = cacheMap.entrySet();
        for (Map.Entry<String, FormulaCacheEntry> ent : cacheSet)
          ent.getValue().usageCount = 1;
      }
      cacheMap.put(key, new FormulaCacheEntry(node));
    }
  }

  protected FormulaCache ntfFormulaCache = new FormulaCache();
  protected FormulaCache focFormulaCache = new FormulaCache();

  /**
   * Returns a the Formatter for this parser
   *
   * @return the formatter
   */
  public Formatter getFormatter() {
    return formatter;
  }

  /**
   * If you parse multiple formulas, you should call "reset" to clear predefined formulas!
   */
  public void reset() {
    customFunc = new HashMap<String, Function>();
    ntfFormulaCache.reset();
    focFormulaCache.reset();
  }

  /**
   * Querys the functionFactory for a function. You can declare custom functions. These functions overrides implemented functions (if it
   * is not an AST-Function)
   *
   * @param funcName
   *            the functionName (lowercase)
   * @return the function or null
   */
  public Function getFunctionLC(final String funcName) {

    Function func = customFunc.get(funcName);
    if (func != null) {
      return func;
    }
    return functionFactory.getFunction(funcName);
  }

  /**
   * Declares a new function. This mehtod is called by the <code>@Function(methodDecl ; [variables])</code> formula
   *
   * @param func
   *            the function to declare
   */
  public void declareFunction(final Function func) {
    String funcName = func.getImage();
    customFunc.put(funcName.toLowerCase(), func);
  }

  /**
   * Parses the given formoula
   *
   * @param reader
   *            the reader where the formula is read
   * @param useFocFormula
   *            parses the formula in a special mode needed by Foconis. (You can inline formulas in normal text)
   * @return an AST-node that can be evaluated
   * @throws ParseException
   *             if the formula contains errors
   */
  final public ASTNode parse(final Reader reader, final boolean useFocFormula) throws FormulaParseException {
    if (parsing) {
      return getCopy().parse(reader, useFocFormula);
    }

    parsing = true;
    try {
      ReInit(reader);
      if (useFocFormula) {
        return parseFocFormula();
      } else {
        return parseFormula();
      }
    } catch (TokenMgrError e) {
      throw new ParseException(e.getMessage(), e);
    } finally {
      parsing = false;
    }
  }

  /**
   * Parses the given formula from an inputStream
   *
   * @param sr
   *            the input stream where the formula is read
   * @param encoding
   *            encoding of the stream
   * @param useFocFormula
   *            see: {@link #parse(Reader, boolean)}
   * @return see: {@link #parse(Reader, boolean)}
   * @throws ParseException
   *             see: {@link #parse(Reader, boolean)}
   */
  final public ASTNode parse(final InputStream sr, final String encoding, final boolean useFocFormula) throws FormulaParseException {
    if (parsing) {
      return getCopy().parse(sr, useFocFormula);
    }
    parsing = true;
    try {
      ReInit(sr, encoding);
      if (useFocFormula) {
        return parseFocFormula();
      } else {
        return parseFormula();
      }
    } catch (TokenMgrError e) {
      throw new ParseException(e.getMessage());

    } finally {
      parsing = false;
    }
  }

  /**
   * return a copy of the current parser. This is needed for include, because we cannot start a new parse task unless the old one is
   * terminated
   *
   * @return
   */
  private FormulaParser getCopy() {
    AtFormulaParserImpl parser = new AtFormulaParserImpl(new java.io.StringReader(""));
    parser.reset();
    parser.formatter = formatter;
    parser.functionFactory = functionFactory;
    parser.includeProvider = includeProvider;
    parser.customFunc = customFunc;
    parser.focFormulaCache = focFormulaCache;
    parser.ntfFormulaCache = ntfFormulaCache;
    return parser;
  }

  /**
   * Parses the given formula from an inputStream with default encoding
   *
   * @param sr
   *            the input stream where the formula is read
   * @param useFocFormula
   *            see: {@link #parse(Reader, boolean)}
   * @return see: {@link #parse(Reader, boolean)}
   * @throws ParseException
   *             see: {@link #parse(Reader, boolean)}
   */
  final public ASTNode parse(final InputStream sr, final boolean useFocFormula) throws FormulaParseException {
    return parse(sr, null, useFocFormula);
  }

  /**
   * Parses the given formula from an inputStream
   *
   * @param formula
   *            String with formula
   * @param useFocFormula
   *            see: {@link #parse(Reader, boolean)}
   * @return see: {@link #parse(Reader, boolean)}
   * @throws ParseException
   *             see: {@link #parse(Reader, boolean)}
   */
  final public ASTNode parse(final String formula, final boolean useFocFormula) throws FormulaParseException {
    FormulaCache formulaCache = useFocFormula ? focFormulaCache : ntfFormulaCache;
    ASTNode node = formulaCache.get(formula);
    if (node == null) {
      StringReader sr = new java.io.StringReader(formula);
      node = parse(sr, useFocFormula);
      node.setFormula(formula);
      formulaCache.put(formula, node);
    }
    return node;
  }

  /**
   * Parses the given formula from an inputStream
   *
   * @param formula
   *            String with formul
   * @return see: {@link #parse(Reader, boolean)}
   * @throws ParseException
   *             see: {@link #parse(Reader, boolean)}
   */
  final public ASTNode parse(final String formula) throws FormulaParseException {
    ASTNode node = parse(formula, false);
    node.setFormula(formula);
    return node;
  }

  /**
   * Reinits the parser
   *
   * @param sr
   *            reader
   */
  protected abstract void ReInit(Reader reader);

  /**
   * Reinits the parser
   *
   * @param stream
   *            stream with formula
   * @param encoding
   *            encoding of the stream
   */
  protected abstract void ReInit(java.io.InputStream stream, String encoding);

  /**
   * Parses the formula
   *
   * @return AST-Node
   * @throws ParseException
   *             if formula contains errors
   */
  abstract public ASTNode parseFormula() throws FormulaParseException;

  /**
   * Parses the formula in Foconis-mode (inline formulas are supported)
   *
   * @return AST-Node
   * @throws ParseException
   *             if formula contains errors
   */
  abstract public ASTNode parseFocFormula() throws FormulaParseException;

  public void setIncludeProvider(final FormulaProvider<ASTNode> prov) {
    includeProvider = prov;
  }

  /**
   * get a node to include
   *
   */
  public ASTNode getInclude(final String key) {
    if (includeProvider != null) {
      return includeProvider.get(key);
    }
    return null;
  }
}
TOP

Related Classes of org.openntf.formula.FormulaParser$FormulaCache$FCMapEntryComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.