Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Corryvreckan
Corryvreckan
Commits
78edf115
Commit
78edf115
authored
Oct 06, 2017
by
Simon Spannagel
Browse files
Add Configuration and utilities from Allpix Squared
parent
b24abf15
Changes
17
Hide whitespace changes
Inline
Side-by-side
src/core/config/ConfigManager.cpp
0 → 100644
View file @
78edf115
/**
* @file
* @brief Implementation of config manager
* @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#include
"ConfigManager.hpp"
#include
<algorithm>
#include
<fstream>
#include
<string>
#include
<vector>
#include
"Configuration.hpp"
#include
"core/utils/file.h"
#include
"core/utils/log.h"
#include
"exceptions.h"
using
namespace
corryvreckan
;
/**
* @throws ConfigFileUnavailableError If the main configuration file cannot be accessed
*/
ConfigManager
::
ConfigManager
(
std
::
string
file_name
)
:
file_name_
(
std
::
move
(
file_name
))
{
LOG
(
TRACE
)
<<
"Using "
<<
file_name_
<<
" as main configuration file"
;
// Check if the file exists
std
::
ifstream
file
(
file_name_
);
if
(
!
file
)
{
throw
ConfigFileUnavailableError
(
file_name_
);
}
// Convert main file to absolute path
file_name_
=
corryvreckan
::
get_absolute_path
(
file_name_
);
// Read the file
reader_
.
add
(
file
,
file_name_
);
}
/**
* @warning Only one header can be added in this way to define its main name
*/
void
ConfigManager
::
setGlobalHeaderName
(
std
::
string
name
)
{
global_default_name_
=
name
;
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
global_names_
.
emplace
(
std
::
move
(
name
));
}
void
ConfigManager
::
addGlobalHeaderName
(
std
::
string
name
)
{
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
global_names_
.
emplace
(
std
::
move
(
name
));
}
/**
* The global configuration is the combination of all sections with a global header.
*/
Configuration
ConfigManager
::
getGlobalConfiguration
()
{
Configuration
global_config
(
global_default_name_
,
file_name_
);
for
(
auto
&
global_name
:
global_names_
)
{
auto
configs
=
reader_
.
getConfigurations
(
global_name
);
for
(
auto
&
config
:
configs
)
{
global_config
.
merge
(
config
);
}
}
return
global_config
;
}
void
ConfigManager
::
addIgnoreHeaderName
(
std
::
string
name
)
{
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
ignore_names_
.
emplace
(
std
::
move
(
name
));
}
bool
ConfigManager
::
hasConfiguration
(
const
std
::
string
&
name
)
{
return
reader_
.
hasConfiguration
(
name
);
}
/**
* All special global and ignored sections are removed before returning the rest of the configurations. The list of normal
* sections is used by the ModuleManager to instantiate all the required modules.
*/
std
::
vector
<
Configuration
>
ConfigManager
::
getConfigurations
()
const
{
std
::
vector
<
Configuration
>
result
;
for
(
auto
&
config
:
reader_
.
getConfigurations
())
{
// ignore all global and ignores names
std
::
string
config_name
=
config
.
getName
();
std
::
transform
(
config_name
.
begin
(),
config_name
.
end
(),
config_name
.
begin
(),
::
tolower
);
if
(
global_names_
.
find
(
config_name
)
!=
global_names_
.
end
()
||
ignore_names_
.
find
(
config_name
)
!=
ignore_names_
.
end
())
{
continue
;
}
result
.
push_back
(
config
);
}
return
result
;
}
src/core/config/ConfigManager.hpp
0 → 100644
View file @
78edf115
/**
* @file
* @brief Interface to the main configuration and its normal and special sections
* @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#ifndef CORRYVRECKAN_CONFIG_MANAGER_H
#define CORRYVRECKAN_CONFIG_MANAGER_H
#include
<set>
#include
<string>
#include
<vector>
#include
"ConfigReader.hpp"
#include
"Configuration.hpp"
namespace
corryvreckan
{
/**
* @ingroup Managers
* @brief Manager responsible for loading and providing access to the main configuration
*
* The main configuration is the single most important source of configuration. It is split up in:
* - Global headers that are combined into a single global (not module specific) configuration
* - Ignored headers that are not used at all (mainly useful for debugging)
* - All other headers representing all modules that have to be instantiated by the ModuleManager
*
* Configuration sections are always case-sensitive.
*/
class
ConfigManager
{
public:
/**
* @brief Construct the configuration manager
* @param file_name Path to the main configuration file
*/
explicit
ConfigManager
(
std
::
string
file_name
);
/**
* @brief Use default destructor
*/
~
ConfigManager
()
=
default
;
/// @{
/**
* @brief Copying the manager is not allowed
*/
ConfigManager
(
const
ConfigManager
&
)
=
delete
;
ConfigManager
&
operator
=
(
const
ConfigManager
&
)
=
delete
;
/// @}
/// @{
/**
* @brief Use default move behaviour
*/
ConfigManager
(
ConfigManager
&&
)
noexcept
=
default
;
ConfigManager
&
operator
=
(
ConfigManager
&&
)
noexcept
=
default
;
/// @}
/**
* @brief Set the name of the global header and add to the global names
* @param name Name of a global header that should be used as the name
*/
// TODO [doc] Should only set the name and do not add it
void
setGlobalHeaderName
(
std
::
string
name
);
/**
* @brief Add a global header name
* @param name Name of a global header section
*/
// TODO [doc] Rename to addGlobalHeader
void
addGlobalHeaderName
(
std
::
string
name
);
/**
* @brief Get the global configuration
* @return Global configuration
*/
Configuration
getGlobalConfiguration
();
/**
* @brief Add a header name to fully ignore
* @param name Name of a header to ignore
*/
// TODO [doc] Rename to ignoreHeader
void
addIgnoreHeaderName
(
std
::
string
name
);
/**
* @brief Return if section with given name exists
* @param name Name of the section
* @return True if at least one section with that name exists, false otherwise
*/
bool
hasConfiguration
(
const
std
::
string
&
);
/**
* @brief Get all configurations that are not global or ignored
* @return List of all normal configurations
*/
std
::
vector
<
Configuration
>
getConfigurations
()
const
;
private:
std
::
string
file_name_
;
ConfigReader
reader_
;
std
::
string
global_default_name_
;
std
::
set
<
std
::
string
>
global_names_
;
std
::
set
<
std
::
string
>
ignore_names_
;
};
}
// namespace corryvreckan
#endif
/* CORRYVRECKAN_CONFIG_MANAGER_H */
src/core/config/ConfigReader.cpp
0 → 100644
View file @
78edf115
/**
* @file
* @brief Implementation of config reader
* @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#include
"ConfigReader.hpp"
#include
<algorithm>
#include
<cstdlib>
#include
<fstream>
#include
<string>
#include
<vector>
#include
"core/utils/file.h"
#include
"core/utils/log.h"
#include
"exceptions.h"
using
namespace
corryvreckan
;
ConfigReader
::
ConfigReader
()
=
default
;
ConfigReader
::
ConfigReader
(
std
::
istream
&
stream
,
std
::
string
file_name
)
:
ConfigReader
()
{
add
(
stream
,
std
::
move
(
file_name
));
}
ConfigReader
::
ConfigReader
(
const
ConfigReader
&
other
)
:
conf_array_
(
other
.
conf_array_
)
{
copy_init_map
();
}
ConfigReader
&
ConfigReader
::
operator
=
(
const
ConfigReader
&
other
)
{
conf_array_
=
other
.
conf_array_
;
copy_init_map
();
return
*
this
;
}
void
ConfigReader
::
copy_init_map
()
{
conf_map_
.
clear
();
for
(
auto
iter
=
conf_array_
.
begin
();
iter
!=
conf_array_
.
end
();
++
iter
)
{
conf_map_
[
iter
->
getName
()].
push_back
(
iter
);
}
}
/**
* @throws ConfigParseError If an error occurred during the parsing of the stream
*
* The configuration is immediately parsed and all of its configurations are available after the functions returns.
*/
void
ConfigReader
::
add
(
std
::
istream
&
stream
,
std
::
string
file_name
)
{
LOG
(
TRACE
)
<<
"Parsing configuration file "
<<
file_name
;
// Convert file name to absolute path (if given)
if
(
!
file_name
.
empty
())
{
file_name
=
corryvreckan
::
get_absolute_path
(
file_name
);
}
// Build first empty configuration
std
::
string
section_name
;
Configuration
conf
(
section_name
,
file_name
);
int
line_num
=
0
;
while
(
true
)
{
// Process config line by line
std
::
string
line
;
if
(
stream
.
eof
())
{
break
;
}
std
::
getline
(
stream
,
line
);
++
line_num
;
// Find equal sign
size_t
equals_pos
=
line
.
find
(
'='
);
if
(
equals_pos
==
std
::
string
::
npos
)
{
line
=
corryvreckan
::
trim
(
line
);
// Ignore empty lines or comments
if
(
line
==
""
||
line
[
0
]
==
'#'
)
{
continue
;
}
// Parse new section
if
(
line
[
0
]
==
'['
&&
line
[
line
.
length
()
-
1
]
==
']'
)
{
// Ignore empty sections if they contain no configurations
if
(
!
conf
.
getName
().
empty
()
||
conf
.
countSettings
()
>
0
)
{
// Add previous section
addConfiguration
(
conf
);
}
// Begin new section
section_name
=
std
::
string
(
line
,
1
,
line
.
length
()
-
2
);
conf
=
Configuration
(
section_name
,
file_name
);
}
else
{
// FIXME: should be a bit more helpful...
throw
ConfigParseError
(
file_name
,
line_num
);
}
}
else
{
std
::
string
key
=
trim
(
std
::
string
(
line
,
0
,
equals_pos
));
std
::
string
value
=
trim
(
std
::
string
(
line
,
equals_pos
+
1
));
char
ins
=
0
;
for
(
size_t
i
=
0
;
i
<
value
.
size
();
++
i
)
{
if
(
value
[
i
]
==
'\''
||
value
[
i
]
==
'\"'
)
{
if
(
ins
==
0
)
{
ins
=
value
[
i
];
}
else
if
(
ins
==
value
[
i
])
{
ins
=
0
;
}
}
if
(
ins
==
0
&&
value
[
i
]
==
'#'
)
{
value
=
std
::
string
(
value
,
0
,
i
);
break
;
}
}
// Add the config key
conf
.
setText
(
key
,
trim
(
value
));
}
}
// Add last section
addConfiguration
(
conf
);
}
void
ConfigReader
::
addConfiguration
(
Configuration
config
)
{
conf_array_
.
push_back
(
std
::
move
(
config
));
std
::
string
section_name
=
conf_array_
.
back
().
getName
();
std
::
transform
(
section_name
.
begin
(),
section_name
.
end
(),
section_name
.
begin
(),
::
tolower
);
conf_map_
[
section_name
].
push_back
(
--
conf_array_
.
end
());
}
void
ConfigReader
::
clear
()
{
conf_map_
.
clear
();
conf_array_
.
clear
();
}
bool
ConfigReader
::
hasConfiguration
(
std
::
string
name
)
const
{
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
return
conf_map_
.
find
(
name
)
!=
conf_map_
.
end
();
}
unsigned
int
ConfigReader
::
countConfigurations
(
std
::
string
name
)
const
{
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
if
(
!
hasConfiguration
(
name
))
{
return
0
;
}
return
static_cast
<
unsigned
int
>
(
conf_map_
.
at
(
name
).
size
());
}
/**
* @warning This will have the file path of the first header section
* @note An empty configuration is returned if no empty section is found
*/
Configuration
ConfigReader
::
getHeaderConfiguration
()
const
{
// Get empty configurations
std
::
vector
<
Configuration
>
configurations
=
getConfigurations
(
""
);
if
(
configurations
.
empty
())
{
// Use all configurations to find the file name if no empty
configurations
=
getConfigurations
();
std
::
string
file_name
;
if
(
!
configurations
.
empty
())
{
file_name
=
configurations
.
at
(
0
).
getFilePath
();
}
return
Configuration
(
""
,
file_name
);
}
// Merge all configurations
Configuration
header_config
=
configurations
.
at
(
0
);
for
(
auto
&
config
:
configurations
)
{
// NOTE: Merging first configuration again has no effect
header_config
.
merge
(
config
);
}
return
header_config
;
}
std
::
vector
<
Configuration
>
ConfigReader
::
getConfigurations
(
std
::
string
name
)
const
{
std
::
transform
(
name
.
begin
(),
name
.
end
(),
name
.
begin
(),
::
tolower
);
if
(
!
hasConfiguration
(
name
))
{
return
std
::
vector
<
Configuration
>
();
}
std
::
vector
<
Configuration
>
result
;
for
(
auto
&
iter
:
conf_map_
.
at
(
name
))
{
result
.
push_back
(
*
iter
);
}
return
result
;
}
std
::
vector
<
Configuration
>
ConfigReader
::
getConfigurations
()
const
{
return
std
::
vector
<
Configuration
>
(
conf_array_
.
begin
(),
conf_array_
.
end
());
}
src/core/config/ConfigReader.hpp
0 → 100644
View file @
78edf115
/**
* @file
* @brief Provides a reader for configuration files
* @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#ifndef CORRYVRECKAN_CONFIG_READER_H
#define CORRYVRECKAN_CONFIG_READER_H
#include
<istream>
#include
<list>
#include
<map>
#include
<string>
#include
<vector>
#include
"Configuration.hpp"
namespace
corryvreckan
{
/**
* @brief Reader of configuration files
*
* Read the internal configuration file format used in the framework. The format contains
* - A set of section header between [ and ] brackets
* - Key/value pairs linked to the last defined section (or the empty section if none has been defined yet)
*/
class
ConfigReader
{
public:
/**
* @brief Constructs a config reader without any attached streams
*/
ConfigReader
();
/**
* @brief Constructs a config reader with a single attached stream
* @param stream Stream to read configuration from
* @param file_name Name of the file related to the stream or empty if not linked to a file
*/
explicit
ConfigReader
(
std
::
istream
&
stream
,
std
::
string
file_name
=
""
);
/**
* @brief Adds a configuration stream to read
* @param stream Stream to read configuration from
* @param file_name Name of the file related to the stream or empty if not linked to a file
*/
void
add
(
std
::
istream
&
,
std
::
string
file_name
=
""
);
/**
* @brief Directly add a configuration object to the reader
* @param config Configuration object to add
*/
void
addConfiguration
(
Configuration
config
);
/// @{
/**
* @brief Implement correct copy behaviour
*/
ConfigReader
(
const
ConfigReader
&
);
ConfigReader
&
operator
=
(
const
ConfigReader
&
);
/// @}
/// @{
/**
* @brief Use default move behaviour
*/
ConfigReader
(
ConfigReader
&&
)
noexcept
=
default
;
ConfigReader
&
operator
=
(
ConfigReader
&&
)
noexcept
=
default
;
/// @}
/**
* @brief Removes all streams and all configurations
*/
void
clear
();
/**
* @brief Check if a configuration exists
* @param name Name of a configuration header to search for
* @return True if at least a single configuration with this name exists, false otherwise
*/
bool
hasConfiguration
(
std
::
string
name
)
const
;
/**
* @brief Count the number of configurations with a particular name
* @param name Name of a configuration header
* @return The number of configurations with the given name
*/
unsigned
int
countConfigurations
(
std
::
string
name
)
const
;
/**
* @brief Get cmobined configuration of all empty sections (usually the header)
* @note Typically this is only the section at the top of the file
* @return Configuration object for the empty section
*/
Configuration
getHeaderConfiguration
()
const
;
/**
* @brief Get all configurations with a particular header
* @param name Header name of the configurations to return
* @return List of configurations with the given name
*/
std
::
vector
<
Configuration
>
getConfigurations
(
std
::
string
name
)
const
;
/**
* @brief Get all configurations
* @return List of all configurations
*/
std
::
vector
<
Configuration
>
getConfigurations
()
const
;
private:
/**
* @brief Initialize the configuration map after copy of the class
*/
void
copy_init_map
();
std
::
map
<
std
::
string
,
std
::
vector
<
std
::
list
<
Configuration
>::
iterator
>>
conf_map_
;
std
::
list
<
Configuration
>
conf_array_
;
};
}
// namespace corryvreckan
#endif
/* CORRYVRECKAN_CONFIG_MANAGER_H */
src/core/config/Configuration.cpp
0 → 100644
View file @
78edf115
/**
* @file
* @brief Implementation of configuration
* @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#include
"Configuration.hpp"
#include
<cassert>
#include
<ostream>
#include
<stdexcept>
#include
<string>
#include
"core/utils/file.h"
#include
"exceptions.h"
#include
<iostream>
using
namespace
corryvreckan
;
Configuration
::
Configuration
(
std
::
string
name
,
std
::
string
path
)
:
name_
(
std
::
move
(
name
)),
path_
(
std
::
move
(
path
))
{}
bool
Configuration
::
has
(
const
std
::
string
&
key
)
const
{
return
config_
.
find
(
key
)
!=
config_
.
cend
();
}
std
::
string
Configuration
::
getName
()
const
{
return
name_
;
}
std
::
string
Configuration
::
getFilePath
()
const
{
return
path_
;
}
std
::
string
Configuration
::
getText
(
const
std
::
string
&
key
)
const
{
try
{
// NOTE: returning literally including ""
return
config_
.
at
(
key
);
}
catch
(
std
::
out_of_range
&
e
)
{
throw
MissingKeyError
(
key
,
getName
());
}
}
std
::
string
Configuration
::
getText
(
const
std
::
string
&
key
,
const
std
::
string
&
def
)
const
{
if
(
!
has
(
key
))
{
return
def
;
}
return
getText
(
key
);
}
/**
* @throws InvalidValueError If the path did not exists while the check_exists parameter is given
*
* For a relative path the absolute path of the configuration file is preprended. Absolute paths are not changed.
*/
// TODO [doc] Document canonicalizing behaviour
std
::
string
Configuration
::
getPath
(
const
std
::
string
&
key
,
bool
check_exists
)
const
{
try
{
return
path_to_absolute
(
get
<
std
::
string
>
(
key
),
check_exists
);
}
catch
(
std
::
invalid_argument
&
e
)
{
throw
InvalidValueError
(
*
this
,
key
,
e
.
what
());
}
}
/**
* @throws InvalidValueError If the path did not exists while the check_exists parameter is given
*
* For all relative paths the absolute path of the configuration file is preprended. Absolute paths are not changed.
*/
// TODO [doc] Document canonicalizing behaviour
std
::
vector
<
std
::
string
>
Configuration
::
getPathArray
(
const
std
::
string
&
key
,
bool
check_exists
)
const
{
std
::
vector
<
std
::
string
>
path_array
=
getArray
<
std
::
string
>
(
key
);
// Convert all paths to absolute
try
{
for
(
auto
&
path
:
path_array
)
{
path
=
path_to_absolute
(
path
,
check_exists
);
}
return
path_array
;
}
catch
(
std
::
invalid_argument
&
e
)
{
throw
InvalidValueError
(
*
this
,
key
,
e
.
what
());
}
}
/**
* @throws std::invalid_argument If the path does not exists
*/
std
::
string
Configuration
::
path_to_absolute
(
std
::
string
path
,
bool
canonicalize_path
)
const
{