// // 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. // #include "pxr/pxr.h" #include "pxr/usd/sdf/spec.h" #include "pxr/usd/sdf/specType.h" #include "pxr/usd/sdf/changeBlock.h" #include "pxr/usd/sdf/cleanupTracker.h" #include "pxr/usd/sdf/fileFormat.h" #include "pxr/usd/sdf/layer.h" #include "pxr/usd/sdf/path.h" #include "pxr/usd/sdf/proxyTypes.h" #include "pxr/usd/sdf/schema.h" #include "pxr/base/arch/hints.h" #include "pxr/base/tf/type.h" #include PXR_NAMESPACE_OPEN_SCOPE SDF_DEFINE_BASE_SPEC(SdfSchema, SdfSpec); // // SdfSpec // SdfSpec &SdfSpec::operator=(const SdfSpec &other) { _id = other._id; return *this; } SdfSpec::~SdfSpec() { } const SdfSchemaBase& SdfSpec::GetSchema() const { return _id->GetLayer()->GetSchema(); } SdfLayerHandle SdfSpec::GetLayer() const { return _id ? _id->GetLayer() : SdfLayerHandle(); } SdfPath SdfSpec::GetPath() const { return _id ? _id->GetPath() : SdfPath(); } SdfSpecType SdfSpec::GetSpecType() const { // We can't retrieve an object type for a dormant spec. Sdf_Identity const *idPtr = _id.get(); if (!idPtr) { return SdfSpecTypeUnknown; } SdfLayerHandle const &layer = idPtr->GetLayer(); SdfPath const &path = idPtr->GetPath(); SdfLayer const *layerPtr = get_pointer(layer); return layerPtr ? layerPtr->GetSpecType(path) : SdfSpecTypeUnknown; } bool SdfSpec::IsDormant() const { // If we have no id, we're dormant. if (!_id) return true; // If our path is invalid, we must be dormant. SdfPath const &path = _id->GetPath(); if (path.IsEmpty()) return true; // If our layer is invalid, we're dormant. Otherwise we're dormant if the // layer has no spec at this path. SdfLayerHandle const &layer = _id->GetLayer(); return ARCH_UNLIKELY(!layer) || !layer->HasSpec(path); } bool SdfSpec::PermissionToEdit() const { return _id ? GetLayer()->PermissionToEdit() : false; } std::vector SdfSpec::ListFields() const { if (!_id) { return std::vector(); } const SdfPath & path = _id->GetPath(); return GetLayer()->ListFields(path); } bool SdfSpec::_HasField(const TfToken& name, SdfAbstractDataValue* value) const { return _id ? _id->GetLayer()->HasField(_id->GetPath(), name, value) : false; } bool SdfSpec::HasField(const TfToken &name) const { return _id ? _id->GetLayer()->HasField(_id->GetPath(), name) : false; } VtValue SdfSpec::GetField(const TfToken &name) const { if (Sdf_Identity const *idPtr = _id.get()) { return idPtr->GetLayer()->GetField(idPtr->GetPath(), name); } return VtValue(); } bool SdfSpec::SetField(const TfToken & name, const VtValue& value) { if (!_id) { return false; } _id->GetLayer()->SetField(_id->GetPath(), name, value); return true; } bool SdfSpec::ClearField(const TfToken & name) { if (!_id) { return false; } _id->GetLayer()->EraseField(_id->GetPath(), name); return true; } std::vector SdfSpec::GetMetaDataInfoKeys() const { return GetSchema().GetMetadataFields(GetSpecType()); } TfToken SdfSpec::GetMetaDataDisplayGroup(TfToken const &key) const { if (const SdfSchema::SpecDefinition* specDef = GetSchema().GetSpecDefinition(GetSpecType())) { return specDef->GetMetadataFieldDisplayGroup(key); } return TfToken(); } std::vector SdfSpec::ListInfoKeys() const { const SdfSchemaBase& schema = GetSchema(); if (const SdfSchema::SpecDefinition* specDef = schema.GetSpecDefinition(GetSpecType())) { std::vector result; const std::vector valueFields = specDef->GetFields(); TF_FOR_ALL(field, valueFields) { // Skip fields holding children. if (const SdfSchema::FieldDefinition* fieldDef = schema.GetFieldDefinition(*field)) { if (fieldDef->HoldsChildren()) continue; } if (HasInfo(*field)) { result.push_back(*field); } } return result; } return std::vector(); } // Helper function that performs some common checks to determine if a given // field is editable via the Info API. static bool _CanEditInfoOnSpec( const TfToken& key, SdfSpecType specType, const SdfSchemaBase& schema, const SdfSchema::FieldDefinition* fieldDef, const char* editType) { if (!fieldDef) { TF_CODING_ERROR("Cannot %s value for unknown field '%s'", editType, key.GetText()); return false; } if (fieldDef->IsReadOnly()) { TF_CODING_ERROR("Cannot %s value for read-only field '%s'", editType, key.GetText()); return false; } if (!schema.IsValidFieldForSpec(fieldDef->GetName(), specType)) { TF_CODING_ERROR("Field '%s' is not valid for spec type %s", key.GetText(), TfStringify(specType).c_str()); return false; } return true; } void SdfSpec::SetInfo( const TfToken & key, const VtValue &value ) { // Perform some validation on the field being modified to ensure we don't // author any invalid scene description. Note this function will issue // coding errors as needed. const SdfSchemaBase& schema = GetSchema(); const SdfSchema::FieldDefinition* fieldDef = schema.GetFieldDefinition(key); if (!_CanEditInfoOnSpec(key, GetSpecType(), schema, fieldDef, "set")) { return; } // Attempt to cast the given value to the type specified for the field // in the schema. const VtValue fallback = fieldDef->GetFallbackValue(); const VtValue castValue = (fallback.IsEmpty() ? value : VtValue::CastToTypeOf(value, fallback)); if (castValue.IsEmpty()) { TF_CODING_ERROR("Cannot set field '%s' of type '%s' to provided value " "'%s' because the value is an incompatible type '%s', " "on spec <%s>", key.GetText(), fallback.GetTypeName().c_str(), TfStringify(value).c_str(), value.GetTypeName().c_str(), GetPath().GetString().c_str()); return; } // XXX: // There is a hole here that could lead to problems. For fields whose // value types are container-ish (e.g., SdfListOp, std::vector, std::map) // Sd tries to detect if the container is empty and clears the field // if it is. We don't (yet) have a great way to detect this situation // here. // // The reason this can lead to problems is because HasInfo(...) relies // solely on whether a field has been set. So, we might run into cases // like this: // // prim.SetInfo('nameChildrenOrder', []) // prim.HasInfo('nameChildrenOrder') // --> True, even though this ought to return False because there aren't // any name children specified. // // If this becomes a problem, the interim workaround is to use ClearInfo // to clear a field instead of setting it to an empty value. SetField(key, castValue); } void SdfSpec::SetInfoDictionaryValue(const TfToken &dictionaryKey, const TfToken &entryKey, const VtValue &value) { // XXX: Instead of copying, modifying, then re-setting the dictionary, // could this use the proxy to edit the dictionary directly? VtDictionary dict = SdfDictionaryProxy(SdfCreateHandle(this), dictionaryKey); if (value.IsEmpty()) { dict.erase(entryKey); } else { dict[entryKey] = value; } SetInfo(dictionaryKey, VtValue(dict)); } bool SdfSpec::HasInfo( const TfToken & key ) const { // It's not an error to call this method with a key that isn't registered // with the schema. The file writer needs to be able to query for the // presence of metadata fields registered via plugins, such as wizardData. // It might be the case that no plugins are registered when the file writer // is called, so Plug won't know about the plugin that defines wizardData // and wizardData will not be in the schema. We still want to be able to // write out files in this case. return HasField(key); } void SdfSpec::ClearInfo( const TfToken & key ) { // Perform some validation to ensure we allow the clearing of this field // via the Info API. Note this function will issue coding errors as needed. const SdfSchemaBase& schema = GetSchema(); const SdfSchema::FieldDefinition* fieldDef = schema.GetFieldDefinition(key); if (!_CanEditInfoOnSpec(key, GetSpecType(), schema, fieldDef, "clear")) { return; } SdfChangeBlock block; ClearField(key); // In case this spec is made inert when the info is removed, schedule it to // be cleaned up (if the caller has enabled cleanup tracking) Sdf_CleanupTracker::GetInstance().AddSpecIfTracking(*this); } TfType SdfSpec::GetTypeForInfo( const TfToken & key ) const { return GetSchema().GetFallback(key).GetType(); } const VtValue& SdfSpec::GetFallbackForInfo( const TfToken & key ) const { static VtValue empty; const SdfSchemaBase& schema = GetSchema(); const SdfSchema::FieldDefinition* def = schema.GetFieldDefinition(key); if (!def) { TF_CODING_ERROR("Unknown field '%s'", key.GetText()); return empty; } const SdfSpecType objType = GetSpecType(); const SdfSchema::SpecDefinition* specDef = schema.GetSpecDefinition(objType); if (!specDef || !specDef->IsMetadataField(key)) { TF_CODING_ERROR("Non-metadata key '%s' for type %s", key.GetText(), TfStringify(objType).c_str()); return empty; } return def->GetFallbackValue(); } bool SdfSpec::WriteToStream(std::ostream& out, size_t indent) const { return GetLayer()->GetFileFormat()->WriteToStream(*this, out, indent); } bool SdfSpec::IsInert(bool ignoreChildren) const { return _id ? GetLayer()->_IsInert(_id->GetPath(), ignoreChildren) : false; } VtValue SdfSpec::GetInfo(const TfToken& key) const { const SdfSchema::FieldDefinition* def = GetSchema().GetFieldDefinition(key); if (!def) { TF_CODING_ERROR("Invalid info key: %s",key.GetText()); return VtValue(); } VtValue value = GetField(key); return !value.IsEmpty() ? value : def->GetFallbackValue(); } bool SdfSpec::operator==(const SdfSpec& rhs) const { return (_id == rhs._id); } bool SdfSpec::operator<(const SdfSpec& rhs) const { return (_id < rhs._id); } bool SdfSpec::_MoveSpec(const SdfPath &oldPath, const SdfPath &newPath) const { return GetLayer()->_MoveSpec(oldPath, newPath); } bool SdfSpec::_DeleteSpec(const SdfPath &path) { return GetLayer()->_DeleteSpec(path); } PXR_NAMESPACE_CLOSE_SCOPE