1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 #include <tinyxml2.h>
18 
19 #include <memory>
20 
21 #include "Log.h"
22 #include "GenericFactory.h"
23 #include "task/ModelBuilder.h"
24 
25 using namespace tinyxml2;
26 
27 static const int MAX_NO_CHILDREN = 8;
28 static const ModelBuilder::ChildInfo CASE_TABLE[] = {
29     { TaskGeneric::ETaskSetup, true },
30     { TaskGeneric::ETaskAction, true },
31     { TaskGeneric::ETaskSave, false }
32 };
33 static const ModelBuilder::ChildInfo SETUP_TABLE[] = {
34     { TaskGeneric::ETaskSound, false },
35     { TaskGeneric::ETaskProcess, false },
36     { TaskGeneric::ETaskDownload, false }
37 };
38 static const ModelBuilder::ChildInfo ACTION_TABLE[] = {
39     { TaskGeneric::ETaskSequential, true }
40 };
41 static const ModelBuilder::ChildInfo SEQUENTIAL_TABLE[] = {
42     { TaskGeneric::ETaskSequential, false },
43     { TaskGeneric::ETaskInput, false },
44     { TaskGeneric::ETaskOutput, false },
45     { TaskGeneric::ETaskProcess, false },
46     { TaskGeneric::ETaskMessage, false }
47 };
48 
49 
50 ModelBuilder::ParsingInfo ModelBuilder::mParsingTable[ModelBuilder::PARSING_TABLE_SIZE] = {
51     { "case", TaskGeneric::ETaskCase, CASE_TABLE,
52             sizeof(CASE_TABLE)/sizeof(ModelBuilder::ChildInfo) },
53     { "setup", TaskGeneric::ETaskSetup, SETUP_TABLE,
54             sizeof(SETUP_TABLE)/sizeof(ModelBuilder::ChildInfo) },
55     { "action", TaskGeneric::ETaskAction, ACTION_TABLE,
56             sizeof(ACTION_TABLE)/sizeof(ModelBuilder::ChildInfo) },
57     { "sequential", TaskGeneric::ETaskSequential, SEQUENTIAL_TABLE,
58                 sizeof(SEQUENTIAL_TABLE)/sizeof(ModelBuilder::ChildInfo) },
59     { "process", TaskGeneric::ETaskProcess, NULL, 0 },
60     { "input", TaskGeneric::ETaskInput, NULL, 0 },
61     { "output", TaskGeneric::ETaskOutput, NULL, 0 },
62     { "sound", TaskGeneric::ETaskSound, NULL, 0 },
63     { "save", TaskGeneric::ETaskSave, NULL, 0 },
64     { "message", TaskGeneric::ETaskMessage, NULL, 0 },
65     { "download", TaskGeneric::ETaskDownload, NULL, 0 }
66 };
67 
68 
ModelBuilder()69 ModelBuilder::ModelBuilder()
70     : mFactory(new GenericFactory())
71 {
72 
73 }
74 
ModelBuilder(GenericFactory * factory)75 ModelBuilder::ModelBuilder(GenericFactory* factory)
76     : mFactory(factory)
77 {
78 
79 }
~ModelBuilder()80 ModelBuilder::~ModelBuilder()
81 {
82     delete mFactory;
83 }
84 
parseTestDescriptionXml(const android::String8 & xmlFileName,bool caseOnly)85 TaskGeneric* ModelBuilder::parseTestDescriptionXml(const android::String8& xmlFileName,
86         bool caseOnly)
87 {
88     XMLDocument doc;
89     int error = doc.LoadFile(xmlFileName.string());
90     if (error != XML_NO_ERROR) {
91         LOGE("ModelBuilder::parseTestDescriptionXml cannot load file %s: %d", xmlFileName.string(), error);
92         return NULL;
93     }
94     const XMLElement* root;
95     if ((root = doc.FirstChildElement("case")) != NULL) {
96         return parseCase(*root);
97     } else if (!caseOnly && ((root = doc.FirstChildElement("batch")) != NULL)) {
98         return parseBatch(*root, xmlFileName);
99     } else {
100         LOGE("ModelBuilder::parseTestDescriptionXml wrong root element");
101         return NULL;
102     }
103 }
104 
parseGeneric(const XMLElement & self,int tableIndex)105 TaskGeneric* ModelBuilder::parseGeneric(const XMLElement& self, int tableIndex)
106 {
107     TaskGeneric::TaskType typeSelf(mParsingTable[tableIndex].type);
108     int Nchildren = mParsingTable[tableIndex].Nchildren;
109     std::unique_ptr<TaskGeneric> taskSelf(mFactory->createTask(typeSelf));
110     if (taskSelf.get() == NULL) {
111         return NULL;
112     }
113     if (!parseAttributes(self, *taskSelf.get())) {
114         return NULL;
115     }
116     // copy mandatory flags, and will be cleared once the item is found
117     bool mandatoryAbsence[MAX_NO_CHILDREN];
118     const ModelBuilder::ChildInfo* childTable = mParsingTable[tableIndex].allowedChildren;
119     for (int i = 0; i < Nchildren; i++) {
120         mandatoryAbsence[i] = childTable[i].mandatory;
121     }
122 
123     // handle children
124     const XMLElement* child = self.FirstChildElement();
125     while (child != NULL) {
126         TaskGeneric::TaskType childType(TaskGeneric::ETaskInvalid);
127         int i;
128         // check if type is valid
129         for (i = 0; i < PARSING_TABLE_SIZE; i++) {
130             if (strcmp(child->Value(), mParsingTable[i].name) == 0) {
131                 break;
132             }
133         }
134         if (i == PARSING_TABLE_SIZE) {
135             LOGE("ModelBuilder::parseGeneric unknown element %s", child->Value());
136             return NULL;
137         }
138         childType = mParsingTable[i].type;
139         int j;
140         // check if the type is allowed as child
141         for (j = 0; j < Nchildren; j++) {
142             if (childTable[j].type == childType) {
143                 if (childTable[j].mandatory) {
144                     mandatoryAbsence[j] = false;
145                 }
146                 break;
147             }
148         }
149         if (j == Nchildren) {
150             LOGE("ModelBuilder::parseGeneric unsupported child type %d for type %d", childType,
151                     typeSelf);
152             return NULL;
153         }
154         std::unique_ptr<TaskGeneric> taskChild(parseGeneric(*child, i));
155         if (taskChild.get() == NULL) {
156             LOGE("ModelBuilder::parseGeneric failed in parsing child type %d for type %d",
157                     childType, typeSelf);
158             return NULL;
159         }
160         if (!taskSelf.get()->addChild(taskChild.get())) {
161             LOGE("ModelBuilder::parseGeneric cannot add child type %d to type %d", childType,
162                     typeSelf);
163             return NULL;
164         }
165         TaskGeneric* donotuse = taskChild.release();
166 
167         child = child->NextSiblingElement();
168     }
169     for (int i = 0; i < Nchildren; i++) {
170         if (mandatoryAbsence[i]) {
171             LOGE("ModelBuilder::parseGeneric mandatory child type %d not present in type %d",
172                     childTable[i].type, typeSelf);
173             return NULL;
174         }
175     }
176 
177     return taskSelf.release();
178 }
179 
180 
parseCase(const XMLElement & root)181 TaskCase* ModelBuilder::parseCase(const XMLElement& root)
182 {
183     // position 0 of mParsingTable should be "case"
184     return reinterpret_cast<TaskCase*>(parseGeneric(root, 0));
185 }
186 
187 
parseBatch(const XMLElement & root,const android::String8 & xmlFileName)188 TaskBatch* ModelBuilder::parseBatch(const XMLElement& root, const android::String8& xmlFileName)
189 {
190     std::unique_ptr<TaskBatch> batch(
191             reinterpret_cast<TaskBatch*>(mFactory->createTask(TaskGeneric::ETaskBatch)));
192     if (batch.get() == NULL) {
193         LOGE("ModelBuilder::handleBatch cannot create TaskBatch");
194         return NULL;
195     }
196     if (!parseAttributes(root, *batch.get())) {
197         return NULL;
198     }
199 
200     const XMLElement* inc = root.FirstChildElement("include");
201     if (inc == NULL) {
202         LOGE("ModelBuilder::handleBatch no include inside batch");
203         return NULL;
204     }
205     android::String8 path = xmlFileName.getPathDir();
206 
207     std::unique_ptr<TaskCase> testCase;
208     int i = 0;
209     while (1) {
210         if (inc == NULL) {
211             break;
212         }
213         if (strcmp(inc->Value(),"include") != 0) {
214             LOGE("ModelBuilder::handleBatch invalid element %s", inc->Value());
215         }
216         testCase.reset(parseInclude(*inc, path));
217         if (testCase.get() == NULL) {
218             LOGE("ModelBuilder::handleBatch cannot create test case from include");
219             return NULL;
220         }
221         if (!batch.get()->addChild(testCase.get())) {
222             return NULL;
223         }
224         TaskGeneric* donotuse = testCase.release(); // parent will take care of destruction.
225         inc = inc->NextSiblingElement();
226         i++;
227     }
228     if (i == 0) {
229         // at least one include should exist.
230         LOGE("ModelBuilder::handleBatch no include elements");
231         return NULL;
232     }
233 
234     return batch.release();
235 }
236 
parseInclude(const XMLElement & elem,const android::String8 & path)237 TaskCase* ModelBuilder::parseInclude(const XMLElement& elem, const android::String8& path)
238 {
239     const char* fileName = elem.Attribute("file");
240     if (fileName == NULL) {
241         LOGE("ModelBuilder::handleBatch no include elements");
242         return NULL;
243     }
244     android::String8 incFile = path;
245     incFile.appendPath(fileName);
246 
247     // again no dynamic_cast intentionally
248     return reinterpret_cast<TaskCase*>(parseTestDescriptionXml(incFile, true));
249 }
250 
parseAttributes(const XMLElement & elem,TaskGeneric & task)251 bool ModelBuilder::parseAttributes(const XMLElement& elem, TaskGeneric& task)
252 {
253     const XMLAttribute* attr = elem.FirstAttribute();
254     while (1) {
255         if (attr == NULL) {
256             break;
257         }
258         android::String8 name(attr->Name());
259         android::String8 value(attr->Value());
260         if (!task.parseAttribute(name, value)) {
261             LOGE("ModelBuilder::parseAttributes cannot parse attribute %s:%s for task type %d",
262                     attr->Name(), attr->Value(), task.getType());
263             return false;
264         }
265         attr = attr->Next();
266     }
267     return true;
268 }
269