Package org.exolab.castor.dtx

Source Code of org.exolab.castor.dtx.DTXQuery

/**
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
*    statements and notices.  Redistributions must also contain a
*    copy of this document.
*
* 2. Redistributions in binary form must reproduce the
*    above copyright notice, this list of conditions and the
*    following disclaimer in the documentation and/or other
*    materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
*    products derived from this Software without prior written
*    permission of Intalio, Inc.  For written permission,
*    please contact info@exolab.org.
*
* 4. Products derived from this Software may not be called "Exolab"
*    nor may "Exolab" appear in their names without prior written
*    permission of Intalio, Inc. Exolab is a registered
*    trademark of Intalio, Inc.
*
* 5. Due credit should be given to the Exolab Project
*    (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
* INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 2000 (C) Intalio, Inc. All Rights Reserved.
*
* $Id: DTXQuery.java,v 1.1.1.1 2003/03/03 07:08:05 kvisco Exp $
*/

package org.exolab.castor.dtx;

import java.io.*;
import java.util.*;
import java.sql.*;
import org.xml.sax.*;
import org.xml.sax.helpers.AttributeListImpl;
import org.exolab.castor.jdo.*;
import org.exolab.castor.jdo.engine.*;
import org.exolab.castor.mapping.ClassDescriptor;
import org.exolab.castor.mapping.FieldDescriptor;
import org.exolab.castor.mapping.xml.ClassMapping;
import org.exolab.castor.mapping.xml.FieldMapping;
import org.exolab.castor.mapping.xml.Sql;
import org.exolab.castor.mapping.xml.BindXml;
import org.exolab.castor.mapping.xml.MapTo;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.util.*;
import org.exolab.castor.xml.schema.*;
import org.exolab.castor.xml.schema.reader.*;
import org.exolab.castor.persist.spi.PersistenceFactory;
import org.exolab.castor.persist.spi.QueryExpression;

/**
* A single query that takes OQL query language, reads data from an
* RDBMS, and returns the results as SAX events. The query can be
* parameterized, and be re-used with different bound values.
*
* There are currently some severe limitations on the contents of the
* OQL query. The query needs to be a SELECT and must return a single
* class.  However, it can handle multiple returned objects, which are
* sent to the DocumentHandler as multiple documents.
*
* DTXQuery's can't be created on their own; use the DTXEngine to
* prepare a new query.
*
* An additional note: the bind() methods take the parameters in order
* that they appear, NOT using the number value of the parameter
* name. So, if you have a query like:
*
* <pre>... WHERE foo.id = $2 AND bar.name = $1 ... </pre>
*
* then the statement bind(1, 12) will bind the value 12 to the first
* parameter, even though it's named "$2." This may or may not be
* changed in future releases.
*
* @author <a href="0@intalio.com">Evan Prodromou</a>
* @version $Revision: 1.1.1.1 $ $Date: 2003/03/03 07:08:05 $
*/

public class DTXQuery {

    protected DTXEngine _eng = null;
    protected DocumentHandler _handler = null;
    protected PrintWriter _logWriter = null;
    protected PreparedStatement _stmt = null;
    protected String _objName = null;
    protected String _objType = null;
    protected ClassMapping _clsMapping = null;
    protected ArrayList _ids = null;
    protected int _lastCol = 0;
    protected HashMap _cols = null;
    protected HashMap _classes = null;

    /**
     * Set the DocumentHandler that will receive the results (as SAX
     * events) for this query. By default, the query will use the
     * document handler of its DTXEngine.
     *
     * Changing the DocumentHandler in the middle of a query is
     * ill-advised.
     *
     * @param handler The DocumentHandler to use.
     */

    public void setHandler(DocumentHandler handler) {
  _handler = handler;
    }

    /**
     * Set the DocumentHandler that will receive the results (as SAX
     * events) for this query. By default, the query will use the
     * document handler of its DTXEngine.
     *
     * @param handler The DocumentHandler to use.
     */

    public void setLogWriter(PrintWriter logWriter) {
  _logWriter = logWriter;
    }

    /**
     * Binds an Object value to a parameter in the query.
     *
     * @param param 1-based index of the param (see note above).
     * @param value Object to bind.
     */

    public void bind(int param, Object value) throws DTXException {
  if (_stmt == null) {
      throw new DTXException("No prepared statement.");
  }

  try {
      _stmt.setObject(param, value);
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /**
     * Binds an String value to a parameter in the query.
     *
     * @param param 1-based index of the param (see note above).
     * @param value String to bind.
     */

    public void bind(int param, String value) throws DTXException {
  if (_stmt == null) {
      throw new DTXException("No prepared statement.");
  }

  try {
      _stmt.setString(param, value);
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /**
     * Binds an integer value to a parameter in the query.
     *
     * @param param 1-based index of the param (see note above).
     * @param value int to bind.
     */

    public void bind(int param, int value) throws DTXException {
  if (_stmt == null) {
      throw new DTXException("No prepared statement.");
  }

  try {
      _stmt.setInt(param, value);
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /**
     * Binds a long integer value to a parameter in the query.
     *
     * @param param 1-based index of the param (see note above).
     * @param value long integer to bind.
     */

    public void bind(int param, long value) throws DTXException {
  if (_stmt == null) {
      throw new DTXException("No prepared statement.");
  }

  try {
      _stmt.setLong(param, value);
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /**
     * Binds a float value to a parameter in the query.
     *
     * @param param 1-based index of the param (see note above).
     * @param value float to bind.
     */

    public void bind(int param, float value) throws DTXException {
  if (_stmt == null) {
      throw new DTXException("No prepared statement.");
  }

  try {
      _stmt.setFloat(param, value);
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /**
     * Binds a double value to a parameter in the query.
     *
     * @param param 1-based index of the param (see note above).
     * @param value double to bind.
     */

    public void bind(int param, double value) throws DTXException {
  if (_stmt == null) {
      throw new DTXException("No prepared statement.");
  }

  try {
      _stmt.setDouble(param, value);
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /**
     * Binds a boolean value to a parameter in the query.
     *
     * @param param 1-based index of the param (see note above).
     * @param value boolean to bind.
     */

    public void bind(int param, boolean value) throws DTXException {
  if (_stmt == null) {
      throw new DTXException("No prepared statement.");
  }

  try {
      _stmt.setBoolean(param, value);
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /**
     * This method executes the query. All results of the query are
     * sent to the DocumentHandler specified by setHandler() as SAX
     * events.
     *
     * @param handler The DocumentHandler to use.
     */

    public void execute() throws DTXException {
  if (_stmt == null) {
      throw new DTXException("No prepared statement.");
  }

  try {
      ResultSet rs = _stmt.executeQuery();
      emitSaxEvents(rs);
      rs.close();
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /* Package scope */

    /* Default do-nothing constructor. */

    DTXQuery() {
    }

    /* Sets the engine to use for getting connections and
       descriptors. */

    void setEngine(DTXEngine eng) {
  _eng = eng;
    }

    /* Prepares this query for use. Parses the OQL into SQL (plus some
       residual information), connects to the database and prepares a
       SQL statement. */

    void prepare(String oql) throws DTXException {
  try {
      String sql = parseOQL(oql);
      Connection conn = _eng.getConnection();
      _stmt = conn.prepareStatement(sql);
  } catch (SQLException sqle) {
      throw new DTXException(sqle);
  }
    }

    /* protected */

    /* Translates a JDBC resultset into SAX events. Uses the recursive
       helper to get started. */

    protected void emitSaxEvents(ResultSet rs) throws DTXException {

  try {
      if (rs.next()) {
    emitSaxInt(rs, 0);
      }
  } catch (Exception e) {
      e.printStackTrace(_logWriter);
      throw new DTXException(e);
  }
    }

    /* Recursive SAX emitter. Emits top-level elements, attributes,
       and simple (non-object) elements, then descends recursively
       into lower and lower child elements.

       This code is -highly- loopy, and it makes some assumptions
       about the ordering of sub-elements that may be unacceptable.
    */

    protected boolean emitSaxInt(ResultSet rs, int idIndex) throws DTXException {

  boolean hasValue = true;

  try {
      String initParentValue = null;
      String parentValue = null;
      int parentColNum = 0;

      if (idIndex != 0) {
    String parentCol = (String) _ids.get(idIndex - 1);
    Integer parentColInt = (Integer) _cols.get(parentCol);
    parentColNum = parentColInt.intValue();
    initParentValue = rs.getString(parentColNum);
    parentValue = initParentValue;
      }

      String idCol = (String) _ids.get(idIndex);
      Integer idColNum = (Integer) _cols.get(idCol);

      DTXClassDescriptor desc = (DTXClassDescriptor) _classes.get(idCol);
      ClassMapping clsMapping = desc.getClassMapping();
      String elementName = null;

      if (clsMapping.getMapTo() == null ||
    clsMapping.getMapTo().getXml() == null) {
    elementName = clsMapping.getName();
      } else {
    elementName = clsMapping.getMapTo().getXml();
      }

      String[] attrCols = desc.getAttrCols();
      String[] simpleElementCols = desc.getSimpleElementCols();
      String textCol = desc.getTextCol();

      while (hasValue && (idIndex == 0 || parentValue.equalsIgnoreCase(initParentValue))) {
    if (idIndex == 0) {
        _handler.startDocument();
    }

    String idVal = rs.getString(idColNum.intValue());

    AttributeListImpl attrs = new AttributeListImpl();

    for (int i = 0; i < attrCols.length; i++) {
        String attrCol = attrCols[i];
        Integer attrColNum = (Integer) _cols.get(attrCol);
        FieldMapping field = desc.getAttr(attrCol);
        String attrName = null;
        if (field.getBindXml() == null || field.getBindXml().getName() == null) {
      attrName = field.getName();
        } else {
      attrName = field.getBindXml().getName();
        }

        String attrValue = rs.getString(attrColNum.intValue());
        attrs.addAttribute(attrName, "CDATA", attrValue);
    }

    _handler.startElement(elementName, attrs);

    for (int j = 0; j < simpleElementCols.length; j++) {
        String simpleElementCol = simpleElementCols[j];
        Integer elementColNum = (Integer) _cols.get(simpleElementCol);
        FieldMapping elField = desc.getSimpleElement(simpleElementCol);
        String elName = null;
        if (elField.getBindXml() == null || elField.getBindXml().getName() == null) {
      elName = elField.getName();
        } else {
      elName = elField.getBindXml().getName();
        }

        String elValue = rs.getString(elementColNum.intValue());
        _handler.startElement(elName, new AttributeListImpl());
        _handler.characters(elValue.toCharArray(), 0, elValue.length());
        _handler.endElement(elName);
    }

    if (idIndex < _ids.size() - 1) {
        hasValue = emitSaxInt(rs, idIndex + 1);
    }

    if (textCol != null) {
        Integer textColNum = (Integer) _cols.get(textCol);
        String textColValue = rs.getString(textColNum.intValue());
        _handler.characters(textColValue.toCharArray(), 0, textColValue.length());
    }

    _handler.endElement(elementName);

    if (idIndex == 0) {
        _handler.endDocument();
    }

    String newIdVal = rs.getString(idColNum.intValue());

    // Check to see if sub-call advanced this for us.

    if (newIdVal.equalsIgnoreCase(idVal)) {
        hasValue = rs.next();
    }
    if (hasValue) {
        if (idIndex != 0) {
      parentValue = rs.getString(parentColNum);
        }
    }
      }
  } catch (Exception e) {
      e.printStackTrace(_logWriter);
      throw new DTXException(e);
  }

  return hasValue;
    }

    /* OQL parser. Most of this code is highjacked from the JDO
       OQLQueryImpl, with a lot of changes to keep from using
       JDOClassDescriptors, which depend on loading a Java class.

       It'd be nice to refactor this so there's only one OQL-parsing
       class.
    */

    protected String parseOQL(String oql) throws DTXException {

  try {
      _ids = new ArrayList();
      _cols = new HashMap();
      _classes = new HashMap();

      StringTokenizer token = new StringTokenizer(oql);

      if (! token.hasMoreTokens() || ! token.nextToken().equalsIgnoreCase("SELECT"))
    throw new DTXException("Query must start with SELECT");
      if (! token.hasMoreTokens())
    throw new DTXException("Missing object name");
      _objName = token.nextToken();
      if (! token.hasMoreTokens() || ! token.nextToken().equalsIgnoreCase("FROM"))
    throw new DTXException("Object must be followed by FROM");
      if (! token.hasMoreTokens())
    throw new DTXException("Missing object type");
      _objType = token.nextToken();
      if (! token.hasMoreTokens())
    throw new DTXException("Missing object name");
      if (! _objName.equals(token.nextToken()))
    throw new DTXException("Object name not same in SELECT and FROM");

      if (_logWriter != null) {
    _logWriter.println("Querying " + _objName + " of type " + _objType);
      }

      _clsMapping = _eng.getClassMapping(_objType);

      if (_clsMapping == null) {
    throw new DTXException("dtx.NoClassDescription: " + _objType);
      }

      PersistenceFactory factory = _eng.getFactory();

      if (factory == null) {
    throw new DTXException("dtx.NoFactory");
      }

      QueryExpression expr = factory.getQueryExpression();

      if (expr == null) {
    throw new DTXException("dtx.NoQueryExpression");
      }

      initQuery(_clsMapping, expr);

      if (token.hasMoreTokens()) {
    if (! token.nextToken().equalsIgnoreCase("WHERE")) {
        throw new DTXException("Missing WHERE clause");
    }
    addField(_clsMapping, token, expr);
    while (token.hasMoreTokens()) {
        if (!token.nextToken().equals("AND")) {
      throw new QueryException( "Only AND supported in WHERE clause" );
        }
        addField(_clsMapping, token, expr);
    }
      }

      String sql = expr.getStatement(false);

      sql = sql + " ORDER BY ";

      for (java.util.Iterator it = _ids.iterator(); it.hasNext(); ) {
    String id = (String) it.next();
    sql = sql + " " + id;
    if (it.hasNext()) {
        sql = sql + ",";
    }
      }

      if (_logWriter != null) {
    _logWriter.println("SQL: " + sql);
      }

      return sql;

  } catch (Exception e) {
      if (_logWriter != null) {
    e.printStackTrace(_logWriter);
      }
      throw new DTXException(e);
  }
    }


    /* This makes the basic parts of a query. Again, a lot of code was
       lifted from e.g. SQLEngine, and edited to use *Mapping instead
       of *Descriptor. It also should probably be re-factored
       somewhere.

       Additionally, contained ("rel") elements go through the same
       kind of manipulation as the main class; this stuff should be
       re-factored, too.
    */

    protected void initQuery(ClassMapping clsMapping, QueryExpression expr)
        throws DTXException
    {
        ClassMapping extend;
  MapTo mapTo = clsMapping.getMapTo();

  if (mapTo == null) {
      throw new DTXException("no table mapping for: " + clsMapping.getName());
  }

  String table = mapTo.getTable();

        FieldMapping[] fields = clsMapping.getFieldMapping();
  FieldMapping identity = null;

        String identityName = clsMapping.getIdentity(0);

  for (int j = 0; j < fields.length; j++) {
      if (fields[j].getName().equals(identityName)) {
    identity = fields[j];
    break;
      }
  }

  if (identity == null) {
      throw new DTXException("no identity field in class: " + clsMapping.getName());
  }

  Sql identitySQLElement = identity.getSql();

  if (identitySQLElement == null) {
      throw new DTXException("no identity SQL info in class: " + clsMapping.getName());
  }

  String identitySQL = identitySQLElement.getName()[0];

  _ids.add(table + "." + identitySQL);

  DTXClassDescriptor desc = new DTXClassDescriptor(clsMapping);

  _classes.put(table + "." + identitySQL, desc);

        // If this class extends another class, create a join with the parent table and
        // add the load fields of the parent class (but not the store fields)
        if (clsMapping.getExtends() != null) {

        /**
          * TODO : Needs to be resolved by Hand
          */

        MapTo extendsTo = new MapTo();//(ClassMapping) clsMapping.getExtends()).getMapTo();
      if (extendsTo == null) {
    throw new DTXException("no mapping info for extends table.");
      }
      String extendsTable = extendsTo.getTable();
            expr.addInnerJoin(table, identitySQL, extendsTable, identitySQL);
            /**
             * needs to be resolved by hand
             */
            initQuery(new ClassMapping(), expr);
            //(ClassMapping) clsMapping.getExtends(), expr);
        }

        for (int i = 0; i < fields.length; ++i) {
      FieldMapping field = fields[i];
      Sql fieldSql = field.getSql();
      ClassMapping relMapping = _eng.getClassMapping(field.getType());

      if (fieldSql == null) {
    if (relMapping != null) {

        // We have a one-to-many relationship with a sub object.
        // get those objects, too.

                    FieldMapping[] relFields = relMapping.getFieldMapping();
        MapTo relMapTo = relMapping.getMapTo();

        if (relMapTo == null) {
      throw new DTXException("dtx.NoRelatedMapTo");
        }

        String relTable = relMapTo.getTable();

        String relId = null;
                    String foreKey = null;

                    for (int k = 0; k < relFields.length; k++ ) {
      Sql relSql = relFields[k].getSql();
                        if (relSql != null) {
          String type = relFields[k].getType();
          if (type != null && type.equals(clsMapping.getName())) {
        foreKey = relSql.getName()[0];
          }
      }
                    }

                    if (foreKey != null) {
                        expr.addOuterJoin(table, identitySQL, relTable, foreKey);
      DTXClassDescriptor relDesc = new DTXClassDescriptor(relMapping);

      for (int n = 0; n < relFields.length; n++ ) {
          FieldMapping relField = relFields[n];
          Sql relSql = relFields[n].getSql();
          if (relSql != null) {
        String relFieldName = relSql.getName()[0];
        if (relFieldName == null) {
            relFieldName = relFields[n].getName();
        }
        String relFullName = relTable + "." + relFieldName;

        BindXml relFieldXml = relFields[n].getBindXml();
        String node = "element";
        if (relFieldXml != null) {
            node = relFieldXml.getNode().toString();
        }

        if (!relFieldName.equals(foreKey)) {
            expr.addColumn(relTable, relFieldName);
            _cols.put(relTable + "." + relFieldName, new Integer(++_lastCol));

            if (node.equalsIgnoreCase("attribute")) {
          relDesc.addAttr(relFullName, relField);
            } else if (node.equalsIgnoreCase("element")) {
          relDesc.addSimpleElement(relFullName, relField);
            } else if (node.equalsIgnoreCase("text")) {
          relDesc.setTextCol(relFullName, relField);
            }
        }
        if (relField.getName().equals(relMapping.getIdentity())) {
            _ids.add(relTable + "." + relFieldName);
            desc.addContained(relTable + "." + relFieldName, relMapping);
            _classes.put(relTable + "." + relFieldName, relDesc);
        }
          }
      }
                    }
                }

      } else {

    String fieldName = fieldSql.getName()[0];
    if (fieldName == null) {
        fieldName = fields[i].getName();
    }
    String fullName = table + "." + fieldName;

    BindXml fieldXml = field.getBindXml();
    String node = "element";
    if (fieldXml != null) {
         node = fieldXml.getNode().toString();
    }

    if (node.equalsIgnoreCase("attribute")) {
        desc.addAttr(fullName, field);
    } else if (node.equalsIgnoreCase("element")) {
        desc.addSimpleElement(fullName, field);
    } else if (node.equalsIgnoreCase("text")) {
        desc.setTextCol(fullName, field);
    }

    if (relMapping == null || fieldSql.getManyTable() == null) {
        expr.addColumn(table, fieldName);
        _cols.put(fullName, new Integer(++_lastCol));
    } else {
        expr.addColumn(fieldSql.getManyTable(),
           fieldName);
        _cols.put(fullName, new Integer(++_lastCol));
        expr.addOuterJoin(table, identitySQL,
              fieldSql.getManyTable(),
              fieldSql.getManyKey()[0]);
    }
      }
        }
    }

    /* A misnomer, addField() actually adds a condition. Lifted, once
       again, from JDO stuff. */

    private void addField(ClassMapping clsMapping, StringTokenizer token, QueryExpression expr)
        throws DTXException
    {
        if (! token.hasMoreTokens()) {
            throw new DTXException("Missing field name");
  }
        String name = token.nextToken();
        if (! token.hasMoreTokens()) {
            throw new DTXException("Missing operator");
  }
        String op = token.nextToken();
        if (! token.hasMoreTokens())
            throw new DTXException("Missing field value");

        String value = token.nextToken();
        if (name.indexOf(".") > 0) {
            name = name.substring(name.indexOf(".") + 1);
  }

        FieldMapping[] fields = clsMapping.getFieldMapping();
  FieldMapping field = null;

        for (int i = 0; i < fields.length; ++i) {
            if (fields[i].getSql() != null && fields[i].getName().equals(name)) {
                field = fields[i];
                break;
            }
        }

        if (field == null) {
            throw new DTXException("The field " + name + " was not found");
  }

  Sql fieldSql = field.getSql();
  String table = clsMapping.getMapTo().getTable();

        if (value.startsWith("$")) {
            expr.addParameter(table, fieldSql.getName()[0], op);
        } else {
            expr.addCondition(table, fieldSql.getName()[0], op, value);
        }
    }
}
TOP

Related Classes of org.exolab.castor.dtx.DTXQuery

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.