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 69 ModelBuilder::ModelBuilder() 70 : mFactory(new GenericFactory()) 71 { 72 73 } 74 75 ModelBuilder::ModelBuilder(GenericFactory* factory) 76 : mFactory(factory) 77 { 78 79 } 80 ModelBuilder::~ModelBuilder() 81 { 82 delete mFactory; 83 } 84 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_SUCCESS) { 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 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 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 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 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 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