== FDO Schema !DeepCopy Support == === Overview === As a result of further integrating the FDO API into the Autodesk Map and Autodesk !MapGuide Enterprise product lines, an request was generated senior developers which would see the FDO community implement !DeepCopy or Clone support directly into the FDO API class definitions. The lack of such support was seen as a deficiency in FDO that forced users of the API to write such functionality themselves. The following sections outline how this new functionality will be implemented. === Definitions === The following changes will be made to the FDO API in order to support the process of deep copying or cloning FDO schema and data types. ==== !FdoSchemaCopyContext Class ==== An !FdoSchemaCopyContext class will be added to the FDO Schema API !FdoSchemaCopyContext is context class that will be used to assist in process of cloning schemas and their associated elements. !FdoSchemaCopyContext is designed to contain a schema element map which will store instances of previously copied schema elements. Since FDO schema objects are often associated to one another, and a deep cloning process by design must copy the entire schema object, it is necessary to know if the associated objects have been previously copied. Implementing a mapping for the copied schema elements will avoid creating duplicate objects when cloning an FDO schema. The !FdoSchemaCopyContext class will have the following definition: {{{ /// \brief /// A Map that will allow copied schema elements to be tracked /// and will prevent duplicate elements from being copied. typedef std::map FdoSchemaElementMap; /// \brief /// A Map pair that will allow copied schema elements to identified and stored /// in a FdoSchemaElementMap. typedef std::pair FdoSchemaElementPair; /// \brief /// A Map iterator that will allow iteration over a FdoSchemaElementMap. typedef FdoSchemaElementMap::const_iterator FdoSchemaElementMapIterator; /// \brief /// A schema context class that is used to assist in process /// of cloning schema elemts. The schema element map contained /// by this class assists the DeepCopy process in avoiding duplicate /// schema element objects from being copied. class FdoSchemaCopyContext : public FdoDisposable { public: /// \brief /// Creates a new instance of an FdoSchemaCopyContext /// /// \param identifiers /// An optional set of identifiers that will constrain /// the class indentifiers to copy. Use this collection to /// copy a potion of an FDO class definition /// /// \return /// Returns a new instance of FdoSchemaCopyContext /// static FdoSchemaCopyContext* Create( FdoIdentifierCollection *identifiers = NULL ); public: /// \brief /// Gets the element map containing the copied elements /// referenced by the copy context /// /// \return /// Returns the element map containing the copied elements /// const FdoSchemaElementMap * GetSchemaElementMap(); /// \brief /// Inserts a source/copied pair into the element map /// /// \sourceElement /// The source element from which the element was copied /// /// \copiedElement /// The copied element created from the source element /// void InsertSchemaElement( FdoSchemaElement* sourceElement, FdoSchemaElement* copiedElement ); /// \brief /// Sets the FDO identifier list that will constrain the /// list of class properties to be copied /// /// \param identifiers /// An optional set of identifiers that will constrain /// the class indentifiers to copy. Use this collection to /// copy a potion of an FDO class definition /// void SetIdentifierConstraints( FdoIdentifierCollection* identifiers ); /// \brief /// Find a copied element in the schema element /// map based on it's source object /// /// \param element /// Input the element to search for in the element map /// /// \return /// Returns the previously copied schema element /// if found in the collection, otherwise NULL /// template T* FindSchemaElement( T* element ) { if (m_schemaMap == NULL) { throw FdoException::Create( FdoException::NLSGetMessage(FDO_NLSID(FDO_4_UNREADY))); } T* retElem = NULL; FdoSchemaElementMapIterator elementIterator = m_schemaMap->find(element); if (elementIterator != m_schemaMap->end()) { retElem = dynamic_cast(elementIterator->second); if (retElem == NULL) { throw FdoException::Create( FdoException::NLSGetMessage(FDO_NLSID(CLNT_3_NULLPOINTER))); } } return FDO_SAFE_ADDREF(retElem); } protected: /// \brief /// Default Constructor FdoCommonSchemaCopyContext( void ); /// \brief /// Destructor (Virtual) virtual ~FdoCommonSchemaCopyContext(); /// \brief /// The Disposal method for this object. Dispose is called /// when the objects RefCount == 0. Dispose will in turn /// invoke the objects destructor virtual void Dispose(); private: /// \brief /// The std::map containing references to the copied schema elements FdoSchemaElementMap * m_schemaMap; /// \brief /// The identifier collection that will contrain the class properties /// to be copied. FdoPtr m_identifierConstraints; }; /// \brief /// FdoSchemaCopyContextP is a FdoPtr on FdoSchemaCopyContext. Provided for convenience. typedef FdoPtr FdoSchemaCopyContextP; }}} ==== !DeepCopy Function ==== The actual process of copying or cloning individual elements within an FDO schema shall be accomplished with the addition of a standard !DeepCopy method that will be added to all types that derive from !FdoSchemaElement. It shall be the responsibility of the !DeepCopy method to copy itself and all its contained objects and data. The following !DeepCopy function will be added to the set of schema class definitions listed in the subsequent section. {{{ /// \brief /// Deep copy this instance of class Fdo[ClassName]. /// /// \param context /// An schema copy context which will contain previously copied elements /// from the schema being cloned /// /// \return /// Returns a cloned instance of class Fdo[ClassName] /// FDO_API virtual Fdo[ClassName]* DeepCopy( FdoSchemaCopyContext* context = NULL ); }}} The following schema classes will have a !DeepCopy function added to their class definition. {{{ FdoAssociationPropertyDefinition FdoClass FdoClassDefinition FdoClassCollection FdoDataPropertyDefinition FdoDataPropertyDefinitionCollection FdoFeatureClass FdoFeatureClassCollection FdoFeatureSchema FdoFeatureSchemaCollection FdoGeometricPropertyDefinition FdoObjectPropertyDefinition FdoPropertyDefinition FdoPropertyDefinitionCollection FdoNetworkClass FdoNetworkFeatureClass FdoNetworkLayerClass FdoNetworkLinkFeatureClass FdoNetworkNodeFeatureClass FdoRasterPropertyDefinition FdoReadOnlyDataPropertyDefinitionCollection FdoReadOnlyPropertyDefinitionCollection FdoSchemaCollection FdoTopoFeaturePropertyDefinition FdoTopoGeometryPropertyDefinition }}} NOTE: This list may have to be expanded depending on additional requirements ===== Example Implementation ===== The following example demonstrates how the !DeepCopy method might be implemented for class !FdoFeatureSchema {{{ FdoFeatureSchema * FdoFeatureSchema::DeepCopy( FdoSchemaCopyContext * context ) { FdoSchemaCopyContextP copyContext = FDO_SAFE_ADDREF(context); if (copyContext == NULL) { copyContext = FdoSchemaCopyContext::Create(); } FdoFeatureSchemaP retSchema = localSchemaContext->FindSchemaElement(this); if (retSchema != NULL) { return FDO_SAFE_ADDREF(retSchema.p); } FdoFeatureSchemaP clonedSchema = FdoFeatureSchema::Create( GetName(), GetDescription()); FdoInt32 length = 0; FdoSADP sourceAttributes = this->GetAttributes (); FdoSADP copyAttributes = clonedSchema->GetAttributes (); FdoString** sourceAttributeNames = sourceAttributes->GetAttributeNames (length); for (FdoInt32 i = 0; i < length; i++) { FdoString* name = sourceAttributeNames[i]; FdoString* value = sourceAttributes->GetAttributeValue (name) copyAttributes->Add (name, value); } FdoClassesP classes = schema->GetClasses(); FdoClassesP newClasses = clonedSchema->GetClasses(); for (FdoInt32 i=0; iGetCount(); i++) { FdoClassDefinitionP class = classes->GetItem(i); FdoClassDefinitionP newClass = class->DeepCopy(copyContext); newClasses->Add(newClass); } clonedSchema->AcceptChanges(); copyContext->InsertSchemaElement(this, clonedSchema); return FDO_SAFE_ADDREF(clonedSchema.p); } }}} ===== Example Usage ===== The following example demonstrates how the !DeepCopy method might be used by a client such as the SDF Provider. {{{ FdoClassDefinition* CloneAndPruneClass( FdoClassDefinition *classDef, FdoIdentifierCollection *ids, FdoPropertyDefinitionCollection* computedProps) { // Create a copy context FdoPtr copyContext = NULL; if (ids != NULL && ids->GetCount() > 0) { copyContext = FdoSchemaCopyContext::Create(ids); } // Clone the class definition, copying only the class identifiers // listed in the 'ids' collection FdoClassDefinitionP classDefPruned = classDef->DeepCopy(copyContext); FdoPropertiesP properties = classDefPruned->GetProperties (); // If there are additional computed properties that // need to be added to the cloned class definition if (computedProps != NULL) { // Add the computed properties to the class for (GdoInt32 i=0; iGetCount(); i++) { FdoPtr computedProp = computedProps->GetItem(i); properties->Add(pd); } } // Returned the pruned class definition that also // includes the referenced computed properties return FDO_SAFE_ADDREF(classDefPruned.p); } }}} ==== Copy Function ==== The actual process of copying or cloning individual non !SchemaElement objects within the FDO API shall be accomplished with the addition of a standard Copy method as opposed to a !DeepCopy method. The following Copy method will be added to the set of FDO API classes’ listed in the section below. {{{ /// \brief /// copy this instance of class Fdo[ClassName]. /// /// \return /// Returns a newly created instance of class Fdo[ClassName] /// FDO_API virtual Fdo[ClassName]* Copy( void ); }}} The following list of classes will have a Copy function added to their class definition. {{{ FdoCLOBValue FdoComputedIdentifier FdoComparisonCondition FdoBinaryLogicalOperator FdoBinaryExpression FdoBLOBValue FdoBooleanValue FdoByteValue FdoDataValue FdoDataValueCollection FdoDateTimeValue FdoDecimalValue FdoDistanceCondition FdoDoubleValue FdoExpression FdoExpressionCollection FdoFilter FdoFunction FdoGeometryValue FdoGeometricCondition FdoInt16Value FdoInt32Value FdoInt64Value FdoIdentifier FdoInCondition FdoLogicalOperator FdoNullCondition FdoParameter FdoPropertyValueConstraint FdoPropertyValueConstraintRange FdoPropertyValueConstraintList FdoRasterDataModel FdoSearchCondition FdoSchemaAttributeDictionary FdoSingleValue FdoSpatialCondition FdoStringValue FdoUnaryExpression FdoUnaryLogicalOperator FdoUniqueConstraint FdoUniqueConstraintCollection FdoValueExpression FdoValueExpressionCollection }}} NOTE: This list may have to be expanded depending on additional requirements ===== Example Implementation ===== The following example demonstrates how the Copy method might be implemented for class FdoCLOBValue class. {{{ FdoCLOBValue* FdoCLOBValue::Copy( void ) { FdoPtr clone = FdoCLOBValue::Create();; if (IsNull()) { clone->SetNull(); } else { FdoPtr sourceData = GetData(); FdoInt32 count = sourceData->GetCount() FdoByteArray* ba = sourceData->GetData(); FdoPtr clonedArray = FdoByteArray::Create(ba, count); clone->SetData(clonedArray); } return FDO_SAFE_ADDREF(clone.p); } }}}