subsystems/library_manager.hpp
#ifndef STLPLUS_LIBRARY_MANAGER
#define STLPLUS_LIBRARY_MANAGER
////////////////////////////////////////////////////////////////////////////////
// Author: Andy Rushton
// Copyright: (c) Southampton University 1999-2004
// (c) Andy Rushton 2004 onwards
// License: BSD License, see ../docs/license.html
// Generalised library manager.
// Manages library units in a set of library directories. A unit is both a file
// on-disk and a data-structure in memory. To use the library manager, you need
// to:
// - design a type based on lm_unit with serialising functions read/write
// - decide on a file extension for the type
// - decide on a description of the type
// - write a create callback for this type
// - register the file extension, description and callback with the library manager
////////////////////////////////////////////////////////////////////////////////
#include "subsystems_fixes.hpp"
#include "ini_manager.hpp"
#include "smart_ptr.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <time.h>
namespace stlplus
{
////////////////////////////////////////////////////////////////////////////////
// Internals
////////////////////////////////////////////////////////////////////////////////
class lm_library;
class library_manager;
////////////////////////////////////////////////////////////////////////////////
// unit names
////////////////////////////////////////////////////////////////////////////////
class lm_unit_name
{
public:
lm_unit_name(const std::string& name = std::string(), const std::string& type = std::string());
~lm_unit_name(void);
const std::string& name(void) const;
void set_name(const std::string& name);
void lowercase(void);
const std::string& type(void) const;
void set_type(const std::string& type);
bool write(std::ostream& context) const;
bool read(std::istream& context);
std::string to_string(void) const;
bool print(std::ostream&) const;
private:
std::string m_name;
std::string m_type;
};
std::ostream& operator << (std::ostream&, const lm_unit_name&);
bool operator == (const lm_unit_name& l, const lm_unit_name& r);
bool operator < (const lm_unit_name& l, const lm_unit_name& r);
////////////////////////////////////////////////////////////////////////////////
// dependencies
////////////////////////////////////////////////////////////////////////////////
// dependencies on external files
class lm_file_dependency
{
public:
lm_file_dependency(void);
lm_file_dependency(const std::string& library_path, const std::string& path, unsigned line = 0, unsigned column = 0);
~lm_file_dependency(void);
// a path can be retrieved as either a relative path to the library or as a
// full path by providing the library path as an argument
const std::string& path(void) const;
std::string path_full(const std::string& library_path) const;
void set_path(const std::string& library_path, const std::string& path);
unsigned line(void) const;
void set_line(unsigned line = 0);
unsigned column(void) const;
void set_column(unsigned column = 0);
bool write(std::ostream& context) const;
bool read(std::istream& context);
bool print(std::ostream&) const;
private:
std::string m_path; // file dependencies are stored as paths relative to the containing library
unsigned m_line; // line - starts at 1, 0 means no line/column information
unsigned m_column; // column - starts at 0
};
std::ostream& operator <<(std::ostream&, const lm_file_dependency&);
// dependencies on other units
class lm_unit_dependency
{
public:
lm_unit_dependency(void);
lm_unit_dependency(const std::string& library, const lm_unit_name& name);
~lm_unit_dependency(void);
const std::string& library(void) const;
void set_library(const std::string& library);
const lm_unit_name& unit_name(void) const;
void set_unit_name(const lm_unit_name& unit_name);
const std::string& name(void) const;
void set_name(const std::string& name);
const std::string& type(void) const;
void set_type(const std::string& type);
bool write(std::ostream& context) const;
bool read(std::istream& context);
bool print(std::ostream&) const;
private:
std::string m_library;
lm_unit_name m_name;
};
std::ostream& operator<<(std::ostream&, const lm_unit_dependency&);
// the set of all dependencies
class lm_dependencies
{
public:
lm_dependencies(void);
lm_dependencies(const lm_dependencies&);
lm_dependencies& operator=(const lm_dependencies&);
~lm_dependencies(void);
// source file dependency
void set_source_file(const lm_file_dependency&);
bool source_file_present(void) const;
const lm_file_dependency& source_file(void) const;
// other file dependencies
unsigned file_add(const lm_file_dependency& dependency);
unsigned file_size(void) const;
const lm_file_dependency& file_dependency(unsigned) const;
void file_erase(unsigned);
// unit dependencies
unsigned unit_add(const lm_unit_dependency& dependency);
unsigned unit_size(void) const;
const lm_unit_dependency& unit_dependency(unsigned) const;
void unit_erase(unsigned);
void clear(void);
bool empty(void) const;
bool write(std::ostream& context) const;
bool read(std::istream& context);
bool print(std::ostream&) const;
private:
lm_file_dependency* m_source; // source file dependency (optional)
std::vector<lm_file_dependency> m_files; // other file dependencies
std::vector<lm_unit_dependency> m_units; // unit dependencies
};
std::ostream& operator << (std::ostream&, const lm_dependencies&);
////////////////////////////////////////////////////////////////////////////////
// library unit superclass
// user's units must be derivatives of lm_unit and overload all the virtuals
class lm_unit
{
friend class lm_library;
public:
////////////////////////////////////////
// constructor/destructor
lm_unit(const lm_unit_name& name, lm_library* library);
virtual ~lm_unit(void);
////////////////////////////////////////
// Header data
// unit name
const lm_unit_name& unit_name(void) const;
const std::string& name(void) const;
const std::string& type(void) const;
// dependencies
// all file dependencies are converted for internal use to a path relative to the library
// they can be retrieved either in this form or as a full path
// source file dependency
void set_source_file(const lm_file_dependency&);
bool source_file_present(void) const;
const lm_file_dependency& source_file(void) const;
// other file dependencies
unsigned file_add(const lm_file_dependency& dependency);
unsigned file_size(void) const;
const lm_file_dependency& file_dependency(unsigned) const;
void file_erase(unsigned);
// unit dependencies
unsigned unit_add(const lm_unit_dependency& dependency);
unsigned unit_size(void) const;
const lm_unit_dependency& unit_dependency(unsigned) const;
void unit_erase(unsigned);
const lm_dependencies& dependencies(void) const;
void set_dependencies(const lm_dependencies&);
void clear_dependencies(void);
bool empty_dependencies(void) const;
// dependency checking
bool out_of_date(void) const;
bool up_to_date(void) const;
lm_dependencies out_of_date_reason(void) const;
// supplementary data
const std::string& supplementary_data(void) const;
void set_supplementary_data(const std::string& data);
////////////////////////////////////////
// unit data management
bool load(void);
bool save(void);
bool loaded(void) const;
void mark(void);
// file modified time - only changes after a save
time_t modified(void) const;
////////////////////////////////////////
// containing library manager details
// get the owning library
const lm_library* library(void) const;
lm_library* library(void);
// owning library name and path
const std::string& library_name(void) const;
const std::string& library_path(void) const;
////////////////////////////////////////
// error handling - these apply to the last read/write operation
bool error(void) const;
////////////////////////////////////////
// functions that customise subclasses of this superclass
// You MUST provide at least:
// - read - either read operation can be overloaded
// - write - either write operation can be overloaded
// - clone
// read(filename) is the one actually called to read your data
// the default read(filename) simply calls read(istream) to actually read the file
// the default read(istream) does nothing but fail by returning false so you must overload one or other
// you can just overload read(istream) if you want to use IOstream, or you
// can overload read(filename) to use any I/O system
virtual bool read(const std::string& filename, void* type_data);
virtual bool read(std::istream& file, void* type_data);
// as above, but for writing the data type
// write(filename) is the one actually called to write your data
// the default write(filename) simply calls write(ostream) to actually write the file
// the default write(ostream) does nothing but fail by returning false so you must overload one or other
// you can just overload write(ostream) if you want to use IOstream, or you
// can overload write(filename) to use any I/O system
virtual bool write(const std::string& filename, void* type_data);
virtual bool write(std::ostream& file, void* type_data);
// purge clears any memory associated with the unit - makes the unit unloaded
// the default does nothing
virtual bool purge(void);
// the clone function creates a new-ed copy of the subclass
virtual lm_unit* clone(void) const;
// the default print routines print header-only information
// you can overload these to provide a debug printout of the data structure
virtual bool print(std::ostream&) const;
virtual bool print_long(std::ostream&) const;
protected:
// header file management
std::string filename(void) const;
std::string header_filename(void) const;
bool write_header(void);
bool read_header(void);
private:
// header fields
lm_unit_name m_name; // name
lm_dependencies m_dependencies; // file and unit dependencies
std::string m_supplement; // supplementary data
bool m_header_modified; // header modified
// internal fields
bool m_loaded; // loaded flag
bool m_marked; // mark - determines whether the unit needs saving
// library manager fields
lm_library* m_library; // parent library
// error handling fields
bool m_error; // error flag if load or save fails - from IOstream
};
// Iostream print calls the short print method
std::ostream& operator << (std::ostream& str, const lm_unit& u);
////////////////////////////////////////////////////////////////////////////////
// other types used in the library manager
////////////////////////////////////////////////////////////////////////////////
// user types
typedef smart_ptr_nocopy<lm_unit> lm_unit_ptr;
typedef lm_unit* (*lm_create_callback)(const lm_unit_name& unit_name, lm_library* parent_library, void* type_data);
// internal types used in the library manager but made global because they are shared
struct lm_callback_entry
{
lm_create_callback m_callback;
std::string m_description;
void* m_type_data;
lm_callback_entry(lm_create_callback callback = 0, const std::string& description = std::string(), void* type_data = 0) :
m_callback(callback), m_description(description), m_type_data(type_data) {}
};
////////////////////////////////////////////////////////////////////////////////
// Library
// Must be contained in a library_manager
// Manages objects of class lm_unit and its subclasses
////////////////////////////////////////////////////////////////////////////////
class lm_library
{
public:
friend class library_manager;
friend class lm_unit;
//////////////////////////////////////////////////////////////////////////////
// constructors/destructor - lm_library should only ever be constructed by library_manager
lm_library(library_manager* manager);
lm_library(const lm_library&);
lm_library& operator = (const lm_library&);
~lm_library(void);
public:
const library_manager* manager(void) const;
library_manager* manager(void);
//////////////////////////////////////////////////////////////////////////////
// initialisers
bool create(const std::string& name, const std::string& path, bool writable);
bool open(const std::string& path);
//////////////////////////////////////////////////////////////////////////////
// management of types
bool load_type(const std::string& type);
bool load_types(void);
bool remove_type(const std::string& type);
//////////////////////////////////////////////////////////////////////////////
// whole library operations
bool load(void);
bool save(void);
bool purge(void);
bool close(void);
bool erase(void);
const std::string& name(void) const;
const std::string& path(void) const;
//////////////////////////////////////////////////////////////////////////////
// managing read/write status
bool set_read_write(bool writable);
bool set_writable(void);
bool set_read_only(void);
bool writable(void) const;
bool read_only(void) const;
bool os_writable(void) const;
bool os_read_only(void) const;
bool lm_writable(void) const;
bool lm_read_only(void) const;
//////////////////////////////////////////////////////////////////////////////
// unit management
bool exists(const lm_unit_name& name) const;
lm_unit_ptr create(const lm_unit_name&);
bool loaded(const lm_unit_name& name) const;
bool load(const lm_unit_name& unit);
bool purge(const lm_unit_name& unit);
bool save(const lm_unit_name& unit);
bool erase(const lm_unit_name& name);
bool mark(const lm_unit_name& name);
time_t modified(const lm_unit_name& name) const;
bool erase_by_source(const std::string& source_file);
const lm_unit_ptr find(const lm_unit_name& name) const;
lm_unit_ptr find(const lm_unit_name& name);
std::vector<lm_unit_name> names(void) const;
std::vector<std::string> names(const std::string& type) const;
std::vector<lm_unit_ptr> handles(void) const;
std::vector<lm_unit_ptr> handles(const std::string& type) const;
//////////////////////////////////////////////////////////////////////////////
// dependency checking
bool out_of_date(const lm_unit_name& name) const;
bool up_to_date(const lm_unit_name& name) const;
lm_dependencies out_of_date_reason(const lm_unit_name& name) const;
std::pair<bool,unsigned> tidy(void);
//////////////////////////////////////////////////////////////////////////////
// do-everything print function
bool pretty_print(std::ostream& str,
bool print_units = false, // print the unit names not just the library names
const std::string& type = std::string()) const; // print just this type ("" means all)
////////////////////////////////////////////////////////////////////////////////
// diagnostic print routines
bool print(std::ostream& str) const;
bool print_long(std::ostream& str) const;
private:
std::map<lm_unit_name,lm_unit_ptr>::iterator local_find(const lm_unit_name& name);
std::map<lm_unit_name,lm_unit_ptr>::const_iterator local_find(const lm_unit_name& name) const;
std::string m_name; // name
std::string m_path; // path
bool m_writable; // writable
std::map<lm_unit_name,lm_unit_ptr> m_units; // units
library_manager* m_manager; // parent library manager
};
std::ostream& operator << (std::ostream& str, const lm_library& lib);
////////////////////////////////////////////////////////////////////////////////
// Library Manager
////////////////////////////////////////////////////////////////////////////////
class library_manager
{
public:
friend class lm_library;
friend class lm_unit;
////////////////////////////////////////////////////////////////////////////////
// static functions allow you to test whether a directory is a library before opening it
// you can also find the library's name without opening it
static bool is_library(const std::string& path, const std::string& owner);
static std::string library_name(const std::string& path, const std::string& owner);
// non-static forms test for libraries with the same owner as the library manager
bool is_library(const std::string& path);
std::string library_name(const std::string& path);
//////////////////////////////////////////////////////////////////////////////
// tructors
explicit library_manager(const std::string& owner, bool library_case = false, bool unit_case = false);
~library_manager(void);
//////////////////////////////////////////////////////////////////////////////
// case sensitivity
bool library_case(void) const;
void set_library_case(bool library_case);
bool unit_case(void) const;
void set_unit_case(bool library_case);
//////////////////////////////////////////////////////////////////////////////
// type handling
// only units of types added in this way will be recognised
bool add_type(const std::string& type,
const std::string& description,
lm_create_callback fn = 0,
void* type_data = 0);
bool remove_type(const std::string& type);
std::vector<std::string> types(void) const;
std::string description(const std::string& type) const;
lm_create_callback callback(const std::string& type) const;
void* type_data(const std::string& type) const;
//////////////////////////////////////////////////////////////////////////////
// Library mappings
// The library manager implements two different styles of library mappings
// - mapping file
// - ini file
// mapping file handling uses a simple text file to store the mappings in an internally-defined format
// ini file handling stores library mappings using the ini_manager component
// These modes are switched on by simply specifying a mapping file or an ini file to hold the mappings
// mapping file methods
// set but do not load - use this when you want to create a new mapping file
void set_mapping_file(const std::string& mapping_file);
// set and load - use this with an existing mapping file
bool load_mappings (const std::string& mapping_file);
// return the mapping file string
std::string mapping_file();
// ini file methods - the ini manager must be pre-loaded with the list of ini files to manage
// set and load - this will create the relevant sections in the local ini file if not present already
bool set_ini_manager(ini_manager* ini_files, const std::string& library_section, const std::string& work_section);
ini_manager* get_ini_manager(void) const;
// save to the library mapping handler, whichever kind it is
bool save_mappings (void);
//////////////////////////////////////////////////////////////////////////////
// library management
// operations on a single library
// test whether a named library exists
bool exists(const std::string& name) const;
// create a new libarry in the specified directory
lm_library* create(const std::string& name, const std::string& path, bool writable = true);
// open an existing library
lm_library* open(const std::string& path);
// load all units in the library
bool load(const std::string& name);
// save all marked units in the library
bool save(const std::string& name);
// purge all loaded units in the library
bool purge(const std::string& name);
// close the library - remove it from the manager but leave on disk
bool close(const std::string& name);
// erase the library - delete the directory and remove the library from the manager
bool erase(const std::string& name);
// operations on all libraries - as above but applied to all the libraries in the manager
bool load(void);
bool save(void);
bool purge(void);
bool close(void);
bool erase(void);
// get name and path of a library - name can differ in case if the library manager is case-insensitive
std::string name(const std::string& library) const;
std::string path(const std::string& library) const;
// control and test read/write status
bool set_writable(const std::string& library);
bool set_read_only(const std::string& library);
bool writable(const std::string& library) const;
bool read_only(const std::string& library) const;
bool os_writable(const std::string& library) const;
bool os_read_only(const std::string& library) const;
bool lm_writable(const std::string& library) const;
bool lm_read_only(const std::string& library) const;
// find a library in the manager - returns null if not found
lm_library* find(const std::string& name);
const lm_library* find(const std::string& name) const;
// get the set of all library names
std::vector<std::string> names(void) const;
// get the set of all libraries
std::vector<const lm_library*> handles(void) const;
std::vector<lm_library*> handles(void);
//////////////////////////////////////////////////////////////////////////////
// current library management
bool setwork(const std::string& library);
bool unsetwork(void);
const lm_library* work(void) const;
lm_library* work(void);
std::string work_name(void) const;
//////////////////////////////////////////////////////////////////////////////
// unit management within a library
// Note: you can also manipulate the library class through a handle returned by find() or handles()
bool exists(const std::string& library, const lm_unit_name& name) const;
lm_unit_ptr create(const std::string& library, const lm_unit_name& name);
bool loaded(const std::string& library, const lm_unit_name& name) const;
bool load(const std::string& library, const lm_unit_name& name);
bool purge(const std::string& library, const lm_unit_name& name);
bool save(const std::string& library, const lm_unit_name& name);
bool erase(const std::string& library, const lm_unit_name& name);
bool mark(const std::string& library, const lm_unit_name& name);
time_t modified(const std::string& library, const lm_unit_name& name) const;
bool erase_by_source(const std::string& source_file);
const lm_unit_ptr find(const std::string& library, const lm_unit_name& name) const;
lm_unit_ptr find(const std::string& library, const lm_unit_name& name);
std::vector<lm_unit_name> names(const std::string& library) const;
std::vector<std::string> names(const std::string& library, const std::string& type) const;
std::vector<lm_unit_ptr> handles(const std::string& library) const;
std::vector<lm_unit_ptr> handles(const std::string& library, const std::string& type) const;
//////////////////////////////////////////////////////////////////////////////
// dependency checking
bool out_of_date(const std::string& library, const lm_unit_name& name) const;
bool up_to_date(const std::string& library, const lm_unit_name& name) const;
lm_dependencies out_of_date_reason(const std::string& library, const lm_unit_name& name) const;
// delete out of date units from a library or all libraries
// return the number of units tidied and a flag to say whether all units were successfully tidied
std::pair<bool,unsigned> tidy(const std::string& library);
std::pair<bool,unsigned> tidy(void);
//////////////////////////////////////////////////////////////////////////////
// do-everything print routine!
bool pretty_print(std::ostream& str,
bool print_units = false, // print the unit names not just the library names
const std::string& library = std::string(), // print just the specified library ("" means all)
const std::string& type = std::string()) const; // print just this type ("" means all)
////////////////////////////////////////////////////////////////////////////////
// diagnostic print routines
bool print(std::ostream& str) const;
bool print_long(std::ostream& str) const;
//////////////////////////////////////////////////////////////////////////////
// internals
private:
// NOT a copyable object
library_manager(const library_manager&);
library_manager& operator = (const library_manager&);
protected:
std::list<lm_library>::iterator local_find(const std::string& name);
std::list<lm_library>::const_iterator local_find(const std::string& name) const;
std::string m_owner; // owner application name
std::string m_mapping_file; // mapping file method of library management
ini_manager* m_ini_files; // ini manager method of library management
std::string m_ini_section; // ini manager method of library management
std::string m_ini_work; // ini manager method of library management
std::list<lm_library> m_libraries; // libraries
std::string m_work; // work library
std::map<std::string,lm_callback_entry> m_callbacks; // callbacks
bool m_library_case; // case sensitivity for library names
bool m_unit_case; // case sensitivity for unit names
};
std::ostream& operator << (std::ostream& str, const library_manager& libraries);
////////////////////////////////////////////////////////////////////////////////
} // end namespace stlplus
#endif