wiki:FdoSchemaDeepCopy

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 <FdoSchemaElement*, FdoSchemaElement*> FdoSchemaElementMap;

/// \brief
/// A Map pair that will allow copied schema elements to identified and stored
/// in a FdoSchemaElementMap.
typedef std::pair <FdoSchemaElement*, FdoSchemaElement*> 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 <typename T> 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<T*>(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<FdoIdentifierCollection> m_identifierConstraints;
};

/// \brief
/// FdoSchemaCopyContextP is a FdoPtr on FdoSchemaCopyContext. Provided for convenience.
typedef FdoPtr<FdoSchemaCopyContext> 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; i<classes->GetCount(); 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<FdoSchemaCopyContext> 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; i<computedProps->GetCount(); i++) {
            FdoPtr<FdoPropertyDefinition> 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<FdoCLOBValue> clone = FdoCLOBValue::Create();;
    if (IsNull()) 
    {
        clone->SetNull();
    }
    else 
    {
        FdoPtr<FdoByteArray> sourceData = GetData();

        FdoInt32 count = sourceData->GetCount()
        FdoByteArray* ba = sourceData->GetData();

        FdoPtr<FdoByteArray> clonedArray = FdoByteArray::Create(ba, count);
        clone->SetData(clonedArray);
    }

    return FDO_SAFE_ADDREF(clone.p);
}
Last modified 10 years ago Last modified on Oct 17, 2007 3:36:51 PM