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 <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