/*
* ====================================================================
* Copyright (c) 2004-2010 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.db;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.tmatesoft.sqljet.core.SqlJetException;
import org.tmatesoft.sqljet.core.SqlJetValueType;
import org.tmatesoft.sqljet.core.internal.ISqlJetMemoryPointer;
import org.tmatesoft.sqljet.core.internal.SqlJetUtility;
import org.tmatesoft.sqljet.core.schema.ISqlJetColumnDef;
import org.tmatesoft.sqljet.core.table.ISqlJetCursor;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema;
/**
* @version 1.4
* @author TMate Software Ltd.
*/
public class SVNSqlJetSelectStatement extends SVNSqlJetTableStatement {
private String indexName;
private Map<String, Object> rowValues;
public SVNSqlJetSelectStatement(SVNSqlJetDb sDb, Enum<?> fromTable) throws SVNException {
this(sDb, fromTable.toString());
}
public SVNSqlJetSelectStatement(SVNSqlJetDb sDb, Enum<?> fromTable, Enum<?> indexName) throws SVNException {
this(sDb, fromTable.toString(), indexName != null ? indexName.toString() : null);
}
public SVNSqlJetSelectStatement(SVNSqlJetDb sDb, String fromTable) throws SVNException {
super(sDb, fromTable);
}
public SVNSqlJetSelectStatement(SVNSqlJetDb sDb, String fromTable, String indexName) throws SVNException {
this(sDb, fromTable);
this.indexName = indexName;
}
protected ISqlJetCursor openCursor() throws SVNException {
try {
Object[] where = getWhere();
if (isPathScoped()) {
where = new Object[] {where[0], getPathScope()};
return getTable().scope(getIndexName(), where, null);
}
return getTable().lookup(getIndexName(), where);
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
return null;
}
}
private boolean isPathScoped() throws SVNException {
Object[] where = getWhere();
if (getPathScope() != null && getIndexName() == null && SVNWCDbSchema.NODES.toString().equals(getTableName()) && where.length == 1) {
return true;
}
if (getPathScope() != null && getIndexName() == SVNWCDbSchema.NODES__Indices.I_NODES_PARENT.name() && SVNWCDbSchema.NODES.toString().equals(getTableName()) && where.length == 1) {
return true;
}
if (getPathScope() != null && getIndexName() == SVNWCDbSchema.NODES__Indices.I_NODES_MOVED.name() && SVNWCDbSchema.NODES.toString().equals(getTableName()) && where.length == 1) {
return true;
}
return false;
}
protected String getPathScope() {
return null;
}
protected boolean isStrictiDescendant() {
return false;
}
protected String getIndexName() {
return indexName;
}
public void setIndexName(String indexName) {
this.indexName = indexName;
}
protected Object[] getWhere() throws SVNException {
if (binds.size() == 0) {
return null;
}
return binds.toArray();
}
public boolean next() throws SVNException {
boolean next = false;
do {
next = super.next();
loadRowValues(next);
if (next && !pathScopeMatches()) {
continue;
}
} while(next && !pathIsDecendant());
while (next && !isFilterPassed()) {
do {
next = super.next();
loadRowValues(next);
if (next && !pathScopeMatches()) {
return false;
}
} while(next && !pathIsDecendant());
}
return next;
}
private boolean pathScopeMatches() throws SVNException {
if (isPathScoped()) {
final String rowPath = getRowPath();
if ("".equals(getPathScope()) && !(isStrictiDescendant() && "".equals(rowPath))) {
return true;
}
if (rowPath != null) {
return (!isStrictiDescendant() && getPathScope().equals(rowPath)) || rowPath.startsWith(getPathScope());
}
return false;
}
return true;
}
private boolean pathIsDecendant() throws SVNException {
if (getPathScope() != null) {
final String rowPath = getRowPath();
if (rowPath != null) {
if ("".equals(getPathScope()) && !(isStrictiDescendant() && "".equals(rowPath))) {
return true;
}
return (!isStrictiDescendant() && getPathScope().equals(rowPath)) || rowPath.startsWith(getPathScope() + "/");
}
return false;
}
return true;
}
protected Enum<?> getRowPathField() throws SVNException {
if (SVNWCDbSchema.NODES__Indices.I_NODES_PARENT.toString().equals(getIndexName())) {
return SVNWCDbSchema.NODES__Fields.parent_relpath;
}
if (SVNWCDbSchema.NODES__Indices.I_NODES_MOVED.toString().equals(getIndexName())) {
return SVNWCDbSchema.NODES__Fields.moved_to;
}
return SVNWCDbSchema.NODES__Fields.local_relpath;
}
protected String getRowPath() throws SVNException {
final Enum<?> rowPathField = getRowPathField();
if (rowPathField == null) {
return null;
}
return (String) rowValues.get(rowPathField.name());
}
protected boolean isFilterPassed() throws SVNException {
return true;
}
public boolean eof() throws SVNException {
boolean eof = true;
do {
eof = eof ? super.eof() : !super.next();
loadRowValues(!eof);
if (!eof && !pathScopeMatches()) {
return true;
}
} while(!eof && !pathIsDecendant());
while (!eof && !isFilterPassed()) {
do {
eof = !super.next();
loadRowValues(!eof);
if (!eof && !pathScopeMatches()) {
return true;
}
} while(!eof && !pathIsDecendant());
}
return eof;
}
private void loadRowValues(boolean has) throws SVNException {
if (has) {
rowValues = getRowValues2(rowValues);
} else if (rowValues != null) {
rowValues.clear();
}
}
public Map<String, Object> getRowValues2(Map<String, Object> v) throws SVNException {
v = v == null ? new HashMap<String, Object>() : v;
try {
final List<ISqlJetColumnDef> columns = getTable().getDefinition().getColumns();
final Object[] rValues = getCursor().getRowValues();
final Object[] values;
if (rValues.length < columns.size()) {
values = new Object[columns.size()];
System.arraycopy(rValues, 0, values, 0, rValues.length);
} else {
values = rValues;
}
for (int i = 0; i < values.length; i++) {
v.put(columns.get(i).getName(), values[i]);
}
return v;
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
return null;
}
}
public Map<String, Object> getRowValues() throws SVNException {
final HashMap<String, Object> v = new HashMap<String, Object>();
try {
final List<ISqlJetColumnDef> columns = getTable().getDefinition().getColumns();
for (ISqlJetColumnDef column : columns) {
final String colName = column.getName();
final SqlJetValueType fieldType = getCursor().getFieldType(colName);
if (fieldType == SqlJetValueType.NULL) {
v.put(colName, null);
} else if (fieldType == SqlJetValueType.BLOB) {
v.put(colName, getCursor().getBlobAsArray(colName));
} else {
v.put(colName, getCursor().getValue(colName));
}
}
return v;
} catch (SqlJetException e) {
SVNSqlJetDb.createSqlJetError(e);
return null;
}
}
@Override
protected Object getColumn(String f) throws SVNException {
return rowValues != null ? rowValues.get(f) : null;
}
@Override
protected long getColumnLong(String f) throws SVNException {
if (rowValues == null) {
return 0;
}
Object v = rowValues.get(f);
if (v instanceof Long) {
return (Long) v;
} else if (v instanceof String) {
try {
return Long.parseLong((String) v);
} catch (NumberFormatException nfe) {
return 0;
}
}
return 0;
}
@Override
protected String getColumnString(String f) throws SVNException {
if (rowValues == null) {
return null;
}
Object v = rowValues.get(f);
if (v == null) {
return null;
}
return v instanceof String ? (String) v : v.toString();
}
@Override
protected boolean isColumnNull(String f) throws SVNException {
if (rowValues == null) {
return true;
}
return rowValues.get(f) == null;
}
@Override
protected byte[] getColumnBlob(String f) throws SVNException {
if (rowValues == null) {
return null;
}
Object v = rowValues.get(f);
if (v instanceof ISqlJetMemoryPointer) {
ISqlJetMemoryPointer buffer = (ISqlJetMemoryPointer) v;
return buffer != null ? SqlJetUtility.readByteBuffer(buffer) : null;
} else if (v instanceof String) {
try {
return ((String) v).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
return ((String) v).getBytes();
}
} else if (v instanceof byte[]) {
return (byte[]) v;
}
return null;
}
@Override
public void reset() throws SVNException {
if (rowValues != null) {
rowValues.clear();
}
super.reset();
}
protected static boolean isStrictDescendantOf(String descendant, String ancestor) {
return descendant.startsWith(ancestor + "/");
}
}