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