public static final int COLTYPE_UNKNOWN  		= 0;
 public static final int COLTYPE_DATE			= 6;
 public static final int COLTYPE_NUMBER			= 3;
 public static final int COLTYPE_INTEGER			= 2;
 public static final int COLTYPE_BOOLEAN			= 5;
 public static final int COLTYPE_STRING  		= 1;
 public static final String SYSCOL_ANYUNREAD     = "@anyunread"; 
 public static final String SYSCOL_CHILDREN      = "@children"; 
 public static final String SYSCOL_CLASS         = "@class"; 
 public static final String SYSCOL_DESCENDANTS   = "@descendants"; 
 public static final String SYSCOL_ID            = "@id"; 
 public static final String SYSCOL_LEVELS        = "@levels"; 
 public static final String SYSCOL_POSITION      = "@position";        
 public static final String SYSCOL_ROWID      	= "@rowid";        
 public static final String SYSCOL_SCORE         = "@score"; 
 public static final String SYSCOL_SIBLINGS      = "@siblings"; 
 public static final String SYSCOL_UNID          = "@unid"; 
 public static final String SYSCOL_UNREAD        = "@unread"; 
 public class DomSQLException {

	public static SQLException create(Throwable t, String message) {
		SQLException e = new SQLException(message);
		if(t!=null) {
            e.initCause(t);
		}
		return e;
	}

	public static SQLException create(Throwable t, String message, Object... params) {
        SQLException e = new SQLException(StringUtil.format(message, params));
        if(t!=null) {
            e.initCause(t);
        }
        return e;
	}

	public static void setSqliteCode(SQLException exception, int sqliteCode) {
		//this.sqliteCode = sqliteCode;
	}
}
 public class Constants {

    // =========================================================
    // Notes.ini entries
    // =========================================================
	
	public static final String NOTESINI_DOMSQLPORT		= "DomSQL_Port";
	public static final String NOTESINI_DOMSQLTIMEOUT	= "DomSQL_Timeout";
	
	public static final int DEFAULT_PORT				= DomSQLDriver.DEFAULT_PORT;
	public static final int DEFAULT_TIMEOUT				= 10 * 60 * 1000; // 10 minutes
	//public static final int DEFAULT_TIMEOUT				= 20 * 1000; // 20 secs, for test
	
	public static final int TIMEOUT_JOB_DELAY			= 1 * 60 * 1000; // 1 minute
	//public static final int TIMEOUT_JOB_DELAY			= 1000; // 1 sec, for test
	
	
    // =========================================================
    // Options
    // =========================================================

	public static final String SOPTIONS_SEPARATEMULTI	= "SeparateMulti";

	// Flag as a parameter
	public static final int OPTIONS_SEPARATEMULTI	= 1;
	//public static final int OPTIONS_TYPEDCOLUMNS	= 2;

	
    // =========================================================
    // Basic SQLite types
    // =========================================================

	// Columns types
    public static final int COLTYPE_UNKNOWN  		= 0;
    public static final int COLTYPE_STRING  		= 1;
    public static final int COLTYPE_INTEGER			= 2;
    public static final int COLTYPE_NUMBER			= 3;
    public static final int COLTYPE_BLOB			= 4;
    
    // Extended types
    public static final int COLTYPE_BOOLEAN			= 5;
    public static final int COLTYPE_DATE			= 6;
    
    
    // =========================================================
    // System column names
    // =========================================================
    
    public static final String SYSCOL_ID            = "@id"; 
    public static final String SYSCOL_UNID          = "@unid"; 
    public static final String SYSCOL_CLASS         = "@class"; 
    public static final String SYSCOL_SIBLINGS      = "@siblings"; 
    public static final String SYSCOL_CHILDREN      = "@children"; 
    public static final String SYSCOL_DESCENDANTS   = "@descendants"; 
    public static final String SYSCOL_ANYUNREAD     = "@anyunread"; 
    public static final String SYSCOL_LEVELS        = "@levels"; 
    public static final String SYSCOL_SCORE         = "@score"; 
    public static final String SYSCOL_UNREAD        = "@unread"; 
    public static final String SYSCOL_POSITION      = "@position";        
    public static final String SYSCOL_ROWID      	= "@rowid";        

    // Pseudo columns
    public static final String SYSCOL_ALL      		= "@all";   
    public static final String SYSCOL_ALLDEF   		= "@id|@unid|@class|@siblings|@children|@descendants|@anyunread|@levels|@score|@unread|@position|@rowid";        
    
}
 public abstract class Context {
	
    private static ContextFinder contextFinder;

    public static abstract class ContextFinder {
    	ContextFinder next;
    	protected ContextFinder(ContextFinder next) {
    		this.next = next;
    	}
        public abstract Context find();
    }

    public static ContextFinder getContextFinder() {
        return contextFinder;
    }

    public static void setContextFinder(ContextFinder contextFinder) {
        Context.contextFinder=contextFinder;
    }
    
    
    private static ThreadLocal<Context> contexts = new ThreadLocal<Context>();
    
    public static Context get() {
        Context c = contexts.get();
        if(c!=null) {
            return c;
        }
        for(ContextFinder f=contextFinder; f!=null; f=f.next) {
        	c = f.find();
        	if(c!=null) {
        		return c;
        	}
        }
        throw new IllegalStateException("No current DomSQL context set");
    }

    public static void push(Context context) {
        contexts.set(context);
    }
    
    public static void pop() {
        contexts.remove();
    }
    
    private HashMap<String, Long> handles = new HashMap<String, Long>();
    
    public Context() {
    }

    public synchronized void close() {
        for(Long hDb: handles.values()) {
            try {
                if(shouldClose(hDb)) {
                    NotesAPI.NSFDBClose(hDb);
                }
            } catch(SQLException ex) {
                ex.printStackTrace();
            }
        }
    }
    protected boolean shouldClose(long hDb) {
        return true;
    }
    
    public long getDBHandle(String path) throws SQLException {
        if(path==null) path = "";
        Long l = handles.get(path);
        if(l!=null) {
            return l;
        }
        long h = path.length()==0 ? getDefaultDBHandle() : openDBHandle(path);
        handles.put(path, h);
        return h;
    }
    
    protected long openDBHandle(String path) throws SQLException {
        return NotesAPI.NSFDBOpen(path);
    }

    protected long getDefaultDBHandle() throws SQLException {
        throw DomSQLException.create(null, "No default database handle available");
    }
}
 public class DomSQL {
	
	public static final Debug debug = new Debug();
	
	// Used as a base class for bean as well
	public final static class Debug implements Serializable {
		private static final long serialVersionUID = 1L;

		public boolean TRACE_FILE;
		public String  TRACE_FILE_NAME;
		
		public boolean TRACE_JAVA_SQL;
	    public boolean TRACE_JAVA_REMOTE;

	    public boolean TRACE_DATABASE_LIFECYCLE;

	    public boolean NATIVE_TRACE;
	    public boolean NATIVE_TRACE_FUNCTION;
	    
	    public boolean NATIVE_DUMP_DESIGN;
	    public boolean NATIVE_DUMP_SECRETCODE;
	    public boolean NATIVE_TRACE_DICTIONARY;
	    public boolean NATIVE_TRACE_NIFCALLS;
	    public boolean NATIVE_TRACE_XFILTER;
	    public boolean NATIVE_TRACE_XNEXT;
	    public boolean NATIVE_TRACE_READDATA;
	    public boolean NATIVE_TRACE_FINDBYKEY;
	    public boolean NATIVE_TRACE_BESTINDEX;
	    public boolean NATIVE_TRACE_PERFORMANCE_HINTS;

	    public boolean NATIVE_TRACE_EOF;
	    
		private Debug() {
            if(false) {
            	TRACE_FILE = true;

//	    		TRACE_JAVA_SQL = true;
//	    		TRACE_JAVA_REMOTE = true;
//            	TRACE_DATABASE_LIFECYCLE = true;
//
//	    		NATIVE_TRACE = true;
//	    		NATIVE_TRACE_FUNCTION = true;
//	    	    
//	    		NATIVE_DUMP_DESIGN = true;
//	    		NATIVE_DUMP_SECRETCODE = true;
//	    		NATIVE_TRACE_DICTIONARY = true;
//	    		NATIVE_TRACE_NIFCALLS = true;
//	    		NATIVE_TRACE_XFILTER = true;
//	    		NATIVE_TRACE_XNEXT = true;
//	    		NATIVE_TRACE_READDATA = true;
//	    		NATIVE_TRACE_FINDBYKEY = true;
//	    		NATIVE_TRACE_BESTINDEX = true;
            	NATIVE_TRACE_PERFORMANCE_HINTS = true;
//
//	    		NATIVE_TRACE_EOF = true;
            }
            
	    }
		
	    public void updateNative() {
	    	setDebugFlag("TRACE", NATIVE_TRACE);
	    	setDebugFlag("TRACE_FUNCTION", NATIVE_TRACE_FUNCTION);
	    	setDebugFlag("DUMP_DESIGN", NATIVE_DUMP_DESIGN);
	    	setDebugFlag("DUMP_SECRETCODE", NATIVE_DUMP_SECRETCODE);
	    	setDebugFlag("TRACE_DICTIONARY", NATIVE_TRACE_DICTIONARY);
	    	setDebugFlag("TRACE_NIFCALLS", NATIVE_TRACE_NIFCALLS);
	    	setDebugFlag("TRACE_XFILTER", NATIVE_TRACE_XFILTER);
	    	setDebugFlag("TRACE_XNEXT", NATIVE_TRACE_XNEXT);
	    	setDebugFlag("TRACE_READDATA", NATIVE_TRACE_READDATA);
	    	setDebugFlag("TRACE_FINDBYKEY", NATIVE_TRACE_FINDBYKEY);
	    	setDebugFlag("TRACE_BESTINDEX", NATIVE_TRACE_BESTINDEX);
	    	setDebugFlag("TRACE_PERFORMANCE_HINTS", NATIVE_TRACE_PERFORMANCE_HINTS);
	    	setDebugFlag("TRACE_EOF", NATIVE_TRACE_EOF);
	    }
	}
	
	
    // ================================================================================
    // Initialization
    // ================================================================================

    // Set the value of the debug flag in the native layer
    public static native void setDebugFlag(String name, boolean value);
    
    // Virtual table secret code
    // This is used to prevent random creation of DomSQL virtual code
    public static native void setSecretCode(int secretCode);

    
    // ================================================================================
    // Call the native layer
    // ================================================================================
    
    public static native void readViewList(long hDB, List list) throws SQLException;
    public static native ViewDesign readViewDesign(int noteID, long hNote) throws SQLException;
    public static native int findDomsql(long hDB, String name) throws SQLException;
    public static native String readItemAsString(long hDB, String itemName) throws SQLException;    
    
    // ================================================================================
    // Callback functions for the JNI layer
    // We made these functions as simple as possible to call from the "C" code
    // ================================================================================
    
    public static final int VIEW_COL_UNKNOWN    = -1;
    public static final int VIEW_COL_NUMBER     = 0;
    public static final int VIEW_COL_TIMEDATE   = 1;
    public static final int VIEW_COL_TEXT       = 2;

    // Access the runtime context
    public static long getDatabaseHandle(String databasePath) throws SQLException {
        Context ctx = Context.get();
        return ctx.getDBHandle(databasePath);
    }
    
    // Handling design elements
    public static Object createView(int noteID, String title) {
        return new ViewDesign(noteID,title);
    }
    public static Object createViewColumn(Object parent, String name, String title, int type, int summaryIndex) {
        ViewDesign.Column col =  new ViewDesign.Column(name, title, type, summaryIndex);
        ((ViewDesign)parent).getColumns().add(col);
        return col;
    }
    public static Object createViewIndex(Object parent, int collation) {
        ViewDesign.Index idx =  new ViewDesign.Index(collation);
        ((ViewDesign)parent).getIndexes().add(idx);
        return idx;
    }
    public static Object createViewIndexEntry(Object parent, String colName, boolean desc) {
        ViewDesign.IndexEntry ent =  new ViewDesign.IndexEntry(colName,desc);
        ((ViewDesign.Index)parent).getEntries().add(ent);
        return ent;
    }

    
    // ================================================================================
    // Data handling
    // ================================================================================
    
    public static Long toSQLiteDate(java.util.Date value) {
        return Long.valueOf(((java.util.Date) value).getTime())/1000;
    }
    public static Long toSQLiteDate(Date value) {
        return Long.valueOf(((Date) value).getTime())/1000;
    }
    public static Long toSQLiteDate(Time value) {
        return Long.valueOf(((Time) value).getTime())/1000;
    }
    public static Long toSQLiteDate(Timestamp value) {
        return Long.valueOf(((Timestamp) value).getTime())/1000;
    }
}
 public class JNIUtils {
	
	private static PrintWriter fileWriter;
	private static PrintWriter getPrintWriter() {
		if(fileWriter==null) {
			try {
				String fileName = DomSQL.debug.TRACE_FILE_NAME;
				if(StringUtil.isEmpty(fileName)) {
					fileName = "c:\\temp\\domsql.log";
				}
				fileWriter = new PrintWriter(new FileWriter(fileName));
			} catch(IOException ex) {
			}
		}
		return fileWriter;
	}
	
	private static boolean beginLine = true;
	
    public static void flush() {
    	if(DomSQL.debug.TRACE_FILE) {
    		PrintWriter pw = getPrintWriter();
    		if(pw!=null) {
    			pw.flush();
    			return;
    		}
    	}
        System.err.flush();
    }

    public static void print(String s) {
    	if(DomSQL.debug.TRACE_FILE) {
    		PrintWriter pw = getPrintWriter();
    		if(pw!=null) {
    			pw.print(format(s));
    			pw.flush();
    			beginLine = false;
    			return;
    		}
    	}
        System.err.print(format(s));
		beginLine = false;
    }

    public static void println(String s) {
    	if(DomSQL.debug.TRACE_FILE) {
    		PrintWriter pw = getPrintWriter();
    		if(pw!=null) {
    			pw.println(format(s));
    			pw.flush();
    			beginLine = true;
    			return;
    		}
    	}
        System.err.println(format(s));
		beginLine = true;
    }

    public static void trace(String s, Object...params) {
        println(format(StringUtil.format(s, params)));
    }
    
    public static void _flush() {
        flush();
    }
    
    public static void _print(String s) {
        print("Native: "+s);
    }

    public static void _println(String s) {
        println("Native: "+s);
    }

    
    public static Object createException(String msg) {
        return DomSQLException.create(null,msg);
    }

    @SuppressWarnings("unchecked")
    public static void addObjectToList(List list, Object v) {
        list.add(v);
    }    
    @SuppressWarnings("unchecked")
    public static void addIntToList(List list, int v) {
        list.add(v);
    }    
    @SuppressWarnings("unchecked")
    public static void addLongToList(List list, long v) {
        list.add(v);
    }
    
    
    private static DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT);
    private static String format(String s) {
    	if(beginLine) {
    		return df.format(new Date())+": "+s;
    	} else {
    		return s;
    	}
    }
}
 public class NotesAPI {
	
	public static final int READ_MASK_NOTEID			= 0x00000001;
	public static final int READ_MASK_NOTEUNID			= 0x00000002;
	public static final int READ_MASK_NOTECLASS			= 0x00000004;
	public static final int READ_MASK_INDEXSIBLINGS		= 0x00000008;
	public static final int READ_MASK_INDEXCHILDREN		= 0x00000010;
	public static final int READ_MASK_INDEXDESCENDANTS	= 0x00000020;
	public static final int READ_MASK_INDEXANYUNREAD	= 0x00000040;
	public static final int READ_MASK_INDENTLEVELS		= 0x00000080;
	public static final int READ_MASK_SCORE				= 0x00000200;
	public static final int READ_MASK_INDEXUNREAD		= 0x00000400;
	public static final int READ_MASK_COLLECTIONSTATS	= 0x00000100;
	public static final int READ_MASK_INDEXPOSITION		= 0x00004000;
	public static final int READ_MASK_SUMMARYVALUES		= 0x00002000;
	public static final int READ_MASK_SUMMARY			= 0x00008000;
	

    ///////////////////////////////////////////////////////////
    // Notes initialization routines
    // For standalone apps
    ///////////////////////////////////////////////////////////    
    public static native void NotesInit() throws SQLException;
    public static native void NotesTerm() throws SQLException;
    public static native void NotesInitThread() throws SQLException;
    public static native void NotesTermThread() throws SQLException;

    ///////////////////////////////////////////////////////////
    // Access to notes.ini
    ///////////////////////////////////////////////////////////
	public static final native int OSGetEnvironmentInt(String name) throws SQLException;
	public static final native long OSGetEnvironmentLong(String name) throws SQLException;
	public static final native String OSGetEnvironmentString(String name) throws SQLException;
	public static final native void OSSetEnvironmentInt(String name, int value) throws SQLException;
	public static final native void OSSetEnvironmentVariable(String name, String value) throws SQLException;
    
    
    ///////////////////////////////////////////////////////////
    // Database routines
    ///////////////////////////////////////////////////////////
    public static native long NSFDBOpen(String dbPath) throws SQLException;
    public static native long NSFDBOpenEx(String dbPath, long hNames) throws SQLException;
    public static native void NSFDBClose(long hDb) throws SQLException;
    public static native long NSFDbNonDataModifiedTime(long hDb) throws SQLException;

    ///////////////////////////////////////////////////////////
    // Note routines
    ///////////////////////////////////////////////////////////
    public static native long NSFNoteOpen(long hDb, int noteID, int flags) throws SQLException;
    public static native void NSFNoteClose(long hNote) throws SQLException;
    
    ///////////////////////////////////////////////////////////
    // View routines
    ///////////////////////////////////////////////////////////
    public static native int NIFFindView(long hDb, String viewName) throws SQLException;
    
    ///////////////////////////////////////////////////////////
    // KFM
    ///////////////////////////////////////////////////////////
    public static native String SECKFMGetUserName() throws SQLException;
}
 public static class Column {
    private String name;
    private String viewCol;
    private int colType;

    public Column(String name) {
        this.name = name;
        this.viewCol = name;
        if(name.startsWith("@")) {
         if(name.equalsIgnoreCase(SYSCOL_ID)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_UNID)) {
             this.colType = COLTYPE_STRING;
         } else if(name.equalsIgnoreCase(SYSCOL_CLASS)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_SIBLINGS)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_CHILDREN)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_DESCENDANTS)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_ANYUNREAD)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_LEVELS)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_SCORE)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_UNREAD)) {
             this.colType = COLTYPE_INTEGER;
         } else if(name.equalsIgnoreCase(SYSCOL_POSITION)) {
             this.colType = COLTYPE_STRING;
         } else if(name.equalsIgnoreCase(SYSCOL_ROWID)) {
             this.colType = COLTYPE_INTEGER;
         } else {
             throw new IllegalArgumentException(StringUtil.format("Invalid system column {0}",name));
         }
        }
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getViewCol() {
        return viewCol;
    }
    public void setViewCol(String viewCol) {
        this.viewCol = viewCol;
    }
    public int getColType() {
        return colType;
    }
    public void setColType(int colType) {
        this.colType = colType;
    }
        
    public String validateColumn() {
        // Force the type of the system column
        if(name.equalsIgnoreCase(SYSCOL_ID)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_UNID)) {
            this.colType = COLTYPE_STRING;
        } else if(name.equalsIgnoreCase(SYSCOL_CLASS)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_SIBLINGS)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_CHILDREN)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_DESCENDANTS)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_ANYUNREAD)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_LEVELS)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_SCORE)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_UNREAD)) {
            this.colType = COLTYPE_INTEGER;
        } else if(name.equalsIgnoreCase(SYSCOL_POSITION)) {
            this.colType = COLTYPE_STRING;
        } else if(name.equalsIgnoreCase(SYSCOL_ROWID)) {
            this.colType = COLTYPE_INTEGER;
        }
        return null;
    }
    public int getReturnMask() {
    	if(viewCol.equals(SYSCOL_ID)) {
    		return NotesAPI.READ_MASK_NOTEID;
    	}
    	if(viewCol.equals(SYSCOL_UNID)) {
    		return NotesAPI.READ_MASK_NOTEUNID;
    	}
        if(viewCol.equals(SYSCOL_CLASS)) {
            return NotesAPI.READ_MASK_NOTECLASS;
        }
        if(viewCol.equals(SYSCOL_SIBLINGS)) {
            return NotesAPI.READ_MASK_INDEXSIBLINGS;
        }
        if(viewCol.equals(SYSCOL_CHILDREN)) {
            return NotesAPI.READ_MASK_INDEXCHILDREN;
        }
        if(viewCol.equals(SYSCOL_DESCENDANTS)) {
            return NotesAPI.READ_MASK_INDEXDESCENDANTS;
        }
        if(viewCol.equals(SYSCOL_ANYUNREAD)) {
            return NotesAPI.READ_MASK_INDEXANYUNREAD;
        }
        if(viewCol.equals(SYSCOL_LEVELS)) {
            return NotesAPI.READ_MASK_INDENTLEVELS;
        }
        if(viewCol.equals(SYSCOL_SCORE)) {
            return NotesAPI.READ_MASK_SCORE;
        }
        if(viewCol.equals(SYSCOL_UNREAD)) {
            return NotesAPI.READ_MASK_INDEXUNREAD;
        }
        if(viewCol.equals(SYSCOL_POSITION)) {
            return NotesAPI.READ_MASK_INDEXPOSITION;
        }
        if(viewCol.equals(SYSCOL_ROWID)) {
        	// This is a pseudo column so it doesn't have any particular mask
            return 0;
        }
		return NotesAPI.READ_MASK_SUMMARYVALUES;
    }
}
 public static class Index {
    private int                     collation;
    private ArrayList<IndexEntry>   entries = new ArrayList<IndexEntry>();
    public Index(int collation) {
        this.collation = collation;
    }
    public int getCollation() {
        return collation;
    }
    public ArrayList<IndexEntry> getEntries() {
        return entries;
    }
}
 public static class IndexEntry {
    private String      colName;
    private boolean     desc;
    public IndexEntry(String colName, boolean desc) {
        this.colName = colName;
        this.desc = desc;
    }
    public String getColName() {
        return colName;
    }
    public boolean isDesc() {
        return desc;
    }
}

import static com.ibm.domino.domsql.sqlite.driver.Constants.COLTYPE_UNKNOWN;
import static com.ibm.domino.domsql.sqlite.driver.Constants.COLTYPE_DATE;
import static com.ibm.domino.domsql.sqlite.driver.Constants.COLTYPE_NUMBER;
import static com.ibm.domino.domsql.sqlite.driver.Constants.COLTYPE_INTEGER;
import static com.ibm.domino.domsql.sqlite.driver.Constants.COLTYPE_BOOLEAN;
import static com.ibm.domino.domsql.sqlite.driver.Constants.COLTYPE_STRING;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_ANYUNREAD;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_CHILDREN;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_CLASS;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_DESCENDANTS;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_ID;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_LEVELS;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_POSITION;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_ROWID;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_SCORE;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_SIBLINGS;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_UNID;
import static com.ibm.domino.domsql.sqlite.driver.Constants.SYSCOL_UNREAD;
import java.io.StringReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.ibm.commons.Platform;
import com.ibm.commons.util.QuickSort;
import com.ibm.commons.util.StringUtil;
import com.ibm.domino.domsql.sqlite.DomSQLException;
import com.ibm.domino.domsql.sqlite.driver.Constants;
import com.ibm.domino.domsql.sqlite.driver.Context;
import com.ibm.domino.domsql.sqlite.driver.jni.DomSQL;
import com.ibm.domino.domsql.sqlite.driver.jni.JNIUtils;
import com.ibm.domino.domsql.sqlite.driver.jni.NotesAPI;
import com.ibm.domino.domsql.sqlite.driver.meta.DominoViewTable.Column;
import com.ibm.domino.domsql.sqlite.driver.meta.ViewDesign.Index;
import com.ibm.domino.domsql.sqlite.driver.meta.ViewDesign.IndexEntry;

		        b.append(")");
		    }
		} catch(Exception e) {
			Platform.getInstance().log(e);
		}
	}

    private void addViewColumns(DominoViewTable vt, List<Column> tableColumns, List<ViewDesign.Column> viewCols) {
		// Look if the default columns are filtered
    	String[] filteredCols = null;
		String defaultColumns = vt.getDefaultColumns();
		if(defaultColumns!=null) {
	    	filteredCols = StringUtil.splitString(defaultColumns, '|', true);
		}
		
		for(ViewDesign.Column vc : viewCols) {
			String name = vc.getColumnName();
			String colPos = vc.getColumnName();
			// Find if a column is predefined
			Column c = vt.findDominoColumn(name);
			if(c==null) {
				// The column doesn't exist in the list of columns explicitly set
				// Only add it if it is part of the list of columns
				if(filteredCols!=null && !isColumnAvailable(filteredCols, name)) {
					continue;
				}
				// Ok, we create a new col object
				c = new Column(name);
			} else {
				// Ok the column exists, we should just add it
			}
			int colType = c.getColType();
			if(colType==COLTYPE_UNKNOWN) { // Already set by the XML file...
				switch(vc.getColumnType()) {
					case DomSQL.VIEW_COL_TEXT:
						colType = COLTYPE_STRING;
						break;
					case DomSQL.VIEW_COL_NUMBER:
						colType = COLTYPE_NUMBER;
						break;
					case DomSQL.VIEW_COL_TIMEDATE:
						colType = COLTYPE_DATE;
						break;
				}
			}
			c.setViewCol(colPos);
			tableColumns.add(c);
		}
    }
    
    private boolean isColumnAvailable(String[] filteredCols, String colName) {
		for(int i=0; i<filteredCols.length; i++) {
			if(StringUtil.endsWithIgnoreCase(filteredCols[i], colName)) {
				return true;
			}
		}
		return false;
    }

    private void addViewSystemColumns(List<Column> tableColumns, String systemColumns) {
        if(StringUtil.isNotEmpty(systemColumns)) {
        	if(systemColumns.equals(Constants.SYSCOL_ALL)) {
        		systemColumns = Constants.SYSCOL_ALLDEF;
        	}
            String[] cols = StringUtil.splitString(systemColumns, '|', true);
            for(int i=0; i<cols.length; i++) {
                String s = cols[i];
                tableColumns.add(new Column(s));
            }
        }
    }

    private void addViewOptions(ViewDesign vd, String options) {
    	int opt = 0;
        if(StringUtil.isNotEmpty(options)) {
        	if(StringUtil.indexOfIgnoreCase(options,Constants.SOPTIONS_SEPARATEMULTI)>=0) {
        		opt |= Constants.OPTIONS_SEPARATEMULTI;
        	}
//       	if(options.indexOf(Constants.XML_TYPEDCOLUMNS)>=0) {
//    			opt |= Constants.OPTIONS_TYPEDCOLUMNS;
//    		}
        }
        vd.setOptions(opt);
    }
    
    private ViewDesign readView(Context context, String dbPath, String viewName) throws SQLException {
        if(StringUtil.isEmpty(dbPath)) {
            dbPath = nsfPath;
        }
        long hDb = context.getDBHandle(dbPath);
        int noteID = NotesAPI.NIFFindView(hDb,viewName);
		if(noteID != 0) {
            long hNote = NotesAPI.NSFNoteOpen(hDb, noteID, 0);
            try {
                ViewDesign vd = DomSQL.readViewDesign(noteID,hNote);
                if(vd==null) {
                    throw DomSQLException.create(null,"Cannot read view {0} from database {1}",viewName,dbPath);
                }
                return vd;
            } finally {
                NotesAPI.NSFNoteClose(hNote);
            }
		} else {
			JNIUtils.println(StringUtil.format("Unable to find view {0}",viewName));
		}
		return null;
    }

    private int getColType(ViewDesign vd, String viewName, Column col) throws SQLException {
        int colType = col.getColType();
        return colType;
    }

    private int getColumnIndexByName(ViewDesign vd, List<Column> cols, String viewName, Column col) throws SQLException {
        String name = col.getViewCol();
        // Pseudo columns
        if(name.startsWith("@")) {
            if(StringUtil.equalsIgnoreCase(name,SYSCOL_ID)) {
                return COLIDX_ID; 
            }

            if(StringUtil.equalsIgnoreCase(name,SYSCOL_UNID)) {
