001//
002// Name
003//  $RCSfile: KeyQuery.java,v $
004// 
005// Copyright
006//  Copyright 2009-2012 Cloud Software Group, Inc. ALL RIGHTS RESERVED. 
007//  Cloud Software Group, Inc. Confidential Information
008//
009// History
010//  $Revision: 1.1.2.17.2.4 $ $Date: 2012/11/01 05:07:21 $
011//
012package com.kabira.platform;
013
014import java.util.ArrayList;
015import java.util.Iterator;
016import com.kabira.platform.KeyConstructor;
017import java.util.Map;
018import java.util.concurrent.ConcurrentHashMap;
019
020//
021// Common utility class that is package private so it can be shared.
022//
023class FieldEntry
024{
025    String              m_name;
026    Object              m_value;
027    KeyComparisonOperator       m_op;
028}
029
030
031/**
032 * Class used to define a query and obtain results using keys. 
033 * <p>
034 * Instances of this class are created by KeyManager, so it has no
035 * public constructors.
036 * <p>
037 * Once a query is defined, it may be executed multiple times.
038 */
039public final class KeyQuery<T>
040{
041    /**
042     * Returns the results of a query.
043     *
044     * @param objectLock LockMode to apply to each instance returned.
045     *
046     * @return Iterable containing set.
047     *
048     * @exception KeyIncompleteError
049     *  All the fields for the key have not been defined. 
050     * <p>
051     * Executes the previously defined query and returns the resulting set.
052     * For ordered keys, the default orderBy of ASCENDING is used.
053     */
054    public Iterable<T> getResults(
055            LockMode objectLock)
056        throws KeyIncompleteError
057    {
058        validateExecution();
059        if (objectLock == null)
060        {
061            throw new NullPointerException();
062        }
063        if (m_hasKeyCoverage)
064        {
065            return new KeyResultSet<T>(objectLock);
066        }
067        assert( m_descriptor.m_ordered == true );
068        return getResults(KeyOrderedBy.ASCENDING, objectLock);
069    }
070
071    /**
072     * Returns the results for a query using an ordered key.
073     *
074     * @param orderBy
075     * @param objectLock
076     *
077     * @return Iterable containing set.
078     *
079     * @exception KeyIncompleteError
080     *  All the fields for the key have not been defined. 
081     *
082     * @exception KeyIllegalKeyTypeError
083     *  The key is not ordered.
084     * <p>
085     * Executes the previously defined query and returns the resulting set.
086     */
087    public Iterable<T> getResults(
088            KeyOrderedBy orderBy,
089            LockMode objectLock)
090        throws KeyIncompleteError
091    {
092        validateExecution();
093        validateOrdered();
094        if (objectLock == null)
095        {
096            throw new NullPointerException();
097        }
098        return new KeyResultSet<T>(orderBy, objectLock);
099    }
100
101    /**
102     * Returns the instance matching the given unique key.
103     *
104     * @param objectLock The object lock applied if an instance is found.
105     *
106     * @return Object containing result, or null if key not found.
107     *
108     * @exception KeyIncompleteError
109     *  The fields for the key have not been defined. 
110     *
111     * @exception KeyIllegalKeyTypeError
112     *  The key is not unique.
113     * <p>
114     * Executes the previously defined query and returns an instance.
115     */
116    public T getSingleResult(LockMode objectLock)
117        throws KeyIncompleteError, KeyIllegalKeyTypeError
118    {
119        validateExecution();
120        if ((m_descriptor.m_unique == false) ||
121            (m_descriptor.m_ordered == true && m_hasKeyCoverage == false))
122        {
123            throw new KeyIllegalKeyTypeError(
124                "Key is not unique");
125        }
126        return attachObject(objectLock, KeyOptNoPlaceholder);
127    }
128
129    /**
130     * Returns the mininum value for the given ordered key.
131     *
132     * @param objectLock The object lock applied if an instance is found.
133     *
134     * @return Object containing result, or null if key not found.
135     *
136     * @exception KeyIllegalKeyTypeError
137     *  The key is not ordered.
138     *
139     * @exception KeyMalformedQueryError
140     *  An illegal operator was used when defining the query.
141     * <p>
142     * Executes the previously defined query (if any) and returns an
143     * instance containing the minimum value. If no query was defined,
144     * the instance containing the minimum key is returned. If a query was
145     * defined, the instance containing the minimum key value that
146     * matches the given fields is returned.
147     */
148    public T getMinimumResult(LockMode objectLock)
149        throws KeyMalformedQueryError, KeyIllegalKeyTypeError
150    {
151        validateOrdered();
152        if (m_query == null)
153        {
154            m_query = new CompiledQuery();
155            m_query.m_keyInfo1 = new KeyInfo();
156        }
157        CompiledQuery query = m_query;
158        if (m_query.m_filters != null)
159        {
160            query = compileOrderedQuery(CompileFilter.FILTER_NONE);
161        }
162        validateMinMaxQuery(query);
163        return attachMinimum(query, objectLock);
164    }
165
166    /**
167     * Returns the maximum value for the given ordered key.
168     *
169     * @param objectLock The object lock applied if an instance is found.
170     *
171     * @return Object containing result, or null if key not found.
172     *
173     * @exception KeyIllegalKeyTypeError
174     *  The key is not ordered.
175     *
176     * @exception KeyMalformedQueryError
177     *  An illegal operator was used when defining the query.
178     * <p>
179     * Executes the previously defined query (if any) and returns an
180     * instance containing the maximum value. If no query was defined,
181     * the instance containing the maximum key is returned. If a query was
182     * defined, the instance containing the maximum key value that
183     * matches the given fields is returned.
184     */
185    public T getMaximumResult(LockMode objectLock)
186        throws KeyMalformedQueryError, KeyIllegalKeyTypeError
187    {
188        validateOrdered();
189        if (m_query == null)
190        {
191            m_query = new CompiledQuery();
192            m_query.m_keyInfo1 = new KeyInfo();
193        }
194        CompiledQuery query = m_query;
195        if (m_query.m_filters != null)
196        {
197            query = compileOrderedQuery(CompileFilter.FILTER_NONE);
198        }
199        validateMinMaxQuery(query);
200        return attachMaximum(query, objectLock);
201    }
202
203    /**
204     * Returns the instance for the given unique key.
205     *
206     * @param  objectLock The object lock applied if a pre-existing instance
207     *      is found.
208     * @param  additionalFields Additional field values to use if an
209     *      instance in created. These values are ignored if
210     *      the instance already exists.  null can be specified
211     *      if no additional field values are needed.
212     *
213     * @return Object containing result.
214     *
215     * @exception KeyIncompleteError
216     *  The fields for the key have not been defined. 
217     *
218     * @exception KeyIllegalKeyTypeError
219     *  The key is not unique.
220     *
221     * @exception KeyIllegalFieldError
222     *  A field in the additionalFields list is not valid.
223     * <p>
224     * Executes the previously defined query and returns an instance.
225     * This method will atomically insure that an instance of
226     * the type will be created when searching for a instance by key.
227     * The additionalFields list passed in allows the programmer to
228     * populate secondary key fields if the instance is created.
229     * This should be used instead of doing:
230     *<pre>
231KeyedObject ko = kq.getSingleResult(lockMode);
232if (ko == null)
233{
234    ko = new KeyedObject(keys);
235}</pre>
236     * <p>
237     * The above new can throw an ObjectNotUniqueError exception
238     * if another transaction creates the instance between the 
239     * getSingleResult() and the new call. 
240     */
241    @SuppressWarnings("unchecked")
242    public T getOrCreateSingleResult(
243            LockMode objectLock,
244            KeyFieldValueList additionalFields)
245        throws KeyIncompleteError, KeyIllegalKeyTypeError,
246            KeyIllegalFieldError
247    {
248        validateExecution();
249        if (m_descriptor.m_unique == false)
250        {
251            throw new KeyIllegalKeyTypeError(
252                "Key is not unique");
253        }
254
255        ArrayList<CreateBuffer> createValues = null;
256
257        if (additionalFields != null)
258        {
259            createValues = new ArrayList<CreateBuffer>();
260            for (FieldEntry fe : additionalFields.m_entries)
261            {
262                //
263                // We need to audit that these fields are
264                // not part of the key. Otherwise we'll
265                // create the wrong instance in the "on
266                // empty" case, since the key will
267                // subsequently be updated.
268                //
269                if (findField(fe))
270                {
271                    throw new KeyIllegalFieldError(
272                        "Field " + fe.m_name +
273                        " in additionalFields list " +
274                        "is part of the key.");
275                }
276                CreateBuffer cb = new CreateBuffer(fe);
277                createValues.add(cb);
278            }
279        }
280 
281        T obj = null;
282        boolean retry = true;
283        int retryCount = 0;
284        while (retry)
285        {
286            obj = attachObject(objectLock, KeyOptNoPlaceholder);
287            if (obj != null)
288            {
289                retry = false;
290            }
291            else
292            {
293                try
294                {
295                    //
296                    // Attempt to create the key'd object using the
297                    // constructor. The placeholder created above will
298                    // prevent another transaction from creating the
299                    // instance from underneath us.
300                    //
301                    // Note: we cast and use the above SuppressWarnings
302                    // annotation since KeyConstructor uses reflection so it
303                    // cannot be a generic.
304                    //
305                    KeyConstructor  kc = new KeyConstructor();
306                    for (FieldEntry fe : m_entries)
307                    {
308                        kc.addKeyValue(fe.m_name, fe.m_value);
309                    }
310                    if (additionalFields != null)
311                    {
312                        for (FieldEntry fe : additionalFields.m_entries)
313                        {
314                            kc.addKeyValue(fe.m_name, fe.m_value);
315                        }
316                    }
317                    obj = (T)kc.invokeConstructor(m_descriptor.m_className);
318                    if (obj == null)
319                    {
320                        //
321                        // Cannot construct instance, create a shared
322                        // memory image.
323                        //
324                        obj = createObject(createValues);
325                    }
326                    retry = false;
327                }
328                catch (ObjectNotUniqueError ex)
329                {
330                    if (retryCount++ > MaxSelectRetries)
331                    {
332                        throw ex;
333                    }
334                    //
335                    // Just ignore this exception, we'll try and select again.
336                    //
337                }
338            }
339        }
340        return obj;
341    }
342
343    /**
344     * Returns the number of instances of the given key.
345     *
346     * @return Number of instances.
347     *
348     * @exception KeyIncompleteError
349     *  All the fields for the key have not been defined. 
350     * <p>
351     * Executes the previously defined query and returns the number of
352     * instances that would result.
353     * <p>
354     * No transaction locks are take when this method executes.
355     * This means the number returned by cardinality() may
356     * change even if called multiple times within a single
357     * transaction.
358     *
359     */
360    public int cardinality()
361        throws KeyIncompleteError
362    {
363        validateExecution();
364
365        KeyIterator<T> ki = new KeyIterator<T>(
366                        KeyOrderedBy.ASCENDING,
367                        LockMode.NOLOCK);
368
369        return ki.size();
370    }
371
372    /**
373     * Defines the fields that form a key query.
374     *
375     * @param  keyFields The fields to use.
376     *
377     * @exception KeyIncompleteError
378     *  All the fields for the key have not been defined. 
379     *
380     * @exception KeyIllegalFieldError
381     *  A given field is not  part of the key.
382     *
383     * @exception KeyMalformedQueryError
384     *  The resulting key lookup cannot perform a range query
385     *  on an ordered key, or duplicate fields exist 
386     *  for a non-ordered key.
387     * <p>
388     * When executed, this defines the values used for a key lookup. 
389     * Any previous query is overwritten.
390     */
391    public void defineQuery(
392            KeyFieldValueList keyFields)
393        throws KeyIllegalFieldError, KeyMalformedQueryError
394    {
395        processFields(keyFields.getEntries());
396
397        if (!m_descriptor.m_ordered && !m_hasKeyCoverage)
398        {
399            //
400            // Build missing list. Note that we ignore dups, since
401            // KeyMalformedQueryError is thrown when this happens.
402            //
403            String missing = "";
404            for (FieldDescriptor fd : m_descriptor.m_fields)
405            {
406                boolean found = false;
407                for (FieldEntry fe : m_entries)
408                {
409                    if (fd.m_name.equals(fe.m_name))
410                    {
411                        found = true;
412                        break;
413                    }
414                }
415                if (found == false)
416                {
417                    missing += ", missing field '" + fd.m_name + "'";
418                }
419            }
420            throw new KeyIncompleteError(
421                "Not all required fields are present" + missing);
422        }
423        else if (!m_hasKeyCoverage)
424        {
425            validateOrderedKey();
426        }
427
428        m_query = compileQuery();
429    }
430
431    /**
432     * Defines the fields that form a range query.
433     *
434     * @param  keyFields The fields to use.
435     *
436     * @exception KeyIllegalFieldError
437     *  A given field is not  part of the key.
438     *
439     * @exception KeyMalformedQueryError
440     *  The resulting key lookup cannot perform a range query
441     *  on an ordered key, duplicate fields are defined
442     *  for a non-ordered key, or a range query was attempted
443     *  on a non-ordered key.
444     * <p>
445     * When executed, this defines the values used for a range query
446     * on an ordered key. Any previous query is overwritten.
447     */
448    public void defineQuery(
449            KeyFieldValueRangeList keyFields)
450        throws KeyIllegalFieldError, KeyMalformedQueryError
451    {
452        validateOrdered();
453
454        processFields(keyFields.getEntries());
455
456        if (!m_hasKeyCoverage)
457        {
458            validateOrderedKey();
459        }
460
461        m_query = compileQuery();
462    }
463
464    /**
465     * Creates a human readable query string.
466     *
467     * @exception KeyIncompleteError
468     *  All the fields for the key have not been defined. 
469     * <p>
470     * This method returns a string containing a "select" or "for"
471     * statement. The string also contains any optimization warnings for
472     * the defined query.
473     * This information can be used to help diagnose queries.
474     */
475    public String toString()
476        throws KeyIncompleteError
477    {
478        String res = new String();
479
480        if (m_descriptor.m_unique && m_hasKeyCoverage)
481        {
482            res += "select obj from ";
483        }
484        else
485        {
486            res += "for obj in ";
487        }
488        res += m_descriptor.m_className;
489        res += " using ";
490        res += m_descriptor.m_keyName;
491        res += " where (";
492
493        boolean firstField = true;
494        for (FieldEntry fe : m_entries)
495        {
496            if (!firstField)
497            {
498                res += " && ";
499            }
500            else
501            {
502                firstField = false;
503            }
504            res += fe.m_name;
505            res += getComparisonOp(fe.m_op);
506            res += fe.m_value;
507        }
508        res += ")";
509        if (m_descriptor.m_unique && m_hasKeyCoverage)
510        {
511            res += ";";
512        }
513        else
514        {
515            res += " { }";
516        }
517
518        if (m_descriptor.m_unique && m_hasKeyCoverage)
519        {
520            KeyConstructor  kc = new KeyConstructor();
521            for (FieldEntry fe : m_entries)
522            {
523                kc.addKeyValue(fe.m_name, fe.m_value);
524            }
525            res += " Java constructor: ";
526            res += kc.displayConstructor(m_descriptor.m_className);
527        }
528
529        if (m_query != null && m_query.m_filters != null)
530        {
531            for (int i = 0; i < m_query.m_filters.size(); i++)
532            {
533                KeyInfo filter = m_query.m_filters.get(i);
534                int keyDepth = 0;
535
536                for (FieldDescriptor fd : m_descriptor.m_fields)
537                {
538                    if (keyDepth == filter.m_keyDepth)
539                    {
540                        res += " [ Warning: field '";
541                        res += fd.m_name;
542                        res += "' filtered ]";
543                        break;
544                    }
545                    keyDepth++;
546                }
547            }
548        }
549
550        return res;
551    }
552
553    /////////////////////////////////////////////////////////////////
554    //
555    // Private fields, classes and utility routines
556    //
557    /////////////////////////////////////////////////////////////////
558
559    //
560    // Note: The following enumeration "know" about the actual values
561    // in the C++ enum definitions. We use them instead of java enums
562    // to avoid having to map things in JNI code.
563    //
564
565    // enumeration values for SWRBTree::RBDirection
566    private static final int SWRBTreeAscending  = 1;
567    private static final int SWRBTreeDescending = 2;
568
569    // enumeration values for OSKeyOpt::Placeholder
570    private static final int KeyOptCreatePlaceholder= 1;
571    private static final int KeyOptNoPlaceholder    = 2;
572
573    // enumeration values for OSKeyOpt::ObjectLock
574    private static final int KeyOptReadLock     = 1;
575    private static final int KeyOptWriteLock    = 2;
576    private static final int KeyOptNoLock       = 3;
577
578    // enumeration values for CGObject::MaxSelectRetries
579    private static final int MaxSelectRetries   = 10;
580
581    // enumeration values for ObjSrv::RangeOperator
582    private static final int ObjSrv_EQ      = 1;
583    private static final int ObjSrv_NEQ     = 2;
584    private static final int ObjSrv_GTE     = 3;
585    private static final int ObjSrv_GT      = 4;
586    private static final int ObjSrv_LTE     = 5;
587    private static final int ObjSrv_LT      = 6;
588
589    // enumeration values for DSETypeCode
590    private static final int tkInvalid      = 0;
591    private static final int tkShort        = 1;
592    private static final int tkLong         = 2;
593    private static final int tkLongLong     = 3;
594    private static final int tkUShort       = 4;
595    private static final int tkULong        = 5;
596    private static final int tkULongLong        = 6;
597    private static final int tkFloat        = 7;
598    private static final int tkDouble       = 8;
599    private static final int tkBoolean      = 9;
600    private static final int tkChar         = 10;
601    private static final int tkOctet        = 11;
602    private static final int tkObjectReference  = 12;
603    private static final int tkStruct       = 13;
604    private static final int tkEnum         = 14;
605    private static final int tkString       = 15;
606    private static final int tkSequence     = 16;
607    private static final int tkArray        = 17;
608    private static final int tkException        = 18;
609    private static final int tkVoid         = 19;
610    private static final int tkOffset       = 20;
611    private static final int tkSize         = 21;
612    private static final int tkWChar        = 22;
613    private static final int tkAny          = 23;
614    private static final int tkNative       = 24;
615    private static final int tkType         = 25;
616    private static final int tkUnion        = 26;
617    private static final int tkDate         = 27;
618
619    enum CompileFilter
620    {
621        FILTER_LTEGTE,
622        FILTER_NONE
623    }
624
625    //
626    // Return a string containing the Java type for the given typeCode
627    //
628    private static String getTCString(int tc)
629    {
630        String  ret = "<unknown>";
631        switch (tc)
632        {
633            case tkShort:
634            case tkUShort:
635                ret = "short";
636                break;
637            case tkLong:
638            case tkULong:
639                ret = "int";
640                break;
641            case tkULongLong:
642            case tkLongLong:
643                ret = "long";
644                break;
645            case tkFloat:
646                ret = "float";
647                break;
648            case tkDouble:
649                ret = "double";
650                break;
651            case tkBoolean:
652                ret = "boolean";
653                break;
654            case tkWChar:
655                ret = "char";
656                break;
657            case tkChar:
658            case tkOctet:
659                ret = "byte";
660                break;
661            case tkObjectReference:
662                ret = "Object";
663                break;
664            case tkString:
665                ret = "String";
666                break;
667            case tkDate:
668                ret = "java.util.Date";
669                break;
670            default:
671                break;
672        }
673        return ret;
674    }
675
676    //
677    // Return a string containing the operation
678    //
679    private static String getComparisonOp(KeyComparisonOperator op)
680    {
681        String ret = null;
682        switch (op)
683        {
684            case EQ:
685                ret = " == ";
686                break;
687            case NEQ:
688                ret = " != ";
689                break;
690            case GTE:
691                ret = " >= ";
692                break;
693            case GT:
694                ret = " > ";
695                break;
696            case LTE:
697                ret = " <= ";
698                break;
699            case LT:
700                ret = " < ";
701                break;
702        }
703        return ret;
704    }
705
706    //
707    // Return the KTP typeCode for the given java object
708    //
709    private static int getTypeCode(Object obj)
710    {
711        int typeCode = tkInvalid;
712        if (obj instanceof Integer)
713        {
714            typeCode = tkLong;
715        }
716        else if (obj instanceof Long)
717        {
718            typeCode = tkLongLong;
719        }
720        else if (obj instanceof String)
721        {
722            typeCode = tkString;
723        }
724        else if (obj instanceof Short)
725        {
726            typeCode = tkShort;
727        }
728        else if (obj instanceof Character)
729        {
730            typeCode = tkWChar;
731        }
732        else if (obj instanceof Float)
733        {
734            typeCode = tkFloat;
735        }
736        else if (obj instanceof Double)
737        {
738            typeCode = tkDouble;
739        }
740        else if (obj instanceof Enum)
741        {
742            typeCode = tkEnum;
743        }
744        else if (obj instanceof java.util.Date)
745        {
746            typeCode = tkDate;
747        }
748        else if (obj instanceof Boolean)
749        {
750            typeCode = tkBoolean;
751        }
752        else if (obj instanceof Byte)
753        {
754            typeCode = tkOctet;
755        }
756        else
757        {
758            typeCode = tkObjectReference;
759        }
760        assert( typeCode != tkInvalid );
761        return typeCode;
762    }
763    //
764    // Return the number of bytes a null key slot would contain for
765    // the given typeCode. Used when building key filters.
766    //
767    private static int getKeyOffset(int tc)
768    {
769        int size = 0;
770        switch (tc)
771        {
772            case tkShort:
773            case tkUShort:
774                size = 2;
775                break;
776            case tkLong:
777            case tkULong:
778            case tkFloat:
779            case tkWChar:
780            case tkEnum:
781                size = 4;
782                break;
783            case tkULongLong:
784            case tkLongLong:
785            case tkDouble:
786            case tkDate:
787                size = 8;
788                break;
789            case tkBoolean:
790            case tkChar:
791            case tkOctet:
792                size = 1;
793                break;
794            case tkObjectReference:
795                size = 24;
796                break;
797            case tkString:
798                size = 8;   // zero length string
799                break;
800        }
801        assert( size != 0 );
802        return size;
803    }
804
805    //
806    // Descriptors used to manage key information
807    //
808    private static class FieldDescriptor
809    {
810        FieldDescriptor(String name, int typeCode)
811        {
812            m_name = name;
813            m_typeCode = typeCode;
814        }
815        private final String        m_name;
816        private final int       m_typeCode;
817
818        //
819        // Return runtime mapping as per the ERD.
820        //
821        int getTypeCode()
822        {
823            int typeCode = m_typeCode;
824            switch (typeCode)
825            {
826                case tkChar:
827                    typeCode = tkOctet;
828                    break;
829                case tkUShort:
830                    typeCode = tkShort;
831                    break;
832                case tkULong:
833                    typeCode = tkLong;
834                    break;
835                case tkULongLong:
836                    typeCode = tkLongLong;
837                    break;
838                default:
839                    break;
840            }
841            assert( typeCode != tkInvalid );
842            return typeCode;
843        }
844    }
845
846    private static Map<String, KeyDescriptor>   m_map =
847        new ConcurrentHashMap<String, KeyDescriptor>();
848
849    private static class KeyDescriptor
850    {
851        KeyDescriptor(String className, String keyName)
852        {
853            m_className = className;
854            m_keyName = keyName;
855            m_fields = new ArrayList<FieldDescriptor>();
856            m_ordered = false;
857            m_unique = false;
858        }
859        //
860        // These are define by the KeyQuery() constructor
861        //
862        private final String        m_className;
863        private final String        m_keyName;
864        //
865        // These are all populated by _getKeyDescriptor()
866        // 
867        private ArrayList<FieldDescriptor> m_fields;
868        private boolean         m_ordered;
869        private boolean         m_unique;
870        private int         m_objDomainId;
871        private int         m_objTypeId;
872        private int         m_keyDomainId;
873        private int         m_keyTypeId;
874    }
875
876    //
877    // Low level buffer class that maps to the KTP runtime. 
878    //
879    // Types supported (from ERD)
880    //
881    //  o All basic Java types (boolean, byte, char, short,
882    //    int, long, float, double)
883    //
884    //  o Wrapper classes for the basic Java types
885    //    (Integer, Short, Character, etc.) 
886    //
887    //  o java.lang.String; 
888    //
889    //  o java.util.Date; 
890    //
891    //  o Enum types; 
892    //
893    //  o Managed Object types. 
894    //
895    private static class KeyBuffer extends AnyBuffer
896    {
897        void addData(Object obj)
898        {
899            boolean wasPut = putObject(obj);
900            assert( wasPut );
901        }
902    }
903
904    private static class KeyInfo extends KeyBuffer
905    {
906        KeyInfo()
907        {
908            m_rop = ObjSrv_EQ;
909            m_keyDepth = 1;
910        }
911        KeyInfo(int rop)
912        {
913            m_rop = rop;
914            m_keyDepth = 1;
915        }
916        void addKeyData(Object obj)
917        {
918            addData(obj);
919        }
920        int         m_rop;
921        int         m_keyDepth;
922    }
923
924    private static class CreateBuffer extends KeyBuffer
925    {
926        CreateBuffer(FieldEntry fe) throws KeyIllegalFieldError
927        {
928            m_name = fe.m_name;
929            m_typeCode = getTypeCode(fe.m_value);
930            try
931            {
932                writeObject(fe.m_value);
933            }
934            catch (IllegalArgumentException iae)
935            {
936                throw new KeyIllegalFieldError(
937                    "Field " + fe.m_name + " containing " +
938                    fe.m_value.getClass() + " is not supported");
939            }
940        }
941        String          m_name;
942        int         m_typeCode;
943    }
944
945    //
946    // Class containing a compiled query
947    //
948    private static class CompiledQuery
949    {
950        CompiledQuery()
951        {
952            m_keyInfo1 = null;
953            m_keyInfo2 = null;
954            m_filters = null;
955            m_hasRange = false;
956        }
957
958        void addRange(KeyInfo keyInfo)
959        {
960            if (m_keyInfo1 == null)
961            {
962                m_keyInfo1 = keyInfo;
963            }
964            else
965            {
966                assert( m_keyInfo2 == null );
967                m_keyInfo2 = keyInfo;
968            }
969            m_hasRange = true;
970        }
971
972        void addKeyInfo(KeyInfo keyInfo)
973        {
974            assert( !m_hasRange );
975            assert( m_keyInfo1 == null );
976            m_keyInfo1 = keyInfo;
977        }
978
979        void addFilter(KeyInfo keyInfo)
980        {
981            assert( m_hasRange );
982            if (m_filters == null)
983            {
984                m_filters = new ArrayList<KeyInfo>();
985            }
986            m_filters.add(keyInfo);
987        }
988
989        KeyInfo         m_keyInfo1;
990        KeyInfo         m_keyInfo2;
991        ArrayList<KeyInfo>  m_filters; 
992        boolean         m_hasRange;
993    }
994
995    //
996    // Private data members that use the above private classes
997    //
998    private KeyDescriptor       m_descriptor;
999    private CompiledQuery       m_query; 
1000    private ArrayList<FieldEntry>   m_entries; 
1001    private boolean         m_hasKeyCoverage;
1002
1003    //
1004    // Class that implements the Iterable returned from getResults()
1005    //
1006    private class KeyResultSet<T extends Object>
1007        implements Iterable<T>
1008    {
1009        KeyOrderedBy    m_orderBy;
1010        LockMode    m_objectLock;
1011
1012        KeyResultSet(KeyOrderedBy orderBy, LockMode objectLock)
1013        {
1014            m_orderBy = orderBy;
1015            m_objectLock = objectLock;
1016        }
1017        KeyResultSet(LockMode objectLock)
1018        {
1019            m_orderBy = KeyOrderedBy.ASCENDING;
1020            m_objectLock = objectLock;
1021        }
1022
1023        public Iterator<T> iterator()
1024        {
1025            return new KeyIterator<T>(m_orderBy, m_objectLock);
1026        }
1027    }
1028
1029    //
1030    // Class that implements the Iterator returned from Iterable
1031    //
1032    private class KeyIterator<T extends Object>
1033        implements java.util.Iterator<T>
1034    {
1035        private long    m_cHandle;
1036        private T   m_next;
1037
1038        KeyIterator(KeyOrderedBy orderBy, LockMode objectLock)
1039        {
1040            assert( m_query.m_keyInfo1 != null );
1041            if (m_query.m_hasRange == false)
1042            {
1043                m_cHandle = _initKey(
1044                    m_descriptor.m_objDomainId,
1045                    m_descriptor.m_objTypeId,
1046                    m_descriptor.m_keyDomainId,
1047                    m_descriptor.m_keyTypeId,
1048                    mapObjectLock(objectLock),
1049                    m_query.m_keyInfo1.array());
1050            }
1051            else if (m_query.m_keyInfo2 == null)
1052            {
1053                assert( m_query.m_hasRange == true );
1054                assert( m_query.m_keyInfo2 == null );
1055                Object [] filters = (m_query.m_filters == null)
1056                    ? null : m_query.m_filters.toArray();
1057                m_cHandle = _initRange1(
1058                    m_descriptor.m_objDomainId,
1059                    m_descriptor.m_objTypeId,
1060                    m_descriptor.m_keyDomainId,
1061                    m_descriptor.m_keyTypeId,
1062                    mapOrderBy(orderBy),
1063                    mapObjectLock(objectLock),
1064                    m_query.m_keyInfo1.m_keyDepth,
1065                    m_query.m_keyInfo1.m_rop,
1066                    m_query.m_keyInfo1.array(),
1067                    filters);
1068            }
1069            else
1070            {
1071                assert( m_query.m_hasRange == true );
1072                assert( m_query.m_keyInfo2 != null );
1073                Object [] filters = (m_query.m_filters == null)
1074                    ? null : m_query.m_filters.toArray();
1075                m_cHandle = _initRange2(
1076                    m_descriptor.m_objDomainId,
1077                    m_descriptor.m_objTypeId,
1078                    m_descriptor.m_keyDomainId,
1079                    m_descriptor.m_keyTypeId,
1080                    mapOrderBy(orderBy),
1081                    mapObjectLock(objectLock),
1082                    m_query.m_keyInfo1.m_keyDepth,
1083                    m_query.m_keyInfo1.m_rop,
1084                    m_query.m_keyInfo1.array(),
1085                    m_query.m_keyInfo2.m_keyDepth,
1086                    m_query.m_keyInfo2.m_rop,
1087                    m_query.m_keyInfo2.array(),
1088                    filters);
1089            }
1090            m_next = _next(m_cHandle);
1091        }
1092
1093        public boolean hasNext()
1094        {
1095            return (m_next != null);
1096        }
1097
1098        public T next()
1099        {
1100            T t = m_next;
1101
1102            m_next = _next(m_cHandle);
1103
1104            return t;
1105        }
1106
1107        public void remove()
1108        {
1109            throw new UnsupportedOperationException();
1110        }
1111
1112        protected void finalize()
1113        {
1114            if (m_cHandle != 0)
1115            {
1116                _delete(m_cHandle);
1117            }
1118            m_cHandle = 0;
1119        }
1120
1121        int size()
1122        {
1123            return _size(m_cHandle);
1124        }
1125
1126        native long _initKey(
1127                int objDomainId,
1128                int objTypeId,
1129                int keyDomainId,
1130                int keyTypeId,
1131                int objLock,
1132                byte [] keyData);
1133        native long _initRange1(
1134                int objDomainId,
1135                int objTypeId,
1136                int keyDomainId,
1137                int keyTypeId,
1138                int direction,
1139                int objLock,
1140                int keyDepth,
1141                int rop,
1142                byte [] keyData,
1143                Object [] filters);
1144        native long _initRange2(
1145                int objDomainId,
1146                int objTypeId,
1147                int keyDomainId,
1148                int keyTypeId,
1149                int direction,
1150                int objLock,
1151                int keyDepth1,
1152                int rop1,
1153                byte [] keyData1,
1154                int keyDepth2,
1155                int rop2,
1156                byte [] keyData2,
1157                Object [] filters);
1158
1159        native int _size(long cHandle);
1160        native T _next(long cHandle);
1161        native void _delete(long cHandle);
1162    }
1163
1164    //
1165    // Constructors are not exposed.
1166    //
1167    private KeyQuery() { }
1168    KeyQuery(
1169        final java.lang.Class<T> klass,
1170        final String keyName) throws KeyUnknownKeyNameError
1171    {
1172        String keyDescriptorKey = klass.getName() + "." + keyName;
1173
1174        m_descriptor = m_map.get(keyDescriptorKey);
1175
1176        if (m_descriptor == null)
1177        {
1178            m_descriptor = new KeyDescriptor(klass.getName(), keyName);
1179
1180            if (!_getKeyDescriptor(m_descriptor))
1181            {
1182                throw new KeyUnknownKeyNameError(
1183                    "Unknown keyName " + keyName);
1184            }
1185
1186            // No synchronization is needed here.  If two threads
1187            // get here at the same time, the second put will replace
1188            // the first, and the GC will clean up at some later time.
1189            m_map.put(keyDescriptorKey, m_descriptor);
1190        }
1191        m_hasKeyCoverage = false;
1192    }
1193
1194    private void processFields(ArrayList<FieldEntry> entries)
1195        throws KeyIllegalFieldError, KeyMalformedQueryError
1196    {
1197        //
1198        // Clear out previous query to insure validateExecution()
1199        // fails if the new query encounters an error.
1200        //
1201        m_query = null;
1202
1203        //
1204        // entries contains a copy of the FieldEntry array defined by
1205        // the user. We keep a copy around mostly for so that
1206        // toString() can access it, otherwise we could just pass
1207        // it around for processing and compilation.
1208        //
1209        m_entries = entries;
1210
1211        //
1212        // Validate the fields we just stored.
1213        //
1214        validateFields();
1215
1216        //
1217        // Determine if the fields cover the key.
1218        //
1219        checkForKeyCoverage();
1220    }
1221
1222    private void validateFields()
1223        throws KeyIllegalFieldError, KeyMalformedQueryError
1224    {
1225        //
1226        // Validate each field
1227        //
1228        int count = 0;
1229        for (FieldEntry fe : m_entries)
1230        {
1231            validateField(fe);
1232            count += 1;
1233        }
1234        if (count == 0)
1235        {
1236            throw new KeyMalformedQueryError("No fields defined");
1237        }
1238
1239        //
1240        // Check for dups
1241        //
1242        if ((!m_descriptor.m_ordered) && (count > 1))
1243        {
1244            validateNoDups();
1245        }
1246    }
1247
1248    private void validateField(FieldEntry fe)
1249        throws KeyIllegalFieldError, KeyMalformedQueryError
1250    {
1251        boolean found = false;
1252
1253        for (FieldDescriptor fd : m_descriptor.m_fields)
1254        {
1255            if (fe.m_name.equals(fd.m_name))
1256            {
1257                validateValue(fd, fe.m_value);
1258                found = true;
1259            }
1260        }
1261        if (!found)
1262        {
1263            throw new KeyIllegalFieldError(
1264                "Field " + fe.m_name +
1265                " is not part of the key");
1266        }
1267    }
1268
1269    private boolean findField(FieldEntry fe)
1270    {
1271        boolean found = false;
1272
1273        for (FieldDescriptor fd : m_descriptor.m_fields)
1274        {
1275            if (fe.m_name.equals(fd.m_name))
1276            {
1277                return true;
1278            }
1279        }
1280        return false;
1281    }
1282
1283    private void validateNoDups()
1284        throws KeyMalformedQueryError
1285    {
1286        assert( !m_descriptor.m_ordered );
1287
1288        for (FieldDescriptor fd : m_descriptor.m_fields)
1289        {
1290            int count = 0;
1291            for (FieldEntry fe : m_entries)
1292            {
1293                if (fd.m_name.equals(fe.m_name))
1294                {
1295                    count += 1;
1296                }
1297            }
1298            if (count > 1)
1299            {
1300                throw new KeyMalformedQueryError(
1301                    "Field " + fd.m_name +
1302                    " exists more than once in " +
1303                    "the KeyFieldValueList list");
1304            }
1305        }
1306    }
1307
1308    private void validateValue(FieldDescriptor fd, Object value)
1309        throws KeyIllegalFieldError
1310    {
1311        assert( value != null );
1312
1313        int typeCode = getTypeCode(value);
1314
1315        if (typeCode != fd.getTypeCode())
1316        {
1317            throw new KeyIllegalFieldError(
1318                "Illegal type " + getTCString(typeCode) +
1319                " for field " + fd.m_name + ", expected " +
1320                getTCString(fd.getTypeCode()));
1321        }
1322    }
1323
1324    private void checkForKeyCoverage() 
1325    {
1326        //
1327        // If any of the field entries are not ==, we don't have
1328        // key coverage.
1329        //
1330        for (FieldEntry fe : m_entries)
1331        {
1332            if (fe.m_op != KeyComparisonOperator.EQ)
1333            {
1334                m_hasKeyCoverage = false;
1335                return;
1336            }
1337        }
1338
1339        //
1340        // If all fields are defined once and only one we have
1341        // a complete key.
1342        //
1343        int count = 0;
1344        int [] fieldCnt = new int[m_descriptor.m_fields.size()];
1345        for (FieldDescriptor fd : m_descriptor.m_fields)
1346        {
1347            fieldCnt[count] = 0;
1348            for (FieldEntry fe : m_entries)
1349            {
1350                if (fd.m_name.equals(fe.m_name))
1351                {
1352                    fieldCnt[count] += 1;
1353                }
1354            }
1355            count += 1;
1356        }
1357        m_hasKeyCoverage = true;
1358        for (int i = 0; i < count; i++)
1359        {
1360            if (fieldCnt[i] != 1)
1361            {
1362                m_hasKeyCoverage = false;
1363                break;
1364            }
1365        }
1366    }
1367
1368    //
1369    // If key coverage doesn't exist, make sure ordered query
1370    // isn't malformed.
1371    //
1372    private void validateOrderedKey()
1373        throws KeyMalformedQueryError
1374    {
1375        assert( !m_hasKeyCoverage );
1376
1377        String      firstField;
1378        int         numKeys = 0;
1379
1380        firstField = m_descriptor.m_fields.get(0).m_name;
1381
1382        for (FieldEntry fe : m_entries)
1383        {
1384            if (!fe.m_name.equals(firstField) && numKeys == 0)
1385            {
1386                throw new KeyMalformedQueryError(
1387                    "Range queries must use the '" +
1388                    firstField +
1389                    "' field as the first entry");
1390            }
1391            if (fe.m_op == KeyComparisonOperator.NEQ &&
1392                numKeys == 0)
1393            {
1394                throw new KeyMalformedQueryError(
1395                    "Illegal operator != in range query");
1396            }
1397            numKeys += 1;
1398        }
1399    }
1400
1401    private void validateMinMaxQuery(CompiledQuery query)
1402        throws KeyMalformedQueryError
1403    {
1404        if (m_entries != null)
1405        {
1406            for (FieldEntry fe : m_entries)
1407            {
1408                if (fe.m_op != KeyComparisonOperator.EQ &&
1409                    fe.m_op != KeyComparisonOperator.GTE &&
1410                    fe.m_op != KeyComparisonOperator.LTE)
1411                {
1412                    throw new KeyMalformedQueryError(
1413                        "Illegal operator" + getComparisonOp(fe.m_op) +
1414                        "used in query \"" + this + "\""); 
1415                }
1416            }
1417        }
1418        if (query.m_filters != null)
1419        {
1420            throw new KeyMalformedQueryError(
1421                        "filter present on query \"" + this + "\"");
1422        }
1423    }
1424
1425    private void validateExecution()
1426        throws KeyIncompleteError
1427    {
1428        if (m_query == null)
1429        {
1430            throw new KeyIncompleteError("No query has been defined");
1431        }
1432        if (m_entries == null)
1433        {
1434            throw new KeyIncompleteError("No fields have been defined");
1435        }
1436
1437        // We should have passed all audits here
1438        assert( m_hasKeyCoverage || m_descriptor.m_ordered );
1439    }
1440
1441    private void validateOrdered()
1442        throws KeyIllegalKeyTypeError
1443    {
1444        if (m_descriptor.m_ordered == false)
1445        {
1446            throw new KeyIllegalKeyTypeError(
1447                "Key is not ordered");
1448        }
1449    }
1450
1451    private CompiledQuery compileSimpleQuery()
1452    {
1453        assert( m_hasKeyCoverage == true );
1454
1455        CompiledQuery query = new CompiledQuery();
1456
1457        KeyInfo keyInfo = new KeyInfo();
1458
1459        //
1460        // build key data based on field order
1461        //
1462        for (FieldDescriptor fd : m_descriptor.m_fields)
1463        {
1464            for (FieldEntry fe : m_entries)
1465            {
1466                if (fd.m_name.equals(fe.m_name))
1467                {
1468                    keyInfo.addKeyData(fe.m_value);
1469                    break;
1470                }
1471            }
1472        }
1473
1474        query.addKeyInfo(keyInfo);
1475
1476        return query;
1477    }
1478
1479    private CompiledQuery compileOrderedQuery(CompileFilter filter)
1480    {
1481        assert( m_descriptor.m_ordered == true );
1482
1483        CompiledQuery query = new CompiledQuery();
1484
1485        //
1486        // Build first range. This is either a multi-part key
1487        // defined using == clauses, or a one field key using
1488        // another comparison operator.
1489        //
1490        String firstField = m_descriptor.m_fields.get(0).m_name;
1491        Iterator<FieldEntry>    iter = m_entries.iterator();
1492
1493        assert( iter.hasNext() );
1494        FieldEntry fe = iter.next();
1495
1496        assert( firstField.equals(fe.m_name) );
1497        assert( fe.m_op != KeyComparisonOperator.NEQ );
1498        KeyComparisonOperator firstOp = fe.m_op;
1499
1500        KeyInfo keyInfo = new KeyInfo(mapComparisonOp(fe.m_op));
1501
1502        if (fe.m_op == KeyComparisonOperator.EQ)
1503        {
1504            //
1505            // Build the key based on the number of == operators.
1506            //
1507            createEqualsKey(keyInfo, filter);
1508
1509            //
1510            // Move the iterator to the next field based on
1511            // the key depth of the built key.
1512            //
1513            assert( keyInfo.m_keyDepth > 0 );
1514            int keyDepth = keyInfo.m_keyDepth;
1515
1516            while (keyDepth > 0)
1517            {
1518                fe = iter.hasNext() ? iter.next() : null;
1519                keyDepth--;
1520            }
1521        }
1522        else
1523        {
1524            keyInfo.addKeyData(fe.m_value);
1525            fe = iter.hasNext() ? iter.next() : null;
1526        }
1527        query.addRange(keyInfo);
1528
1529        // 
1530        // The field entry is now positioned to the next field.
1531        // Determine if we have a range.
1532        //
1533        if (fe != null && firstField.equals(fe.m_name) &&
1534            isRange(firstOp, fe.m_op))
1535        {
1536            keyInfo = new KeyInfo(mapComparisonOp(fe.m_op));
1537            keyInfo.addKeyData(fe.m_value);
1538            query.addRange(keyInfo);
1539            fe = iter.hasNext() ? iter.next() : null;
1540        }
1541
1542        //
1543        // The rest are filters
1544        //
1545        while (fe != null)
1546        {
1547            query.addFilter(createFilterKey(fe));
1548            fe = iter.hasNext() ? iter.next() : null;
1549        }
1550
1551        return query;
1552    }
1553
1554    private CompiledQuery compileQuery()
1555    {
1556        if (m_hasKeyCoverage)
1557        {
1558            return compileSimpleQuery();
1559        }
1560        return compileOrderedQuery(CompileFilter.FILTER_LTEGTE);
1561    }
1562
1563    private void createEqualsKey(KeyInfo keyInfo, CompileFilter filter)
1564    {
1565        //
1566        // Normally, a range query is of only one depth; that is, a
1567        // query that does:
1568        //
1569        //  part1 > value
1570        //
1571        // will position the start iterator using the first field
1572        // of a key. When multiple fields use ==, we can optimize
1573        // the position by specifying all the fields instead of
1574        // setting the first one, and filtering on the rest. As
1575        // an example:
1576        //  
1577        //  part1 == v1 && part2 == v2
1578        //
1579        // can be optimized by defining both part1 and part2 in
1580        // the key, and adjusting the depth so the runtime knows
1581        // to look at both of them when positioning the iterator.
1582        //
1583        // The code below iterates over both the FieldDescriptor
1584        // and FieldEntry, and it continues to build a temp list
1585        // of field entries until the field names don't match, or
1586        // an == is not seen.
1587        //
1588        // The runtime supports positioning of >= and <=, so we also
1589        // allow one of these. But once one is ecountered, the processing
1590        // of the fields terminates, any subsequent expressions are filtered.
1591        //
1592        Iterator<FieldDescriptor> fdIter =
1593                    m_descriptor.m_fields.iterator();
1594        Iterator<FieldEntry>    feIter = m_entries.iterator();
1595        ArrayList<FieldEntry>   entries = new ArrayList<FieldEntry>(); 
1596
1597        int lastOp = keyInfo.m_rop;
1598
1599        while (feIter.hasNext() && fdIter.hasNext())
1600        {
1601            FieldDescriptor fd = fdIter.next();
1602            FieldEntry  fe = feIter.next();
1603
1604            if (filter == CompileFilter.FILTER_LTEGTE)
1605            {
1606                if (fd.m_name.equals(fe.m_name) &&
1607                    fe.m_op == KeyComparisonOperator.EQ)
1608                {
1609                    entries.add(fe);
1610                }
1611                else
1612                {
1613                    break;
1614                }
1615            }
1616            else
1617            {
1618                assert( filter == CompileFilter.FILTER_NONE );
1619                if (fd.m_name.equals(fe.m_name) &&
1620                    (fe.m_op == KeyComparisonOperator.EQ ||
1621                        fe.m_op == KeyComparisonOperator.GTE ||
1622                        fe.m_op == KeyComparisonOperator.LTE))
1623                {
1624                    entries.add(fe);
1625                    lastOp = mapComparisonOp(fe.m_op);
1626
1627                    if (fe.m_op == KeyComparisonOperator.GTE ||
1628                        fe.m_op == KeyComparisonOperator.LTE)
1629                    {
1630                        break;
1631                    }
1632                }
1633                else
1634                {
1635                    break;
1636                }
1637            }
1638        }
1639
1640        //
1641        // Save the last op, which the runtime uses to position the key.
1642        //
1643        keyInfo.m_rop = lastOp;
1644
1645        //
1646        // Using the temp list, build the actual key
1647        //
1648        keyInfo.m_keyDepth = 0;
1649        for (FieldEntry fe : entries)
1650        {
1651            keyInfo.addKeyData(fe.m_value);
1652            keyInfo.m_keyDepth += 1;
1653        }
1654        assert( keyInfo.m_keyDepth > 0 );
1655    }
1656
1657    private boolean isRange(
1658        KeyComparisonOperator rop1,
1659        KeyComparisonOperator rop2)
1660    {
1661        //
1662        // See if the operators form a "range" of values.
1663        //
1664        if (rop1 == KeyComparisonOperator.GTE ||
1665            rop1 == KeyComparisonOperator.GT)
1666        {
1667            if (rop2 == KeyComparisonOperator.LTE ||
1668                rop2 == KeyComparisonOperator.LT)
1669            {
1670                return true;
1671            }
1672        }
1673        if (rop2 == KeyComparisonOperator.GTE ||
1674            rop2 == KeyComparisonOperator.GT)
1675        {
1676            if (rop1 == KeyComparisonOperator.LTE ||
1677                rop1 == KeyComparisonOperator.LT)
1678            {
1679                return true;
1680            }
1681        }
1682        return false;
1683    }
1684
1685    private KeyInfo createFilterKey(FieldEntry fe)
1686    {
1687        //
1688        // Key filters require a complete key with empty field
1689        // values up to the filtered key field. The runtime uses
1690        // keyDepth to determine which field slot to look at.
1691        //
1692        int keyDepth = 0;
1693        int pos = 0;
1694        for (FieldDescriptor fd : m_descriptor.m_fields)
1695        {
1696            if (fd.m_name.equals(fe.m_name))
1697            {
1698                break;
1699            }
1700            pos += getKeyOffset(fd.getTypeCode());
1701            keyDepth++;
1702        }
1703        //
1704        // The filter operator is the opposite of the field
1705        // operator (i.e. to find all the EQ keys, we filter on
1706        // all those that are NEQ). 
1707        //
1708        KeyComparisonOperator filterOp = getFilterOp(fe.m_op);
1709        KeyInfo keyInfo = new KeyInfo(mapComparisonOp(filterOp));
1710
1711        //
1712        // Allocate all the data and position it to the field we
1713        // need to set. We add a null byte in a loop instead of
1714        // the position() call to insure that the contents of the
1715        // preceding key data is zero.
1716        //
1717        while (pos > 0)
1718        {
1719            keyInfo.addKeyData((byte)0);
1720            pos--;
1721        }
1722        keyInfo.addKeyData(fe.m_value);
1723        keyInfo.m_keyDepth = keyDepth;
1724
1725        return keyInfo;
1726    }
1727
1728    private KeyComparisonOperator getFilterOp(KeyComparisonOperator op)
1729    {
1730        KeyComparisonOperator filterOp;
1731
1732        if (op == KeyComparisonOperator.GTE)
1733        {
1734            filterOp = KeyComparisonOperator.LT;
1735        }
1736        else if (op == KeyComparisonOperator.GT)
1737        {
1738            filterOp = KeyComparisonOperator.LTE;
1739        }
1740        else if (op == KeyComparisonOperator.LTE)
1741        {
1742            filterOp = KeyComparisonOperator.GT;
1743        }
1744        else if (op == KeyComparisonOperator.LT)
1745        {
1746            filterOp = KeyComparisonOperator.GTE;
1747        }
1748        else if (op == KeyComparisonOperator.NEQ)
1749        {
1750            filterOp = KeyComparisonOperator.EQ;
1751        }
1752        else
1753        {
1754            assert( op == KeyComparisonOperator.EQ );
1755            filterOp = KeyComparisonOperator.NEQ;
1756        }
1757        return filterOp;
1758    }
1759
1760    private int mapOrderBy(KeyOrderedBy orderBy)
1761    {
1762        return (orderBy == KeyOrderedBy.ASCENDING)
1763            ? SWRBTreeAscending : SWRBTreeDescending;
1764    }
1765
1766    private int mapObjectLock(LockMode objectLock)
1767    {
1768        switch (objectLock)
1769        {
1770            case NOLOCK:
1771                return KeyOptNoLock;
1772            case READLOCK:
1773                return KeyOptReadLock;
1774        }
1775        assert( objectLock == LockMode.WRITELOCK );
1776
1777        return KeyOptWriteLock;
1778    }
1779
1780    private int mapComparisonOp(KeyComparisonOperator op)
1781    {
1782        switch (op)
1783        {
1784            case NEQ:
1785                return ObjSrv_NEQ;
1786            case GTE:
1787                return ObjSrv_GTE;
1788            case GT:
1789                return ObjSrv_GT;
1790            case LTE:
1791                return ObjSrv_LTE;
1792            case LT:
1793                return ObjSrv_LT;
1794        }
1795        assert( op == KeyComparisonOperator.EQ );
1796
1797        return ObjSrv_EQ;
1798    }
1799
1800    private T attachObject(LockMode objectLock, int placeHolder)
1801    {
1802        return _attachObject(
1803                m_descriptor.m_objDomainId,
1804                m_descriptor.m_objTypeId,
1805                m_descriptor.m_keyDomainId,
1806                m_descriptor.m_keyTypeId,
1807                m_query.m_keyInfo1.array(),
1808                placeHolder,
1809                mapObjectLock(objectLock));
1810    }
1811
1812    private T attachMinimum(CompiledQuery query, LockMode objectLock)
1813    {
1814        return _attachMinimum(
1815                m_descriptor.m_objDomainId,
1816                m_descriptor.m_objTypeId,
1817                m_descriptor.m_keyDomainId,
1818                m_descriptor.m_keyTypeId,
1819                query.m_keyInfo1.m_keyDepth,
1820                query.m_keyInfo1.m_rop,
1821                query.m_keyInfo1.array(),
1822                mapObjectLock(objectLock));
1823    }
1824
1825    private T attachMaximum(CompiledQuery query, LockMode objectLock)
1826    {
1827        return _attachMaximum(
1828                m_descriptor.m_objDomainId,
1829                m_descriptor.m_objTypeId,
1830                m_descriptor.m_keyDomainId,
1831                m_descriptor.m_keyTypeId,
1832                query.m_keyInfo1.m_keyDepth,
1833                query.m_keyInfo1.m_rop,
1834                query.m_keyInfo1.array(),
1835                mapObjectLock(objectLock));
1836    }
1837
1838    private T createObject(ArrayList<CreateBuffer> createValues)
1839    {
1840        Object [] cvArray = (createValues == null)
1841                ? null : createValues.toArray();
1842        return _createObject(
1843                m_descriptor.m_objDomainId,
1844                m_descriptor.m_objTypeId,
1845                m_descriptor.m_keyDomainId,
1846                m_descriptor.m_keyTypeId,
1847                m_query.m_keyInfo1.array(),
1848                cvArray);
1849    }
1850
1851    private native boolean _getKeyDescriptor(KeyDescriptor descriptor);
1852    private native T _attachObject(
1853                int objDomainId,
1854                int objTypeId,
1855                int keyDomainId,
1856                int keyTypeId,
1857                byte [] keyData,
1858                int placeHolder,
1859                int objLock);
1860    private native T _attachMinimum(
1861                int objDomainId,
1862                int objTypeId,
1863                int keyDomainId,
1864                int keyTypeId,
1865                int keyDepth,
1866                int rop,
1867                byte [] keyData,
1868                int objLock);
1869    private native T _attachMaximum(
1870                int objDomainId,
1871                int objTypeId,
1872                int keyDomainId,
1873                int keyTypeId,
1874                int keyDepth,
1875                int rop,
1876                byte [] keyData,
1877                int objLock);
1878    private native T _createObject(
1879                int objDomainId,
1880                int objTypeId,
1881                int keyDomainId,
1882                int keyTypeId,
1883                byte [] keyData,
1884                Object [] createValues);
1885}