1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 ///
25 /// \file Sdf/AssetPathResolver.cpp
26 
27 #include "pxr/pxr.h"
28 #include "pxr/usd/sdf/assetPathResolver.h"
29 #include "pxr/usd/sdf/debugCodes.h"
30 #include "pxr/usd/sdf/fileFormat.h"
31 
32 #include "pxr/base/trace/trace.h"
33 #include "pxr/base/arch/systemInfo.h"
34 #include "pxr/usd/ar/assetInfo.h"
35 #include "pxr/usd/ar/packageUtils.h"
36 #include "pxr/usd/ar/resolver.h"
37 #include "pxr/base/tf/fileUtils.h"
38 #include "pxr/base/tf/pathUtils.h"
39 #include "pxr/base/tf/staticData.h"
40 #include "pxr/base/tf/staticTokens.h"
41 
42 #include <utility>
43 #include <vector>
44 
45 using std::make_pair;
46 using std::pair;
47 using std::string;
48 using std::vector;
49 
50 PXR_NAMESPACE_OPEN_SCOPE
51 
52 TF_DEFINE_PRIVATE_TOKENS(_Tokens,
53     ((AnonLayerPrefix,  "anon:"))
54     ((ArgsDelimiter, ":SDF_FORMAT_ARGS:"))
55     );
56 
57 bool
operator ==(const Sdf_AssetInfo & lhs,const Sdf_AssetInfo & rhs)58 operator==(
59     const Sdf_AssetInfo& lhs,
60     const Sdf_AssetInfo& rhs)
61 {
62     return (lhs.identifier == rhs.identifier)
63         && (lhs.resolvedPath == rhs.resolvedPath)
64         && (lhs.resolverContext == rhs.resolverContext)
65         && (lhs.assetInfo == rhs.assetInfo);
66 }
67 
68 bool
Sdf_CanCreateNewLayerWithIdentifier(const string & identifier,string * whyNot)69 Sdf_CanCreateNewLayerWithIdentifier(
70     const string& identifier,
71     string* whyNot)
72 {
73     if (identifier.empty()) {
74         if (whyNot) {
75             *whyNot = "cannot use empty identifier.";
76         }
77         return false;
78     }
79 
80     if (Sdf_IsAnonLayerIdentifier(identifier)) {
81         if (whyNot) {
82             *whyNot = "cannot use anonymous layer identifier.";
83         }
84         return false;
85     }
86 
87     if (Sdf_IdentifierContainsArguments(identifier)) {
88         if (whyNot) {
89             *whyNot = "cannot use arguments in the identifier.";
90         }
91         return false;
92     }
93 
94 #if AR_VERSION == 1
95     return ArGetResolver().CanCreateNewLayerWithIdentifier(identifier, whyNot);
96 #else
97     return true;
98 #endif
99 }
100 
101 ArResolvedPath
Sdf_ResolvePath(const string & layerPath,ArAssetInfo * assetInfo)102 Sdf_ResolvePath(
103     const string& layerPath,
104     ArAssetInfo* assetInfo)
105 {
106     TRACE_FUNCTION();
107 #if AR_VERSION == 1
108     return ArResolvedPath(
109         ArGetResolver().ResolveWithAssetInfo(layerPath, assetInfo));
110 #else
111     return ArGetResolver().Resolve(layerPath);
112 #endif
113 }
114 
115 bool
Sdf_CanWriteLayerToPath(const ArResolvedPath & resolvedPath)116 Sdf_CanWriteLayerToPath(
117     const ArResolvedPath& resolvedPath)
118 {
119 #if AR_VERSION == 1
120     return ArGetResolver().CanWriteLayerToPath(
121         resolvedPath, /* whyNot = */ nullptr);
122 #else
123     return ArGetResolver().CanWriteAssetToPath(
124         resolvedPath, /* whyNot = */ nullptr);
125 #endif
126 }
127 
128 ArResolvedPath
Sdf_ComputeFilePath(const string & layerPath,ArAssetInfo * assetInfo)129 Sdf_ComputeFilePath(
130     const string& layerPath,
131     ArAssetInfo* assetInfo)
132 {
133     TRACE_FUNCTION();
134 
135     ArResolvedPath resolvedPath = Sdf_ResolvePath(layerPath, assetInfo);
136     if (resolvedPath.empty()) {
137 #if AR_VERSION == 1
138         // If we can't resolve layerPath, it means no layer currently
139         // exists at that location. Compute the local path to figure
140         // out where this layer would go if we were to create a new
141         // one.
142         //
143         // However, we skip this for search paths since the real path
144         // is ambiguous if we can't resolve the search path above.
145         // This is important for layers with search path identifiers,
146         // because otherwise we may compute a confusing real path
147         // for these layers.
148         ArResolver& resolver = ArGetResolver();
149         if (!resolver.IsSearchPath(layerPath)) {
150             resolvedPath = ArResolvedPath(resolver.ComputeLocalPath(layerPath));
151         }
152 #else
153         // If we can't resolve layerPath, it means no layer currently
154         // exists at that location. Use ResolveForNewAsset to figure
155         // out where this layer would go if we were to create a new
156         // one.
157         ArResolver& resolver = ArGetResolver();
158         resolvedPath = resolver.ResolveForNewAsset(layerPath);
159 #endif
160     }
161 
162     return resolvedPath;
163 }
164 
165 Sdf_AssetInfo*
Sdf_ComputeAssetInfoFromIdentifier(const string & identifier,const string & filePath,const ArAssetInfo & inResolveInfo,const string & fileVersion)166 Sdf_ComputeAssetInfoFromIdentifier(
167     const string& identifier,
168     const string& filePath,
169     const ArAssetInfo& inResolveInfo,
170     const string& fileVersion)
171 {
172     // Allocate a new asset info object. The caller is responsible for
173     // managing the returned object.
174     Sdf_AssetInfo* assetInfo = new Sdf_AssetInfo;
175     ArAssetInfo resolveInfo = inResolveInfo;
176 
177     TF_DEBUG(SDF_ASSET).Msg(
178         "Sdf_ComputeAssetInfoFromIdentifier('%s', '%s', '%s')\n",
179         identifier.c_str(),
180         filePath.c_str(),
181         fileVersion.c_str());
182 
183     if (Sdf_IsAnonLayerIdentifier(identifier)) {
184         // If the identifier is an anonymous layer identifier, don't
185         // normalize, and also don't set any of the other assetInfo fields.
186         // Anonymous layers do not have repository, overlay, or real paths.
187         assetInfo->identifier = identifier;
188     } else {
189 #if AR_VERSION == 1
190         assetInfo->identifier = ArGetResolver()
191             .ComputeNormalizedPath(identifier);
192 #else
193         assetInfo->identifier = identifier;
194 #endif
195 
196         string layerPath, arguments;
197         Sdf_SplitIdentifier(assetInfo->identifier, &layerPath, &arguments);
198         if (filePath.empty()) {
199             assetInfo->resolvedPath =
200                 Sdf_ComputeFilePath(layerPath, &resolveInfo);
201         } else {
202             assetInfo->resolvedPath = ArResolvedPath(filePath);
203         }
204 
205 #if AR_VERSION == 1
206         assetInfo->resolvedPath = ArResolvedPath(
207             Sdf_CanonicalizeRealPath(assetInfo->resolvedPath));
208 
209         ArGetResolver().UpdateAssetInfo(
210             assetInfo->identifier, assetInfo->resolvedPath, fileVersion,
211             &resolveInfo);
212 #else
213         resolveInfo = ArGetResolver().GetAssetInfo(
214             layerPath, assetInfo->resolvedPath);
215 #endif
216     }
217 
218     assetInfo->resolverContext =
219         ArGetResolver().GetCurrentContext();
220     assetInfo->assetInfo = resolveInfo;
221 
222     TF_DEBUG(SDF_ASSET).Msg("Sdf_ComputeAssetInfoFromIdentifier:\n"
223         "  assetInfo->identifier = '%s'\n"
224         "  assetInfo->resolvedPath = '%s'\n"
225         "  assetInfo->repoPath = '%s'\n"
226         "  assetInfo->assetName = '%s'\n"
227         "  assetInfo->version = '%s'\n",
228         assetInfo->identifier.c_str(),
229         assetInfo->resolvedPath.GetPathString().c_str(),
230         resolveInfo.repoPath.c_str(),
231         resolveInfo.assetName.c_str(),
232         resolveInfo.version.c_str());
233 
234     return assetInfo;
235 }
236 
237 string
Sdf_ComputeAnonLayerIdentifier(const string & identifierTemplate,const SdfLayer * layer)238 Sdf_ComputeAnonLayerIdentifier(
239     const string& identifierTemplate,
240     const SdfLayer* layer)
241 {
242     TF_VERIFY(layer);
243     return TfStringPrintf(identifierTemplate.c_str(), layer);
244 }
245 
246 bool
Sdf_IsAnonLayerIdentifier(const string & identifier)247 Sdf_IsAnonLayerIdentifier(
248     const string& identifier)
249 {
250     return TfStringStartsWith(identifier,
251         _Tokens->AnonLayerPrefix.GetString());
252 }
253 
254 string
Sdf_GetAnonLayerDisplayName(const string & identifier)255 Sdf_GetAnonLayerDisplayName(
256     const string& identifier)
257 {
258     // We want to find the second occurence of ':', traversing from the left,
259     // in our identifier which is of the form anon:0x4rfs23:displayName
260     auto fst = std::find(identifier.begin(), identifier.end(), ':');
261     if (fst == identifier.end()) {
262         return std::string();
263     }
264 
265     auto snd = std::find(fst + 1, identifier.end(), ':');
266     if (snd == identifier.end()) {
267         return std::string();
268     }
269 
270     return identifier.substr(std::distance(identifier.begin(), snd) + 1);
271 }
272 
273 string
Sdf_GetAnonLayerIdentifierTemplate(const string & tag)274 Sdf_GetAnonLayerIdentifierTemplate(
275     const string& tag)
276 {
277     string idTag = tag.empty() ? tag : TfStringTrim(tag);
278     return _Tokens->AnonLayerPrefix.GetString() + "%p" +
279         (idTag.empty() ? idTag : ":" + idTag);
280 }
281 
282 string
Sdf_CreateIdentifier(const string & layerPath,const string & arguments)283 Sdf_CreateIdentifier(
284     const string& layerPath,
285     const string& arguments)
286 {
287     return layerPath + arguments;
288 }
289 
290 // XXX: May need to escape characters in the arguments map
291 // when encoding arguments and unescape then when decoding?
292 static string
Sdf_EncodeArguments(const SdfLayer::FileFormatArguments & args)293 Sdf_EncodeArguments(
294     const SdfLayer::FileFormatArguments& args)
295 {
296     const char* delimiter = _Tokens->ArgsDelimiter.GetText();
297     string argString;
298     for (const auto& entry : args) {
299         argString += delimiter;
300         argString += entry.first;
301         argString += '=';
302         argString += entry.second;
303 
304         delimiter = "&";
305     }
306 
307     return argString;
308 }
309 
310 static bool
Sdf_DecodeArguments(const string & argString,SdfLayer::FileFormatArguments * args)311 Sdf_DecodeArguments(
312     const string& argString,
313     SdfLayer::FileFormatArguments* args)
314 {
315     if (argString.empty() || argString.size() == _Tokens->ArgsDelimiter.size()) {
316         args->clear();
317         return true;
318     }
319 
320     const size_t argStringLength = argString.size();
321     if (!TF_VERIFY(argStringLength > _Tokens->ArgsDelimiter.size())) {
322         return false;
323     }
324 
325     SdfLayer::FileFormatArguments tmpArgs;
326 
327     size_t startIdx = _Tokens->ArgsDelimiter.size();
328     while (startIdx < argStringLength) {
329         const size_t eqIdx = argString.find('=', startIdx);
330         if (eqIdx == string::npos) {
331             TF_CODING_ERROR(
332                 "Invalid file format arguments: %s", argString.c_str());
333             return false;
334         }
335 
336         const string key = argString.substr(startIdx, eqIdx - startIdx);
337         startIdx = eqIdx + 1;
338 
339         const size_t sepIdx = argString.find('&', startIdx);
340         if (sepIdx == string::npos) {
341             tmpArgs[key] = argString.substr(startIdx);
342             break;
343         }
344         else {
345             tmpArgs[key] = argString.substr(startIdx, sepIdx - startIdx);
346             startIdx = sepIdx + 1;
347         }
348     }
349 
350     args->swap(tmpArgs);
351     return true;
352 }
353 
354 string
Sdf_CreateIdentifier(const string & layerPath,const SdfLayer::FileFormatArguments & arguments)355 Sdf_CreateIdentifier(
356     const string& layerPath,
357     const SdfLayer::FileFormatArguments& arguments)
358 {
359     return layerPath + Sdf_EncodeArguments(arguments);
360 }
361 
362 bool
Sdf_SplitIdentifier(const string & identifier,string * layerPath,string * arguments)363 Sdf_SplitIdentifier(
364     const string& identifier,
365     string* layerPath,
366     string* arguments)
367 {
368     size_t argPos = identifier.find(_Tokens->ArgsDelimiter.GetString());
369     if (argPos == string::npos) {
370         argPos = identifier.size();
371     }
372 
373     *layerPath = string(identifier, 0, argPos);
374     *arguments = string(identifier, argPos, string::npos);
375     return true;
376 }
377 
378 bool
Sdf_SplitIdentifier(const string & identifier,string * layerPath,SdfLayer::FileFormatArguments * args)379 Sdf_SplitIdentifier(
380     const string& identifier,
381     string* layerPath,
382     SdfLayer::FileFormatArguments* args)
383 {
384     string tmpLayerPath, tmpArgs;
385     if (!Sdf_SplitIdentifier(identifier, &tmpLayerPath, &tmpArgs)) {
386         return false;
387     }
388 
389     if (!Sdf_DecodeArguments(tmpArgs, args)) {
390         return false;
391     }
392 
393     layerPath->swap(tmpLayerPath);
394     return true;
395 }
396 
397 bool
Sdf_IdentifierContainsArguments(const string & identifier)398 Sdf_IdentifierContainsArguments(
399     const string& identifier)
400 {
401     return identifier.find(_Tokens->ArgsDelimiter.GetString()) != string::npos;
402 }
403 
404 string
Sdf_GetLayerDisplayName(const string & identifier)405 Sdf_GetLayerDisplayName(
406     const string& identifier)
407 {
408 
409     string layerPath, arguments;
410     Sdf_SplitIdentifier(identifier, &layerPath, &arguments);
411 
412     if (Sdf_IsAnonLayerIdentifier(layerPath)) {
413         return Sdf_GetAnonLayerDisplayName(layerPath);
414     }
415 
416     // If the layer path is a package-relative path, we want
417     // the basename of the outermost package combined with
418     // the packaged path. For example, given:
419     //    "/tmp/asset.package[sub/dir/file.sdf]",
420     // we want:
421     //    "asset.package[sub/dir/file.sdf]".
422     if (ArIsPackageRelativePath(layerPath)) {
423         std::pair<std::string, std::string> packagePath =
424             ArSplitPackageRelativePathOuter(layerPath);
425         packagePath.first = TfGetBaseName(packagePath.first);
426         return ArJoinPackageRelativePath(packagePath);
427     }
428 
429     return TfGetBaseName(layerPath);
430 }
431 
432 string
Sdf_GetExtension(const string & identifier)433 Sdf_GetExtension(
434     const string& identifier)
435 {
436     // Split the identifier to get the layer asset path without
437     // any file format arguments.
438     string assetPath;
439     std::string dummyArgs;
440     Sdf_SplitIdentifier(identifier, &assetPath, &dummyArgs);
441 
442     if (Sdf_IsAnonLayerIdentifier(assetPath)) {
443         // Strip off the "anon:0x...:" portion of the anonymous layer
444         // identifier and look for an extension in the remainder. This
445         // allows clients to create anonymous layers using tags that
446         // match their asset path scheme and retrieve the extension
447         // via ArResolver.
448         assetPath = Sdf_GetAnonLayerDisplayName(assetPath);
449     }
450 
451     // XXX: If the asset path is a dot file (e.g. ".sdf"), we append
452     // a temporary name so that the path we pass to Ar is not
453     // interpreted as a directory name. This is legacy behavior that
454     // should be fixed.
455     if (!assetPath.empty() && assetPath[0] == '.') {
456         assetPath = "temp_file_name" + assetPath;
457     }
458 
459     return ArGetResolver().GetExtension(assetPath);
460 }
461 
462 bool
Sdf_IsPackageOrPackagedLayer(const SdfLayerHandle & layer)463 Sdf_IsPackageOrPackagedLayer(
464     const SdfLayerHandle& layer)
465 {
466     return Sdf_IsPackageOrPackagedLayer(
467         layer->GetFileFormat(), layer->GetIdentifier());
468 }
469 
470 bool
Sdf_IsPackageOrPackagedLayer(const SdfFileFormatConstPtr & fileFormat,const std::string & identifier)471 Sdf_IsPackageOrPackagedLayer(
472     const SdfFileFormatConstPtr& fileFormat,
473     const std::string& identifier)
474 {
475     return fileFormat->IsPackage() || ArIsPackageRelativePath(identifier);
476 }
477 
478 string
Sdf_CanonicalizeRealPath(const string & realPath)479 Sdf_CanonicalizeRealPath(
480     const string& realPath)
481 {
482     // Use the given realPath as-is if it's a relative path, otherwise
483     // use TfAbsPath to compute a platform-dependent real path.
484     //
485     // XXX: This method needs to be re-examined as we move towards a
486     // less filesystem-dependent implementation.
487 
488     // If realPath is a package-relative path, absolutize just the
489     // outer path; the packaged path has a specific format defined in
490     // Ar that we don't want to modify.
491     if (ArIsPackageRelativePath(realPath)) {
492         pair<string, string> packagePath =
493             ArSplitPackageRelativePathOuter(realPath);
494         return TfIsRelativePath(packagePath.first) ?
495             realPath : ArJoinPackageRelativePath(
496                 TfAbsPath(packagePath.first), packagePath.second);
497     }
498 
499     return TfIsRelativePath(realPath) ? realPath : TfAbsPath(realPath);
500 }
501 
502 PXR_NAMESPACE_CLOSE_SCOPE
503