Home | History | Annotate | Download | only in task
      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