/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.translator.salesforce.execution;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import javax.resource.ResourceException;
import org.teiid.language.Argument;
import org.teiid.language.Command;
import org.teiid.language.Literal;
import org.teiid.language.visitor.SQLStringVisitor;
import org.teiid.metadata.RuntimeMetadata;
import org.teiid.translator.DataNotAvailableException;
import org.teiid.translator.ExecutionContext;
import org.teiid.translator.ProcedureExecution;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.salesforce.SalesForcePlugin;
import org.teiid.translator.salesforce.SalesforceConnection;
import org.teiid.translator.salesforce.Util;
import org.teiid.translator.salesforce.execution.visitors.CriteriaVisitor;
import com.sforce.soap.partner.QueryResult;
import com.sforce.soap.partner.sobject.SObject;
import com.sforce.ws.bind.XmlObject;
public class DirectQueryExecution implements ProcedureExecution {
public static final String SEARCH = "search;"; //$NON-NLS-1$
private static final String ATTRIBUTES = "attributes"; //$NON-NLS-1$
private static final String TYPE = "type"; //$NON-NLS-1$
private static final String ID = "id"; //$NON-NLS-1$
protected List<Argument> arguments;
protected Command command;
private SalesforceConnection connection;
protected RuntimeMetadata metadata;
private ExecutionContext context;
private QueryResult results;
private List<List<Object>> currentBatch;
private int updateCount = -1;
private boolean updateQuery = false;
private String query;
private boolean returnsArray = true;
/**
*
* @param arguments parameter arguments only
* @param command
* @param connection
* @param metadata
* @param context
* @param query
*/
public DirectQueryExecution(List<Argument> arguments, Command command, SalesforceConnection connection, RuntimeMetadata metadata, ExecutionContext context, String query, boolean returnsArray) {
this.arguments = arguments;
this.command = command;
this.connection = connection;
this.metadata = metadata;
this.context = context;
this.query = query;
this.returnsArray = returnsArray;
}
@Override
public void execute() throws TranslatorException {
if (query.startsWith(SEARCH)) {
StringBuilder buffer = new StringBuilder();
SQLStringVisitor.parseNativeQueryParts(query, arguments, buffer, new SQLStringVisitor.Substitutor() {
@Override
public void substitute(Argument arg, StringBuilder builder, int index) {
Literal argumentValue = arg.getArgumentValue();
CriteriaVisitor.appendLiteralValue(builder, argumentValue);
}
});
doSelect(buffer.toString().substring(7));
}
else if (query.startsWith("create;")) { //$NON-NLS-1$
doInsert(query.substring(7));
}
else if (query.startsWith("upsert;")) { //$NON-NLS-1$
doUpsert(query.substring(7));
}
else if (query.startsWith("update;")) { //$NON-NLS-1$
doUpdate(query.substring(7));
}
else if (query.startsWith("delete;")) { //$NON-NLS-1$
doDelete();
}
else {
throw new TranslatorException(SalesForcePlugin.Util.gs(SalesForcePlugin.Event.TEIID13002));
}
}
private void doDelete() throws TranslatorException {
List<String> ids = new ArrayList<String>();
for (Argument arg : arguments) {
Object val = arg.getArgumentValue().getValue();
if (val != null) {
ids.add(Util.stripQutes(val.toString()));
}
}
try {
this.updateCount = this.connection.delete(ids.toArray(new String[ids.size()]));
this.updateQuery = true;
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
private void doUpdate(String update) throws TranslatorException {
DataPayload payload = buildDataPlayload(update);
try {
this.updateCount = this.connection.update(Arrays.asList(payload));
this.updateQuery = true;
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
private void doInsert(String insert) throws TranslatorException {
DataPayload payload = buildDataPlayload(insert);
try {
this.updateCount = this.connection.create(payload);
this.updateQuery = true;
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
private void doUpsert(String upsert) throws TranslatorException {
DataPayload payload = buildDataPlayload(upsert);
try {
this.updateCount = this.connection.upsert(payload); //$NON-NLS-1$
this.updateQuery = true;
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
private void doSelect(String select) throws TranslatorException {
try {
this.results = this.connection.query(select, this.context.getBatchSize(), Boolean.FALSE);
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
@Override
public List<?> next() throws TranslatorException, DataNotAvailableException {
List<?> vals = getRow(this.results);
if (vals == null) {
return null;
}
if (returnsArray) {
List<Object[]> row = new ArrayList<Object[]>(1);
row.add(vals.toArray(new Object[vals.size()]));
return row;
}
return vals;
}
private List<?> getRow(QueryResult result) throws TranslatorException {
// for insert/update/delete clauses
if (this.updateQuery) {
if (this.updateCount != -1) {
List<?> updateResult = Arrays.asList(this.updateCount);
this.updateCount = -1;
return updateResult;
}
return null;
}
// select clauses
List<Object> row = null;
if(this.currentBatch == null) {
this.currentBatch = loadBatch(this.results);
}
if(!this.currentBatch.isEmpty()) {
row = this.currentBatch.remove(0);
}
else {
if(!result.isDone()) {
// fetch more results
try {
this.results = this.connection.queryMore(results.getQueryLocator(), context.getBatchSize());
} catch (ResourceException e) {
throw new TranslatorException(e);
}
this.currentBatch = loadBatch(this.results);
// read next row
row = this.currentBatch.remove(0);
}
}
return row;
}
private List<List<Object>> loadBatch(QueryResult queryResult) {
List<List<Object>> batch = new ArrayList<List<Object>>();
for(SObject sObject : queryResult.getRecords()) {
Iterator<XmlObject> fields = sObject.getChildren();
List<Object> row = new ArrayList<Object>();
while (fields.hasNext()) {
XmlObject elem = fields.next();
if (elem.getName().getLocalPart().equals("type")) { //$NON-NLS-1$
continue;
}
Object value = elem.getValue();
row.add(value);
}
batch.add(row);
}
return batch;
}
@Override
public void close() {
}
@Override
public void cancel() throws TranslatorException {
}
private DataPayload buildDataPlayload(String update) throws TranslatorException {
StringTokenizer st = new StringTokenizer(update, ";"); //$NON-NLS-1$
if (!st.hasMoreTokens()) {
throw new TranslatorException(SalesForcePlugin.Util.gs(SalesForcePlugin.Event.TEIID13004));
}
String type = null;
String id = null;
DataPayload payload = new DataPayload();
while(st.hasMoreElements()) {
String var = st.nextToken();
int index = var.indexOf('=');
if (index == -1) {
continue;
}
String key = var.substring(0, index).trim().toLowerCase();
String value = var.substring(index+1).trim();
if (key.equalsIgnoreCase(ATTRIBUTES)) {
StringTokenizer attrTokens = new StringTokenizer(value, ","); //$NON-NLS-1$
int attrCount = 0;
while(attrTokens.hasMoreElements()) {
String name = attrTokens.nextToken().trim();
if (arguments.size() <= attrCount) {
throw new TranslatorException(SalesForcePlugin.Util.gs(SalesForcePlugin.Event.TEIID13005, name));
}
Argument argument = arguments.get(attrCount++);
Object anObj = argument.getArgumentValue().getValue();
if (anObj == null) {
continue;
}
anObj = Util.stripQutes(anObj.toString());
payload.addField(name, anObj);
}
}
else if (key.equalsIgnoreCase(TYPE)) {
type = value;
}
else if (key.equalsIgnoreCase(ID)) {
id = value;
}
}
payload.setID(id);
payload.setType(type);
return payload;
}
@Override
public List<?> getOutputParameterValues() throws TranslatorException {
return null;
}
}