From c56c976a22dbbe6ed4553fb2365c37cd84a986b7 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Mon, 4 Nov 2013 19:04:28 +0100 Subject: [PATCH 1/2] FieldBuilder (incl. tests) --- pvDataApp/factory/FieldCreateFactory.cpp | 162 +++++++++++++++ pvDataApp/pv/pvIntrospect.h | 153 ++++++++++++++- testApp/pv/Makefile | 4 + testApp/pv/testFieldBuilder.cpp | 239 +++++++++++++++++++++++ 4 files changed, 555 insertions(+), 3 deletions(-) create mode 100644 testApp/pv/testFieldBuilder.cpp 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(); +} From cbf7b69ef08bcf52e406055069f07b2c5516ebf8 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 5 Nov 2013 16:15:12 +0100 Subject: [PATCH 2/2] Union/UnionArray implemented, PVUnion/PVUnionArray not yet implemented; also implemented reuse of scalar/scalarArray instances in FieldCreate --- pvDataApp/factory/FieldCreateFactory.cpp | 354 +++++++++++++++++++++-- pvDataApp/pv/pvIntrospect.h | 215 +++++++++++++- testApp/pv/testIntrospect.cpp | 59 +++- 3 files changed, 590 insertions(+), 38 deletions(-) diff --git a/pvDataApp/factory/FieldCreateFactory.cpp b/pvDataApp/factory/FieldCreateFactory.cpp index d41336b..87f50ec 100644 --- a/pvDataApp/factory/FieldCreateFactory.cpp +++ b/pvDataApp/factory/FieldCreateFactory.cpp @@ -69,7 +69,7 @@ struct StructureArrayHashFunction { Scalar::Scalar(ScalarType scalarType) : Field(scalar),scalarType(scalarType) { - if(scalarTypepvString) + if(scalarType<0 || scalarType>MAX_SCALAR_TYPE) throw std::invalid_argument("Can't construct Scalar from invalid ScalarType"); } @@ -170,10 +170,49 @@ static StructureConstPtr deserializeStructureField(const FieldCreate* fieldCreat return fieldCreate->createStructure(id, fieldNames, fields); } +static void serializeUnionField(const Union* punion, ByteBuffer* buffer, SerializableControl* control) +{ + // to optimize default (non-empty) IDs optimization + // empty IDs are not allowed + String id = punion->getID(); + if (id == Union::DEFAULT_ID) // TODO slow comparison + SerializeHelper::serializeString(emptyString, buffer, control); + else + SerializeHelper::serializeString(id, buffer, control); + + FieldConstPtrArray const & fields = punion->getFields(); + StringArray const & fieldNames = punion->getFieldNames(); + std::size_t len = fields.size(); + SerializeHelper::writeSize(len, buffer, control); + for (std::size_t i = 0; i < len; i++) + { + SerializeHelper::serializeString(fieldNames[i], buffer, control); + control->cachedSerialize(fields[i], buffer); + } +} + +static UnionConstPtr deserializeUnionField(const FieldCreate* fieldCreate, ByteBuffer* buffer, DeserializableControl* control) +{ + String id = SerializeHelper::deserializeString(buffer, control); + const std::size_t size = SerializeHelper::readSize(buffer, control); + FieldConstPtrArray fields; fields.reserve(size); + StringArray fieldNames; fieldNames.reserve(size); + for (std::size_t i = 0; i < size; i++) + { + fieldNames.push_back(SerializeHelper::deserializeString(buffer, control)); + fields.push_back(control->cachedDeserialize(buffer)); + } + + if (id.empty()) + return fieldCreate->createUnion(fieldNames, fields); + else + return fieldCreate->createUnion(id, fieldNames, fields); +} + ScalarArray::ScalarArray(ScalarType elementType) : Field(scalarArray),elementType(elementType) { - if(elementTypepvString) + if(elementType<0 || elementType>MAX_SCALAR_TYPE) throw std::invalid_argument("Can't construct ScalarArray from invalid ScalarType"); } @@ -265,6 +304,44 @@ void StructureArray::deserialize(ByteBuffer */*buffer*/, DeserializableControl * throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); } +UnionArray::UnionArray(UnionConstPtr const & _punion) +: Field(unionArray),punion(_punion) +{ +} + +UnionArray::~UnionArray() { + if(debugLevel==highDebug) printf("~UnionArray\n"); +} + +String UnionArray::getID() const +{ + return punion->getID() + "[]"; +} + +void UnionArray::toString(StringBuilder buffer,int indentLevel) const { + *buffer += getID(); + newLine(buffer,indentLevel + 1); + punion->toString(buffer,indentLevel + 1); +} + +void UnionArray::serialize(ByteBuffer *buffer, SerializableControl *control) const { + control->ensureBuffer(1); + if (punion->isVariant()) + { + // unrestricted/variant union + buffer->putByte(0x92); + } + else + { + buffer->putByte(0x91); + control->cachedSerialize(punion, buffer); + } +} + +void UnionArray::deserialize(ByteBuffer */*buffer*/, DeserializableControl */*control*/) { + throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); +} + String Structure::DEFAULT_ID = "structure"; Structure::Structure ( @@ -357,6 +434,17 @@ void Structure::toStringCommon(StringBuilder buffer,int indentLevel) const{ newLine(buffer,indentLevel +1); pfield->toString(buffer,indentLevel +1); break; + case union_: + { + Field const *xxx = pfield.get(); + Union const *pstruct = static_cast(xxx); + pstruct->toStringCommon(buffer,indentLevel + 1); + break; + } + case unionArray: + newLine(buffer,indentLevel +1); + pfield->toString(buffer,indentLevel +1); + break; } if(igetID() + " " + fieldNames[i]; + switch(pfield->getType()) { + case scalar: + case scalarArray: + break; + case structure: + { + Field const *xxx = pfield.get(); + Structure const *pstruct = static_cast(xxx); + pstruct->toStringCommon(buffer,indentLevel + 1); + break; + } + case structureArray: + newLine(buffer,indentLevel +1); + pfield->toString(buffer,indentLevel +1); + break; + case union_: + { + Field const *xxx = pfield.get(); + Union const *pstruct = static_cast(xxx); + pstruct->toStringCommon(buffer,indentLevel + 1); + break; + } + case unionArray: + newLine(buffer,indentLevel +1); + pfield->toString(buffer,indentLevel +1); + break; + } + if(iensureBuffer(1); + if (fields.size() == 0) + { + // unrestricted/variant union + buffer->putByte(0x82); + } + else + { + buffer->putByte(0x81); + serializeUnionField(this, buffer, control); + } +} + +void Union::deserialize(ByteBuffer */*buffer*/, DeserializableControl */*control*/) { + throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); +} + + FieldBuilder::FieldBuilder() : fieldCreate(getFieldCreate()), idSet(false) {} FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder, @@ -425,9 +656,9 @@ FieldBuilderPtr FieldBuilder::addArray(std::string const & name, FieldConstPtr c 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 union_: + fields.push_back(fieldCreate->createUnionArray(static_pointer_cast(element))); + break; case scalar: fields.push_back(fieldCreate->createScalarArray(static_pointer_cast(element)->getScalarType())); break; @@ -441,25 +672,22 @@ FieldBuilderPtr FieldBuilder::addArray(std::string const & name, FieldConstPtr c FieldConstPtr FieldBuilder::createFieldInternal(Type type) { -/* TODO // minor optimization - if (fieldNames.size() == 0 && type == union) + 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) + else if (type == union_) { return (idSet) ? fieldCreate->createUnion(id, fieldNames, fields) : fieldCreate->createUnion(fieldNames, fields); } - */ else throw std::invalid_argument("unsupported type: " + type); } @@ -475,42 +703,37 @@ StructureConstPtr FieldBuilder::createStructure() return field; } -/* UnionConstPtr FieldBuilder::createUnion() { if (parentBuilder.get()) throw std::runtime_error("createUnion() called in nested FieldBuilder"); - UnionConstPtr field(static_pointer_cast(createFieldInternal(union))); + 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)); + 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)); + return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, true)); } -*/ - FieldBuilderPtr FieldBuilder::createNested() { @@ -529,22 +752,23 @@ FieldBuilderPtr FieldBuilder::createNested() FieldBuilderPtr FieldCreate::createFieldBuilder() const { - FieldBuilderPtr builder(new FieldBuilder()); - return builder; + return FieldBuilderPtr(new FieldBuilder()); } ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const { - // TODO use singleton instance - ScalarConstPtr scalar(new Scalar(scalarType), Field::Deleter()); - return scalar; + if(scalarType<0 || scalarType>MAX_SCALAR_TYPE) + throw std::invalid_argument("Can't construct Scalar from invalid ScalarType"); + + return scalars[scalarType]; } ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const { - // TODO use singleton instance - ScalarArrayConstPtr scalarArray(new ScalarArray(elementType), Field::Deleter()); - return scalarArray; + if(elementType<0 || elementType>MAX_SCALAR_TYPE) + throw std::invalid_argument("Can't construct ScalarArray from invalid ScalarType"); + + return scalarArrays[elementType]; } StructureConstPtr FieldCreate::createStructure ( @@ -573,6 +797,42 @@ StructureArrayConstPtr FieldCreate::createStructureArray( return structureArray; } +UnionConstPtr FieldCreate::createUnion ( + StringArray const & fieldNames,FieldConstPtrArray const & fields) const +{ + UnionConstPtr punion( + new Union(fieldNames,fields), Field::Deleter()); + return punion; +} + +UnionConstPtr FieldCreate::createUnion ( + String const & id, + StringArray const & fieldNames, + FieldConstPtrArray const & fields) const +{ + UnionConstPtr punion( + new Union(fieldNames,fields,id), Field::Deleter()); + return punion; +} + +UnionConstPtr FieldCreate::createVariantUnion () const +{ + return variantUnion; +} + +UnionArrayConstPtr FieldCreate::createUnionArray( + UnionConstPtr const & punion) const +{ + UnionArrayConstPtr unionArray( + new UnionArray(punion), Field::Deleter()); + return unionArray; +} + +UnionArrayConstPtr FieldCreate::createVariantUnionArray () const +{ + return variantUnionArray; +} + StructureConstPtr FieldCreate::appendField( StructureConstPtr const & structure, String const & fieldName, @@ -684,13 +944,23 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl int scalarType = decodeScalar(code); if (scalarType == -1) throw std::invalid_argument("invalid scalar type encoding"); - return FieldConstPtr(new Scalar(static_cast(scalarType)), Field::Deleter()); + return scalars[scalarType]; } else if (typeCode == 0x80) { // Type type = Type.structure; return deserializeStructureField(this, buffer, control); } + else if (typeCode == 0x81) + { + // Type type = Type.union; + return deserializeUnionField(this, buffer, control); + } + else if (typeCode == 0x82) + { + // Type type = Type.union; variant union (aka any type) + return variantUnion; + } else throw std::invalid_argument("invalid type encoding"); } @@ -702,7 +972,7 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl int scalarType = decodeScalar(code); if (scalarType == -1) throw std::invalid_argument("invalid scalarArray type encoding"); - return FieldConstPtr(new ScalarArray(static_cast(scalarType)), Field::Deleter()); + return scalarArrays[scalarType]; } else if (typeCode == 0x80) { @@ -710,6 +980,17 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl StructureConstPtr elementStructure = std::tr1::static_pointer_cast(control->cachedDeserialize(buffer)); return FieldConstPtr(new StructureArray(elementStructure), Field::Deleter()); } + else if (typeCode == 0x81) + { + // Type type = Type.unionArray; + UnionConstPtr elementUnion = std::tr1::static_pointer_cast(control->cachedDeserialize(buffer)); + return FieldConstPtr(new UnionArray(elementUnion), Field::Deleter()); + } + else if (typeCode == 0x82) + { + // Type type = Type.unionArray; variant union (aka any type) + return variantUnionArray; + } else throw std::invalid_argument("invalid type encoding"); } @@ -726,7 +1007,16 @@ FieldCreatePtr FieldCreate::getFieldCreate() return fieldCreate; } -FieldCreate::FieldCreate(){} +FieldCreate::FieldCreate() +{ + for (int i = 0; i <= MAX_SCALAR_TYPE; i++) + { + scalars.push_back(ScalarConstPtr(new Scalar(static_cast(i)), Field::Deleter())); + scalarArrays.push_back(ScalarArrayConstPtr(new ScalarArray(static_cast(i)), Field::Deleter())); + } + variantUnion = UnionConstPtr(new Union(), Field::Deleter()); + variantUnionArray = UnionArrayConstPtr(new UnionArray(variantUnion), Field::Deleter()); +} FieldCreatePtr getFieldCreate() { return FieldCreate::getFieldCreate(); diff --git a/pvDataApp/pv/pvIntrospect.h b/pvDataApp/pv/pvIntrospect.h index 83feb8a..12871eb 100644 --- a/pvDataApp/pv/pvIntrospect.h +++ b/pvDataApp/pv/pvIntrospect.h @@ -11,7 +11,6 @@ #define PVINTROSPECT_H #include #include -#include #include #include @@ -25,6 +24,8 @@ class Scalar; class ScalarArray; class Structure; class StructureArray; +class Union; +class UnionArray; /** * typedef for a shared pointer to an immutable Field. @@ -50,6 +51,14 @@ typedef std::tr1::shared_ptr StructureConstPtr; * typedef for a shared pointer to an immutable StructureArray. */ typedef std::tr1::shared_ptr StructureArrayConstPtr; +/** + * typedef for a shared pointer to an immutable Union. + */ +typedef std::tr1::shared_ptr UnionConstPtr; +/** + * typedef for a shared pointer to an immutable UnionArray. + */ +typedef std::tr1::shared_ptr UnionArrayConstPtr; /** * Definition of support field types. @@ -70,7 +79,15 @@ enum Type { /** * The type is structureArray. Each element is a structure. */ - structureArray + structureArray, + /** + * The type is an union. + */ + union_, + /** + * The type is an array of unions. + */ + unionArray }; /** @@ -145,6 +162,8 @@ enum ScalarType { pvString }; +#define MAX_SCALAR_TYPE pvString + /** * Convenience functions for ScalarType. */ @@ -382,6 +401,48 @@ private: friend class FieldCreate; }; +/** + * This class implements introspection object for a unionArray + */ +class UnionArray : public Field{ +public: + POINTER_DEFINITIONS(UnionArray); + typedef UnionArray& reference; + typedef const UnionArray& const_reference; + + /** + * Get the introspection interface for the array elements. + * @return The introspection interface. + */ + UnionConstPtr getUnion() const {return punion;} + + /** + * Convert the scalarType to a string and add it to builder. + * @param builder The string builder. + * @param indentLevel The number of blanks at the beginning of new lines. + */ + virtual void toString(StringBuilder buf,int indentLevel=0) const; + + virtual String getID() const; + + virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; + virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control); + +protected: + /** + * Constructor. + * @param union The introspection interface for the elements. + */ + UnionArray(UnionConstPtr const & _punion); + /** + * Destructor. + */ + virtual ~UnionArray(); +private: + UnionConstPtr punion; + friend class FieldCreate; +}; + /** * This class implements introspection object for a structure. */ @@ -402,7 +463,7 @@ public: typedef const Structure& const_reference; /** - * Get the number of immediate subfields in the structure/ + * Get the number of immediate subfields in the structure. * @return The number of fields. */ std::size_t getNumberFields() const {return fieldNames.size();} @@ -469,6 +530,108 @@ private: FieldConstPtrArray fields; String id; friend class FieldCreate; + friend class Union; +}; + +/** + * This class implements introspection object for a union. + */ +class Union : public Field { +public: + POINTER_DEFINITIONS(Union); + + /** + * Default union ID. + */ + static epics::pvData::String DEFAULT_ID; + + /** + * Default variant union ID. + */ + static epics::pvData::String ANY_ID; + + /** + * Destructor. + */ + virtual ~Union(); + typedef Union& reference; + typedef const Union& const_reference; + + /** + * Get the number of immediate subfields in the union. + * @return The number of fields. + */ + std::size_t getNumberFields() const {return fieldNames.size();} + /** + * Get the field for the specified fieldName. + * @param fieldName The name of the field to get; + * @return The introspection interface. + * This will hold a null pointer if the field is not in the union. + */ + FieldConstPtr getField(String const &fieldName) const; + /** + * Get the field for the specified fieldName. + * @param fieldName The index of the field to get; + * @return The introspection interface. + * This will hold a null pointer if the field is not in the union. + */ + FieldConstPtr getField(std::size_t index) const {return fields[index];} + /** + * Get the field index for the specified fieldName. + * @return The introspection interface. + * This will be -1 if the field is not in the union. + */ + std::size_t getFieldIndex(String const &fieldName) const; + /** + * Get the fields in the union. + * @return The array of fields. + */ + FieldConstPtrArray const & getFields() const {return fields;} + /** + * Get the names of the fields in the union. + * @return The array of fieldNames. + */ + StringArray const & getFieldNames() const {return fieldNames;} + void renameField(std::size_t fieldIndex,String const & newName) + {fieldNames[fieldIndex] = newName;} + /** + * Get the name of the field with the specified index; + * @param fieldIndex The index of the desired field. + * @return The fieldName. + */ + String getFieldName(std::size_t fieldIndex) const {return fieldNames[fieldIndex];} + /** + * Check if this union is variant union (aka any type). + * @return true if this union is variant union, otherwise false. + */ + bool isVariant() const {return (fieldNames.size() == 0);} + /** + * Convert the union to a string and add it to builder. + * @param builder The string builder. + */ + virtual void toString(StringBuilder buf) const{toString(buf,0);} + /** + * Convert the union to a string and add it to builder. + * @param builder The string builder. + * @param indentLevel The number of blanks at the beginning of new lines. + */ + virtual void toString(StringBuilder buf,int indentLevel) const; + + virtual String getID() const; + + virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; + virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control); + +protected: + Union(); + Union(StringArray const & fieldNames, FieldConstPtrArray const & fields, String const & id = DEFAULT_ID); +private: + void toStringCommon(StringBuilder buf,int indentLevel) const; + StringArray fieldNames; + FieldConstPtrArray fields; + String id; + friend class FieldCreate; + friend class Structure; }; /** @@ -541,7 +704,7 @@ public: * This resets this instance state and allows new {@code Field} instance to be created. * @return a new instance of an {@code Union}. */ - //UnionConstPtr createUnion(); + UnionConstPtr createUnion(); /** * Add new nested {@code Structure}. @@ -581,7 +744,7 @@ public: * @return a new instance of a {@code FieldBuilder} is returned. * @see #createNested() */ - //FieldBuilderPtr addUnionArray(std::string const & name); + FieldBuilderPtr addUnionArray(std::string const & name); /** * Complete the creation of a nested object. @@ -666,6 +829,43 @@ public: String const & id, StringArray const & fieldNames, FieldConstPtrArray const & fields) const; + /** + * Create an {@code Array} field that is has element type Union + * @param fieldName The field name + * @param elementUnion The {@code Union} for each array element. + * @return An {@code Array} Interface for the newly created object. + */ + UnionArrayConstPtr createUnionArray(UnionConstPtr const & punion) const; + /** + * Create a variant {@code UnionArray} (aka any type) field. + * @return a {@code UnionArray} interface for the newly created object. + */ + UnionArrayConstPtr createVariantUnionArray() const; + /** + * Create a variant {@code Union} (aka any type) field. + * @return a {@code Union} interface for the newly created object. + */ + UnionConstPtr createVariantUnion() const; + /** + * Create a {@code Union} field. + * @param fieldNames The array of {@code fieldNames} for the union. + * @param fields The array of {@code fields} for the union. + * @return a {@code Union} interface for the newly created object. + */ + UnionConstPtr createUnion ( + StringArray const & fieldNames, + FieldConstPtrArray const & fields) const; + /** + * Create a {@code Union} field with identification string. + * @param id The identification string for the union. + * @param fieldNames The array of {@code fieldNames} for the union. + * @param fields The array of {@code fields} for the union. + * @return a {@code Union} interface for the newly created object. + */ + UnionConstPtr createUnion ( + String const & id, + StringArray const & fieldNames, + FieldConstPtrArray const & fields) const; /** * Append a field to a structure. * @param structure The structure to which the field is appended. @@ -697,6 +897,11 @@ public: private: FieldCreate(); + + std::vector scalars; + std::vector scalarArrays; + UnionConstPtr variantUnion; + UnionArrayConstPtr variantUnionArray; }; /** diff --git a/testApp/pv/testIntrospect.cpp b/testApp/pv/testIntrospect.cpp index ffd9475..1e57e23 100644 --- a/testApp/pv/testIntrospect.cpp +++ b/testApp/pv/testIntrospect.cpp @@ -128,6 +128,62 @@ static void testStructure() testOk1(struct1arr->getID()=="structure[]"); } +static void testUnion() +{ + testDiag("testUnion"); + StringArray names1(2); + names1[0] = "innerA"; + names1[1] = "innerB"; + FieldConstPtrArray fields1(2); + fields1[0] = fieldCreate->createScalar(pvDouble); + fields1[1] = fieldCreate->createScalarArray(pvString); + + UnionConstPtr union1 = fieldCreate->createUnion(names1, fields1); + + testOk1(union1->getNumberFields()==2); + testOk1(union1->getField("innerA")==fields1[0]); + testOk1(union1->getField("innerB")==fields1[1]); + testOk1(union1->getFieldIndex("innerA")==0); + testOk1(union1->getFieldIndex("innerB")==1); + testOk1(union1->getField(0)==fields1[0]); + testOk1(union1->getField(1)==fields1[1]); + testOk1(union1->getFieldName(0)==names1[0]); + testOk1(union1->getFieldName(1)==names1[1]); + + testOk1(union1->getID() == Union::DEFAULT_ID); + + testOk1(fields1 == union1->getFields()); // vector equality + + StringArray names2(2); + names2[0] = "outerA"; + names2[1] = "outerB"; + FieldConstPtrArray fields2(2); + fields2[0] = fieldCreate->createScalar(pvInt); + fields2[1] = std::tr1::static_pointer_cast(union1); + + UnionConstPtr union2 = fieldCreate->createUnion(names2, fields2); + + testOk1(union2->getNumberFields()==2); // not recursive + testOk1(union2->getField(1)==fields2[1]); + + UnionArrayConstPtr union1arr = fieldCreate->createUnionArray(union1); + + testOk1(union1arr->getUnion()==union1); + testOk1(union1arr->getID()=="union[]"); + + UnionConstPtr variantUnion1 = fieldCreate->createVariantUnion(); + + testOk1(variantUnion1->getNumberFields()==0); + testOk1(variantUnion1->getID() == Union::ANY_ID); + + UnionArrayConstPtr variantUnion1arr = fieldCreate->createVariantUnionArray(); + + testOk1(variantUnion1arr->getUnion()==variantUnion1); + testOk1(variantUnion1arr->getID()=="any[]"); + +} + + #define testExcept(EXCEPT, CMD) try{ CMD; testFail( "No exception from: " #CMD); } \ catch(EXCEPT& e) {testPass("Got expected exception from: " #CMD);} \ catch(std::exception& e) {testFail("Got wrong exception %s(%s) from: " #CMD, typeid(e).name(),e.what());} \ @@ -198,13 +254,14 @@ static void testMapping() MAIN(testIntrospect) { - testPlan(161); + testPlan(180); fieldCreate = getFieldCreate(); pvDataCreate = getPVDataCreate(); standardField = getStandardField(); testScalar(); testScalarArray(); testStructure(); + testUnion(); testError(); testMapping(); return testDone();