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

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