1 /** @file packagefeed.cpp  Links to loaded packages.
2  *
3  * @authors Copyright (c) 2014-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  *
5  * @par License
6  * LGPL: http://www.gnu.org/licenses/lgpl.html
7  *
8  * <small>This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or (at your
11  * option) any later version. This program is distributed in the hope that it
12  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
14  * General Public License for more details. You should have received a copy of
15  * the GNU Lesser General Public License along with this program; if not, see:
16  * http://www.gnu.org/licenses</small>
17  */
18 
19 #include "de/PackageFeed"
20 #include "de/LinkFile"
21 #include "de/ArchiveFolder"
22 #include "de/PackageLoader"
23 #include "de/FS"
24 
25 namespace de {
26 
27 static String const VAR_LINK_PACKAGE_ID("link.package");
28 
DENG2_PIMPL(PackageFeed)29 DENG2_PIMPL(PackageFeed)
30 {
31     PackageLoader &loader;
32     LinkMode linkMode;
33     Filter filter;
34 
35     Impl(Public *i, PackageLoader &ldr, LinkMode lm)
36         : Base(i), loader(ldr), linkMode(lm)
37     {}
38 
39     File *linkToPackage(Package &pkg, String const &linkName, Folder const &folder)
40     {
41         /// @todo Resolve conflicts: replace, ignore, or fail. -jk
42 
43         if (folder.has(linkName)) return nullptr; // Already there, keep the existing link.
44 
45         // Packages can be optionally filtered from the feed.
46         if (filter && !filter(pkg)) return nullptr;
47 
48         // Create a link to the loaded package's file.
49         String name;
50         if (linkMode == LinkIdentifier)
51         {
52             name = linkName;
53         }
54         else
55         {
56             name = Package::versionedIdentifierForFile(pkg.file());
57         }
58         LinkFile *link = LinkFile::newLinkToFile(pkg.file(), name);
59 
60         // We will decide on pruning this.
61         link->setOriginFeed(thisPublic);
62 
63         // Identifier also in metadata.
64         link->objectNamespace().addText(VAR_LINK_PACKAGE_ID, pkg.identifier());
65 
66         return link;
67     }
68 
69     PopulatedFiles populate(Folder const &folder)
70     {
71         PopulatedFiles populated;
72         DENG2_FOR_EACH_CONST(PackageLoader::LoadedPackages, i, loader.loadedPackages())
73         {
74             Package *pkg = i.value();
75             populated << linkToPackage(*pkg, i.key(), folder);
76 
77             // Also link it under its possible alias identifier (for variants).
78             if (pkg->objectNamespace().has(Package::VAR_PACKAGE_ALIAS))
79             {
80                 populated << linkToPackage(*pkg, pkg->objectNamespace()
81                                            .gets(Package::VAR_PACKAGE_ALIAS), folder);
82             }
83 
84             // Link each contained asset, too.
85             foreach (String ident, pkg->assets())
86             {
87                 populated << linkToPackage(*pkg, "asset." + ident, folder);
88             }
89         }
90         return populated;
91     }
92 };
93 
PackageFeed(PackageLoader & loader,LinkMode linkMode)94 PackageFeed::PackageFeed(PackageLoader &loader, LinkMode linkMode)
95     : d(new Impl(this, loader, linkMode))
96 {}
97 
setFilter(Filter filter)98 void PackageFeed::setFilter(Filter filter)
99 {
100     d->filter = filter;
101 }
102 
loader()103 PackageLoader &PackageFeed::loader()
104 {
105     return d->loader;
106 }
107 
description() const108 String PackageFeed::description() const
109 {
110     return "loaded packages";
111 }
112 
populate(Folder const & folder)113 Feed::PopulatedFiles PackageFeed::populate(Folder const &folder)
114 {
115     return d->populate(folder);
116 }
117 
prune(File & file) const118 bool PackageFeed::prune(File &file) const
119 {
120     if (LinkFile const *link = maybeAs<LinkFile>(file))
121     {
122         // Links to unloaded packages should be pruned.
123         if (!d->loader.isLoaded(link->objectNamespace().gets(VAR_LINK_PACKAGE_ID)))
124             return true;
125 
126         //if (Folder const *pkg = maybeAs<Folder>(link->target()))
127         {
128             // Links to unloaded packages should be pruned.
129             //if (!d->loader.isLoaded(*pkg)) return true;
130 
131 //            qDebug() << "Link:" << link->description() << link->status().modifiedAt.asText();
132 //            qDebug() << " tgt:" << link->target().description() << link->target().status().modifiedAt.asText();
133 
134             // Package has been modified, should be pruned.
135             if (link->status() != link->target().status()) return true;
136         }
137     }
138     return false; // Don't prune.
139 }
140 
141 } // namespace de
142