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