6 #include "boost/mpl/and.hpp" 7 #include "boost/mpl/bool.hpp" 8 #include "boost/iterator/transform_iterator.hpp" 9 #include "boost/iterator/filter_iterator.hpp" 10 #include "boost/preprocessor/seq/for_each.hpp" 11 #include "boost/preprocessor/tuple/to_seq.hpp" 30 inline char getDelimiter() {
return '_'; }
47 struct KeyHelper :
public boost::static_visitor<bool> {
48 explicit KeyHelper(ItemVariant
const *other_) :
other(other_) {}
51 bool operator()(SchemaItem<T>
const &a)
const {
52 SchemaItem<T>
const *b = boost::get<SchemaItem<T> >(
other);
53 return (b) && a.key == b->key;
60 struct NameHelper :
public boost::static_visitor<std::string const &> {
62 std::string const &operator()(SchemaItem<T>
const &a)
const {
63 return a.field.getName();
68 struct DocHelper :
public boost::static_visitor<std::string const &> {
70 std::string const &operator()(SchemaItem<T>
const &a)
const {
71 return a.field.getDoc();
76 struct UnitsHelper :
public boost::static_visitor<std::string const &> {
78 std::string const &operator()(SchemaItem<T>
const &a)
const {
79 return a.field.getUnits();
84 static bool compareKeys(ItemVariant
const &a, ItemVariant
const &b) {
85 return boost::apply_visitor(KeyHelper(&b), a);
88 static bool compareNames(ItemVariant
const &a, ItemVariant
const &b) {
89 return boost::apply_visitor(NameHelper(), a) == boost::apply_visitor(NameHelper(), b);
92 static bool compareDocs(ItemVariant
const &a, ItemVariant
const &b) {
93 return boost::apply_visitor(DocHelper(), a) == boost::apply_visitor(DocHelper(), b);
96 static bool compareUnits(ItemVariant
const &a, ItemVariant
const &b) {
97 return boost::apply_visitor(UnitsHelper(), a) == boost::apply_visitor(UnitsHelper(), b);
118 template <
typename T>
119 inline int findNamedSubfield(
123 if (name.
size() <= item.field.getName().size())
return -1;
126 name.
compare(0, item.field.getName().size(), item.field.getName()) == 0 &&
127 name[item.field.getName().size()] ==
delimiter) {
128 int const position = item.field.getName().size() + 1;
129 int const size = name.
size() - position;
130 int const nElements = item.field.getElementCount();
131 for (
int i = 0; i < nElements; ++i) {
132 if (name.
compare(position, size, Key<T>::subfields[i]) == 0) {
142 template <
typename T>
143 inline int findNamedSubfield(
144 SchemaItem<T>
const &item,
std::string const &name,
char delimiter,
152 template <
typename T,
typename U>
153 inline void makeSubfieldItem(
158 Field<U>(join(item.field.getName(), Key<T>::subfields[index],
delimiter),
159 item.field.getDoc(), item.field.getUnits())));
163 template <
typename T,
typename U>
164 inline void makeSubfieldItem(
173 template <
typename U>
174 struct ExtractItemByName :
public boost::static_visitor<> {
175 explicit ExtractItemByName(
std::string const &name_,
char delimiter_)
178 template <
typename T>
179 void operator()(SchemaItem<T>
const &item)
const {
184 typedef typename boost::mpl::and_<std::is_same<U, typename Field<T>::Element>,
185 boost::mpl::bool_<KeyBase<T>::HAS_NAMED_SUBFIELDS> >
::type 188 int n = findNamedSubfield(item, name, delimiter, (IsMatchPossible *)0);
190 if (n >= 0) makeSubfieldItem(item, n, delimiter,
result, (IsMatchPossible *)0);
201 template <
typename T>
203 NameMap::const_iterator i = _names.lower_bound(name);
204 if (i != _names.end()) {
205 if (i->first == name) {
208 return boost::get<SchemaItem<T>
const>(_items[i->second]);
209 }
catch (boost::bad_get &err) {
211 (
boost::format(
"Field '%s' does not have the given type.") % name).str());
217 ExtractItemByName<T> extractor(name, getDelimiter());
218 while (i != _names.begin()) {
220 boost::apply_visitor(extractor, _items[i->second]);
221 if (extractor.result)
return *extractor.result;
224 (
boost::format(
"Field or subfield with name '%s' not found with type '%s'.") % name %
238 template <
typename T,
typename U>
239 inline int findKeySubfield(
243 int n = (key.
getOffset() - item.
key.getOffset()) /
sizeof(U);
244 if (n >= 0 && n < item.
key.getElementCount()) {
252 template <
typename T,
typename U>
253 inline int findKeySubfield(
261 template <
typename U>
262 struct ExtractItemByKey :
public boost::static_visitor<> {
263 explicit ExtractItemByKey(
Key<U> const &key_,
char delimiter_) :
delimiter(delimiter_),
key(key_) {}
265 template <
typename T>
270 typedef typename boost::mpl::and_<std::is_same<U, typename Field<T>::Element>,
271 boost::mpl::bool_<KeyBase<T>::HAS_NAMED_SUBFIELDS> >
::type 274 int n = findKeySubfield(item, key, (IsMatchPossible *)0);
277 if (n >= 0) makeSubfieldItem(item, n, delimiter,
result, (IsMatchPossible *)0);
288 template <
typename T>
290 OffsetMap::const_iterator i = _offsets.lower_bound(key.
getOffset());
291 if (i != _offsets.end()) {
294 return boost::get<SchemaItem<T>
const>(_items[i->second]);
295 }
catch (boost::bad_get &err) {
301 ExtractItemByKey<T> extractor(key, getDelimiter());
302 while (i != _offsets.begin()) {
304 boost::apply_visitor(extractor, _items[i->second]);
305 if (extractor.result)
return *extractor.result;
309 (
boost::format(
"Field or subfield with offset %d not found with type '%s'.") %
317 if (i != _flags.end()) {
318 if (i->first.first == key.
getOffset() && i->first.second == key.
getBit()) {
320 return boost::get<SchemaItem<Flag>
const>(_items[i->second]);
321 }
catch (boost::bad_get &err) {
323 (
boost::format(
"Flag field with offset %d and bit %d not found.") %
345 template <
typename T>
347 bool throwIfMissing =
true) {
348 SchemaImpl::OffsetMap::const_iterator i = offsets.
find(key.
getOffset());
349 if (i == offsets.
end()) {
350 if (throwIfMissing) {
352 (
boost::format(
"Key of type %s with offset %d not found in Schema") %
364 Key<Flag> const &key,
bool throwIfMissing =
true) {
366 if (i == flags.
end()) {
367 if (throwIfMissing) {
370 (
boost::format(
"Key of type Flag with offset %d and bit %d not found in Schema") %
382 template <
typename T>
384 NameMap::iterator j = _names.find(field.
getName());
386 if (j != _names.end()) {
389 item = boost::get<SchemaItem<T> >(&_items[j->second]);
390 if (!item || key != item->
key) {
393 (
boost::format(
"Field with name '%s' already present in schema with a different key.") %
399 int index = findKey(_offsets, _flags, key);
400 item = boost::get<SchemaItem<T> >(&_items[index]);
405 j = _names.find(item->
field.getName());
414 template <
typename T>
418 "Can only check whether item is in schema if flags & EQUAL_KEYS");
421 int index = findKey(_offsets, _flags, item.
key,
false);
423 cmpItem = boost::get<SchemaItem<T> >(&_items[index]);
426 flags &= ~
Schema::EQUAL_NAMES;
429 flags &= ~
Schema::EQUAL_DOCS;
432 flags &= ~
Schema::EQUAL_UNITS;
444 for (NameMap::const_iterator i = _names.begin(); i != _names.end(); ++i) {
446 if (sep == std::string::npos) {
449 result.
insert(result.
end(), i->first.substr(0, sep));
453 for (NameMap::const_iterator i = _names.begin(); i != _names.end(); ++i) {
463 for (NameMap::const_iterator i = _names.lower_bound(prefix); i != _names.end(); ++i) {
464 if (i->first.compare(0, prefix.
size(),
prefix) != 0)
break;
466 if (sep == std::string::npos) {
468 i->first.substr(prefix.
size() + 1, i->first.size() - prefix.
size()));
470 result.
insert(result.
end(), i->first.substr(prefix.
size() + 1, sep - prefix.
size() - 1));
474 for (NameMap::const_iterator i = _names.lower_bound(prefix); i != _names.end(); ++i) {
475 if (i->first.compare(0, prefix.
size(),
prefix) != 0)
break;
477 i->first.substr(prefix.
size() + 1, i->first.size() - prefix.
size() - 1));
483 template <
typename T>
485 if (
field.isVariableLength()) {
487 return addFieldImpl(
sizeof(ndarray::Array<T, 1, 1>), 1,
field, doReplace);
494 if (field.isVariableLength()) {
496 return addFieldImpl(
sizeof(
std::string), 1, field, doReplace);
503 template <
typename T>
512 if (!result.second) {
514 SchemaItem<Flag> *item = boost::get<SchemaItem<Flag> >(&_items[result.first->second]);
518 (
boost::format(
"Cannot replace field with name '%s' because types differ.") %
525 (
boost::format(
"Cannot replace field with name '%s' because sizes differ.") %
538 if (_lastFlagField < 0 || _lastFlagBit >= ELEMENT_SIZE * 8) {
539 int padding = ELEMENT_SIZE - _recordSize % ELEMENT_SIZE;
540 if (padding != ELEMENT_SIZE) {
541 _recordSize += padding;
543 _lastFlagField = _recordSize;
551 _items.push_back(item);
556 template <
typename T>
557 Key<T> SchemaImpl::addFieldImpl(
int elementSize,
int elementCount,
Field<T> const &
field,
bool doReplace) {
560 if (!result.second) {
562 SchemaItem<T> *item = boost::get<SchemaItem<T> >(&_items[result.first->second]);
566 (
boost::format(
"Cannot replace field with name '%s' because types differ.") %
576 (
boost::format(
"Cannot replace field with name '%s' because sizes differ.") %
589 int padding = elementSize - _recordSize % elementSize;
590 if (padding != elementSize) {
591 _recordSize += padding;
594 _recordSize += elementCount * elementSize;
596 _items.push_back(item);
643 return afw::table::join(a, b, getDelimiter());
646 void Schema::_edit() {
647 if (!_impl.unique()) {
655 template <
typename T>
658 _aliases->_apply(tmp);
659 return _impl->find<
T>(tmp);
662 template <
typename T>
664 return _impl->find(key);
667 template <
typename T>
670 return _impl->addField(field, doReplace);
673 template <
typename T>
676 _impl->replaceField(key, field);
680 if (_impl == other._impl)
return flags;
681 if (_impl->getItems().size() < other._impl->getItems().size())
return 0;
684 for (Impl::ItemContainer::const_iterator i1 = _impl->getItems().begin(),
685 i2 = other._impl->getItems().begin();
686 i2 != other._impl->getItems().end(); ++i1, ++i2) {
687 if ((result &
EQUAL_KEYS) && !ItemFunctors::compareKeys(*i1, *i2)) result &= ~EQUAL_KEYS;
688 if ((result &
EQUAL_NAMES) && !ItemFunctors::compareNames(*i1, *i2)) result &= ~EQUAL_NAMES;
689 if ((result &
EQUAL_DOCS) && !ItemFunctors::compareDocs(*i1, *i2)) result &= ~EQUAL_DOCS;
690 if ((result &
EQUAL_UNITS) && !ItemFunctors::compareUnits(*i1, *i2)) result &= ~EQUAL_UNITS;
700 if (_impl->getItems().size() != other._impl->getItems().size()) {
717 template <
typename T>
719 return _impl->contains(item, flags);
724 aliases = std::make_shared<AliasMap>();
737 typedef void result_type;
739 template <
typename T>
741 *
os <<
" (" << item.
field <<
", " << item.
key <<
"),\n";
755 os <<
" '" <<
iter->first <<
"'->'" <<
iter->second <<
"'\n";
766 return afw::table::join(a, b, getDelimiter());
770 : _impl(impl), _aliases(aliases), _name(name) {}
772 template <
typename T>
774 return _impl->find<
T>(_aliases->apply(
join(_name, name)));
791 #define INSTANTIATE_LAYOUT(r, data, elem) \ 792 template Key<elem> Schema::addField(Field<elem> const &, bool); \ 793 template SchemaItem<elem> Schema::find(std::string const &) const; \ 794 template SchemaItem<elem> Schema::find(Key<elem> const &) const; \ 795 template SchemaItem<elem> detail::SchemaImpl::find(std::string const &name) const; \ 796 template int Schema::contains(SchemaItem<elem> const &, int) const; \ 797 template void Schema::replaceField(Key<elem> const &, Field<elem> const &); \ 798 template SchemaItem<elem> SubSchema::find(std::string const &) const; Defines the fields and offsets for a table.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
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.
Schema()
Construct an empty Schema.
A proxy type for name lookups in a Schema.
int getOffset() const noexcept
Return the offset (in bytes) of this field within a record.
int compare(Schema const &other, int flags=EQUAL_KEYS) const
Do a detailed equality comparison of two schemas.
Keys have the same types offsets, and sizes.
Class for storing ordered metadata with comments.
std::string join(std::string const &a, std::string const &b) const
Join strings using the field delimiter appropriate for this Schema.
static Schema fromFitsMetadata(daf::base::PropertyList &header, bool stripMetadata=true)
Construct from reading a FITS header.
Mapping class that holds aliases for a Schema.
static Key< T > makeKey(int offset)
void disconnectAliases()
Sever the connection between this schema and any others with which it shares aliases.
static Schema readFits(std::string const &filename, int hdu=fits::DEFAULT_HDU)
Construct from reading a FITS file.
Schemas have identical AliasMaps.
boost::make_variant_over< ItemTypes >::type ItemVariant
A Boost.Variant type that can hold any one of the allowed SchemaItem types.
SubSchema operator[](std::string const &name) const
Return a nested proxy.
ItemVariant const * other
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
#define AFW_TABLE_FIELD_TYPE_TUPLE
void forEach(F &&func) const
Apply a functor to each SchemaItem in the Schema.
Reports attempts to access elements using an invalid key.
Fields have the same units (ordered).
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
Fields have the same names (ordered).
A base class for image defects.
int contains(Schema const &other, int flags=EQUAL_KEYS) const
Test whether the given schema is a subset of this.
Lifetime-management for memory that goes into FITS memory files.
static Key< typename Key< T >::Element > extractElement(KeyBase< T > const &kb, int n)
A description of a field in a table.
Tag types used to declare specialized field types.
Reports errors in the logical structure of the program.
int getBit() const
The index of this field's bit within the integer it shares with other Flag fields.
std::unique_ptr< SchemaItem< U > > result
std::set< std::string > getNames(bool topOnly=false) const
Return a set of nested names that start with the SubSchema's prefix.
A private implementation class to hide the messy details of Schema.
std::string const & getName() const noexcept
Return the name of the field.
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
std::size_t hash_value() const noexcept
Return a hash of this object.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
std::shared_ptr< AliasMap > getAliasMap() const
Return the map of aliases.
friend std::ostream & operator<<(std::ostream &os, Schema const &schema)
Stringification.
A class used as a handle to a particular field in a table.
Key specialization for Flag.
#define INSTANTIATE_LAYOUT(r, data, elem)
void replaceField(Key< T > const &key, Field< T > const &field)
Replace the Field (name/description) for an existing Key.
FieldBase< T >::Element Element
Type used to store field data in the table (a field may have multiple elements).
Reports invalid arguments.
int getElementCount() const noexcept
Return the number of subfield elements (always one for scalars).
int getElementCount() const
Return the number of subfield elements (always one for scalars).
std::size_t hashCombine(std::size_t seed) noexcept
Combine hashes.
Reports errors from accepting an object of an unexpected or inappropriate type.
int getOffset() const
Return the offset in bytes of the integer element that holds this field's bit.
static std::string getTypeString()
Return a string description of the field type.
Fields have the same documentation (ordered).
std::set< std::string > getNames(bool topOnly=false) const
Return a set of field names in the schema.
Schema & operator=(Schema const &other)
Fields are identical (but aliases may not be).
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
SchemaItem< T > find(std::string const &name) const
Find a nested SchemaItem by name.
void readMetadata(daf::base::PropertySet &metadata, bool strip=false)
Read a FITS header into a PropertySet or PropertyList.
#define AFW_TABLE_FIELD_TYPE_N
A simple pair-like struct for mapping a Field (name and description) with a Key (used for actual data...