source: branches/work_311/core/ARQualification.cpp @ 583

Revision 583, 12.1 KB checked in by jls17, 6 years ago (diff)
  • replaced code in CARAssignHelper and CARQualification with a call to CDocStatusHistoryField
Line 
1//Copyright (C) 2009 Stefan Nerlich | stefan.nerlich@hotmail.com
2//
3//This file is part of ARInside.
4//
5//    ARInside is free software: you can redistribute it and/or modify
6//    it under the terms of the GNU General Public License as published by
7//    the Free Software Foundation, version 2 of the License.
8//
9//    ARInside is distributed in the hope that it will be useful,
10//    but WITHOUT ANY WARRANTY; without even the implied warranty of
11//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12//    GNU General Public License for more details.
13//
14//    You should have received a copy of the GNU General Public License
15//    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
16
17#include "stdafx.h"
18#include "ARQualification.h"
19#include "../doc/DocCurrencyField.h"
20#include "../doc/DocStatusHistoryField.h"
21#include "../output/URLLink.h"
22
23CARQualification::CARQualification(CARInside &arIn, const CRefItem &referenceItem, int currentFormId, int rootLevel)
24: refItem(referenceItem)
25{
26        this->arIn = &arIn;
27        this->tmpFormId = 0;
28        arsStructItemType = AR_STRUCT_ITEM_XML_NONE;
29
30        this->primaryFormId = currentFormId;
31        this->secondaryFormId = currentFormId;
32
33        this->primaryFormDelimiter = '\'';
34        this->secondaryFormDelimiter = '\'';
35
36        this->rootLevel = rootLevel;
37}
38
39CARQualification::CARQualification(CARInside &arIn, const CRefItem &referenceItem, int currentFormId, int otherFormId, int rootLevel)
40: refItem(referenceItem)
41{
42        this->arIn = &arIn;
43        this->tmpFormId = 0;
44        arsStructItemType = AR_STRUCT_ITEM_XML_NONE;
45
46        this->primaryFormId = currentFormId;
47        this->secondaryFormId = otherFormId;
48
49        this->primaryFormDelimiter = '$';
50        this->secondaryFormDelimiter = '\'';
51
52        this->rootLevel = rootLevel;
53}
54
55CARQualification::~CARQualification(void)
56{
57}
58
59
60void CARQualification::CheckQuery(const ARQualifierStruct *query, stringstream &qText)
61{
62        qualLevels.push_back(query);
63       
64        if (query != NULL)
65        {
66                switch(query->operation)
67                {
68                case AR_COND_OP_NONE:
69                        break;
70                case AR_COND_OP_AND:
71                case AR_COND_OP_OR:
72                        {
73                                if (query->u.andor.operandLeft->operation != query->operation && query->u.andor.operandLeft->operation != AR_COND_OP_REL_OP) qText << "(";
74                                CheckQuery(query->u.andor.operandLeft, qText);
75                                if (query->u.andor.operandLeft->operation != query->operation && query->u.andor.operandLeft->operation != AR_COND_OP_REL_OP) qText << ")";
76
77                                switch (query->operation)
78                                {
79                                case AR_COND_OP_AND: qText << " AND "; break;
80                                case AR_COND_OP_OR: qText << " OR "; break;
81                                }       
82
83                                if (query->u.andor.operandRight->operation != query->operation && query->u.andor.operandRight->operation != AR_COND_OP_REL_OP) qText << "(";
84                                CheckQuery(query->u.andor.operandRight, qText);
85                                if (query->u.andor.operandRight->operation != query->operation && query->u.andor.operandRight->operation != AR_COND_OP_REL_OP) qText << ")";
86                        }
87                        break;
88                case AR_COND_OP_NOT:
89                        qText << "NOT ";
90                        if(query->u.notQual != NULL)
91                        {
92                                if (query->u.notQual->operation != AR_COND_OP_REL_OP) qText << "(";
93                                CheckQuery(query->u.notQual, qText);
94                                if (query->u.notQual->operation != AR_COND_OP_REL_OP) qText << ")"; 
95                        }
96                        break;
97                case AR_COND_OP_REL_OP:
98                        CheckOperand(&query->u.relOp->operandLeft, NULL, qText);
99                        switch (query->u.relOp->operation) 
100                        {               
101                        case AR_REL_OP_EQUAL:
102                                qText << " = ";
103                                break;
104                        case AR_REL_OP_GREATER:
105                                qText << " > ";
106                                break;
107                        case AR_REL_OP_GREATER_EQUAL:
108                                qText << " >= ";
109                                break;
110                        case AR_REL_OP_LESS:
111                                qText << " < ";
112                                break;
113                        case AR_REL_OP_LESS_EQUAL:
114                                qText << " <= ";
115                                break;
116                        case AR_REL_OP_NOT_EQUAL:
117                                qText << " != ";
118                                break;
119                        case AR_REL_OP_LIKE:
120                                qText << " LIKE ";
121                                break;
122                        }
123                        CheckOperand(&query->u.relOp->operandRight, NULL, qText);
124                        break;
125                case AR_COND_OP_FROM_FIELD: //A qualification located in a field on the form.
126                        qText << "EXTERNAL(" << primaryFormDelimiter << arIn->LinkToField(primaryFormId, query->u.fieldId, rootLevel) << primaryFormDelimiter << ")";
127
128                        arIn->AddFieldReference(primaryFormId, query->u.fieldId, refItem);
129                        break;
130                }
131        }
132        qualLevels.pop_back();
133}
134
135void CARQualification::CheckOperand(ARFieldValueOrArithStruct *operand, ARFieldValueOrArithStruct *parent, stringstream &qText)
136{               
137        switch(operand->tag)
138        {
139        case AR_FIELD:
140        case AR_FIELD_TRAN:
141        case AR_FIELD_DB:
142        case AR_FIELD_CURRENT:
143                int formId;
144                char delimiter;
145                getFormIdAndDelimiter(operand, formId, delimiter);
146
147                tmpFormId = formId;
148
149                qText << delimiter << arIn->LinkToField(formId, operand->u.fieldId, rootLevel) << delimiter;
150
151                if(!arIn->FieldreferenceExists(formId, operand->u.fieldId, refItem))
152                {
153                        arIn->AddFieldReference(formId, operand->u.fieldId, refItem);
154                }
155                break; 
156        case AR_QUERY:
157                qText << "*QUERY*";
158                break;
159        case AR_VALUE:
160                ARValueStruct *data;
161                data = &operand->u.value;
162                switch(data->dataType)
163                {
164                case AR_DATA_TYPE_NULL:
165                        qText << "$NULL$";
166                        break;
167                case AR_DATA_TYPE_KEYWORD:
168                        qText << "$" << CAREnum::Keyword(data->u.keyNum) << "$";
169                        break;
170                case AR_DATA_TYPE_INTEGER:
171                case AR_DATA_TYPE_ENUM:
172                        try
173                        {
174                                int tmpFieldId = FindCurrentEnumFieldId();
175                                string tmp = arIn->GetFieldEnumValue(tmpFormId, tmpFieldId, data->u.intVal);
176
177                                if(!tmp.empty())
178                                        qText << "\"" << tmp << "\"";
179                                else
180                                        qText << data->u.intVal;
181                        }
182                        catch(exception& e)
183                        {
184                                cout << "EXCEPTION enumerating enum value: " << e.what() << endl;
185                        }                                               
186                        break;
187                case AR_DATA_TYPE_REAL:
188                        qText << data->u.realVal;                                               
189                        break;
190                case AR_DATA_TYPE_CHAR:
191                        qText << "\""<< data->u.charVal << "\"";
192                        break;
193                case AR_DATA_TYPE_DIARY:
194                        qText << "\""<< data->u.diaryVal << "\"";
195                        break;
196                case AR_DATA_TYPE_TIME:
197                        qText << "\"" << CUtil::DateTimeToHTMLString(data->u.timeVal) << "\"";
198                        break;
199                case AR_DATA_TYPE_DECIMAL:
200                        qText << data->u.decimalVal;
201                        break;
202                case AR_DATA_TYPE_ATTACH:
203                        qText << data->u.attachVal;
204                        break;
205                case AR_DATA_TYPE_CURRENCY:
206                        qText << data->u.currencyVal;
207                        break;
208                case AR_DATA_TYPE_DATE:
209                        qText << "\"" << CUtil::DateToString(data->u.dateVal) << "\"";
210                        break;
211                case AR_DATA_TYPE_TIME_OF_DAY:
212                        qText << "\"" << CUtil::TimeOfDayToString(data->u.timeOfDayVal) << "\"";
213                        break;
214                default:
215                        qText << "n/a";
216                        break;
217                }
218                break;
219        case AR_ARITHMETIC:
220        {
221                // Rule 1:
222                // if the parent has a higher precedence (in this case a lower value), we have to add parentheses
223                // Example 1: 2 * (3 + 4)      Original: (2 * (3 + 4))
224                // Example 2: 2 + 3 * 4        Original: (2 + (3 * 4))
225                // Example 3: (2 + 3) * 4      Original: ((2 + 3) * 4)
226                //
227                // Rule 2:
228                // if the parent and the current operand have the same precedence (mul, div and modulo use the
229                // same, at least in C) and the current modulo operation is at the right side of the parent
230                // operand, only then parentheses are needed. (If the modulo is on the left side, the operation
231                // is executed from left to right and doesn't need any parentheses.)
232                // Example 1: 2 * (3 mod 4)    Original: (2 * (3 mod 4))
233                // Example 2: 2 * 3 mod 4      Original: ((2 * 3) mod 4)
234                // Example 3: 2 mod 3 * 4      Original: ((2 mod 3) * 4)
235                // Example 4: 2 mod (3 * 4)    Original: (2 mod (3 * 4))
236
237                bool addBracket = false;
238                if (parent != NULL && parent->tag == operand->tag)
239                {
240                        unsigned int parentPrecedence = CAREnum::OperandPrecedence(parent->u.arithOp->operation);
241                        unsigned int currentPrecedence = CAREnum::OperandPrecedence(operand->u.arithOp->operation);
242
243                        if (parentPrecedence < currentPrecedence || operand->u.arithOp->operation == AR_ARITH_OP_MODULO &&
244                            parentPrecedence == currentPrecedence && &parent->u.arithOp->operandRight == operand)
245                                addBracket = true;
246                }
247
248                switch (operand->u.arithOp->operation) 
249                {
250                case AR_ARITH_OP_ADD:
251                        if (addBracket) qText << "(";
252                        CheckOperand(&operand->u.arithOp->operandLeft, operand, qText);
253                        qText << CAREnum::Operand(AR_ARITH_OP_ADD);
254                        CheckOperand(&operand->u.arithOp->operandRight, operand, qText);
255                        if (addBracket) qText << ")";
256                        break;
257                case AR_ARITH_OP_SUBTRACT:
258                        if (addBracket) qText << "(";
259                        CheckOperand(&operand->u.arithOp->operandLeft, operand, qText);
260                        qText << CAREnum::Operand(AR_ARITH_OP_SUBTRACT);
261                        CheckOperand(&operand->u.arithOp->operandRight, operand, qText);
262                        if (addBracket) qText << ")";
263                        break;
264                case AR_ARITH_OP_MULTIPLY:
265                        if (addBracket) qText << "(";
266                        CheckOperand(&operand->u.arithOp->operandLeft, operand, qText);
267                        qText << CAREnum::Operand(AR_ARITH_OP_MULTIPLY);
268                        CheckOperand(&operand->u.arithOp->operandRight, operand, qText);
269                        if (addBracket) qText << ")";
270                        break;
271                case AR_ARITH_OP_DIVIDE:
272                        if (addBracket) qText << "(";
273                        CheckOperand(&operand->u.arithOp->operandLeft, operand, qText);
274                        qText << CAREnum::Operand(AR_ARITH_OP_DIVIDE);
275                        CheckOperand(&operand->u.arithOp->operandRight, operand, qText);
276                        if (addBracket) qText << ")";
277                        break;
278                case AR_ARITH_OP_MODULO:
279                        if (addBracket) qText << "(";
280                        CheckOperand(&operand->u.arithOp->operandLeft, operand, qText);
281                        qText << CAREnum::Operand(AR_ARITH_OP_MODULO);
282                        CheckOperand(&operand->u.arithOp->operandRight, operand, qText);
283                        if (addBracket) qText << ")";
284                        break;
285                case AR_ARITH_OP_NEGATE:
286                        qText << CAREnum::Operand(AR_ARITH_OP_NEGATE);
287                        CheckOperand(&operand->u.arithOp->operandRight, operand, qText);
288                        break;
289                }
290                break;
291        }
292        case AR_STAT_HISTORY:
293                {
294                        qText << "'";
295                        CDocStatusHistoryField docStatusHistory(primaryFormId, operand->u.statHistory);
296                        docStatusHistory.GetResolvedAndLinkedField(qText, &refItem, rootLevel);
297                        qText << "'";
298                }
299                break;
300        case AR_CURRENCY_FLD:
301        case AR_CURRENCY_FLD_DB:
302        case AR_CURRENCY_FLD_TRAN:
303        case AR_CURRENCY_FLD_CURRENT:
304                {
305                        int formId;
306                        char delimiter;
307                        getFormIdAndDelimiter(operand, formId, delimiter);
308
309                        CDocCurrencyField docCurrency(formId, *operand->u.currencyField);
310                        char *prefix = getFieldPrefix(operand);
311                       
312                        qText << delimiter;
313                        if (prefix != NULL) qText << prefix;
314                        docCurrency.GetResolvedAndLinkedField(qText, &refItem, rootLevel);
315                        qText << delimiter;
316                }
317                break;
318        }
319}
320
321int CARQualification::FindCurrentEnumFieldId()
322{
323        int pos = (int)qualLevels.size() - 1;
324       
325        for (; pos > -1; --pos)
326        {
327                const ARQualifierStruct* current = qualLevels[pos];
328                if (current->operation == AR_COND_OP_REL_OP)
329                {
330                        // normally all relOps have two operators. check if there is a field on one side
331                        switch (current->u.relOp->operandLeft.tag)
332                        {
333                        case AR_FIELD:
334                                tmpFormId = secondaryFormId;
335                                return current->u.relOp->operandLeft.u.fieldId;
336                        case AR_FIELD_TRAN:
337                        case AR_FIELD_DB:
338                        case AR_FIELD_CURRENT:
339                                tmpFormId = primaryFormId;
340                                return current->u.relOp->operandLeft.u.fieldId;
341                        }
342
343                        switch (current->u.relOp->operandRight.tag)
344                        {
345                        case AR_FIELD:
346                                tmpFormId = secondaryFormId;
347                                return current->u.relOp->operandRight.u.fieldId;
348                        case AR_FIELD_TRAN:
349                        case AR_FIELD_DB:
350                        case AR_FIELD_CURRENT:
351                                tmpFormId = primaryFormId;
352                                return current->u.relOp->operandRight.u.fieldId;
353                        }
354
355                        // if there is a relOp without a field, this can't be a enum!
356                        return -1;
357                }
358        }
359
360        return -1;
361}
362
363char* CARQualification::getFieldPrefix(ARFieldValueOrArithStruct *operand)
364{
365        if (operand == NULL) return NULL;
366        switch (operand->tag)
367        {
368        case AR_FIELD_TRAN:
369        case AR_CURRENCY_FLD_TRAN:
370                return "TR.";
371        case AR_FIELD_DB:
372        case AR_CURRENCY_FLD_DB:
373                return "DB.";
374        }
375        return NULL;
376}
377
378bool CARQualification::getFormIdAndDelimiter(ARFieldValueOrArithStruct *operand, int &formId, char &delimiter)
379{
380        if (operand == NULL) return false;
381        switch (operand->tag)
382        {
383        case AR_FIELD:
384        case AR_CURRENCY_FLD:
385                formId = secondaryFormId;
386                delimiter = secondaryFormDelimiter;
387                return true;
388        case AR_FIELD_TRAN:
389        case AR_FIELD_DB:
390        case AR_FIELD_CURRENT:
391        case AR_CURRENCY_FLD_DB:
392        case AR_CURRENCY_FLD_TRAN:
393        case AR_CURRENCY_FLD_CURRENT:
394                formId = primaryFormId;
395                delimiter = primaryFormDelimiter;
396                return true;
397        }
398        throw exception("NotImplementedException");
399}
400
Note: See TracBrowser for help on using the repository browser.