Package com.orientechnologies.orient.core.sql.filter

Source Code of com.orientechnologies.orient.core.sql.filter.OSQLFilter

/*
* Copyright 1999-2010 Luca Garulli (l.garulli--at--orientechnologies.com)
*
* 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 com.orientechnologies.orient.core.sql.filter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.orientechnologies.common.parser.OStringParser;
import com.orientechnologies.orient.core.command.OCommandToParse;
import com.orientechnologies.orient.core.db.record.ODatabaseRecord;
import com.orientechnologies.orient.core.exception.OCommandExecutionException;
import com.orientechnologies.orient.core.exception.OQueryParsingException;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.record.ORecordSchemaAware;
import com.orientechnologies.orient.core.serialization.serializer.OStringSerializerHelper;
import com.orientechnologies.orient.core.sql.OCommandExecutorSQLAbstract;
import com.orientechnologies.orient.core.sql.OSQLHelper;
import com.orientechnologies.orient.core.sql.operator.OQueryOperator;

/**
* Parsed query. It's built once a query is parsed.
*
* @author Luca Garulli
*
*/
public class OSQLFilter extends OCommandToParse {
  protected ODatabaseRecord              database;
  protected List<String>                targetRecords;
  protected Map<String, String>          targetClusters;
  protected Map<OClass, String>          targetClasses;
  protected Set<OProperty>              properties  = new HashSet<OProperty>();
  protected OSQLFilterCondition          rootCondition;
  protected List<String>                recordTransformed;
  private List<OSQLFilterItemParameter>  parameterItems;
  private int                            braces;

  public OSQLFilter(final ODatabaseRecord iDatabase, final String iText) {
    try {
      database = iDatabase;
      text = iText.trim();
      textUpperCase = text.toUpperCase();

      if (extractTargets()) {
        // IF WHERE EXISTS EXTRACT CONDITIONS

        final StringBuilder word = new StringBuilder();
        int newPos = OSQLHelper.nextWord(text, textUpperCase, currentPos, word, true);
        if (newPos > -1 && word.toString().equals("WHERE")) {
          currentPos = newPos;
          rootCondition = extractConditions(null);
        }
      }
    } catch (OQueryParsingException e) {
      if (e.getText() == null)
        // QUERY EXCEPTION BUT WITHOUT TEXT: NEST IT
        throw new OQueryParsingException("Error on parsing query", text, currentPos, e);

      throw e;
    } catch (Throwable t) {
      throw new OQueryParsingException("Error on parsing query", text, currentPos, t);
    }
  }

  public boolean evaluate(final ODatabaseRecord iDatabase, final ORecordSchemaAware<?> iRecord) {
    if (targetClasses != null) {
      final OClass cls = targetClasses.keySet().iterator().next();
      // CHECK IF IT'S PART OF THE REQUESTED CLASS
      if (iRecord.getSchemaClass() == null || !iRecord.getSchemaClass().isSubClassOf(cls))
        // EXCLUDE IT
        return false;
    }

    if (rootCondition == null)
      return true;

    return (Boolean) rootCondition.evaluate(iRecord);
  }

  private boolean extractTargets() {
    jumpWhiteSpaces();

    if (currentPos == -1)
      throw new OQueryParsingException("No query target found", text, 0);

    targetRecords = new ArrayList<String>();

    if (Character.isDigit(text.charAt(currentPos))) {
      // UNIQUE RID
      final StringBuilder word = new StringBuilder();
      currentPos = OSQLHelper.nextWord(text, textUpperCase, currentPos, word, true);

      targetRecords.add(word.toString());

    } else if (text.charAt(currentPos) == OStringSerializerHelper.COLLECTION_BEGIN) {
      // COLLECTION OF RIDS
      currentPos = OStringSerializerHelper.getCollection(text, currentPos, targetRecords);
    } else {
      String subjectName;
      String alias;
      String subjectToMatch;
      int newPos;

      final StringBuilder word = new StringBuilder();
      currentPos = OSQLHelper.nextWord(text, textUpperCase, currentPos, word, true);

      while (currentPos > -1 && (targetClasses == null && targetClusters == null)) {
        subjectName = word.toString();

        newPos = OSQLHelper.nextWord(text, textUpperCase, currentPos, word, true);
        if (newPos > -1 && word.toString().equals("AS")) {
          currentPos = newPos;

          newPos = OSQLHelper.nextWord(text, textUpperCase, currentPos, word, true);
          if (newPos == -1)
            throw new OQueryParsingException("No alias found. Example: SELECT FROM Customer AS c", text, currentPos);

          currentPos = newPos;

          alias = word.toString();

        } else
          alias = subjectName;

        subjectToMatch = subjectName;
        if (subjectToMatch.startsWith(OCommandExecutorSQLAbstract.CLUSTER_PREFIX)) {
          // REGISTER AS CLUSTER
          if (targetClusters == null)
            targetClusters = new HashMap<String, String>();
          targetClusters.put(subjectName.substring(OCommandExecutorSQLAbstract.CLUSTER_PREFIX.length()), alias);
        } else {
          if (subjectToMatch.startsWith(OCommandExecutorSQLAbstract.CLASS_PREFIX))
            // REGISTER AS CLASS
            subjectName = subjectName.substring(OCommandExecutorSQLAbstract.CLASS_PREFIX.length());

          // REGISTER AS CLASS
          if (targetClasses == null)
            targetClasses = new HashMap<OClass, String>();

          OClass cls = database.getMetadata().getSchema().getClass(subjectName);
          if (cls == null)
            throw new OCommandExecutionException("Class '" + subjectName + "' was not found");

          targetClasses.put(cls, alias);
        }
      }
    }

    return currentPos < text.length();
  }

  private OSQLFilterCondition extractConditions(final OSQLFilterCondition iParentCondition) {
    OSQLFilterCondition currentCondition = extractCondition();

    // CHECK IF THERE IS ANOTHER CONDITION ON RIGHT
    if (!jumpWhiteSpaces())
      // END OF TEXT
      return currentCondition;

    if (currentPos > -1 && text.charAt(currentPos) == ')')
      return currentCondition;

    OQueryOperator nextOperator = extractConditionOperator();

    OSQLFilterCondition parentCondition = new OSQLFilterCondition(currentCondition, nextOperator);

    parentCondition.right = extractConditions(parentCondition);

    return parentCondition;
  }

  protected OSQLFilterCondition extractCondition() {
    if (!jumpWhiteSpaces())
      // END OF TEXT
      return null;

    // CREATE THE CONDITION OBJECT
    return new OSQLFilterCondition(extractConditionItem(), extractConditionOperator(), extractConditionItem());
  }

  private OQueryOperator extractConditionOperator() {
    if (!jumpWhiteSpaces())
      // END OF PARSING: JUST RETURN
      return null;

    if (text.charAt(currentPos) == ')')
      // FOUND ')': JUST RETURN
      return null;

    String word;
    word = nextWord(true, " 0123456789'\"");

    for (OQueryOperator op : OSQLHelper.getRecordOperators()) {
      if (word.startsWith(op.keyword)) {
        final List<String> params = new ArrayList<String>();
        // CHECK FOR PARAMETERS
        if (word.length() > op.keyword.length() && word.charAt(op.keyword.length()) == OStringSerializerHelper.PARENTHESIS_BEGIN) {
          int paramBeginPos = currentPos - (word.length() - op.keyword.length());
          currentPos = OStringSerializerHelper.getParameters(text, paramBeginPos, params);
        } else if (!word.equals(op.keyword))
          throw new OQueryParsingException("Malformed usage of operator '" + op.toString() + "'. Parsed operator is: " + word);

        try {
          return op.configure(params);
        } catch (Exception e) {
          throw new OQueryParsingException("Syntax error using the operator '" + op.toString() + "'. Syntax is: " + op.getSyntax());
        }
      }
    }

    throw new OQueryParsingException("Unknown operator " + word, text, currentPos);
  }

  private Object extractConditionItem() {
    Object result = null;
    String[] words = nextValue(true);
    if (words == null)
      return null;

    if (words[0].length() > 0 && words[0].charAt(0) == OStringSerializerHelper.PARENTHESIS_BEGIN) {
      braces++;

      // SUB-CONDITION
      currentPos = currentPos - words[0].length() + 1;

      OSQLFilterCondition subCondition = extractConditions(null);

      // OSQLFilterCondition subCondition = new OSQLFilterCondition(extractConditionItem(), extractConditionOperator(),
      // extractConditionItem());

      if (!jumpWhiteSpaces() || text.charAt(currentPos) == ')')
        braces--;
      currentPos++;

      return subCondition;
    } else if (words[0].charAt(0) == OStringSerializerHelper.COLLECTION_BEGIN) {
      // COLLECTION OF ELEMENTS
      currentPos = currentPos - words[0].length();

      final List<String> stringItems = new ArrayList<String>();
      currentPos = OStringSerializerHelper.getCollection(text, currentPos, stringItems);

      final List<Object> coll = new ArrayList<Object>();
      for (String s : stringItems) {
        coll.add(OSQLHelper.parseValue(this, database, this, s));
      }

      currentPos++;

      return coll;

    } else if (words[0].startsWith(OSQLFilterItemFieldAll.NAME + OStringSerializerHelper.PARENTHESIS_BEGIN)) {

      result = new OSQLFilterItemFieldAll(this, words[1]);

    } else if (words[0].startsWith(OSQLFilterItemFieldAny.NAME + OStringSerializerHelper.PARENTHESIS_BEGIN)) {

      result = new OSQLFilterItemFieldAny(this, words[1]);

    } else {

      if (words[0].equals("NOT")) {
        // GET THE NEXT VALUE
        String[] nextWord = nextValue(true);
        if (nextWord != null && nextWord.length == 2) {
          words[1] = words[1] + " " + nextWord[1];

          if (words[1].endsWith(")"))
            words[1] = words[1].substring(0, words[1].length() - 1);
        }
      }

      result = OSQLHelper.parseValue(this, database, this, words[1]);
    }

    return result;
  }

  public Map<String, String> getTargetClusters() {
    return targetClusters;
  }

  public Map<OClass, String> getTargetClasses() {
    return targetClasses;
  }

  public List<String> getTargetRecords() {
    return targetRecords;
  }

  public OSQLFilterCondition getRootCondition() {
    return rootCondition;
  }

  private String[] nextValue(final boolean iAdvanceWhenNotFound) {
    if (!jumpWhiteSpaces())
      return null;

    int begin = currentPos;
    char c;
    char stringBeginCharacter = ' ';
    int openBraces = 0;
    int openBraket = 0;
    boolean escaped = false;
    boolean escapingOn = false;

    for (; currentPos < text.length(); ++currentPos) {
      c = text.charAt(currentPos);

      if (stringBeginCharacter == ' ' && (c == '"' || c == '\'')) {
        // QUOTED STRING: GET UNTIL THE END OF QUOTING
        stringBeginCharacter = c;
      } else if (stringBeginCharacter != ' ') {
        // INSIDE TEXT
        if (c == '\\') {
          escapingOn = true;
          escaped = true;
        } else {
          if (c == stringBeginCharacter && !escapingOn) {
            stringBeginCharacter = ' ';

            if (openBraket == 0 && openBraces == 0) {
              if (iAdvanceWhenNotFound)
                currentPos++;
              break;
            }
          }

          if (escapingOn)
            escapingOn = false;
        }
      } else if (c == '#' && currentPos == begin) {
        // BEGIN OF RID
      } else if (c == '(') {
        openBraces++;
      } else if (c == ')') {
        openBraces--;
      } else if (c == OStringSerializerHelper.COLLECTION_BEGIN) {
        openBraket++;
      } else if (c == OStringSerializerHelper.COLLECTION_END) {
        openBraket--;
        if (openBraket == 0 && openBraces == 0) {
          currentPos++;
          break;
        }
      } else if (c == ' ' && openBraces == 0) {
        break;
      } else if (!Character.isLetter(c) && !Character.isDigit(c) && c != '.' && c != ':' && c != '-' && c != '_' && c != '+'
          && c != '@' && openBraces == 0 && openBraket == 0) {
        if (iAdvanceWhenNotFound)
          currentPos++;
        break;
      }
    }

    if (escaped)
      return new String[] { OStringSerializerHelper.decode(textUpperCase.substring(begin, currentPos)),
          OStringSerializerHelper.decode(text.substring(begin, currentPos)) };
    else
      return new String[] { textUpperCase.substring(begin, currentPos), text.substring(begin, currentPos) };
  }

  private String nextWord(final boolean iForceUpperCase, final String iSeparators) {
    StringBuilder word = new StringBuilder();
    currentPos = OSQLHelper.nextWord(text, textUpperCase, currentPos, word, iForceUpperCase, iSeparators);
    return word.toString();
  }

  private boolean jumpWhiteSpaces() {
    currentPos = OStringParser.jumpWhiteSpaces(text, currentPos);
    return currentPos < text.length();
  }

  @Override
  public String toString() {
    if (rootCondition != null)
      return "Parsed: " + rootCondition.toString();
    return "Unparsed: " + text;
  }

  /**
   * Binds parameters.
   *
   * @param iArgs
   */
  public void bindParameters(final Map<Object, Object> iArgs) {
    if (parameterItems == null || iArgs == null || iArgs.size() == 0)
      return;

    if (iArgs.size() < parameterItems.size())
      throw new OCommandExecutionException("Can't execute because " + (parameterItems.size() - iArgs.size())
          + " parameter(s) are unbounded");

    String paramName;
    for (Entry<Object, Object> entry : iArgs.entrySet()) {
      if (entry.getKey() instanceof Integer)
        parameterItems.get(((Integer) entry.getKey())).setValue(entry.setValue(entry.getValue()));
      else {
        paramName = entry.getKey().toString();
        for (OSQLFilterItemParameter value : parameterItems) {
          if (value.getName().equalsIgnoreCase(paramName)) {
            value.setValue(entry.getValue());
            break;
          }
        }
      }
    }
  }

  public OSQLFilterItemParameter addParameter(final String iName) {
    final String name;
    if (iName.charAt(0) == OStringSerializerHelper.PARAMETER_NAMED) {
      name = iName.substring(1);

      // CHECK THE PARAMETER NAME IS CORRECT
      if (!OStringSerializerHelper.isAlphanumeric(name)) {
        throw new OQueryParsingException("Parameter name '" + name + "' is invalid, only alphanumeric characters are allowed");
      }
    } else
      name = iName;

    final OSQLFilterItemParameter param = new OSQLFilterItemParameter(name);

    if (parameterItems == null)
      parameterItems = new ArrayList<OSQLFilterItemParameter>();

    parameterItems.add(param);
    return param;
  }
}
TOP

Related Classes of com.orientechnologies.orient.core.sql.filter.OSQLFilter

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.