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();