FieldBuilder (incl. tests)

This commit is contained in:
Matej Sekoranja
2013-11-04 19:04:28 +01:00
parent 3579d17a05
commit c56c976a22
4 changed files with 555 additions and 3 deletions

View File

@@ -11,6 +11,7 @@
#include <cstdlib>
#include <string>
#include <cstdio>
#include <stdexcept>
#include <pv/lock.h>
#include <pv/pvIntrospect.h>
#include <pv/convert.h>
@@ -371,6 +372,167 @@ void Structure::deserialize(ByteBuffer */*buffer*/, DeserializableControl */*con
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
FieldBuilder::FieldBuilder() : fieldCreate(getFieldCreate()), idSet(false) {}
FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
std::string const & _nestedName,
Type _nestedClassToBuild, bool _nestedArray) :
fieldCreate(getFieldCreate()),
idSet(false),
parentBuilder(_parentBuilder),
nestedClassToBuild(_nestedClassToBuild),
nestedName(_nestedName),
nestedArray(_nestedArray)
{}
void FieldBuilder::reset()
{
id.erase();
idSet = false;
fieldNames.clear();
fields.clear();
}
FieldBuilderPtr FieldBuilder::setId(std::string const & id)
{
this->id = id;
idSet = true;
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::add(std::string const & name, ScalarType scalarType)
{
fields.push_back(fieldCreate->createScalar(scalarType)); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::add(std::string const & name, FieldConstPtr const & field)
{
fields.push_back(field); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addArray(std::string const & name, ScalarType scalarType)
{
fields.push_back(fieldCreate->createScalarArray(scalarType)); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addArray(std::string const & name, FieldConstPtr const & element)
{
switch (element->getType())
{
case structure:
fields.push_back(fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element)));
break;
// TODO case union:
// fields.push_back(fieldCreate->createUnionArray(static_pointer_cast<const Union>(element)));
// break;
case scalar:
fields.push_back(fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType()));
break;
default:
throw std::invalid_argument("unsupported array element type:" + element->getType());
}
fieldNames.push_back(name);
return shared_from_this();
}
FieldConstPtr FieldBuilder::createFieldInternal(Type type)
{
/* TODO
// minor optimization
if (fieldNames.size() == 0 && type == union)
return fieldCreate->createVariantUnion();
*/
if (type == structure)
{
return (idSet) ?
fieldCreate->createStructure(id, fieldNames, fields) :
fieldCreate->createStructure(fieldNames, fields);
}
/* TODO
else if (type == union)
{
return (idSet) ?
fieldCreate->createUnion(id, fieldNames, fields) :
fieldCreate->createUnion(fieldNames, fields);
}
*/
else
throw std::invalid_argument("unsupported type: " + type);
}
StructureConstPtr FieldBuilder::createStructure()
{
if (parentBuilder.get())
throw std::runtime_error("createStructure() called in nested FieldBuilder");
StructureConstPtr field(static_pointer_cast<const Structure>(createFieldInternal(structure)));
reset();
return field;
}
/*
UnionConstPtr FieldBuilder::createUnion()
{
if (parentBuilder.get())
throw std::runtime_error("createUnion() called in nested FieldBuilder");
UnionConstPtr field(static_pointer_cast<const Union>(createFieldInternal(union)));
reset();
return field;
}
*/
FieldBuilderPtr FieldBuilder::addStructure(std::string const & name)
{
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, false));
}
/*
FieldBuilderPtr FieldBuilder::addUnion(std::string const & name)
{
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union, false));
}
*/
FieldBuilderPtr FieldBuilder::addStructureArray(std::string const & name)
{
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, true));
}
/*
FieldBuilderPtr FieldBuilder::addUnionArray(std::string const & name)
{
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union, true));
}
*/
FieldBuilderPtr FieldBuilder::createNested()
{
if (!parentBuilder.get())
throw std::runtime_error("this method can only be called to create nested fields");
FieldConstPtr nestedField = createFieldInternal(nestedClassToBuild);
if (nestedArray)
parentBuilder->addArray(nestedName, nestedField);
else
parentBuilder->add(nestedName, nestedField);
return parentBuilder;
}
FieldBuilderPtr FieldCreate::createFieldBuilder() const
{
FieldBuilderPtr builder(new FieldBuilder());
return builder;
}
ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
{
// TODO use singleton instance

View File

@@ -11,6 +11,7 @@
#define PVINTROSPECT_H
#include <string>
#include <stdexcept>
#include <map>
#include <pv/noDefaultMethods.h>
#include <pv/pvType.h>
@@ -476,16 +477,162 @@ private:
class FieldCreate;
typedef std::tr1::shared_ptr<FieldCreate> FieldCreatePtr;
class FieldBuilder;
typedef std::tr1::shared_ptr<FieldBuilder> FieldBuilderPtr;
/**
* Interface for in-line creating of introspection interfaces.
* One instance can be used to create multiple {@code Field} instances.
* An instance of this object must not be used concurrently (an object has a state).
* @author mse
*/
class FieldBuilder :
public std::tr1::enable_shared_from_this<FieldBuilder>
{
public:
/**
* Set ID of an object to be created.
* @param id id to be set.
* @return this instance of a {@code FieldBuilder}.
*/
FieldBuilderPtr setId(std::string const & id);
/**
* Add a {@code Scalar}.
* @param name name of the array.
* @param scalarType type of a scalar to add.
* @return this instance of a {@code FieldBuilder}.
*/
FieldBuilderPtr add(std::string const & name, ScalarType scalarType);
/**
* Add a {@code Field} (e.g. {@code Structure}, {@code Union}).
* @param name name of the array.
* @param field a field to add.
* @return this instance of a {@code FieldBuilder}.
*/
FieldBuilderPtr add(std::string const & name, FieldConstPtr const & field);
/**
* Add array of {@code Scalar} elements.
* @param name name of the array.
* @param scalarType type of a scalar element.
* @return this instance of a {@code FieldBuilder}.
*/
FieldBuilderPtr addArray(std::string const & name, ScalarType scalarType);
/**
* Add array of {@code Field} elements.
* @param name name of the array.
* @param field a type of an array element.
* @return this instance of a {@code FieldBuilder}.
*/
FieldBuilderPtr addArray(std::string const & name, FieldConstPtr const & element);
/**
* Create a {@code Structure}.
* This resets this instance state and allows new {@code Field} instance to be created.
* @return a new instance of a {@code Structure}.
*/
StructureConstPtr createStructure();
/**
* Create an {@code Union}.
* This resets this instance state and allows new {@code Field} instance to be created.
* @return a new instance of an {@code Union}.
*/
//UnionConstPtr createUnion();
/**
* Add new nested {@code Structure}.
* {@code createNested()} method must be called
* to complete creation of the nested {@code Structure}.
* @param name nested structure name.
* @return a new instance of a {@code FieldBuilder} is returned.
* @see #createNested()
*/
FieldBuilderPtr addStructure(std::string const & name);
/**
* Add new nested {@code Union}.
* {@code createNested()} method must be called
* to complete creation of the nested {@code Union}.
* @param name nested union name.
* @return a new instance of a {@code FieldBuilder} is returned.
* @see #createNested()
*/
FieldBuilderPtr addUnion(std::string const & name);
/**
* Add new nested {@code Structure[]}.
* {@code createNested()} method must be called
* to complete creation of the nested {@code Structure}.
* @param name nested structure name.
* @return a new instance of a {@code FieldBuilder} is returned.
* @see #createNested()
*/
FieldBuilderPtr addStructureArray(std::string const & name);
/**
* Add new nested {@code Union[]}.
* {@code createNested()} method must be called
* to complete creation of the nested {@code Union}.
* @param name nested union name.
* @return a new instance of a {@code FieldBuilder} is returned.
* @see #createNested()
*/
//FieldBuilderPtr addUnionArray(std::string const & name);
/**
* Complete the creation of a nested object.
* @see #addStructure(String)
* @see #addUnion(String)
* @return a previous (parent) {@code FieldBuilder}.
*/
FieldBuilderPtr createNested();
private:
FieldBuilder();
FieldBuilder(FieldBuilderPtr const & parentBuilder,
std::string const & nestedName,
Type nestedClassToBuild, bool nestedArray);
void reset();
FieldConstPtr createFieldInternal(Type type);
friend class FieldCreate;
FieldCreatePtr fieldCreate;
std::string id;
bool idSet;
// NOTE: this preserves order, however it does not handle duplicates
StringArray fieldNames;
FieldConstPtrArray fields;
FieldBuilderPtr parentBuilder;
Type nestedClassToBuild;
std::string nestedName;
bool nestedArray;
};
class FieldCreate {
public:
static FieldCreatePtr getFieldCreate();
static FieldCreatePtr getFieldCreate();
/**
* Create a new instance of in-line {@code Field} builder.
* @return a new instance of a {@code FieldBuilder}.
*/
FieldBuilderPtr createFieldBuilder() const;
/**
* Create a {@code ScalarField}.
* @param scalarType The scalar type.
* @return a {@code Scalar} interface for the newly created object.
* @throws An {@code IllegalArgumentException} if an illegal type is specified.
*/
ScalarConstPtr createScalar(ScalarType scalarType) const;
ScalarConstPtr createScalar(ScalarType scalarType) const;
/**
* Create an {@code Array} field.
* @param elementType The {@code scalarType} for array elements
@@ -549,7 +696,7 @@ public:
FieldConstPtr deserialize(ByteBuffer* buffer, DeserializableControl* control) const;
private:
FieldCreate();
FieldCreate();
};
/**

View File

@@ -53,6 +53,10 @@ PROD_HOST += testOperators
testOperators_SRCS += testOperators.cpp
testOperators_LIBS += pvData Com
PROD_HOST += testFieldBuilder
testFieldBuilder_SRCS += testFieldBuilder.cpp
testFieldBuilder_LIBS += pvData Com
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
include $(TOP)/configure/RULES

View File

@@ -0,0 +1,239 @@
#include <string>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <pv/pvData.h>
using namespace epics::pvData;
using namespace std;
using namespace std::tr1;
void test_factory()
{
testDiag("Test test_factory()");
FieldCreatePtr fieldCreate = getFieldCreate();
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
testOk1(fb.get() != 0);
FieldBuilderPtr fb2 = fieldCreate->createFieldBuilder();
testOk1(fb.get() != fb2.get());
}
void test_structure()
{
testDiag("Test test_structure()");
FieldCreatePtr fieldCreate = getFieldCreate();
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
// test with simple (non-nested) structure
std::string ID = "testStructureID";
StructureConstPtr s = fb->setId(ID)->
add("double", pvDouble)->
addArray("intArray", pvInt)->
createStructure();
testOk1(s.get() != 0);
testOk1(ID == s->getID());
testOk1(2 == s->getFields().size());
FieldConstPtr f0 = s->getField(0);
testOk1(scalar == f0->getType());
testOk1("double" == s->getFieldName(0));
testOk(pvDouble == static_pointer_cast<const Scalar>(f0)->getScalarType(), "f0 scalar type == double");
FieldConstPtr f1 = s->getField(1);
testOk1(scalarArray == f1->getType());
testOk1("intArray" == s->getFieldName(1));
testOk(pvInt == static_pointer_cast<const ScalarArray>(f1)->getElementType(), "f1 element type == int");
// test reuse with empty structure
StructureConstPtr emptyStructure = fb->createStructure();
testOk1(emptyStructure.get() != 0);
testOk1(Structure::DEFAULT_ID == emptyStructure->getID());
testOk1(0 == emptyStructure->getFields().size());
// test add/addArray with Field
StructureConstPtr s2 = fb->add("s", s)->
addArray("sArray", s)->
createStructure();
testOk1(s2 != 0);
testOk1(Structure::DEFAULT_ID == s2->getID());
testOk1(2 == s2->getFields().size());
f0 = s2->getField(0);
testOk1(structure == f0->getType());
testOk1("s" == s2->getFieldName(0));
testOk1(s.get() == f0.get());
f1 = s2->getField(1);
testOk1(structureArray == f1->getType());
testOk1("sArray" == s2->getFieldName(1));
testOk(s.get() == static_pointer_cast<const StructureArray>(f1)->getStructure().get(), "array element is given structure");
}
void test_invalid()
{
testDiag("Test test_invalid()");
FieldCreatePtr fieldCreate = getFieldCreate();
try
{
fieldCreate->createFieldBuilder()->
add("f1", pvByte)->
createNested();
testFail("createNested() allowed in non-nested FieldBuilder");
}
catch (std::runtime_error& re) {
// ok
testPass("createNested() disallowed in non-nested FieldBuilder");
}
try
{
fieldCreate->createFieldBuilder()->
add("f1", pvByte)->
addStructure("nested")->
add("n1", pvUInt)->
createStructure();
testFail("createStructure() allowed in nested FieldBuilder");
}
catch (std::runtime_error& re) {
// ok
testPass("createStructure() disallowed in nested FieldBuilder");
}
}
void test_nestedStructure()
{
testDiag("Test test_nestedStructure()");
FieldCreatePtr fieldCreate = getFieldCreate();
std::string NESTED_ID = "nestedID";
StructureConstPtr s = fieldCreate->createFieldBuilder()->
add("double", pvDouble)->
addStructure("nested")->
setId(NESTED_ID)->
add("short", pvShort)->
add("long", pvLong)->
createNested()->
addArray("intArray", pvInt)->
createStructure();
testOk1(s.get() != 0);
testOk1(Structure::DEFAULT_ID == s->getID());
testOk1(3 == s->getFields().size());
FieldConstPtr f0 = s->getField(0);
testOk1(scalar == f0->getType());
testOk1("double" == s->getFieldName(0));
testOk(pvDouble == static_pointer_cast<const Scalar>(f0)->getScalarType(), "f0 scalar type == double");
FieldConstPtr f1 = s->getField(1);
testOk1(structure == f1->getType());
testOk1("nested" == s->getFieldName(1));
{
StructureConstPtr s2 = static_pointer_cast<const Structure>(f1);
testOk1(s2.get() != 0);
testOk1(NESTED_ID == s2->getID());
testOk1(2 == s2->getFields().size());
FieldConstPtr f20 = s2->getField(0);
testOk1(scalar == f20->getType());
testOk1("short" == s2->getFieldName(0));
testOk(pvShort == static_pointer_cast<const Scalar>(f20)->getScalarType(), "f20 scalar type == short");
FieldConstPtr f21 = s2->getField(1);
testOk1(scalar == f21->getType());
testOk1("long" == s2->getFieldName(1));
testOk(pvLong == static_pointer_cast<const Scalar>(f21)->getScalarType(), "f21 element type == long");
}
FieldConstPtr f2 = s->getField(2);
testOk1(scalarArray == f2->getType());
testOk1("intArray" == s->getFieldName(2));
testOk(pvInt == static_pointer_cast<const ScalarArray>(f2)->getElementType(), "f2 element type == int");
}
void test_nestedStructureArray()
{
testDiag("Test test_nestedStructureArray()");
FieldCreatePtr fieldCreate = getFieldCreate();
std::string NESTED_ID = "nestedID";
StructureConstPtr s = fieldCreate->createFieldBuilder()->
add("double", pvDouble)->
addStructureArray("nested")->
setId(NESTED_ID)->
add("short", pvShort)->
add("long", pvLong)->
createNested()->
addArray("intArray", pvInt)->
createStructure();
testOk1(s.get() != 0);
testOk1(Structure::DEFAULT_ID == s->getID());
testOk1(3 == s->getFields().size());
FieldConstPtr f0 = s->getField(0);
testOk1(scalar == f0->getType());
testOk1("double" == s->getFieldName(0));
testOk(pvDouble == static_pointer_cast<const Scalar>(f0)->getScalarType(), "f0 scalar type == double");
FieldConstPtr f1 = s->getField(1);
testOk1(structureArray == f1->getType());
testOk1("nested" == s->getFieldName(1));
{
StructureConstPtr s2 = static_pointer_cast<const StructureArray>(f1)->getStructure();
testOk1(s2.get() != 0);
testOk1(NESTED_ID == s2->getID());
testOk1(2 == s2->getFields().size());
FieldConstPtr f20 = s2->getField(0);
testOk1(scalar == f20->getType());
testOk1("short" == s2->getFieldName(0));
testOk(pvShort == static_pointer_cast<const Scalar>(f20)->getScalarType(), "f20 scalar type == short");
FieldConstPtr f21 = s2->getField(1);
testOk1(scalar == f21->getType());
testOk1("long" == s2->getFieldName(1));
testOk(pvLong == static_pointer_cast<const Scalar>(f21)->getScalarType(), "f21 element type == long");
}
FieldConstPtr f2 = s->getField(2);
testOk1(scalarArray == f2->getType());
testOk1("intArray" == s->getFieldName(2));
testOk(pvInt == static_pointer_cast<const ScalarArray>(f2)->getElementType(), "f2 element type == int");
}
MAIN(testFieldBuilder)
{
testPlan(65);
testDiag("Tests for FieldBuilder");
test_factory();
test_structure();
test_nestedStructure();
test_nestedStructureArray();
test_invalid();
return testDone();
}