File: FunctionExpression.java

package info (click to toggle)
eclipselink 2.7.11-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 44,820 kB
  • sloc: java: 477,843; xml: 503; makefile: 21
file content (974 lines) | stat: -rw-r--r-- 42,594 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
/*
 * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2019, 2022 IBM Corporation. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     IBM - Bug 537795: CASE THEN and ELSE scalar expression Constants should not be casted to CASE operand type
package org.eclipse.persistence.internal.expressions;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.expressions.ExpressionOperator;
import org.eclipse.persistence.history.AsOfClause;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.NonSynchronizedVector;
import org.eclipse.persistence.internal.queries.ReportItem;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.mappings.querykeys.ForeignReferenceQueryKey;
import org.eclipse.persistence.mappings.querykeys.QueryKey;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.queries.ReportQuery;

/**
 * Used for expressions that have 0 to n children.
 * These include not, between and all functions.
 */
public class FunctionExpression extends BaseExpression {
    protected Vector<Expression> children;
    protected ExpressionOperator operator;
    protected transient ExpressionOperator platformOperator;
    protected Class resultType;

    public FunctionExpression() {
        this.children = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(2);
        this.resultType = null;
    }

    /**
     * INTERNAL:
     * Return if the expression is equal to the other.
     * This is used to allow dynamic expression's SQL to be cached.
     * This must be over written by each subclass.
     */
    @Override
    public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (!super.equals(object)) {
            return false;
        }
        FunctionExpression expression = (FunctionExpression) object;
        if ((this.operator != expression.getOperator()) && ((this.operator == null) || (!this.operator.equals(expression.getOperator())))) {
            return false;
        }
        List<?> children = getChildren();
        List<?> otherChildren = expression.getChildren();
        int size = children.size();
        if (size != otherChildren.size()) {
            return false;
        }
        for (int index = 0; index < size; index++) {
            if (!children.get(index).equals(otherChildren.get(index))) {
                return false;
            }
        }
        return true;
    }

    /**
     * INTERNAL:
     * Compute a consistent hash-code for the expression.
     * This is used to allow dynamic expression's SQL to be cached.
     */
    @Override
    public int computeHashCode() {
        int hashCode = super.computeHashCode();
        if (this.operator != null) {
            hashCode = hashCode + this.operator.hashCode();
        }
        List<?> children = getChildren();
        int size = children.size();
        for (int index = 0; index < size; index++) {
            hashCode = hashCode + children.get(index).hashCode();
        }
        return hashCode;
    }

    public void addChild(Expression child) {
        getChildren().addElement(child);
    }

    /**
     * INTERNAL:
     * Find the alias for a given table
     */
    @Override
    public DatabaseTable aliasForTable(DatabaseTable table) {
        return getBaseExpression().aliasForTable(table);
    }

    @Override
    public Expression asOf(AsOfClause clause) {
        final AsOfClause finalClause = clause;
        ExpressionIterator iterator = new ExpressionIterator() {
            @Override
            public void iterate(Expression each) {
                if (each.isDataExpression()) {
                    each.asOf(finalClause);
                }
            }

            @Override
            public boolean shouldIterateOverSubSelects() {
                return true;
            }
        };
        iterator.iterateOn(this);
        return this;
    }

    /**
     * INTERNAL:
     */
    @Override
    public Expression create(Expression base, Object singleArgument, ExpressionOperator anOperator) {
        baseExpression = base;
        addChild(base);
        Expression localBase = base;
        if(anOperator.isFunctionOperator()) {
            ExpressionBuilder builder = getBuilder();
            if(builder != null) {
                localBase = builder;
            }
        }
        Expression arg = Expression.from(singleArgument, localBase);
        addChild(arg);
        setOperator(anOperator);
        return this;
    }

    /**
     * INTERNAL:
     * added for Trim support.  TRIM([trim_character FROM] string_primary)
     */
    @Override
    public Expression createWithBaseLast(Expression base, Object singleArgument, ExpressionOperator anOperator) {
        baseExpression = base;
        Expression localBase = base;
        if(anOperator.isFunctionOperator()) {
            ExpressionBuilder builder = getBuilder();
            if(builder != null) {
                localBase = builder;
            }
        }
        Expression arg = Expression.from(singleArgument, localBase);
        addChild(arg);
        addChild(base);
        setOperator(anOperator);
        return this;
    }

    /**
     * INTERNAL:
     */
    @Override
    public Expression create(Expression base, List arguments, ExpressionOperator anOperator) {
        this.baseExpression = base;
        setOperator(anOperator);
        addChild(base);
        Expression localBase = base;
        if (anOperator.isFunctionOperator()) {
            ExpressionBuilder builder = getBuilder();
            if (builder != null) {
                localBase = builder;
            }
        }
        for (Object argument : arguments) {
            Expression arg = Expression.from(argument, localBase);
            addChild(arg);
        }
        return this;
    }

    /**
     * INTERNAL:
     * Used for debug printing.
     */
    @Override
    public String descriptionOfNodeType() {
        return "Function";
    }

    /**
     * INTERNAL:
     * Check if the object conforms to the expression in memory.
     * This is used for in-memory querying.
     * If the expression in not able to determine if the object conform throw a not supported exception.
     */
    @Override
    public boolean doesConform(Object object, AbstractSession session, AbstractRecord translationRow, int valueHolderPolicy, boolean isObjectUnregistered) {
        int selector = this.operator.getSelector();

        // Must check for NOT and negate entire base expression.
        if (selector == ExpressionOperator.Not) {
            return !getBaseExpression().doesConform(object, session, translationRow, valueHolderPolicy, isObjectUnregistered);
        }

        // Conform between or in function.
        if ((selector == ExpressionOperator.Between) || (selector == ExpressionOperator.NotBetween)
                || (selector == ExpressionOperator.In) || (selector == ExpressionOperator.NotIn)
                || (selector == ExpressionOperator.Like) || (selector == ExpressionOperator.Regexp)
                || (selector == ExpressionOperator.NotLike)) {
            // Extract the value from the left side.
            Object leftValue = getBaseExpression().valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered);

            // Extract the value from the arguments, skip the first child which is the base.
            int size = this.children.size();
            Vector rightValue = new Vector(size);
            for (int index = 1; index < size; index++) {
                Object valueFromRight;
                Expression child = this.children.get(index);
                if (child instanceof Expression) {
                    valueFromRight = child.valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered);
                } else {
                    valueFromRight = child;
                }
                //If valueFromRight is a Vector, then there is only one child other than the base, e.g. valueFromRight is a collection of constants.
                //Then it should be the vector to be compared with.  Don't add it to another collection.
                if (valueFromRight instanceof Vector) {
                    rightValue = (Vector)valueFromRight;
                //Single values should be added to the rightValue, which will be compared with leftValue.
                } else {
                    rightValue.add(valueFromRight);
                }
            }

            // If left is anyof collection of values, check each one.
            // If the right had an anyof not supported will be thrown from the operator.
            if (leftValue instanceof Vector) {
                for (Object tempLeft : (Vector)leftValue) {
                    if (this.operator.doesRelationConform(tempLeft, rightValue)) {
                        return true;
                    }
                }

                // Only return false if none of the values match.
                return false;
            } else {
                return this.operator.doesRelationConform(leftValue, rightValue);
            }
        } else if ((selector == ExpressionOperator.IsNull) || (selector == ExpressionOperator.NotNull)) {
            // Extract the value from the left side.
            Object leftValue = getBaseExpression().valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered);

            // If left is anyof collection of values, check each one.
            if (leftValue instanceof Vector) {
                for (Object tempLeft : (Vector)leftValue) {
                    if (this.operator.doesRelationConform(tempLeft, null)) {
                        return true;
                    }
                }

                // Only return false if none of the values match.
                return false;
            } else {
                return this.operator.doesRelationConform(leftValue, null);
            }
        }

        // No other relation functions are supported.
        // Non-relation functions are supported through valueFromObject().
        throw QueryException.cannotConformExpression();
    }

    public Vector<Expression> getChildren() {
        return this.children;
    }

    /**
     * INTERNAL: Not to be confused with the public getField(String)
     * This returns a collection of all fields associated with this object. Really
     * only applies to query keys representing an object or to expression builders.
     *
     */
    @Override
    public Vector getFields() {
        return getBaseExpression().getFields();
    }

    /**
     * INTERNAL:
     */
    @Override
    public List<DatabaseField> getSelectionFields(ReadQuery query) {
        return getBaseExpression().getSelectionFields(query);
    }

    @Override
    public ExpressionOperator getOperator() {
        return operator;
    }

    public ExpressionOperator getPlatformOperator(DatabasePlatform platform) {
        if (platformOperator == null) {
            initializePlatformOperator(platform);
        }
        return platformOperator;
    }

    public Class getResultType() {
        return resultType;
    }

    public boolean hasResultType() {
        return resultType != null;
    }

    /**
     * INTERNAL:
     */
    public void initializePlatformOperator(DatabasePlatform platform) {
        // First, check that the platform operator doesn't override the operator behavior
        platformOperator = platform.getOperator(this.operator.getSelector());
        if (platformOperator == null) {
            // If the platform doesn't specifically override, fallback on the internal operator
            // This operator should be either user-defined or one from ExpressionOperator.initializeInternalOperators.
            platformOperator = this.operator;
            if (platformOperator == null) {
                throw QueryException.invalidOperator(this.operator);
            }
        }
    }

    @Override
    public boolean isFunctionExpression() {
        return true;
    }

    /**
     * INTERNAL:
     * Return if the represents an object comparison.
     */
    protected boolean isObjectComparison() {
        int selector = this.operator.getSelector();
        if (((selector != ExpressionOperator.IsNull) && (selector != ExpressionOperator.NotNull)) || (this.children.size() != 1)) {
            if (((selector != ExpressionOperator.InSubQuery) && (selector != ExpressionOperator.NotInSubQuery))
                    || (this.children.size() != 2)) {
                return false;
            }
        }

        Expression base = getBaseExpression();
        //bug 384641 - check that directCollections are not treated as object comparisons
        return (base.isObjectExpression() && (!((ObjectExpression)base).isAttribute()) &&
                !((ObjectExpression)base).isDirectCollection() );
    }

    /**
     * INTERNAL:
     * For iterating using an inner class
     */
    @Override
    public void iterateOn(ExpressionIterator iterator) {
        super.iterateOn(iterator);
        for (Enumeration<Expression> childrenEnum = this.children.elements(); childrenEnum.hasMoreElements();) {
            Expression child = childrenEnum.nextElement();
            child.iterateOn(iterator);
        }
    }

    /**
     * INTERNAL:
     * Normalize into a structure that is printable.
     * Also compute printing information such as outer joins.
     * This checks for object isNull, notNull, in and notIn comparisons.
     */
    @Override
    public Expression normalize(ExpressionNormalizer normalizer) {
        //This method has no validation but we should still make the method call for consistency
        //bug # 2956674
        //validation is moved into normalize to ensure that expressions are valid before we attempt to work with them
        validateNode();
        if (this.children.isEmpty()) {
            return this;
        }

        // Ensure session has been set.
        ExpressionBuilder builder = getBuilder();
        if ((builder != null) && (builder.getSession() == null)) {
            builder.setSession(normalizer.getSession().getRootSession(null));
        }

        if (this.operator.getSelector() == ExpressionOperator.Count) {
            // Attempting to count an Entity and not an attribute.  Need to augment this expression.
            // This is normally normalized in ReportQuery, but can get to here in a having clause.
            prepareObjectAttributeCount(normalizer, null, null, null);
        }

        if (!isObjectComparison()) {
            for (int index = 0; index < this.children.size(); index++) {
            	this.children.set(index, this.children.get(index).normalize(normalizer));
            }
            return this;
        } else {
            //if not normalizing we must still validate the corresponding node to make sure that they are valid
            //bug # 2956674
            for (int index = 0; index < this.children.size(); index++) {
            	this.children.get(index).validateNode();
            }
        }

        // This code is executed only in the case of an is[not]Null, or [not]in on an
        // object attribute.
        ObjectExpression base = (ObjectExpression)getBaseExpression();

        // For cr2334, fix code so that normalize is first called on base expressions.
        // I.e. if base itself had a base expression this expression would not be normalized.
        if (base.getBaseExpression() != null) {
            base.getBaseExpression().normalize(normalizer);
        }

        // Check for IN with objects, "e IN (Select e2 from Employee e2)".
        if ((this.operator.getSelector() == ExpressionOperator.InSubQuery)
                || (this.operator.getSelector() == ExpressionOperator.NotInSubQuery)) {
            // Switch object comparison to compare on primary key.
            if (this.children.size() != 2) {
                throw QueryException.invalidExpression(this);
            }
            // Check if the left is for a 1-1 mapping, then optimize to compare on foreign key to avoid join.
            DatabaseMapping mapping = null;
            if (base.isQueryKeyExpression()) {
                mapping = base.getMapping();
            }
            List<DatabaseField> sourceFields = null;
            List<DatabaseField> targetFields = null;
            if ((mapping != null) && mapping.isOneToOneMapping()
                    && (!((OneToOneMapping)mapping).hasRelationTableMechanism())
                    && (!((OneToOneMapping)mapping).hasCustomSelectionQuery())) {
                base = (ObjectExpression)base.getBaseExpression();
                Map<DatabaseField, DatabaseField> targetToSourceKeyFields = ((OneToOneMapping)mapping).getTargetToSourceKeyFields();
                sourceFields = new ArrayList(targetToSourceKeyFields.size());
                targetFields = new ArrayList(targetToSourceKeyFields.size());
                for (Map.Entry<DatabaseField, DatabaseField> entry : targetToSourceKeyFields.entrySet()) {
                    sourceFields.add(entry.getValue());
                    targetFields.add(entry.getKey());
                }
            } else {
                mapping = null;
                sourceFields = base.getDescriptor().getPrimaryKeyFields();
                targetFields = sourceFields;
            }
            if (sourceFields.size() != 1) {
                base = (ObjectExpression)getBaseExpression();
                // For composite ids an exists and subselect is used.
                SubSelectExpression subSelectExp = (SubSelectExpression)this.children.get(1);
                ReportQuery subQuery = subSelectExp.getSubQuery();

                // some db (derby) require that in EXIST(SELECT...) subquery returns a single column
                subQuery.getItems().clear();
                subQuery.addItem("one", new ConstantExpression(Integer.valueOf(1), subQuery.getExpressionBuilder()));

                Expression subSelectCriteria = subQuery.getSelectionCriteria();
                ExpressionBuilder subBuilder = subQuery.getExpressionBuilder();
                Expression newExp;
                // Any or Some
                if (this.operator.getSelector() == ExpressionOperator.InSubQuery) {
                    subSelectCriteria = subBuilder.equal(base).and(subSelectCriteria);
                } else {
                    subSelectCriteria = subBuilder.notEqual(base).and(subSelectCriteria);
                }
                subQuery.setSelectionCriteria(subSelectCriteria);
                newExp = builder.exists(subQuery);
                return newExp.normalize(normalizer);
            }
            Expression newBase = base.getField(sourceFields.get(0));
            setBaseExpression(newBase);
            this.children.set(0, newBase);
            Expression right = this.children.get(1);
            if (right.isSubSelectExpression()) {
                // Check for sub-select, need to replace sub-selects on object to select its id.
                ReportQuery query = ((SubSelectExpression)right).getSubQuery();
                if (query.getItems().size() != 1) {
                    throw QueryException.invalidExpression(this);
                }
                ReportItem item = query.getItems().get(0);
                item.setAttributeExpression(item.getAttributeExpression().getField(targetFields.get(0)));
            } else {
                throw QueryException.invalidExpression(this);
            }
            // Still need to normalize the children.
            for (int index = 0; index < this.children.size(); index++) {
                this.children.set(index, this.children.get(index).normalize(normalizer));
            }
            return this;
        }
        // else isNull/notNull

        Expression foreignKeyJoin = null;
        if (base.getMapping() == null) {
            // Is an expression builder, transform to a null primary key expression.
            foreignKeyJoin = base.getDescriptor().getObjectBuilder().buildPrimaryKeyExpressionFromKeys(null, getSession());
            foreignKeyJoin = foreignKeyJoin.rebuildOn(base);
        } else {
            // Switch to null foreign key comparison (i.e. get('c').isNull() to getField('C_ID').isNull()).
            // For bug 3105559 also must handle aggregates: get("period").isNull();
            foreignKeyJoin = base.getMapping().buildObjectJoinExpression(base, (Object)null, getSession());
        }

        if (this.operator.getSelector() == ExpressionOperator.NotNull) {
            foreignKeyJoin = foreignKeyJoin.not();
        }
        return foreignKeyJoin.normalize(normalizer);
    }

    /**
     * INTERNAL:
     * Used for cloning.
     */
    @Override
    protected void postCopyIn(Map alreadyDone) {
        super.postCopyIn(alreadyDone);
        Vector<Expression> oldChildren = this.children;
        this.children = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance();
        for (int i = 0; i < oldChildren.size(); i++) {
        	addChild((oldChildren.elementAt(i).copiedVersionFrom(alreadyDone)));
        }
    }

    /**
     * INTERNAL:
     * Print SQL using the operator.
     */
    @Override
    public void printSQL(ExpressionSQLPrinter printer) {
        /*
         * If this ExpressionOperator does not support binding, and the platform allows,
         * then disable binding for the whole query
         */
        if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !this.children.isEmpty()) {
            boolean allParams = true;
            for (Iterator<Expression> iterator = this.children.iterator(); iterator.hasNext(); ) {
                Expression child = iterator.next();
                if (!(child.isParameterExpression() || child.isConstantExpression())) {
                    allParams = false;
                }
            }
            if (allParams) {
                printer.getCall().setUsesBinding(false);
            }
        }
        ExpressionOperator realOperator;
        realOperator = getPlatformOperator(printer.getPlatform());
        realOperator.printCollection(this.children, printer);
    }

    /**
     * INTERNAL:
     * Print java for project class generation
     */
    @Override
    public void printJava(ExpressionJavaPrinter printer) {
        ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform());
        realOperator.printJavaCollection(this.children, printer);
    }

    /**
     * INTERNAL:
     * This expression is built on a different base than the one we want. Rebuild it and
     * return the root of the new tree
     */
    @Override
    public Expression rebuildOn(Expression newBase) {
        Expression newLocalBase = getBaseExpression().rebuildOn(newBase);
        Vector newChildren = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(this.children.size());
        for (int i = 1; i < this.children.size(); i++) {// Skip the first one, since it's also the base
            newChildren.addElement(this.children.elementAt(i).rebuildOn(newBase));
        }
        newLocalBase.setSelectIfOrderedBy(getBaseExpression().selectIfOrderedBy());
        FunctionExpression rebuilt = (FunctionExpression) newLocalBase.performOperator(this.operator, newChildren);
        rebuilt.setResultType(this.getResultType()); //copy over result type.
        return rebuilt;
    }

    /**
     * INTERNAL:
     * Search the tree for any expressions (like SubSelectExpressions) that have been
     * built using a builder that is not attached to the query.  This happens in case of an Exists
     * call using a new ExpressionBuilder().  This builder needs to be replaced with one from the query.
     */
    @Override
    public void resetPlaceHolderBuilder(ExpressionBuilder queryBuilder){
        getBaseExpression().resetPlaceHolderBuilder(queryBuilder);
        for (int i = this.children.size()-1; i > 0; i--) {// Skip the first one, since it's also the base
            this.children.elementAt(i).resetPlaceHolderBuilder(queryBuilder);
        }
    }
    // Set the local base expression, ie the one on the other side of the operator
    // Most types will ignore this, since they don't need it.
    @Override
    public void setLocalBase(Expression exp) {
        getBaseExpression().setLocalBase(exp);
    }

    public void setOperator(ExpressionOperator theOperator) {
        operator = theOperator;
    }

    public void setResultType(Class resultType) {
        this.resultType = resultType;
    }

    /**
     * INTERNAL:
     * Rebuild myself against the base, with the values of parameters supplied by the context
     * expression. This is used for transforming a standalone expression (e.g. the join criteria of a mapping)
     * into part of some larger expression. You normally would not call this directly, instead calling twist
     * See the comment there for more details"
     */
    @Override
    public Expression twistedForBaseAndContext(Expression newBase, Expression context, Expression oldBase) {
        if (this.children.isEmpty()) {
            return (Expression)clone();
        }
        Vector newChildren = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(this.children.size());

        // For functions the base is the first child, we only want the arguments so start at the second.
        for (int index = 1; index < this.children.size(); index++) {
            newChildren.addElement(this.children.elementAt(index).twistedForBaseAndContext(newBase, context, oldBase));
        }

        // Aply the function to the twisted old base.
        Expression oldBaseExp = this.children.elementAt(0);
        return oldBaseExp.twistedForBaseAndContext(newBase, context, oldBase).performOperator(this.operator, newChildren);
    }

    /**
     * INTERNAL:
     * Return the value for in memory comparison.
     * This is only valid for valueable expressions.
     */
    @Override
    public Object valueFromObject(Object object, AbstractSession session, AbstractRecord translationRow, int valueHolderPolicy, boolean isObjectUnregistered) {
        Object baseValue = getBaseExpression().valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered);
        Vector arguments = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(this.children.size());
        for (int index = 1; index < this.children.size(); index++) {
            if (this.children.elementAt(index) instanceof Expression) {
                arguments.addElement((this.children.elementAt(index)).valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered));
            } else {
                arguments.addElement(this.children.elementAt(index));
            }
        }
        if (baseValue instanceof Vector) {// baseValue might be a vector, so the individual values must be extracted before applying the function call to them
            Vector baseVector = new Vector();
            for (Enumeration valuesToCompare = ((Vector)baseValue).elements();
                     valuesToCompare.hasMoreElements();) {
                Object baseObject = valuesToCompare.nextElement();
                if (baseObject == null) {
                    baseVector.addElement(null);
                } else {
                    baseVector.addElement(this.operator.applyFunction(baseObject, arguments));
                }
            }
            return baseVector;
        } else {
            // Do not apply functions to null, just leave as null.
            if (baseValue == null) {
                return null;
            } else {
                return this.operator.applyFunction(baseValue, arguments);
            }
        }
    }

    /**
     * INTERNAL:
     * Used to print a debug form of the expression tree.
     */
    @Override
    public void writeDescriptionOn(BufferedWriter writer) throws IOException {
        writer.write(operator.toString());
    }

    /**
     * INTERNAL: called from SQLSelectStatement.writeFieldsFromExpression(...)
     */
    @Override
    public void writeFields(ExpressionSQLPrinter printer, Vector newFields, SQLSelectStatement statement) {
        //print ", " before each selected field except the first one
        if (printer.isFirstElementPrinted()) {
            printer.printString(", ");
        } else {
            printer.setIsFirstElementPrinted(true);
        }

        if (getBaseExpression().isDataExpression()) {
            DatabaseField field = ((DataExpression)getBaseExpression()).getField();

            if (field == null) {
                // This means the select wants a *.
                field = new DatabaseField("*");
            } else {
                // Clone the field since we will change its type.
                field = field.clone();
            }

            // If the result type is set, use it.
            field.setSqlType(DatabaseField.NULL_SQL_TYPE);
            //we also cache the JDBC type now so reset it as well.
            if (hasResultType()) {
                field.setType(getResultType());
            } else {
                // If the function is anything but min or max, null out the
                // field type. The type will be calculated based on the
                // function.
                int selector = this.operator.getSelector();
                if (selector != ExpressionOperator.Maximum && selector != ExpressionOperator.Minimum) {
                    field.setType(null);
                }
            }

            newFields.addElement(field);
        } else {
            // This field is a complex function value so any name can be used.
            DatabaseField field = new DatabaseField("*");
            // If the result type is set, use it.
            field.setSqlType(DatabaseField.NULL_SQL_TYPE);
            field.setType(getResultType());
            newFields.addElement(field);
        }

        printSQL(printer);
    }

    /**
     * INTERNAL:
     * Used in SQL printing.
     */
    @Override
    public void writeSubexpressionsTo(BufferedWriter writer, int indent) throws IOException {
        if (baseExpression != null) {
            baseExpression.toString(writer, indent);
        }
    }

    /**
     * INTERNAL:
     * JPQL allows count([distinct] e), where e can be an object, not just a single field,
     * however the database only allows a single field, so object needs to be translated to a single field.
     * If the descriptor has a single pk, it is used, otherwise any pk is used if distinct, otherwise a subselect is used.
     * If the object was obtained through an outer join, then the subselect also will not work, so an error is thrown.
     */
    public void prepareObjectAttributeCount(ExpressionNormalizer normalizer, ReportItem item, ReportQuery query, Map clonedExpressions) {
        // ** Note that any of the arguments may be null depending on the caller.
        if (getOperator().getSelector() == ExpressionOperator.Count) {
            Expression baseExp = getBaseExpression();
            boolean distinctUsed = false;
            if (baseExp.isFunctionExpression() && (((FunctionExpression)baseExp).getOperator().getSelector() == ExpressionOperator.Distinct)) {
                distinctUsed = true;
                baseExp = ((FunctionExpression)baseExp).getBaseExpression();
            }
            boolean outerJoin = false;
            ClassDescriptor newDescriptor = null;
            AbstractSession session = null;
            if (query != null) {
                session = query.getSession();
            } else {
                session = normalizer.getSession();
            }
            if (baseExp.isQueryKeyExpression()) {
                // now need to find out if it is a direct to field or something else.
                ClassDescriptor descriptor = null;
                if (query == null) {
                    descriptor = ((QueryKeyExpression) baseExp).getDescriptor();
                } else {
                    descriptor = query.getDescriptor();
                }
                DatabaseMapping mapping = baseExp.getLeafMapping(query, descriptor, session);
                if ((mapping != null) && !mapping.isAbstractDirectMapping()) {
                    outerJoin = ((QueryKeyExpression)baseExp).shouldUseOuterJoin();
                    if (mapping.isAggregateMapping()){
                        newDescriptor = mapping.getDescriptor();
                        baseExp = ((QueryKeyExpression)baseExp).getBaseExpression();
                    } else {
                        newDescriptor = mapping.getReferenceDescriptor();
                    }
                } else {
                    QueryKey queryKey = getLeafQueryKeyFor(query, baseExp, descriptor, session);
                    if ((queryKey != null) && queryKey.isForeignReferenceQueryKey()){
                        outerJoin = ((QueryKeyExpression) baseExp).shouldUseOuterJoin();
                        newDescriptor = session.getDescriptor(((ForeignReferenceQueryKey)queryKey).getReferenceClass());
                    }
                }
            } else if (baseExp.isExpressionBuilder()) {
                if (((ExpressionBuilder)baseExp).getQueryClass() == null) {
                    if (item != null) {
                        item.setResultType(ClassConstants.INTEGER);
                    }
                } else {
                    newDescriptor = session.getDescriptor(((ExpressionBuilder)baseExp).getQueryClass());
                }
            }

            if (newDescriptor != null) {
                // At this point we are committed to rewriting the query.
                if ((newDescriptor.getPrimaryKeyFields().size() == 1) || !distinctUsed) {
                    // case 1: single PK =>
                    // treat COUNT(entity) as COUNT(entity.pk)
                    Expression countArg = baseExp.getField(newDescriptor.getPrimaryKeyFields().get(0));
                    if (distinctUsed) {
                        countArg = countArg.distinct();
                    }
                    setBaseExpression(countArg);
                    getChildren().set(0, countArg);
                } else if (((DatabasePlatform)session.getPlatform(newDescriptor.getJavaClass())).supportsCountDistinctWithMultipleFields()) {
                    // case 3, is database allows multiple fields, then just print them
                    // treat COUNT(distinct entity) as COUNT(distinct entity.pk1, entity.pk2)
                    List args = new ArrayList(newDescriptor.getPrimaryKeyFields().size());
                    Expression firstField = null;
                    for (DatabaseField field : newDescriptor.getPrimaryKeyFields()) {
                        if (firstField == null) {
                            firstField = baseExp.getField(field);
                        } else {
                            args.add(baseExp.getField(field));
                        }
                    }

                    ExpressionOperator anOperator = new ExpressionOperator();
                    anOperator.setType(ExpressionOperator.FunctionOperator);
                    Vector v = NonSynchronizedVector.newInstance(args.size());
                    v.addElement("DISTINCT ");
                    for (int index = 0; index < args.size(); index++) {
                        v.add(", ");
                    }
                    v.add("");
                    anOperator.printsAs(v);
                    anOperator.bePrefix();
                    anOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
                    Expression distinctFunction = anOperator.expressionForArguments(firstField, args);

                    setBaseExpression(distinctFunction);
                    getChildren().set(0, distinctFunction);
                } else if (!outerJoin && (query != null)) {
                    // case 4: composite PK and DISTINCT, but no
                    // outer join => previous solution using
                    // COUNT(*) and EXISTS subquery
                    // TODO, this doesn't really work for most cases (joins, other things selected, group by),
                    // this should probably be removed and throw an error,
                    // or changed to just concat all the pks together.

                    // If this is a subselect baseExp is yet uncloned,
                    // and will miss out if moved now from items into a selection criteria.
                    if (clonedExpressions != null) {
                        if (clonedExpressions.get(baseExp.getBuilder()) != null) {
                            baseExp = baseExp.copiedVersionFrom(clonedExpressions);
                        } else {
                            baseExp = baseExp.rebuildOn(query.getExpressionBuilder());
                        }
                    }

                    // Now the reference class of the query needs to be reversed.
                    // See the bug description for an explanation.
                    ExpressionBuilder countBuilder = baseExp.getBuilder();
                    ExpressionBuilder outerBuilder ;

                    ReportQuery subSelect = new ReportQuery(query.getReferenceClass(), countBuilder);
                    query.getSession().getPlatform().retrieveFirstPrimaryKeyOrOne(subSelect);

                    // Make sure the outerBuilder does not appear on the left of the subselect.
                    // Putting a builder on the left is desirable to trigger an optimization.
                    if (query.getSelectionCriteria() != null) {
                        outerBuilder = new ExpressionBuilder(newDescriptor.getJavaClass());
                        query.setExpressionBuilder(outerBuilder);
                        subSelect.setSelectionCriteria(baseExp.equal(outerBuilder).and(query.getSelectionCriteria()));
                    } else {
                        outerBuilder = new ExpressionBuilder(newDescriptor.getJavaClass());
                        query.setExpressionBuilder(outerBuilder);
                        subSelect.setSelectionCriteria(baseExp.equal(outerBuilder));
                    }
                    query.setNonFetchJoinAttributeExpressions(null);
                    query.setSelectionCriteria(outerBuilder.exists(subSelect));
                    setBaseExpression(outerBuilder);
                    getChildren().set(0, outerBuilder);
                    query.setReferenceClass(newDescriptor.getJavaClass());
                    query.changeDescriptor(session);
                } else {
                    // case 4: composite PK, DISTINCT, outer join =>
                    // not supported, throw exception
                    DatabaseQuery reportQuery = query;
                    if (query == null) {
                        reportQuery = normalizer.getStatement().getQuery();
                    }
                    throw QueryException.distinctCountOnOuterJoinedCompositePK(newDescriptor, reportQuery);
                }
            }
        }
    }

    /**
     * INTERNAL:
     * Lookup the query key for this item.
     * If an aggregate of foreign mapping is found it is traversed.
     */
    protected QueryKey getLeafQueryKeyFor(DatabaseQuery query, Expression expression, ClassDescriptor rootDescriptor, AbstractSession session) throws QueryException {
        // Check for database field expressions or place holder
        if ((expression == null) || (expression.isFieldExpression())) {
            return null;
        }

        if (!(expression.isQueryKeyExpression())) {
            return null;
        }

        QueryKeyExpression qkExpression = (QueryKeyExpression)expression;
        Expression baseExpression = qkExpression.getBaseExpression();

        ClassDescriptor descriptor = baseExpression.getLeafDescriptor(query, rootDescriptor, session);
        return descriptor.getQueryKeyNamed(qkExpression.getName());
    }

    protected DatabaseMapping getMappingOfFirstPrimaryKey(ClassDescriptor descriptor) {
        if (descriptor != null) {
            for (Iterator i = descriptor.getMappings().iterator(); i.hasNext(); ) {
                DatabaseMapping m = (DatabaseMapping)i.next();
                if (m.isPrimaryKeyMapping()) {
                    return m;
                }
            }
        }
        return null;
    }

    /**
     * INTERNAL:
     * Lookup the mapping for this item by traversing its expression recursively.
     */
    @Override
    public DatabaseMapping getLeafMapping(DatabaseQuery query, ClassDescriptor rootDescriptor, AbstractSession session) {
        int selector = this.operator.getSelector();

        //MAX and MIN functions require mappings for their result value. See JPA 2.1; section 4.8.5
        if (this.baseExpression != null && ((selector == ExpressionOperator.Maximum) || (selector == ExpressionOperator.Minimum))) {
            return this.baseExpression.getLeafMapping(query, rootDescriptor, session);
        }
        return super.getLeafMapping(query, rootDescriptor, session);
    }
}