Commit 3d0191215d84d28fe63c2b5d78c0f710fd089c13

Authored by m-holger
1 parent a148b870

Refactor QPDF_Dictionary

Move all dictionary specific methods to BaseDictionary. Create new subclass
Dictionary to allow QPDFObjectHandle to access the moved methods.
include/qpdf/ObjectHandle.hh
... ... @@ -22,9 +22,10 @@
22 22  
23 23 #include <qpdf/Constants.h>
24 24 #include <qpdf/DLL.h>
  25 +#include <qpdf/QPDFObjGen.hh>
25 26 #include <qpdf/Types.h>
26 27  
27   -#include <qpdf/QPDFObjGen.hh>
  28 +#include <cstdint>
28 29  
29 30 class QPDF_Dictionary;
30 31 class QPDFObject;
... ... @@ -32,6 +33,11 @@ class QPDFObjectHandle;
32 33  
33 34 namespace qpdf
34 35 {
  36 + class Dictionary;
  37 + class BaseDictionary;
  38 +
  39 + enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4};
  40 +
35 41 // Basehandle is only used as a base-class for QPDFObjectHandle like classes. Currently the only
36 42 // methods exposed in public API are operators to convert derived objects to QPDFObjectHandle,
37 43 // QPDFObjGen and bool.
... ... @@ -44,6 +50,8 @@ namespace qpdf
44 50  
45 51 // The rest of the header file is for qpdf internal use only.
46 52  
  53 + inline qpdf_object_type_e type_code() const;
  54 +
47 55 protected:
48 56 BaseHandle() = default;
49 57 BaseHandle(std::shared_ptr<QPDFObject> const& obj) :
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -304,7 +304,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle
304 304 // other one changes color." This does not perform a structural comparison of the contents of
305 305 // the objects.
306 306 QPDF_DLL
307   - bool isSameObjectAs(QPDFObjectHandle const&) const noexcept;
  307 + bool isSameObjectAs(QPDFObjectHandle const&) const;
308 308  
309 309 // Return type code and type name of underlying object. These are useful for doing rapid type
310 310 // tests (like switch statements) or for testing and debugging.
... ... @@ -1355,6 +1355,8 @@ class QPDFObjectHandle final: public qpdf::BaseHandle
1355 1355  
1356 1356 void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const;
1357 1357  
  1358 + inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const;
  1359 +
1358 1360 private:
1359 1361 QPDF_Array* asArray() const;
1360 1362 QPDF_Bool* asBool() const;
... ...
libqpdf/QPDFObjectHandle.cc
1   -#include <qpdf/QPDFObjectHandle.hh>
  1 +#include <qpdf/QPDFObjectHandle_private.hh>
2 2  
3 3 #include <qpdf/BufferInputSource.hh>
4 4 #include <qpdf/JSON_writer.hh>
... ... @@ -38,7 +38,6 @@
38 38 #include <stdexcept>
39 39  
40 40 using namespace std::literals;
41   -
42 41 using namespace qpdf;
43 42  
44 43 BaseHandle::
... ... @@ -230,7 +229,7 @@ LastChar::getLastChar()
230 229 }
231 230  
232 231 bool
233   -QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const noexcept
  232 +QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const& rhs) const
234 233 {
235 234 return this->obj == rhs.obj;
236 235 }
... ... @@ -986,9 +985,9 @@ QPDFObjectHandle::ditems()
986 985 bool
987 986 QPDFObjectHandle::hasKey(std::string const& key) const
988 987 {
989   - auto dict = asDictionary();
  988 + auto dict = as_dictionary(strict);
990 989 if (dict) {
991   - return dict->hasKey(key);
  990 + return dict.hasKey(key);
992 991 } else {
993 992 typeWarning("dictionary", "returning false for a key containment request");
994 993 QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
... ... @@ -999,14 +998,13 @@ QPDFObjectHandle::hasKey(std::string const&amp; key) const
999 998 QPDFObjectHandle
1000 999 QPDFObjectHandle::getKey(std::string const& key) const
1001 1000 {
1002   - if (auto dict = asDictionary()) {
1003   - return dict->getKey(key);
1004   - } else {
1005   - typeWarning("dictionary", "returning null for attempted key retrieval");
1006   - QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
1007   - static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;
1008   - return QPDF_Null::create(obj, msg, "");
  1001 + if (auto dict = as_dictionary(strict)) {
  1002 + return dict.getKey(key);
1009 1003 }
  1004 + typeWarning("dictionary", "returning null for attempted key retrieval");
  1005 + QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
  1006 + static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;
  1007 + return QPDF_Null::create(obj, msg, "");
1010 1008 }
1011 1009  
1012 1010 QPDFObjectHandle
... ... @@ -1018,29 +1016,23 @@ QPDFObjectHandle::getKeyIfDict(std::string const&amp; key) const
1018 1016 std::set<std::string>
1019 1017 QPDFObjectHandle::getKeys() const
1020 1018 {
1021   - std::set<std::string> result;
1022   - auto dict = asDictionary();
1023   - if (dict) {
1024   - result = dict->getKeys();
1025   - } else {
1026   - typeWarning("dictionary", "treating as empty");
1027   - QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
  1019 + if (auto dict = as_dictionary(strict)) {
  1020 + return dict.getKeys();
1028 1021 }
1029   - return result;
  1022 + typeWarning("dictionary", "treating as empty");
  1023 + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
  1024 + return {};
1030 1025 }
1031 1026  
1032 1027 std::map<std::string, QPDFObjectHandle>
1033 1028 QPDFObjectHandle::getDictAsMap() const
1034 1029 {
1035   - std::map<std::string, QPDFObjectHandle> result;
1036   - auto dict = asDictionary();
1037   - if (dict) {
1038   - result = dict->getAsMap();
1039   - } else {
1040   - typeWarning("dictionary", "treating as empty");
1041   - QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
  1030 + if (auto dict = as_dictionary(strict)) {
  1031 + return dict.getAsMap();
1042 1032 }
1043   - return result;
  1033 + typeWarning("dictionary", "treating as empty");
  1034 + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap");
  1035 + return {};
1044 1036 }
1045 1037  
1046 1038 // Array and Name accessors
... ... @@ -1221,14 +1213,13 @@ QPDFObjectHandle::getUniqueResourceName(
1221 1213 void
1222 1214 QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value)
1223 1215 {
1224   - auto dict = asDictionary();
1225   - if (dict) {
  1216 + if (auto dict = as_dictionary(strict)) {
1226 1217 checkOwnership(value);
1227   - dict->replaceKey(key, value);
1228   - } else {
1229   - typeWarning("dictionary", "ignoring key replacement request");
1230   - QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
  1218 + dict.replaceKey(key, value);
  1219 + return;
1231 1220 }
  1221 + typeWarning("dictionary", "ignoring key replacement request");
  1222 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
1232 1223 }
1233 1224  
1234 1225 QPDFObjectHandle
... ... @@ -1249,22 +1240,20 @@ QPDFObjectHandle::replaceKeyAndGetOld(std::string const&amp; key, QPDFObjectHandle c
1249 1240 void
1250 1241 QPDFObjectHandle::removeKey(std::string const& key)
1251 1242 {
1252   - auto dict = asDictionary();
1253   - if (dict) {
1254   - dict->removeKey(key);
1255   - } else {
1256   - typeWarning("dictionary", "ignoring key removal request");
1257   - QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
  1243 + if (auto dict = as_dictionary(strict)) {
  1244 + dict.removeKey(key);
  1245 + return;
1258 1246 }
  1247 + typeWarning("dictionary", "ignoring key removal request");
  1248 + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey");
1259 1249 }
1260 1250  
1261 1251 QPDFObjectHandle
1262 1252 QPDFObjectHandle::removeKeyAndGetOld(std::string const& key)
1263 1253 {
1264 1254 auto result = QPDFObjectHandle::newNull();
1265   - auto dict = asDictionary();
1266   - if (dict) {
1267   - result = dict->getKey(key);
  1255 + if (auto dict = as_dictionary(strict)) {
  1256 + result = dict.getKey(key);
1268 1257 }
1269 1258 removeKey(key);
1270 1259 return result;
... ... @@ -2115,9 +2104,9 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams)
2115 2104 this->obj = QPDF_Array::create(items);
2116 2105 } else if (isDictionary()) {
2117 2106 std::map<std::string, QPDFObjectHandle> items;
2118   - auto dict = asDictionary();
  2107 + auto dict = as_dictionary(strict);
2119 2108 for (auto const& key: getKeys()) {
2120   - items[key] = dict->getKey(key);
  2109 + items[key] = dict.getKey(key);
2121 2110 items[key].makeDirect(visited, stop_at_streams);
2122 2111 }
2123 2112 this->obj = QPDF_Dictionary::create(items);
... ...
libqpdf/QPDF_Dictionary.cc
1 1 #include <qpdf/QPDF_Dictionary.hh>
2 2  
3 3 #include <qpdf/JSON_writer.hh>
4   -#include <qpdf/QPDFObjectHandle.hh>
  4 +#include <qpdf/QPDFObjectHandle_private.hh>
5 5 #include <qpdf/QPDFObject_private.hh>
6 6 #include <qpdf/QPDF_Name.hh>
7   -#include <qpdf/QPDF_Null.hh>
  7 +#include <qpdf/QTC.hh>
8 8 #include <qpdf/QUtil.hh>
9 9  
10 10 using namespace std::literals;
... ... @@ -96,31 +96,45 @@ QPDF_Dictionary::writeJSON(int json_version, JSON::Writer&amp; p)
96 96 p.writeEnd('}');
97 97 }
98 98  
  99 +QPDF_Dictionary*
  100 +BaseDictionary::dict() const
  101 +{
  102 + if (obj) {
  103 + if (auto d = obj->as<QPDF_Dictionary>()) {
  104 + return d;
  105 + }
  106 + }
  107 + throw std::runtime_error("Expected a dictionary but found a non-dictionary object");
  108 + return nullptr; // unreachable
  109 +}
  110 +
99 111 bool
100   -QPDF_Dictionary::hasKey(std::string const& key)
  112 +BaseDictionary::hasKey(std::string const& key) const
101 113 {
102   - return ((this->items.count(key) > 0) && (!this->items[key].isNull()));
  114 + auto d = dict();
  115 + return d->items.count(key) > 0 && !d->items[key].isNull();
103 116 }
104 117  
105 118 QPDFObjectHandle
106   -QPDF_Dictionary::getKey(std::string const& key)
  119 +BaseDictionary::getKey(std::string const& key) const
107 120 {
  121 + auto d = dict();
  122 +
108 123 // PDF spec says fetching a non-existent key from a dictionary returns the null object.
109   - auto item = this->items.find(key);
110   - if (item != this->items.end()) {
  124 + auto item = d->items.find(key);
  125 + if (item != d->items.end()) {
111 126 // May be a null object
112 127 return item->second;
113   - } else {
114   - static auto constexpr msg = " -> dictionary key $VD"sv;
115   - return QPDF_Null::create(shared_from_this(), msg, key);
116 128 }
  129 + static auto constexpr msg = " -> dictionary key $VD"sv;
  130 + return QPDF_Null::create(obj, msg, key);
117 131 }
118 132  
119 133 std::set<std::string>
120   -QPDF_Dictionary::getKeys()
  134 +BaseDictionary::getKeys()
121 135 {
122 136 std::set<std::string> result;
123   - for (auto& iter: this->items) {
  137 + for (auto& iter: dict()->items) {
124 138 if (!iter.second.isNull()) {
125 139 result.insert(iter.first);
126 140 }
... ... @@ -129,28 +143,29 @@ QPDF_Dictionary::getKeys()
129 143 }
130 144  
131 145 std::map<std::string, QPDFObjectHandle> const&
132   -QPDF_Dictionary::getAsMap() const
  146 +BaseDictionary::getAsMap() const
133 147 {
134   - return this->items;
  148 + return dict()->items;
135 149 }
136 150  
137 151 void
138   -QPDF_Dictionary::replaceKey(std::string const& key, QPDFObjectHandle value)
  152 +BaseDictionary::removeKey(std::string const& key)
139 153 {
140   - if (value.isNull() && !value.isIndirect()) {
141   - // The PDF spec doesn't distinguish between keys with null values and missing keys. Allow
142   - // indirect nulls which are equivalent to a dangling reference, which is permitted by the
143   - // spec.
144   - removeKey(key);
145   - } else {
146   - // add or replace value
147   - this->items[key] = value;
148   - }
  154 + // no-op if key does not exist
  155 + dict()->items.erase(key);
149 156 }
150 157  
151 158 void
152   -QPDF_Dictionary::removeKey(std::string const& key)
  159 +BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value)
153 160 {
154   - // no-op if key does not exist
155   - this->items.erase(key);
  161 + auto d = dict();
  162 + if (value.isNull() && !value.isIndirect()) {
  163 + // The PDF spec doesn't distinguish between keys with null values and missing keys.
  164 + // Allow indirect nulls which are equivalent to a dangling reference, which is
  165 + // permitted by the spec.
  166 + d->items.erase(key);
  167 + } else {
  168 + // add or replace value
  169 + d->items[key] = value;
  170 + }
156 171 }
... ...
libqpdf/qpdf/QPDFObjectHandle_private.hh 0 โ†’ 100644
  1 +#ifndef OBJECTHANDLE_PRIVATE_HH
  2 +#define OBJECTHANDLE_PRIVATE_HH
  3 +
  4 +#include <qpdf/QPDFObjectHandle.hh>
  5 +
  6 +#include <qpdf/QPDFObject_private.hh>
  7 +#include <qpdf/QPDF_Dictionary.hh>
  8 +
  9 +namespace qpdf
  10 +{
  11 + // BaseDictionary is only used as a base class. It does not contain any methods exposed in the
  12 + // public API.
  13 + class BaseDictionary: public BaseHandle
  14 + {
  15 + public:
  16 + // The following methods are not part of the public API.
  17 + bool hasKey(std::string const& key) const;
  18 + QPDFObjectHandle getKey(std::string const& key) const;
  19 + std::set<std::string> getKeys();
  20 + std::map<std::string, QPDFObjectHandle> const& getAsMap() const;
  21 + void removeKey(std::string const& key);
  22 + void replaceKey(std::string const& key, QPDFObjectHandle value);
  23 +
  24 + protected:
  25 + BaseDictionary() = default;
  26 + BaseDictionary(std::shared_ptr<QPDFObject> const& obj) :
  27 + BaseHandle(obj) {};
  28 + BaseDictionary(std::shared_ptr<QPDFObject>&& obj) :
  29 + BaseHandle(std::move(obj)) {};
  30 + BaseDictionary(BaseDictionary const&) = default;
  31 + BaseDictionary& operator=(BaseDictionary const&) = default;
  32 + BaseDictionary(BaseDictionary&&) = default;
  33 + BaseDictionary& operator=(BaseDictionary&&) = default;
  34 + ~BaseDictionary() = default;
  35 +
  36 + QPDF_Dictionary* dict() const;
  37 + };
  38 +
  39 + class Dictionary final: public BaseDictionary
  40 + {
  41 + public:
  42 + explicit Dictionary(std::shared_ptr<QPDFObject> const& obj) :
  43 + BaseDictionary(obj)
  44 + {
  45 + }
  46 +
  47 + explicit Dictionary(std::shared_ptr<QPDFObject>&& obj) :
  48 + BaseDictionary(std::move(obj))
  49 + {
  50 + }
  51 + };
  52 +
  53 + inline qpdf_object_type_e
  54 + BaseHandle::type_code() const
  55 + {
  56 + return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized;
  57 + }
  58 +
  59 +} // namespace qpdf
  60 +
  61 +inline qpdf::Dictionary
  62 +QPDFObjectHandle::as_dictionary(qpdf::typed options) const
  63 +{
  64 + if (options & qpdf::any_flag || type_code() == ::ot_dictionary ||
  65 + (options & qpdf::optional && type_code() == ::ot_null)) {
  66 + return qpdf::Dictionary(obj);
  67 + }
  68 + if (options & qpdf::error) {
  69 + assertType("dictionary", false);
  70 + }
  71 + return qpdf::Dictionary(std::shared_ptr<QPDFObject>());
  72 +}
  73 +
  74 +#endif // OBJECTHANDLE_PRIVATE_HH
... ...
libqpdf/qpdf/QPDF_Dictionary.hh
... ... @@ -6,7 +6,10 @@
6 6 #include <map>
7 7 #include <set>
8 8  
  9 +#include <qpdf/QPDFExc.hh>
9 10 #include <qpdf/QPDFObjectHandle.hh>
  11 +#include <qpdf/QPDFObject_private.hh>
  12 +#include <qpdf/QPDF_Null.hh>
10 13  
11 14 class QPDF_Dictionary: public QPDFValue
12 15 {
... ... @@ -19,22 +22,12 @@ class QPDF_Dictionary: public QPDFValue
19 22 void writeJSON(int json_version, JSON::Writer& p) override;
20 23 void disconnect() override;
21 24  
22   - // hasKey() and getKeys() treat keys with null values as if they aren't there. getKey() returns
23   - // null for the value of a non-existent key. This is as per the PDF spec.
24   - bool hasKey(std::string const&);
25   - QPDFObjectHandle getKey(std::string const&);
26   - std::set<std::string> getKeys();
27   - std::map<std::string, QPDFObjectHandle> const& getAsMap() const;
28   -
29   - // If value is null, remove key; otherwise, replace the value of key, adding it if it does not
30   - // exist.
31   - void replaceKey(std::string const& key, QPDFObjectHandle value);
32   - // Remove key, doing nothing if key does not exist
33   - void removeKey(std::string const& key);
34   -
35 25 private:
  26 + friend class qpdf::BaseDictionary;
  27 +
36 28 QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items);
37 29 QPDF_Dictionary(std::map<std::string, QPDFObjectHandle>&& items);
  30 +
38 31 std::map<std::string, QPDFObjectHandle> items;
39 32 };
40 33  
... ...