23int InterrogateDatabase::_file_major_version = 0;
24int InterrogateDatabase::_file_minor_version = 0;
25int InterrogateDatabase::_current_major_version = 3;
26int InterrogateDatabase::_current_minor_version = 3;
32InterrogateDatabase() {
43 if (_global_ptr ==
nullptr) {
44 if (interrogatedb_cat->is_debug()) {
45 interrogatedb_cat->debug()
46 <<
"Creating interrogate database\n";
61 if (interrogatedb_cat->is_debug()) {
62 if (def->library_name ==
nullptr) {
63 interrogatedb_cat->debug()
64 <<
"Got interrogate data for anonymous module\n";
66 interrogatedb_cat->debug()
67 <<
"Got interrogate data for module " << def->library_name <<
"\n";
71 int num_indices = def->next_index - def->first_index;
72 if (num_indices > 0) {
75 def->first_index = _next_index;
76 _next_index += num_indices;
77 def->next_index = _next_index;
81 _modules.push_back(def);
84 if (def->num_unique_names > 0 && def->library_name !=
nullptr) {
87 _modules_by_hash[def->library_hash_name] = def;
90 if (def->database_filename !=
nullptr) {
91 _requests.push_back(def);
114 return _global_types.size();
123 if (n >= 0 && n < (
int)_global_types.size()) {
124 return _global_types[n];
137 return _all_types.size();
146 if (n >= 0 && n < (
int)_all_types.size()) {
147 return _all_types[n];
160 return _global_functions.size();
170 if (n >= 0 && n < (
int)_global_functions.size()) {
171 return _global_functions[n];
184 return _all_functions.size();
193 if (n >= 0 && n < (
int)_all_functions.size()) {
194 return _all_functions[n];
206 return _global_manifests.size();
216 if (n >= 0 && n < (
int)_global_manifests.size()) {
217 return _global_manifests[n];
229 return _global_elements.size();
239 if (n >= 0 && n < (
int)_global_elements.size()) {
240 return _global_elements[n];
253 TypeMap::const_iterator ti;
254 ti = _type_map.find(type);
255 if (ti == _type_map.end()) {
270 FunctionMap::const_iterator fi;
271 fi = _function_map.find(function);
272 if (fi == _function_map.end()) {
273 return bogus_function;
275 return *(*fi).second;
287 FunctionWrapperMap::const_iterator wi;
288 wi = _wrapper_map.find(wrapper);
289 if (wi == _wrapper_map.end()) {
290 return bogus_wrapper;
304 ManifestMap::const_iterator mi;
305 mi = _manifest_map.find(manifest);
306 if (mi == _manifest_map.end()) {
307 return bogus_manifest;
321 ElementMap::const_iterator ei;
322 ei = _element_map.find(element);
323 if (ei == _element_map.end()) {
324 return bogus_element;
338 MakeSeqMap::const_iterator si;
339 si = _make_seq_map.find(make_seq);
340 if (si == _make_seq_map.end()) {
341 return bogus_make_seq;
351 _type_map.erase(type);
359get_fptr(FunctionWrapperIndex wrapper) {
362 if (find_module(wrapper, def, module_index)) {
363 if (module_index >= 0 && module_index < def->num_fptrs) {
364 return def->fptrs[module_index];
381 string library_hash_name = unique_name.substr(0, 4);
382 string wrapper_hash_name = unique_name.substr(4);
385 ModulesByHash::const_iterator mi;
386 mi = _modules_by_hash.find(library_hash_name);
387 if (mi == _modules_by_hash.end()) {
393 binary_search_wrapper_hash(def->unique_names,
394 def->unique_names + def->num_unique_names,
396 if (index_offset >= 0) {
397 return def->first_index + index_offset;
409 return _file_major_version;
418 return _file_minor_version;
427 return _current_major_version;
436 return _current_minor_version;
445 _error_flag = error_flag;
454 return _next_index++;
464 _type_map.insert(TypeMap::value_type(index, type)).second;
470 assert(!old_type.is_fully_defined());
477 _global_types.push_back(index);
479 _all_types.push_back(index);
488 _function_map.insert(FunctionMap::value_type(index, function)).second;
492 _global_functions.push_back(index);
494 _all_functions.push_back(index);
505 _wrapper_map.insert(FunctionWrapperMap::value_type(index, wrapper)).second;
516 _manifest_map.insert(ManifestMap::value_type(index, manifest)).second;
519 _global_manifests.push_back(index);
528 _element_map.insert(ElementMap::value_type(index, element)).second;
532 _global_elements.push_back(index);
542 _make_seq_map.insert(MakeSeqMap::value_type(index, make_seq)).second;
554 return _type_map[type];
564 return *_function_map[function];
574 return _wrapper_map[wrapper];
584 return _manifest_map[manifest];
594 return _element_map[element];
604 return _make_seq_map[make_seq];
632 FunctionWrapperMap new_wrapper_map;
633 FunctionWrapperMap::iterator wi;
634 for (wi = _wrapper_map.begin(); wi != _wrapper_map.end(); ++wi) {
636 new_wrapper_map[first_index] = (*wi).second;
641 FunctionMap new_function_map;
642 FunctionMap::iterator fi;
643 for (fi = _function_map.begin(); fi != _function_map.end(); ++fi) {
645 new_function_map[first_index] = (*fi).second;
649 TypeMap new_type_map;
650 TypeMap::iterator ti;
651 for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) {
652 assert((*ti).first != 0);
654 new_type_map[first_index] = (*ti).second;
658 ManifestMap new_manifest_map;
659 ManifestMap::iterator mi;
660 for (mi = _manifest_map.begin(); mi != _manifest_map.end(); ++mi) {
662 new_manifest_map[first_index] = (*mi).second;
666 ElementMap new_element_map;
667 ElementMap::iterator ei;
668 for (ei = _element_map.begin(); ei != _element_map.end(); ++ei) {
670 new_element_map[first_index] = (*ei).second;
674 MakeSeqMap new_make_seq_map;
675 MakeSeqMap::iterator si;
676 for (si = _make_seq_map.begin(); si != _make_seq_map.end(); ++si) {
678 new_make_seq_map[first_index] = (*si).second;
682 _next_index = first_index;
684 _wrapper_map.swap(new_wrapper_map);
685 _function_map.swap(new_function_map);
686 _type_map.swap(new_type_map);
687 _manifest_map.swap(new_manifest_map);
688 _element_map.swap(new_element_map);
689 _make_seq_map.swap(new_make_seq_map);
692 for (wi = _wrapper_map.begin(); wi != _wrapper_map.end(); ++wi) {
693 (*wi).second.remap_indices(remap);
695 for (fi = _function_map.begin(); fi != _function_map.end(); ++fi) {
696 (*fi).second->remap_indices(remap);
698 for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) {
699 (*ti).second.remap_indices(remap);
701 for (mi = _manifest_map.begin(); mi != _manifest_map.end(); ++mi) {
702 (*mi).second.remap_indices(remap);
704 for (ei = _element_map.begin(); ei != _element_map.end(); ++ei) {
705 (*ei).second.remap_indices(remap);
707 for (si = _make_seq_map.begin(); si != _make_seq_map.end(); ++si) {
708 (*si).second.remap_indices(remap);
710 GlobalFunctions::iterator gfi;
711 for (gfi = _global_functions.begin(); gfi != _global_functions.end(); ++gfi) {
714 for (gfi = _all_functions.begin(); gfi != _all_functions.end(); ++gfi) {
717 GlobalTypes::iterator gti;
718 for (gti = _global_types.begin(); gti != _global_types.end(); ++gti) {
721 for (gti = _all_types.begin(); gti != _all_types.end(); ++gti) {
724 GlobalManifests::iterator gmi;
725 for (gmi = _global_manifests.begin(); gmi != _global_manifests.end(); ++gmi) {
728 GlobalElements::iterator gei;
729 for (gei = _global_elements.begin(); gei != _global_elements.end(); ++gei) {
742 out << def->file_identifier <<
"\n"
743 << _current_major_version <<
" " << _current_minor_version <<
"\n";
753 out << _function_map.size() <<
"\n";
754 FunctionMap::const_iterator fi;
755 for (fi = _function_map.begin(); fi != _function_map.end(); ++fi) {
756 out << (*fi).first <<
" " << *(*fi).second <<
"\n";
759 out << _wrapper_map.size() <<
"\n";
760 FunctionWrapperMap::const_iterator wi;
761 for (wi = _wrapper_map.begin(); wi != _wrapper_map.end(); ++wi) {
762 out << (*wi).first <<
" " << (*wi).second <<
"\n";
765 out << _type_map.size() <<
"\n";
766 TypeMap::const_iterator ti;
767 for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) {
768 out << (*ti).first <<
" " << (*ti).second <<
"\n";
771 out << _manifest_map.size() <<
"\n";
772 ManifestMap::const_iterator mi;
773 for (mi = _manifest_map.begin(); mi != _manifest_map.end(); ++mi) {
774 out << (*mi).first <<
" " << (*mi).second <<
"\n";
777 out << _element_map.size() <<
"\n";
778 ElementMap::const_iterator ei;
779 for (ei = _element_map.begin(); ei != _element_map.end(); ++ei) {
780 out << (*ei).first <<
" " << (*ei).second <<
"\n";
783 out << _make_seq_map.size() <<
"\n";
784 MakeSeqMap::const_iterator si;
785 for (si = _make_seq_map.begin(); si != _make_seq_map.end(); ++si) {
786 out << (*si).first <<
" " << (*si).second <<
"\n";
801 if (!temp.read_new(in, def)) {
805 if (def->first_index == 0 && def->next_index == 0) {
810 if (next != def->next_index) {
811 interrogatedb_cat->error()
812 <<
"Module database file " << def->database_filename
813 <<
" is out of date.\n";
825void InterrogateDatabase::
827 const DSearchPath &searchpath = interrogatedb_path;
829 Requests copy_requests;
830 copy_requests.swap(_requests);
832 Requests::const_iterator ri;
833 for (ri = copy_requests.begin(); ri != copy_requests.end(); ++ri) {
836 if (def->database_filename !=
nullptr) {
837 Filename filename = def->database_filename;
839 if (!pathname.empty() && pathname[0] !=
'/') {
840 pathname = searchpath.
find_file(pathname);
843 if (pathname.empty()) {
844 interrogatedb_cat->error()
845 <<
"Unable to find " << filename <<
" on " << searchpath <<
"\n";
853 interrogatedb_cat->error() <<
"Unable to read " << pathname <<
".\n";
858 input >> file_identifier
859 >> _file_major_version >> _file_minor_version;
861 if (def->file_identifier != 0 &&
862 file_identifier != def->file_identifier) {
863 interrogatedb_cat->warning()
864 <<
"Interrogate data in " << pathname
865 <<
" is out of sync with the compiled-in data"
866 <<
" (" << file_identifier <<
" != " << def->file_identifier <<
").\n";
870 if (_file_major_version != _current_major_version ||
871 _file_minor_version > _current_minor_version) {
872 interrogatedb_cat->error()
873 <<
"Cannot read interrogate data in " << pathname
874 <<
"; database is version " << _file_major_version <<
"."
875 << _file_minor_version <<
" while we are expecting "
876 << _current_major_version <<
"." << _current_minor_version
881 if (interrogatedb_cat->is_debug()) {
882 interrogatedb_cat->debug()
883 <<
"Reading " << filename <<
"\n";
885 if (!
read(input, def)) {
886 interrogatedb_cat->error()
887 <<
"Error reading " << pathname <<
".\n";
904bool InterrogateDatabase::
920 while (num_functions > 0) {
923 in >> index >> *function;
941 while (num_wrappers > 0) {
942 FunctionWrapperIndex index;
944 in >> index >> wrapper;
961 while (num_types > 0) {
974 FunctionIndex dtor = itype.get_destructor();
978 for (
int i = 0; i < itype.number_of_constructors(); ++i) {
979 FunctionIndex ctor = itype.get_constructor(i);
992 while (num_manifests > 0) {
995 in >> index >> manifest;
1012 while (num_elements > 0) {
1015 in >> index >> element;
1027 in >> num_make_seqs;
1032 while (num_make_seqs > 0) {
1035 in >> index >> make_seq;
1052void InterrogateDatabase::
1059 map<string, TypeIndex> types_by_name;
1061 TypeMap::const_iterator ti;
1062 for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) {
1064 if (type.has_true_name()) {
1065 types_by_name[type.get_true_name()] = (*ti).first;
1071 for (ti = other._type_map.begin(); ti != other._type_map.end(); ++ti) {
1072 TypeIndex other_type_index = (*ti).first;
1075 if (other_type.has_name()) {
1076 map<string, TypeIndex>::iterator ni;
1077 ni = types_by_name.find(other_type.get_true_name());
1078 if (ni != types_by_name.end()) {
1081 TypeIndex this_type_index = (*ni).second;
1082 remap.
add_mapping(other_type_index, this_type_index);
1089 for (ti = other._type_map.begin(); ti != other._type_map.end(); ++ti) {
1090 TypeIndex other_type_index = (*ti).first;
1093 if (!remap.
in_map(other_type_index)) {
1095 add_type(other_type_index, other_type);
1100 TypeIndex this_type_index = remap.
map_from(other_type_index);
1106 _global_types.push_back(this_type_index);
1116 FunctionMap::const_iterator fi;
1117 for (fi = other._function_map.begin();
1118 fi != other._function_map.end();
1120 FunctionIndex other_function_index = (*fi).first;
1126 FunctionWrapperMap::const_iterator wi;
1127 for (wi = other._wrapper_map.begin();
1128 wi != other._wrapper_map.end();
1130 FunctionWrapperIndex other_wrapper_index = (*wi).first;
1136 ManifestMap::const_iterator mi;
1137 for (mi = other._manifest_map.begin();
1138 mi != other._manifest_map.end();
1140 ManifestIndex other_manifest_index = (*mi).first;
1146 ElementMap::const_iterator ei;
1147 for (ei = other._element_map.begin();
1148 ei != other._element_map.end();
1150 ElementIndex other_element_index = (*ei).first;
1156 MakeSeqMap::const_iterator si;
1157 for (si = other._make_seq_map.begin();
1158 si != other._make_seq_map.end();
1160 MakeSeqIndex other_make_seq_index = (*si).first;
1178bool InterrogateDatabase::
1180 int &module_index) {
1181 if (_modules.empty()) {
1185 int mi = binary_search_module(0, _modules.size(), wrapper);
1186 assert(mi >= 0 && mi < (
int)_modules.size());
1188 module_index = wrapper - def->first_index;
1190 return (wrapper < def->next_index);
1197int InterrogateDatabase::
1198binary_search_module(
int begin,
int end, FunctionIndex function) {
1199 int mid = begin + (end - begin) / 2;
1204 int index = _modules[mid]->first_index;
1205 if (index <= function) {
1206 return binary_search_module(mid, end, function);
1209 return binary_search_module(begin, mid, function);
1218int InterrogateDatabase::
1221 const string &wrapper_hash_name) {
1227 string name = mid->name;
1228 if (name < wrapper_hash_name) {
1229 return binary_search_wrapper_hash(mid, end, wrapper_hash_name);
1231 }
else if (wrapper_hash_name < name) {
1232 return binary_search_wrapper_hash(begin, mid, wrapper_hash_name);
1235 return mid->index_offset;
1242void InterrogateDatabase::
1243freshen_types_by_name() {
1244 _types_by_name.clear();
1245 TypeMap::const_iterator ti;
1246 for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) {
1247 _types_by_name[(*ti).second.get_name()] = (*ti).first;
1254void InterrogateDatabase::
1255freshen_types_by_scoped_name() {
1256 _types_by_scoped_name.clear();
1257 TypeMap::const_iterator ti;
1258 for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) {
1259 _types_by_scoped_name[(*ti).second.get_scoped_name()] = (*ti).first;
1266void InterrogateDatabase::
1267freshen_types_by_true_name() {
1268 _types_by_true_name.clear();
1269 TypeMap::const_iterator ti;
1270 for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) {
1271 _types_by_true_name[(*ti).second.get_true_name()] = (*ti).first;
1278void InterrogateDatabase::
1279freshen_manifests_by_name() {
1280 _manifests_by_name.clear();
1281 ManifestMap::const_iterator ti;
1282 for (ti = _manifest_map.begin(); ti != _manifest_map.end(); ++ti) {
1283 _manifests_by_name[(*ti).second.get_name()] = (*ti).first;
1290void InterrogateDatabase::
1291freshen_elements_by_name() {
1292 _elements_by_name.clear();
1293 ElementMap::const_iterator ti;
1294 for (ti = _element_map.begin(); ti != _element_map.end(); ++ti) {
1295 _elements_by_name[(*ti).second.get_name()] = (*ti).first;
1302void InterrogateDatabase::
1303freshen_elements_by_scoped_name() {
1304 _elements_by_scoped_name.clear();
1305 ElementMap::const_iterator ti;
1306 for (ti = _element_map.begin(); ti != _element_map.end(); ++ti) {
1307 _elements_by_scoped_name[(*ti).second.get_scoped_name()] = (*ti).first;
1315int InterrogateDatabase::
1316lookup(
const string &name, Lookup &lookup, LookupType type,
1318 if ((_lookups_fresh & (
int)type) == 0) {
1321 _lookups_fresh |= (int)type;
1324 Lookup::const_iterator li;
1325 li = lookup.find(name);
1326 if (li != lookup.end()) {
1327 return (*li).second;
This class stores a list of directories that can be searched, in order, to locate a particular file.
Filename find_file(const Filename &filename) const
Searches all the directories in the search list for the indicated file, in order.
The name of a file, such as a texture file or an Egg file.
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
void set_text()
Indicates that the filename represents a text file.
This class manages a mapping of integers to integers.
bool in_map(int from) const
Returns true if the given 'from' integer has been assigned a mapping, false if it has not.
void add_mapping(int from, int to)
Adds a mapping from the integer 'from' to 'to'.
void clear()
Removes all mappings from the object.
int map_from(int from) const
Returns the integer that the given 'from' integer had been set to map to, or the same integer if noth...
This stores all of the interrogate data and handles reading the data from a disk file when necessary.
int get_num_global_types()
Returns the total number of "global" types known to the interrogate database.
void remove_type(TypeIndex type)
Erases the type from the database.
void request_module(InterrogateModuleDef *def)
Requests that the interrogate data for the given module be made available.
ManifestIndex get_global_manifest(int n)
Returns the index of the nth global manifest constant known to the interrogate database.
const InterrogateFunction & get_function(FunctionIndex function)
Returns the function associated with the given FunctionIndex, if there is one.
InterrogateFunction & update_function(FunctionIndex function)
Returns a non-const reference to the indicated function, allowing the user to update it.
static int get_current_minor_version()
Returns the minor version number currently expected in interrogate database files generated by this c...
InterrogateType & update_type(TypeIndex type)
Returns a non-const reference to the indicated type, allowing the user to update it.
int get_num_all_functions()
Returns the total number of functions known to the interrogate database.
InterrogateElement & update_element(ElementIndex element)
Returns a non-const reference to the indicated data element, allowing the user to update it.
int get_num_all_types()
Returns the total number of types known to the interrogate database.
void add_manifest(ManifestIndex index, const InterrogateManifest &manifest)
Adds the indicated manifest constant to the database at the given index number.
const InterrogateMakeSeq & get_make_seq(MakeSeqIndex element)
Returns the make_seq associated with the given MakeSeqIndex, if there is one.
bool get_error_flag()
Returns the global error flag.
void add_wrapper(FunctionWrapperIndex index, const InterrogateFunctionWrapper &wrapper)
Adds the indicated function wrapper to the database at the given index number.
int get_num_global_elements()
Returns the total number of global data elements known to the interrogate database.
void add_type(TypeIndex index, const InterrogateType &type)
Adds the indicated type to the database at the given index number.
const InterrogateManifest & get_manifest(ManifestIndex manifest)
Returns the manifest constant associated with the given ManifestIndex, if there is one.
const InterrogateType & get_type(TypeIndex type)
Returns the type associated with the given TypeIndex, if there is one.
const InterrogateElement & get_element(ElementIndex element)
Returns the data element associated with the given ElementIndex, if there is one.
int get_num_global_manifests()
Returns the total number of global manifest constants known to the interrogate database.
InterrogateManifest & update_manifest(ManifestIndex manifest)
Returns a non-const reference to the indicated manifest constant, allowing the user to update it.
int get_num_global_functions()
Returns the total number of global functions known to the interrogate database.
InterrogateFunctionWrapper & update_wrapper(FunctionWrapperIndex wrapper)
Returns a non-const reference to the indicated function wrapper, allowing the user to update it.
void add_element(ElementIndex index, const InterrogateElement &element)
Adds the indicated data element to the database at the given index number.
static int get_file_minor_version()
Returns the minor version number of the interrogate database file currently being read.
bool read(std::istream &in, InterrogateModuleDef *def)
Reads a database from the indicated stream, associated with the indicated module definition and merge...
void set_error_flag(bool error_flag)
Sets the global error flag.
const InterrogateFunctionWrapper & get_wrapper(FunctionWrapperIndex wrapper)
Returns the function wrapper associated with the given FunctionWrapperIndex, if there is one.
static int get_current_major_version()
Returns the major version number currently expected in interrogate database files generated by this c...
int get_next_index()
Returns a new index number suitable for the next thing, that will not be shared with any other index ...
static InterrogateDatabase * get_ptr()
Returns the global pointer to the one InterrogateDatabase.
void * get_fptr(FunctionWrapperIndex wrapper)
Returns the function pointer associated with the given function wrapper, if it has a pointer availabl...
FunctionIndex get_all_function(int n)
Returns the index of the nth function known to the interrogate database.
TypeIndex get_all_type(int n)
Returns the index of the nth type known to the interrogate database.
FunctionWrapperIndex get_wrapper_by_unique_name(const std::string &unique_name)
Looks up the function wrapper corresponding to the given unique name, if available.
void add_make_seq(MakeSeqIndex index, const InterrogateMakeSeq &make_seq)
Adds the indicated make_seq to the database at the given index number.
InterrogateMakeSeq & update_make_seq(MakeSeqIndex make_seq)
Returns a non-const reference to the indicated make_seq, allowing the user to update it.
void add_function(FunctionIndex index, InterrogateFunction *function)
Adds the indicated function to the database at the given index number.
int remap_indices(int first_index)
Resequences all of the various index numbers so that all of the functions start at first_index and in...
void write(std::ostream &out, InterrogateModuleDef *def) const
Writes the database to the indicated stream for later reading.
static int get_file_major_version()
Returns the major version number of the interrogate database file currently being read.
ElementIndex get_global_element(int n)
Returns the index of the nth global data element known to the interrogate database.
TypeIndex get_global_type(int n)
Returns the index of the nth global type known to the interrogate database.
FunctionIndex get_global_function(int n)
Returns the index of the nth global function known to the interrogate database.
An internal representation of a data element, like a data member or a global variable.
bool is_global() const
Returns true if the element is marked as 'global'.
void remap_indices(const IndexRemapper &remap)
Remaps all internal index numbers according to the indicated map.
An internal representation of a callable function.
void remap_indices(const IndexRemapper &remap)
Remaps all internal index numbers according to the indicated map.
An internal representation of a function.
void remap_indices(const IndexRemapper &remap)
Remaps all internal index numbers according to the indicated map.
bool is_global() const
Returns true if the function is marked as 'global'.
Represents a synthetic method created via the MAKE_SEQ() macro.
void remap_indices(const IndexRemapper &remap)
Remaps all internal index numbers according to the indicated map.
An internal representation of a manifest constant.
void remap_indices(const IndexRemapper &remap)
Remaps all internal index numbers according to the indicated map.
An internal representation of a type.
bool is_global() const
Returns true if the type is marked as 'global'.
void merge_with(const InterrogateType &other)
Combines type with the other similar definition.
void remap_indices(const IndexRemapper &remap)
Remaps all internal index numbers according to the indicated map.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void idf_input_string(istream &in, string &str)
Reads the given string from the input file, as previously written by output_string().
void idf_output_string(ostream &out, const string &str, char whitespace)
Writes the indicated string to the output file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.