== 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); } }}}