diff --git a/pvDataApp/factory/FieldCreateFactory.cpp b/pvDataApp/factory/FieldCreateFactory.cpp index e8444ba..d41336b 100644 --- a/pvDataApp/factory/FieldCreateFactory.cpp +++ b/pvDataApp/factory/FieldCreateFactory.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -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(element))); + break; + // TODO case union: + // fields.push_back(fieldCreate->createUnionArray(static_pointer_cast(element))); + // break; + case scalar: + fields.push_back(fieldCreate->createScalarArray(static_pointer_cast(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(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(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 diff --git a/pvDataApp/pv/pvIntrospect.h b/pvDataApp/pv/pvIntrospect.h index 5a087cc..83feb8a 100644 --- a/pvDataApp/pv/pvIntrospect.h +++ b/pvDataApp/pv/pvIntrospect.h @@ -11,6 +11,7 @@ #define PVINTROSPECT_H #include #include +#include #include #include @@ -476,16 +477,162 @@ private: class FieldCreate; typedef std::tr1::shared_ptr FieldCreatePtr; +class FieldBuilder; +typedef std::tr1::shared_ptr 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 +{ +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(); }; /** diff --git a/testApp/pv/Makefile b/testApp/pv/Makefile index 78cb614..447457a 100644 --- a/testApp/pv/Makefile +++ b/testApp/pv/Makefile @@ -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 diff --git a/testApp/pv/testFieldBuilder.cpp b/testApp/pv/testFieldBuilder.cpp new file mode 100644 index 0000000..98b97bd --- /dev/null +++ b/testApp/pv/testFieldBuilder.cpp @@ -0,0 +1,239 @@ +#include + +#include +#include + +#include + +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(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(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(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(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(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(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(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(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(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(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(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(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(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(); +}