7#include "boost/preprocessor/seq/for_each.hpp"
8#include "boost/preprocessor/tuple/to_seq.hpp"
10#include "lsst/utils/hashCombine.h"
26inline char getDelimiter() {
return '_'; }
46 bool operator()(SchemaItem<T>
const &
a, SchemaItem<T>
const &
b)
const {
47 return a.key ==
b.key;
50 template <
typename T,
typename U>
51 bool operator()(SchemaItem<T>
const &
a, SchemaItem<U>
const &
b)
const {
57 static bool compareKeys(ItemVariant
const &
a, ItemVariant
const &
b) {
58 return std::visit(KeyHelper(),
a,
b);
61 static bool compareNames(ItemVariant
const &
a, ItemVariant
const &
b) {
63 [](
auto const &
a,
auto const &
b) {
return a.field.getName() ==
b.field.getName(); },
68 static bool compareDocs(ItemVariant
const &
a, ItemVariant
const &
b) {
70 [](
auto const &
a,
auto const &
b) {
return a.field.getDoc() ==
b.field.getDoc(); },
75 static bool compareUnits(ItemVariant
const &
a, ItemVariant
const &
b) {
77 [](
auto const &
a,
auto const &
b) {
return a.field.getUnits() ==
b.field.getUnits(); },
94 if (i != _names.
end() && i->first ==
name) {
97 return std::get<SchemaItem<T>>(_items[i->second]);
98 }
catch (std::bad_variant_access &err) {
100 (boost::format(
"Field '%s' does not have the given type.") %
name).
str());
104 (boost::format(
"Field with name '%s' not found with type '%s'.") %
name %
112 if (i != _offsets.
end() && i->first == key.
getOffset()) {
114 return std::get<SchemaItem<T>>(_items[i->second]);
115 }
catch (std::bad_variant_access &err) {
120 (boost::format(
"Field or subfield with offset %d not found with type '%s'.") %
128 if (i != _flags.
end()) {
129 if (i->first.first == key.
getOffset() && i->first.second == key.
getBit()) {
131 return std::get<SchemaItem<Flag>>(_items[i->second]);
132 }
catch (std::bad_variant_access &err) {
134 (boost::format(
"Flag field with offset %d and bit %d not found.") %
141 (boost::format(
"Flag field with offset %d and bit %d not found.") % key.
getOffset() %
157 bool throwIfMissing =
true) {
158 SchemaImpl::OffsetMap::const_iterator i = offsets.
find(key.
getOffset());
159 if (i == offsets.
end()) {
160 if (throwIfMissing) {
162 (boost::format(
"Key of type %s with offset %d not found in Schema") %
174 Key<Flag>
const &key,
bool throwIfMissing =
true) {
175 SchemaImpl::FlagMap::const_iterator i = flags.find(
std::make_pair(key.getOffset(), key.getBit()));
176 if (i == flags.end()) {
177 if (throwIfMissing) {
179 pex::exceptions::NotFoundError,
180 (boost::format(
"Key of type Flag with offset %d and bit %d not found in Schema") %
181 key.getOffset() % key.getBit())
194 NameMap::iterator j = _names.
find(
field.getName());
196 if (j != _names.
end()) {
199 item = std::get_if<SchemaItem<T>>(&_items[j->second]);
200 if (!item || key != item->
key) {
203 (boost::format(
"Field with name '%s' already present in schema with a different key.") %
209 std::size_t index = findKey(_offsets, _flags, key);
210 item = std::get_if<SchemaItem<T>>(&_items[index]);
213 (boost::format(
"Incorrect key type '%s'.") % key).
str());
228 "Can only check whether item is in schema if flags & EQUAL_KEYS");
233 cmpItem = std::get_if<SchemaItem<T> >(&_items[index]);
254 for (
auto const &_name : _names) {
256 if (sep == std::string::npos) {
257 result.
insert(result.
end(), _name.first);
263 for (
auto const &_name : _names) {
264 result.
insert(result.
end(), _name.first);
276 if (sep == std::string::npos) {
295 if (
field.isVariableLength()) {
297 return addFieldImpl(
sizeof(ndarray::Array<T, 1, 1>), 1,
field, doReplace);
304 if (
field.isVariableLength()) {
322 if (!result.second) {
324 SchemaItem<Flag> *item = std::get_if<SchemaItem<Flag>>(&_items[result.first->second]);
328 (boost::format(
"Cannot replace field with name '%s' because types differ.") %
332 if (item->
field.getElementCount() !=
field.getElementCount()) {
335 (boost::format(
"Cannot replace field with name '%s' because sizes differ.") %
344 (boost::format(
"Field with name '%s' already present in schema.") %
field.getName())
348 if (!_initFlag || _lastFlagBit >= ELEMENT_SIZE * 8) {
349 std::size_t padding = ELEMENT_SIZE - _recordSize % ELEMENT_SIZE;
350 if (padding != ELEMENT_SIZE) {
351 _recordSize += padding;
353 _lastFlagField = _recordSize;
356 _recordSize +=
field.getElementCount() * ELEMENT_SIZE;
371 if (!result.second) {
373 SchemaItem<T> *item = std::get_if<SchemaItem<T>>(&_items[result.first->second]);
377 (boost::format(
"Cannot replace field with name '%s' because types differ.") %
384 if (item->
field.getElementCount() !=
field.getElementCount()) {
387 (boost::format(
"Cannot replace field with name '%s' because sizes differ.") %
396 (boost::format(
"Field with name '%s' already present in schema.") %
field.getName())
400 std::size_t padding = elementSize - _recordSize % elementSize;
401 if (padding != elementSize) {
402 _recordSize += padding;
405 _recordSize += elementCount * elementSize;
454 return afw::table::join(
a,
b, getDelimiter());
457void Schema::_edit() {
458 if (!_impl.unique()) {
469 _aliases->_apply(tmp);
470 return _impl->find<T>(tmp);
475 return _impl->find(key);
481 return _impl->addField(
field, doReplace);
487 _impl->replaceField(key,
field);
491 if (_impl == other._impl)
return flags;
492 if (_impl->getItems().size() < other._impl->getItems().size())
return 0;
495 for (Impl::ItemContainer::const_iterator i1 = _impl->getItems().begin(),
496 i2 = other._impl->getItems().begin();
497 i2 != other._impl->getItems().end(); ++i1, ++i2) {
498 if ((result &
EQUAL_KEYS) && !ItemFunctors::compareKeys(*i1, *i2)) result &= ~EQUAL_KEYS;
499 if ((result &
EQUAL_NAMES) && !ItemFunctors::compareNames(*i1, *i2)) result &= ~EQUAL_NAMES;
500 if ((result &
EQUAL_DOCS) && !ItemFunctors::compareDocs(*i1, *i2)) result &= ~EQUAL_DOCS;
501 if ((result &
EQUAL_UNITS) && !ItemFunctors::compareUnits(*i1, *i2)) result &= ~EQUAL_UNITS;
510 int result =
contains(other, flags);
511 if (_impl->getItems().size() != other._impl->getItems().size()) {
512 result &= ~EQUAL_FIELDS;
515 result &= ~EQUAL_ALIASES;
523 auto hasher = [&result](
auto const &item) { result = utils::hashCombine(result, item.
key); };
530 return _impl->contains(item, flags);
535 aliases = std::make_shared<AliasMap>();
548 using result_type = void;
550 template <
typename T>
552 *
os <<
" (" << item.
field <<
", " << item.
key <<
"),\n";
565 for (
auto const &
iter : *
schema.getAliasMap()) {
566 os <<
" '" <<
iter.first <<
"'->'" <<
iter.second <<
"'\n";
577 return afw::table::join(
a,
b, getDelimiter());
581 : _impl(impl), _aliases(aliases), _name(
name) {}
585 return _impl->find<T>(_aliases->apply(
join(_name,
name)));
602#define INSTANTIATE_LAYOUT(r, data, elem) \
603 template Key<elem> Schema::addField(Field<elem> const &, bool); \
604 template SchemaItem<elem> Schema::find(std::string const &) const; \
605 template SchemaItem<elem> Schema::find(Key<elem> const &) const; \
606 template SchemaItem<elem> detail::SchemaImpl::find(std::string const &name) const; \
607 template int Schema::contains(SchemaItem<elem> const &, int) const; \
608 template void Schema::replaceField(Key<elem> const &, Field<elem> const &); \
609 template SchemaItem<elem> SubSchema::find(std::string const &) const;
table::Key< std::string > name
#define LSST_EXCEPT(type,...)
#define INSTANTIATE_LAYOUT(r, data, elem)
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
void readMetadata(daf::base::PropertySet &metadata, bool strip=false)
Read a FITS header into a PropertySet or PropertyList.
Lifetime-management for memory that goes into FITS memory files.
Mapping class that holds aliases for a Schema.
Tag types used to declare specialized field types.
Key specialization for Flag.
std::size_t getBit() const
The index of this field's bit within the integer it shares with other Flag fields.
std::size_t getOffset() const
Return the offset in bytes of the integer element that holds this field's bit.
A class used as a handle to a particular field in a table.
std::size_t getOffset() const noexcept
Return the offset (in bytes) of this field within a record.
Defines the fields and offsets for a table.
void forEach(F &func) const
Apply a functor to each SchemaItem in the Schema.
Schema()
Construct an empty Schema.
void disconnectAliases()
Sever the connection between this schema and any others with which it shares aliases.
Schema & operator=(Schema const &other)
std::string join(std::string const &a, std::string const &b) const
Join strings using the field delimiter appropriate for this Schema.
void setAliasMap(std::shared_ptr< AliasMap > aliases)
Set the alias map.
std::shared_ptr< AliasMap > getAliasMap() const
Return the map of aliases.
std::set< std::string > getNames(bool topOnly=false) const
Return a set of field names in the schema.
static Schema fromFitsMetadata(daf::base::PropertyList &header, bool stripMetadata=true)
Construct from reading a FITS header.
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
static Schema readFits(std::string const &filename, int hdu=fits::DEFAULT_HDU)
Construct from reading a FITS file.
std::size_t hash_value() const noexcept
Return a hash of this object.
int contains(Schema const &other, int flags=EQUAL_KEYS) const
Test whether the given schema is a subset of this.
int compare(Schema const &other, int flags=EQUAL_KEYS) const
Do a detailed equality comparison of two schemas.
void replaceField(Key< T > const &key, Field< T > const &field)
Replace the Field (name/description) for an existing Key.
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
@ EQUAL_DOCS
Fields have the same documentation (ordered).
@ EQUAL_NAMES
Fields have the same names (ordered).
@ EQUAL_UNITS
Fields have the same units (ordered).
@ EQUAL_KEYS
Keys have the same types offsets, and sizes.
@ EQUAL_FIELDS
Fields are identical (but aliases may not be).
@ EQUAL_ALIASES
Schemas have identical AliasMaps.
A proxy type for name lookups in a Schema.
std::string join(std::string const &a, std::string const &b) const
Join strings using the field delimiter appropriate for this Schema.
SubSchema operator[](std::string const &name) const
Return a nested proxy.
std::set< std::string > getNames(bool topOnly=false) const
Return a set of nested names that start with the SubSchema's prefix.
SchemaItem< T > find(std::string const &name) const
Find a nested SchemaItem by name.
static Key< T > makeKey(std::size_t offset)
A private implementation class to hide the messy details of Schema.
std::set< std::string > getNames(bool topOnly) const
Return a set of field names (used to implement Schema::getNames).
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a field to the schema (used to implement Schema::addField).
int contains(SchemaItem< T > const &item, int flags) const
void replaceField(Key< T > const &key, Field< T > const &field)
Replace the Field in an existing SchemaItem without changing the Key.
SchemaItem< T > find(std::string const &name) const
Find an item by name (used to implement Schema::find).
std::map< std::pair< std::size_t, std::size_t >, std::size_t > FlagMap
A map from Flag field offset/bit pairs to position in the vector, so we can do Flag field lookups.
decltype(makeItemVariantType(FieldTypes{})) ItemVariant
A Boost.Variant type that can hold any one of the allowed SchemaItem types.
std::map< std::size_t, std::size_t > OffsetMap
A map from standard field offsets to position in the vector, so we can do field lookups.
std::ostream & operator<<(std::ostream &os, BaseRecord const &record)
BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_COLUMNVIEW_SCALAR, _, BOOST_PP_TUPLE_TO_SEQ(AFW_TABLE_SCALAR_FIELD_TYPE_N, AFW_TABLE_SCALAR_FIELD_TYPE_TUPLE)) BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_COLUMNVIEW_ARRAY
A base class for image defects.
A description of a field in a table.
typename FieldBase< T >::Element Element
Type used to store field data in the table (a field may have multiple elements).
A simple pair-like struct for mapping a Field (name and description) with a Key (used for actual data...
#define AFW_TABLE_FIELD_TYPE_N
#define AFW_TABLE_FIELD_TYPE_TUPLE