// // Copyright 2016 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #ifndef PXR_USD_SDF_FILE_IO_COMMON_H #define PXR_USD_SDF_FILE_IO_COMMON_H /// \file sdf/fileIO_Common.h #include "pxr/pxr.h" #include "pxr/usd/sdf/attributeSpec.h" #include "pxr/usd/sdf/declareHandles.h" #include "pxr/usd/sdf/layer.h" #include "pxr/usd/sdf/layerOffset.h" #include "pxr/usd/sdf/path.h" #include "pxr/usd/sdf/primSpec.h" #include "pxr/usd/sdf/reference.h" #include "pxr/usd/sdf/relationshipSpec.h" #include "pxr/usd/sdf/schema.h" #include "pxr/usd/sdf/types.h" #include "pxr/usd/sdf/variantSetSpec.h" #include "pxr/base/vt/dictionary.h" #include "pxr/base/vt/value.h" #include "pxr/base/tf/stringUtils.h" #include "pxr/base/tf/token.h" #include #include #include #include #include #include PXR_NAMESPACE_OPEN_SCOPE //////////////////////////////////////////////////////////////////////// // Simple FileIO Utilities class Sdf_FileIOUtility { public: // === Stream output helpers // Non-formatted string output static void Puts(std::ostream &out, size_t indent, const std::string &str); // Printf-style formatted string output static void Write(std::ostream &out, size_t indent, const char *fmt, ...); static bool OpenParensIfNeeded(std::ostream &out, bool didParens, bool multiLine); static void CloseParensIfNeeded(std::ostream &out, size_t indent, bool didParens, bool multiLine); static void WriteQuotedString(std::ostream &out, size_t indent, const std::string &str); static void WriteAssetPath(std::ostream &out, size_t indent, const std::string &str); static void WriteDefaultValue(std::ostream &out, size_t indent, VtValue value); static void WriteSdfPath(std::ostream &out, size_t indent, const SdfPath &path); static bool WriteNameVector(std::ostream &out, size_t indent, const std::vector &vec); static bool WriteNameVector(std::ostream &out, size_t indent, const std::vector &vec); static bool WriteTimeSamples(std::ostream &out, size_t indent, const SdfPropertySpec &); static bool WriteRelocates(std::ostream &out, size_t indent, bool multiLine, const SdfRelocatesMap &reloMap); static void WriteDictionary(std::ostream &out, size_t indent, bool multiLine, const VtDictionary &dictionary, bool stringValuesOnly=false); template static void WriteListOp(std::ostream &out, size_t indent, const TfToken& fieldName, const SdfListOp& listOp); static void WriteLayerOffset(std::ostream &out, size_t indent, bool multiline, const SdfLayerOffset& offset); // === String production and transformation helpers /// Quote \p str, adding quotes before and after and escaping /// unprintable characters and the quote character itself. If /// the string contains newlines it's quoted with triple quotes /// and the newlines are not escaped. static std::string Quote(const std::string &str); static std::string Quote(const TfToken &token); // Create a string from a value static std::string StringFromVtValue(const VtValue &value); // Convert enums to a strings for use in menva syntax. // Note that in some cases we use empty strings to represent the // default values of these enums. static const char* Stringify( SdfPermission val ); static const char* Stringify( SdfSpecifier val ); static const char* Stringify( SdfVariability val ); private: // Helper types to write a VtDictionary so that its keys are ordered. struct _StringLessThan { bool operator()(const std::string *lhs, const std::string *rhs) const { return *lhs < *rhs; } }; typedef std::map _OrderedDictionary; static void _WriteDictionary(std::ostream &out, size_t indent, bool multiLine, _OrderedDictionary &dictionary, bool stringValuesOnly); }; //////////////////////////////////////////////////////////////////////// // Helpers for determining if a field should be included in a spec's // metadata section. struct Sdf_IsMetadataField { Sdf_IsMetadataField(const SdfSpecType specType) : _specDef(SdfSchema::GetInstance().GetSpecDefinition(specType)) { } bool operator()(const TfToken& field) const { // Allow fields tagged explicitly as metadata, or fields // that are invalid, as these may be unrecognized plugin // metadata fields. In this case, there may be a string // representation that needs to be written out. return (!_specDef->IsValidField(field) || _specDef->IsMetadataField(field)); } const SdfSchema::SpecDefinition* _specDef; }; //////////////////////////////////////////////////////////////////////// static bool Sdf_WritePrimPreamble( const SdfPrimSpec &prim, std::ostream &out, size_t indent) { SdfSpecifier spec = prim.GetSpecifier(); bool writeTypeName = true; if (!SdfIsDefiningSpecifier(spec)) { // For non-defining specifiers, we write typeName only if we have // a setting. writeTypeName = prim.HasField(SdfFieldKeys->TypeName); } TfToken typeName; if (writeTypeName) { typeName = prim.GetTypeName(); if (typeName == SdfTokens->AnyTypeToken){ typeName = TfToken(); } } Sdf_FileIOUtility::Write( out, indent, "%s%s%s ", Sdf_FileIOUtility::Stringify(spec), !typeName.IsEmpty() ? " " : "", !typeName.IsEmpty() ? typeName.GetText() : "" ); Sdf_FileIOUtility::WriteQuotedString( out, 0, prim.GetName().c_str() ); return true; } template static bool Sdf_WriteIfListOp( std::ostream& out, size_t indent, const TfToken& field, const VtValue& value) { if (value.IsHolding()) { Sdf_FileIOUtility::WriteListOp( out, indent, field, value.UncheckedGet()); return true; } return false; } static void Sdf_WriteSimpleField( std::ostream &out, size_t indent, const SdfSpec& spec, const TfToken& field) { const VtValue& value = spec.GetField(field); if (Sdf_WriteIfListOp(out, indent, field, value) || Sdf_WriteIfListOp(out, indent, field, value) || Sdf_WriteIfListOp(out, indent, field, value) || Sdf_WriteIfListOp(out, indent, field, value) || Sdf_WriteIfListOp(out, indent, field, value) || Sdf_WriteIfListOp(out, indent, field, value)) { return; } bool isUnregisteredValue = value.IsHolding(); if (isUnregisteredValue) { // The value boxed inside a SdfUnregisteredValue can either be a // std::string, a VtDictionary, or an SdfUnregisteredValueListOp. const VtValue &boxedValue = value.Get().GetValue(); if (boxedValue.IsHolding()) { Sdf_FileIOUtility::WriteListOp( out, indent, field, boxedValue.UncheckedGet()); } else { Sdf_FileIOUtility::Write(out, indent, "%s = ", field.GetText()); if (boxedValue.IsHolding()) { Sdf_FileIOUtility::WriteDictionary(out, indent, true, boxedValue.Get()); } else if (boxedValue.IsHolding()) { Sdf_FileIOUtility::Write(out, 0, "%s\n", boxedValue.Get().c_str()); } } return; } Sdf_FileIOUtility::Write(out, indent, "%s = ", field.GetText()); if (value.IsHolding()) { Sdf_FileIOUtility::WriteDictionary(out, indent, true, value.Get()); } else if (value.IsHolding()) { Sdf_FileIOUtility::Write(out, 0, "%s\n", TfStringify(value.Get()).c_str()); } else { Sdf_FileIOUtility::Write(out, 0, "%s\n", Sdf_FileIOUtility::StringFromVtValue(value).c_str()); } } // Predicate for determining fields that should be included in a // prim's metadata section. struct Sdf_IsPrimMetadataField : public Sdf_IsMetadataField { Sdf_IsPrimMetadataField() : Sdf_IsMetadataField(SdfSpecTypePrim) { } bool operator()(const TfToken& field) const { // Typename is registered as metadata for a prim, but is written // outside the metadata section. if (field == SdfFieldKeys->TypeName) { return false; } return (Sdf_IsMetadataField::operator()(field) || field == SdfFieldKeys->Payload || field == SdfFieldKeys->References || field == SdfFieldKeys->Relocates || field == SdfFieldKeys->InheritPaths || field == SdfFieldKeys->Specializes || field == SdfFieldKeys->VariantSetNames || field == SdfFieldKeys->VariantSelection); } }; static bool Sdf_WritePrimMetadata( const SdfPrimSpec &prim, std::ostream &out, size_t indent) { // Partition this prim's fields so that all fields to write out are // in the range [fields.begin(), metadataFieldsEnd). TfTokenVector fields = prim.ListFields(); TfTokenVector::iterator metadataFieldsEnd = std::partition(fields.begin(), fields.end(), Sdf_IsPrimMetadataField()); // Comment isn't tagged as a metadata field but gets special cased // because it wants to be at the top of the metadata section. std::string comment = prim.GetComment(); bool hasComment = !comment.empty(); bool didParens = false; // As long as there's anything to write in the metadata section, we'll // always use the multi-line format. bool multiLine = hasComment || (fields.begin() != metadataFieldsEnd); // Write comment at the top of the metadata section for readability. if (hasComment) { didParens = Sdf_FileIOUtility::OpenParensIfNeeded(out, didParens, multiLine); Sdf_FileIOUtility::WriteQuotedString(out, indent+1, comment); Sdf_FileIOUtility::Puts(out, 0, "\n"); } // Write out remaining fields in the metadata section in dictionary-sorted // order. std::sort(fields.begin(), metadataFieldsEnd, TfDictionaryLessThan()); for (TfTokenVector::const_iterator fieldIt = fields.begin(); fieldIt != metadataFieldsEnd; ++fieldIt) { didParens = Sdf_FileIOUtility::OpenParensIfNeeded(out, didParens, multiLine); const TfToken& field = *fieldIt; if (field == SdfFieldKeys->Documentation) { Sdf_FileIOUtility::Puts(out, indent+1, "doc = "); Sdf_FileIOUtility::WriteQuotedString(out, 0, prim.GetDocumentation()); Sdf_FileIOUtility::Puts(out, 0, "\n"); } else if (field == SdfFieldKeys->Permission) { if (multiLine) { Sdf_FileIOUtility::Write(out, indent+1, "permission = %s\n", Sdf_FileIOUtility::Stringify(prim.GetPermission()) ); } else { Sdf_FileIOUtility::Write(out, 0, "permission = %s", Sdf_FileIOUtility::Stringify(prim.GetPermission()) ); } } else if (field == SdfFieldKeys->SymmetryFunction) { Sdf_FileIOUtility::Write(out, multiLine ? indent+1 : 0, "symmetryFunction = %s%s", prim.GetSymmetryFunction().GetText(), multiLine ? "\n" : ""); } else if (field == SdfFieldKeys->Payload) { const VtValue v = prim.GetField(field); if (!Sdf_WriteIfListOp( out, indent+1, TfToken("payload"), v)) { TF_CODING_ERROR( "'%s' field holding unexpected type '%s'", field.GetText(), v.GetTypeName().c_str()); } } else if (field == SdfFieldKeys->References) { const VtValue v = prim.GetField(field); if (!Sdf_WriteIfListOp( out, indent+1, TfToken("references"), v)) { TF_CODING_ERROR( "'%s' field holding unexpected type '%s'", field.GetText(), v.GetTypeName().c_str()); } } else if (field == SdfFieldKeys->VariantSetNames) { SdfVariantSetNamesProxy variantSetNameList = prim.GetVariantSetNameList(); if (variantSetNameList.IsExplicit()) { // Explicit list SdfVariantSetNamesProxy::ListProxy setNames = variantSetNameList.GetExplicitItems(); Sdf_FileIOUtility::Puts(out, indent+1, "variantSets = "); Sdf_FileIOUtility::WriteNameVector(out, indent+1, setNames); Sdf_FileIOUtility::Puts(out, 0, "\n"); } else { // List operations SdfVariantSetNamesProxy::ListProxy setNames = variantSetNameList.GetDeletedItems(); if (!setNames.empty()) { Sdf_FileIOUtility::Puts(out, indent+1, "delete variantSets = "); Sdf_FileIOUtility::WriteNameVector(out, indent+1, setNames); Sdf_FileIOUtility::Puts(out, 0, "\n"); } setNames = variantSetNameList.GetAddedItems(); if (!setNames.empty()) { Sdf_FileIOUtility::Puts(out, indent+1, "add variantSets = "); Sdf_FileIOUtility::WriteNameVector(out, indent+1, setNames); Sdf_FileIOUtility::Puts(out, 0, "\n"); } setNames = variantSetNameList.GetPrependedItems(); if (!setNames.empty()) { Sdf_FileIOUtility::Puts(out, indent+1, "prepend variantSets = "); Sdf_FileIOUtility::WriteNameVector(out, indent+1, setNames); Sdf_FileIOUtility::Puts(out, 0, "\n"); } setNames = variantSetNameList.GetAppendedItems(); if (!setNames.empty()) { Sdf_FileIOUtility::Puts(out, indent+1, "append variantSets = "); Sdf_FileIOUtility::WriteNameVector(out, indent+1, setNames); Sdf_FileIOUtility::Puts(out, 0, "\n"); } setNames = variantSetNameList.GetOrderedItems(); if (!setNames.empty()) { Sdf_FileIOUtility::Puts(out, indent+1, "reorder variantSets = "); Sdf_FileIOUtility::WriteNameVector(out, indent+1, setNames); Sdf_FileIOUtility::Puts(out, 0, "\n"); } } } else if (field == SdfFieldKeys->InheritPaths) { const VtValue v = prim.GetField(field); if (!Sdf_WriteIfListOp( out, indent+1, TfToken("inherits"), v)) { TF_CODING_ERROR( "'%s' field holding unexpected type '%s'", field.GetText(), v.GetTypeName().c_str()); } } else if (field == SdfFieldKeys->Specializes) { const VtValue v = prim.GetField(field); if (!Sdf_WriteIfListOp( out, indent+1, TfToken("specializes"), v)) { TF_CODING_ERROR( "'%s' field holding unexpected type '%s'", field.GetText(), v.GetTypeName().c_str()); } } else if (field == SdfFieldKeys->Relocates) { // Relativize all paths in the relocates. SdfRelocatesMap result; SdfPath primPath = prim.GetPath(); SdfRelocatesMap finalRelocates; const SdfRelocatesMapProxy relocates = prim.GetRelocates(); TF_FOR_ALL(mapIt, relocates) { finalRelocates[mapIt->first.MakeRelativePath(primPath)] = mapIt->second.MakeRelativePath(primPath); } Sdf_FileIOUtility::WriteRelocates( out, indent+1, multiLine, finalRelocates); } else if (field == SdfFieldKeys->PrefixSubstitutions) { VtDictionary prefixSubstitutions = prim.GetPrefixSubstitutions(); Sdf_FileIOUtility::Puts(out, indent+1, "prefixSubstitutions = "); Sdf_FileIOUtility::WriteDictionary(out, indent+1, multiLine, prefixSubstitutions, /* stringValuesOnly = */ true ); } else if (field == SdfFieldKeys->SuffixSubstitutions) { VtDictionary suffixSubstitutions = prim.GetSuffixSubstitutions(); Sdf_FileIOUtility::Puts(out, indent+1, "suffixSubstitutions = "); Sdf_FileIOUtility::WriteDictionary(out, indent+1, multiLine, suffixSubstitutions, /* stringValuesOnly = */ true ); } else if (field == SdfFieldKeys->VariantSelection) { SdfVariantSelectionMap refVariants = prim.GetVariantSelections(); if (refVariants.size() > 0) { VtDictionary dictionary; TF_FOR_ALL(it, refVariants) { dictionary[it->first] = VtValue(it->second); } Sdf_FileIOUtility::Puts(out, indent+1, "variants = "); Sdf_FileIOUtility::WriteDictionary(out, indent+1, multiLine, dictionary); } } else { Sdf_WriteSimpleField(out, indent+1, prim, field); } } // end for each field Sdf_FileIOUtility::CloseParensIfNeeded(out, indent, didParens, multiLine); return true; } namespace { struct _SortByNameThenType { template bool operator()(T const &lhs, T const &rhs) const { // If the names are identical, order by spectype. This puts Attributes // before Relationships (if identically named). std::string const &lhsName = lhs->GetName(); std::string const &rhsName = rhs->GetName(); return (lhsName == rhsName && lhs->GetSpecType() < rhs->GetSpecType()) || TfDictionaryLessThan()(lhsName, rhsName); } }; } static bool Sdf_WritePrimProperties( const SdfPrimSpec &prim, std::ostream &out, size_t indent) { std::vector properties = prim.GetProperties().values_as >(); std::sort(properties.begin(), properties.end(), _SortByNameThenType()); TF_FOR_ALL(it, properties) { (*it)->WriteToStream(out, indent+1); } return true; } static bool Sdf_WritePrimNamespaceReorders( const SdfPrimSpec &prim, std::ostream &out, size_t indent) { const std::vector& propertyNames = prim.GetPropertyOrder(); if ( propertyNames.size() > 1 ) { Sdf_FileIOUtility::Puts( out, indent+1, "reorder properties = " ); Sdf_FileIOUtility::WriteNameVector( out, indent+1, propertyNames ); Sdf_FileIOUtility::Puts( out, 0, "\n" ); } const std::vector& childrenNames = prim.GetNameChildrenOrder(); if ( childrenNames.size() > 1 ) { Sdf_FileIOUtility::Puts( out, indent+1, "reorder nameChildren = " ); Sdf_FileIOUtility::WriteNameVector( out, indent+1, childrenNames ); Sdf_FileIOUtility::Puts( out, 0, "\n" ); } return true; } static bool Sdf_WritePrimChildren( const SdfPrimSpec &prim, std::ostream &out, size_t indent) { bool newline = false; TF_FOR_ALL(i, prim.GetNameChildren()) { if (newline) Sdf_FileIOUtility::Puts(out, 0, "\n"); else newline = true; (*i)->WriteToStream(out, indent+1); } return true; } static bool Sdf_WritePrimVariantSets( const SdfPrimSpec &prim, std::ostream &out, size_t indent) { SdfVariantSetsProxy variantSets = prim.GetVariantSets(); if (variantSets) { TF_FOR_ALL(it, variantSets) { SdfVariantSetSpecHandle variantSet = it->second; variantSet->WriteToStream(out, indent+1); } } return true; } static bool Sdf_WritePrimBody( const SdfPrimSpec &prim, std::ostream &out, size_t indent) { Sdf_WritePrimNamespaceReorders( prim, out, indent ); Sdf_WritePrimProperties( prim, out, indent ); if (!prim.GetProperties().empty() && !prim.GetNameChildren().empty()) Sdf_FileIOUtility::Puts(out, 0, "\n"); Sdf_WritePrimChildren( prim, out, indent ); Sdf_WritePrimVariantSets( prim, out, indent ); return true; } static inline bool Sdf_WritePrim( const SdfPrimSpec &prim, std::ostream &out, size_t indent) { Sdf_WritePrimPreamble( prim, out, indent ); Sdf_WritePrimMetadata( prim, out, indent ); Sdf_FileIOUtility::Puts(out, 0, "\n"); Sdf_FileIOUtility::Puts(out, indent, "{\n"); Sdf_WritePrimBody( prim, out, indent ); Sdf_FileIOUtility::Puts(out, indent, "}\n"); return true; } static bool Sdf_WriteConnectionStatement( std::ostream &out, size_t indent, const SdfConnectionsProxy::ListProxy &connections, const std::string &opStr, const std::string &variabilityStr, const std::string &typeStr, const std::string &nameStr, const SdfAttributeSpec* attrOwner) { Sdf_FileIOUtility::Write(out, indent, "%s%s%s %s.connect = ", opStr.c_str(), variabilityStr.c_str(), typeStr.c_str(), nameStr.c_str()); if (connections.size() == 0) { Sdf_FileIOUtility::Puts(out, 0, "None\n"); } else if (connections.size() == 1) { Sdf_FileIOUtility::WriteSdfPath(out, 0, connections.front()); Sdf_FileIOUtility::Puts(out, 0, "\n"); } else { Sdf_FileIOUtility::Puts(out, 0, "[\n"); TF_FOR_ALL(it, connections) { Sdf_FileIOUtility::WriteSdfPath(out, indent+1, (*it)); Sdf_FileIOUtility::Puts(out, 0, ",\n"); } Sdf_FileIOUtility::Puts(out, indent, "]\n"); } return true; } static bool Sdf_WriteConnectionList( std::ostream &out, size_t indent, const SdfConnectionsProxy &connList, const std::string &variabilityStr, const std::string &typeStr, const std::string &nameStr, const SdfAttributeSpec *attrOwner) { if (connList.IsExplicit()) { SdfConnectionsProxy::ListProxy vec = connList.GetExplicitItems(); Sdf_WriteConnectionStatement(out, indent, vec, "", variabilityStr, typeStr, nameStr, attrOwner); } else { SdfConnectionsProxy::ListProxy vec = connList.GetDeletedItems(); if (!vec.empty()) { Sdf_WriteConnectionStatement(out, indent, vec, "delete ", variabilityStr, typeStr, nameStr, NULL); } vec = connList.GetAddedItems(); if (!vec.empty()) { Sdf_WriteConnectionStatement(out, indent, vec, "add ", variabilityStr, typeStr, nameStr, attrOwner); } vec = connList.GetPrependedItems(); if (!vec.empty()) { Sdf_WriteConnectionStatement(out, indent, vec, "prepend ", variabilityStr, typeStr, nameStr, attrOwner); } vec = connList.GetAppendedItems(); if (!vec.empty()) { Sdf_WriteConnectionStatement(out, indent, vec, "append ", variabilityStr, typeStr, nameStr, attrOwner); } vec = connList.GetOrderedItems(); if (!vec.empty()) { Sdf_WriteConnectionStatement(out, indent, vec, "reorder ", variabilityStr, typeStr, nameStr, NULL); } } return true; } // Predicate for determining fields that should be included in an // attribute's metadata section. struct Sdf_IsAttributeMetadataField : public Sdf_IsMetadataField { Sdf_IsAttributeMetadataField() : Sdf_IsMetadataField(SdfSpecTypeAttribute) { } bool operator()(const TfToken& field) const { return (Sdf_IsMetadataField::operator()(field) || field == SdfFieldKeys->DisplayUnit); } }; static inline bool Sdf_WriteAttribute( const SdfAttributeSpec &attr, std::ostream &out, size_t indent) { std::string variabilityStr = Sdf_FileIOUtility::Stringify( attr.GetVariability() ); if (!variabilityStr.empty()) variabilityStr += ' '; bool hasComment = !attr.GetComment().empty(); bool hasDefault = attr.HasField(SdfFieldKeys->Default); bool hasCustomDeclaration = attr.IsCustom(); bool hasConnections = attr.HasField(SdfFieldKeys->ConnectionPaths); bool hasTimeSamples = attr.HasField(SdfFieldKeys->TimeSamples); std::string typeName = SdfValueTypeNames->GetSerializationName(attr.GetTypeName()).GetString(); // Partition this attribute's fields so that all fields to write in the // metadata section are in the range [fields.begin(), metadataFieldsEnd). TfTokenVector fields = attr.ListFields(); TfTokenVector::iterator metadataFieldsEnd = std::partition( fields.begin(), fields.end(), Sdf_IsAttributeMetadataField()); // As long as there's anything to write in the metadata section, we'll // always use the multi-line format. bool hasInfo = hasComment || (fields.begin() != metadataFieldsEnd); bool multiLine = hasInfo; bool didParens = false; // Write the basic line if we have info or a default or if we // have nothing else to write. if (hasInfo || hasDefault || hasCustomDeclaration || (!hasConnections && !hasTimeSamples)) { VtValue value; if(hasDefault) value = attr.GetDefaultValue(); Sdf_FileIOUtility::Write( out, indent, "%s%s%s %s", (hasCustomDeclaration ? "custom " : ""), variabilityStr.c_str(), typeName.c_str(), attr.GetName().c_str() ); // If we have a default value, write it... if (!value.IsEmpty()) { Sdf_FileIOUtility::WriteDefaultValue(out, indent, value); } // Write comment at the top of the metadata section for readability. if (hasComment) { didParens = Sdf_FileIOUtility::OpenParensIfNeeded(out, didParens, multiLine); Sdf_FileIOUtility::WriteQuotedString(out, indent+1, attr.GetComment()); Sdf_FileIOUtility::Puts(out, 0, "\n"); } // Write out remaining fields in the metadata section in // dictionary-sorted order. std::sort(fields.begin(), metadataFieldsEnd, TfDictionaryLessThan()); for (TfTokenVector::const_iterator fieldIt = fields.begin(); fieldIt != metadataFieldsEnd; ++fieldIt) { didParens = Sdf_FileIOUtility::OpenParensIfNeeded(out, didParens, multiLine); const TfToken& field = *fieldIt; if (field == SdfFieldKeys->Documentation) { Sdf_FileIOUtility::Puts(out, indent+1, "doc = "); Sdf_FileIOUtility::WriteQuotedString(out, 0, attr.GetDocumentation()); Sdf_FileIOUtility::Puts(out, 0, "\n"); } else if (field == SdfFieldKeys->Permission) { Sdf_FileIOUtility::Write(out, multiLine ? indent+1 : 0, "permission = %s%s", Sdf_FileIOUtility::Stringify(attr.GetPermission()), multiLine ? "\n" : ""); } else if (field == SdfFieldKeys->SymmetryFunction) { Sdf_FileIOUtility::Write(out, multiLine ? indent+1 : 0, "symmetryFunction = %s%s", attr.GetSymmetryFunction().GetText(), multiLine ? "\n" : ""); } else if (field == SdfFieldKeys->DisplayUnit) { Sdf_FileIOUtility::Write(out, multiLine ? indent+1 : 0, "displayUnit = %s%s", SdfGetNameForUnit(attr.GetDisplayUnit()).c_str(), multiLine ? "\n" : ""); } else { Sdf_WriteSimpleField(out, indent+1, attr, field); } } // end for each field Sdf_FileIOUtility::CloseParensIfNeeded(out, indent, didParens, multiLine); Sdf_FileIOUtility::Puts(out, 0, "\n"); } if (hasTimeSamples) { Sdf_FileIOUtility::Write(out, indent, "%s%s %s.timeSamples = {\n", variabilityStr.c_str(), typeName.c_str(), attr.GetName().c_str() ); Sdf_FileIOUtility::WriteTimeSamples(out, indent, attr); Sdf_FileIOUtility::Puts(out, indent, "}\n"); } if (hasConnections) { Sdf_WriteConnectionList(out, indent, attr.GetConnectionPathList(), variabilityStr, typeName, attr.GetName(), &attr); } return true; } enum Sdf_WriteFlag { Sdf_WriteFlagDefault = 0, Sdf_WriteFlagAttributes = 1, Sdf_WriteFlagNoLastNewline = 2, }; inline Sdf_WriteFlag operator |(Sdf_WriteFlag a, Sdf_WriteFlag b) { return (Sdf_WriteFlag)(static_cast(a) | static_cast(b)); } static bool Sdf_WriteRelationshipTargetList( const SdfRelationshipSpec &rel, const SdfTargetsProxy::ListProxy &targetPaths, std::ostream &out, size_t indent, Sdf_WriteFlag flags) { if (targetPaths.size() > 1) { Sdf_FileIOUtility::Write(out, 0," = [\n"); ++indent; } else { Sdf_FileIOUtility::Write(out, 0," = "); } for (size_t i=0; i < targetPaths.size(); ++i) { if (targetPaths.size() > 1) { Sdf_FileIOUtility::Write(out, indent, ""); } Sdf_FileIOUtility::WriteSdfPath( out, 0, targetPaths[i] ); if (targetPaths.size() > 1) { Sdf_FileIOUtility::Write(out, 0,",\n"); } } if (targetPaths.size() > 1) { --indent; Sdf_FileIOUtility::Write(out, indent, "]"); } if (!(flags & Sdf_WriteFlagNoLastNewline)) { Sdf_FileIOUtility::Write(out, 0,"\n"); } return true; } // Predicate for determining fields that should be included in an // relationship's metadata section. struct Sdf_IsRelationshipMetadataField : public Sdf_IsMetadataField { Sdf_IsRelationshipMetadataField() : Sdf_IsMetadataField(SdfSpecTypeRelationship) { } bool operator()(const TfToken& field) const { return Sdf_IsMetadataField::operator()(field); } }; static inline bool Sdf_WriteRelationship( const SdfRelationshipSpec &rel, std::ostream &out, size_t indent) { // When a new metadata field is added to the spec, it will be automatically // written out generically, so you probably don't need to add a special case // here. If you need to special-case the output of a metadata field, you will // also need to prevent the automatic output by adding the token inside // Sdf_GetGenericRelationshipMetadataFields(). // // These special cases below were all kept to prevent reordering in existing // menva files, which would create noise in file diffs. bool hasComment = !rel.GetComment().empty(); bool hasTargets = rel.HasField(SdfFieldKeys->TargetPaths); bool hasDefaultValue = rel.HasField(SdfFieldKeys->Default); bool hasTimeSamples = rel.HasField(SdfFieldKeys->TimeSamples); bool hasCustom = rel.IsCustom(); // Partition this attribute's fields so that all fields to write in the // metadata section are in the range [fields.begin(), metadataFieldsEnd). TfTokenVector fields = rel.ListFields(); TfTokenVector::iterator metadataFieldsEnd = std::partition( fields.begin(), fields.end(), Sdf_IsRelationshipMetadataField()); bool hasInfo = hasComment || (fields.begin() != metadataFieldsEnd); bool multiLine = hasInfo; bool didParens = false; bool hasExplicitTargets = false; bool hasTargetListOps = false; if (hasTargets) { SdfTargetsProxy targetPathList = rel.GetTargetPathList(); hasExplicitTargets = targetPathList.IsExplicit() && targetPathList.HasKeys(); hasTargetListOps = !targetPathList.IsExplicit() && targetPathList.HasKeys(); } // If relationship is a varying relationship, use varying keyword. bool isVarying = (rel.GetVariability() == SdfVariabilityVarying); std::string varyingStr = isVarying ? "varying " : ""; // the space in "varying " is required... // Write the basic line if we have info or a default (i.e. explicit // targets) or if we have nothing else to write and we're not custom if (hasInfo || (hasTargets && hasExplicitTargets) || (!hasTargetListOps && !rel.IsCustom())) { if (hasCustom) { Sdf_FileIOUtility::Write( out, indent, "custom %srel %s", varyingStr.c_str(), rel.GetName().c_str() ); } else { Sdf_FileIOUtility::Write( out, indent, "%srel %s", varyingStr.c_str(), rel.GetName().c_str() ); } if (hasTargets && hasExplicitTargets) { SdfTargetsProxy targetPathList = rel.GetTargetPathList(); SdfTargetsProxy::ListProxy targetPaths = targetPathList.GetExplicitItems(); if (targetPaths.size() == 0) { Sdf_FileIOUtility::Write(out, 0, " = None"); } else { // Write explicit targets Sdf_WriteRelationshipTargetList(rel, targetPaths, out, indent, Sdf_WriteFlagAttributes | Sdf_WriteFlagNoLastNewline); } } // Write comment at the top of the metadata section for readability. if (hasComment) { didParens = Sdf_FileIOUtility::OpenParensIfNeeded(out, didParens, multiLine); Sdf_FileIOUtility::WriteQuotedString(out, indent+1, rel.GetComment()); Sdf_FileIOUtility::Write(out, 0, "\n"); } // Write out remaining fields in the metadata section in // dictionary-sorted order. std::sort(fields.begin(), metadataFieldsEnd, TfDictionaryLessThan()); for (TfTokenVector::const_iterator fieldIt = fields.begin(); fieldIt != metadataFieldsEnd; ++fieldIt) { didParens = Sdf_FileIOUtility::OpenParensIfNeeded(out, didParens, multiLine); const TfToken& field = *fieldIt; if (field == SdfFieldKeys->Documentation) { Sdf_FileIOUtility::Write(out, indent+1, "doc = "); Sdf_FileIOUtility::WriteQuotedString(out, 0, rel.GetDocumentation()); Sdf_FileIOUtility::Write(out, 0, "\n"); } else if (field == SdfFieldKeys->Permission) { if (multiLine) { Sdf_FileIOUtility::Write(out, indent+1, "permission = %s\n", Sdf_FileIOUtility::Stringify(rel.GetPermission())); } else { Sdf_FileIOUtility::Write(out, 0, "permission = %s", Sdf_FileIOUtility::Stringify(rel.GetPermission())); } } else if (field == SdfFieldKeys->SymmetryFunction) { Sdf_FileIOUtility::Write(out, multiLine ? indent+1 : 0, "symmetryFunction = %s%s", rel.GetSymmetryFunction().GetText(), multiLine ? "\n" : ""); } else { Sdf_WriteSimpleField(out, indent+1, rel, field); } } // end for each field Sdf_FileIOUtility::CloseParensIfNeeded(out, indent, didParens, multiLine); Sdf_FileIOUtility::Write(out, 0,"\n"); } else if (hasCustom) { // If we did not write out the "basic" line AND we are custom, // we need to add a custom decl line, because we won't include // custom in any of the output below Sdf_FileIOUtility::Write( out, indent, "custom %srel %s\n", varyingStr.c_str(), rel.GetName().c_str() ); } if (hasTargets && hasTargetListOps) { // Write deleted targets SdfTargetsProxy targetPathList = rel.GetTargetPathList(); SdfTargetsProxy::ListProxy targetPaths = targetPathList.GetDeletedItems(); if (!targetPaths.empty()) { Sdf_FileIOUtility::Write( out, indent, "delete %srel %s", varyingStr.c_str(), rel.GetName().c_str()); Sdf_WriteRelationshipTargetList(rel, targetPaths, out, indent, Sdf_WriteFlagDefault); } // Write added targets targetPaths = targetPathList.GetAddedItems(); if (!targetPaths.empty()) { Sdf_FileIOUtility::Write( out, indent, "add %srel %s", varyingStr.c_str(), rel.GetName().c_str()); Sdf_WriteRelationshipTargetList(rel, targetPaths, out, indent, Sdf_WriteFlagAttributes); } targetPaths = targetPathList.GetPrependedItems(); if (!targetPaths.empty()) { Sdf_FileIOUtility::Write( out, indent, "prepend %srel %s", varyingStr.c_str(), rel.GetName().c_str()); Sdf_WriteRelationshipTargetList(rel, targetPaths, out, indent, Sdf_WriteFlagAttributes); } targetPaths = targetPathList.GetAppendedItems(); if (!targetPaths.empty()) { Sdf_FileIOUtility::Write( out, indent, "append %srel %s", varyingStr.c_str(), rel.GetName().c_str()); Sdf_WriteRelationshipTargetList(rel, targetPaths, out, indent, Sdf_WriteFlagAttributes); } // Write ordered targets targetPaths = targetPathList.GetOrderedItems(); if (!targetPaths.empty()) { Sdf_FileIOUtility::Write( out, indent, "reorder %srel %s", varyingStr.c_str(), rel.GetName().c_str()); Sdf_WriteRelationshipTargetList(rel, targetPaths, out, indent, Sdf_WriteFlagDefault); } } if (hasTimeSamples) { Sdf_FileIOUtility::Write(out, indent, "%srel %s.timeSamples = {\n", varyingStr.c_str(), rel.GetName().c_str()); Sdf_FileIOUtility::WriteTimeSamples(out, indent, rel); Sdf_FileIOUtility::Puts(out, indent, "}\n"); } // Write out the default value for the relationship if we have one... if (hasDefaultValue) { VtValue value = rel.GetDefaultValue(); if (!value.IsEmpty()) { Sdf_FileIOUtility::Write(out, indent, "%srel %s.default = ", varyingStr.c_str(), rel.GetName().c_str()); Sdf_FileIOUtility::WriteDefaultValue(out, 0, value); Sdf_FileIOUtility::Puts(out, indent, "\n"); } } return true; } PXR_NAMESPACE_CLOSE_SCOPE #endif // PXR_USD_SDF_FILE_IO_COMMON_H