1 #!/usr/bin/env python 2 3 """ 4 Metadata for document conversion. 5 6 Copyright (C) 2018, 2019, 2021, 2023 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from moinformat.input import get_input 23 from moinformat.links import get_linker 24 from moinformat.output import get_output 25 from moinformat.parsers import get_parser, parsers 26 from moinformat.serialisers import get_serialiser, serialisers 27 from moinformat.themes import get_theme 28 29 class Metadata: 30 31 "Metadata employed in the document conversion process." 32 33 defaults = { 34 "attachments" : "attachments", 35 "input_format" : "moin", 36 "output_context" : "standalone", 37 "output_format" : "moin", 38 "root_pagename" : "FrontPage", 39 } 40 41 default_effects = { 42 "output_format" : "link_format", 43 } 44 45 effects = { 46 "input_context" : ["input"], 47 "input_format" : ["parser", "serialiser"], 48 "input_separator" : ["input"], 49 "link_format" : ["linker"], 50 "output_context" : ["output"], 51 "output_format" : ["serialiser"], 52 "theme_name" : ["theme"], 53 } 54 55 def __init__(self, parameters=None): 56 57 "Initialise the metadata collection using the 'parameters'." 58 59 self.parameters = parameters or {} 60 61 def __repr__(self): 62 return "Metadata(%r)" % self.parameters 63 64 def copy(self): 65 66 "Return a copy of this instance." 67 68 parameters = {} 69 parameters.update(self.parameters) 70 return self.__class__(parameters) 71 72 def get(self, name, default=None): 73 74 """ 75 Return the setting for 'name', returning 'default' if 'name' is not 76 set. If 'default' is None or omitted and a default is present in the 77 defaults registry, this is returned if no setting is defined. 78 """ 79 80 value = self.parameters.get(name, default) 81 if value is None: 82 return self.defaults.get(name, default) 83 else: 84 return value 85 86 def has_key(self, name): 87 return self.parameters.has_key(name) 88 89 def set(self, name, value): 90 91 "Set 'name' as 'value' in the metadata." 92 93 self.parameters[name] = value 94 95 # Invalidate any affected settings. 96 97 affected = self.effects.get(name) 98 99 if affected: 100 for affected_name in affected: 101 if self.has_key(affected_name): 102 del self.parameters[affected_name] 103 104 # Set any default values. 105 106 affected = self.default_effects.get(name) 107 108 if affected and not self.get(affected): 109 self.set(affected, value) 110 111 def get_update(self, name, value=None): 112 113 """ 114 Obtain the 'name' setting, this being overwritten by 'value' if 115 specified. Return the updated setting. 116 """ 117 118 # Overwrite any existing setting. 119 120 if value: 121 self.set(name, value) 122 return value 123 else: 124 return self.get(name) 125 126 def make_object(self, name, cls): 127 128 """ 129 Make an object to be stored in the setting 'name', using 'cls' as the 130 object class. 131 """ 132 133 # Return any existing, preserved object. Since updates to various 134 # properties will discard objects, any preserved object should still be 135 # applicable. 136 137 obj = self.get(name) 138 if obj: 139 return obj 140 141 # Without any object class, return None. 142 143 if not cls: 144 self.set(name, None) 145 return None 146 147 # Instantiate the class and record the object. 148 149 obj = cls(self) 150 self.set(name, obj) 151 return obj 152 153 def get_input(self, name=None): 154 155 """ 156 Make an input context using any given 'name' or otherwise using the 157 "input_context" setting which will be replaced by any given 'name'. 158 """ 159 160 cls = get_input(self.get_update("input_context", name)) 161 162 return self.make_object("input", cls) 163 164 def get_linker(self, name=None): 165 166 """ 167 Make a linker using any given 'name' or otherwise using the 168 "link_format" setting which will be replaced by any given 'name'. 169 """ 170 171 cls = get_linker(self.get_update("link_format", name)) 172 173 return self.make_object("linker", cls) 174 175 def get_output(self, name=None): 176 177 """ 178 Make an output context using any given 'name' or otherwise using the 179 "output_context" setting which will be replaced by any given 'name'. 180 """ 181 182 cls = get_output(self.get_update("output_context", name)) 183 184 return self.make_object("output", cls) 185 186 def get_parser(self, name=None): 187 188 """ 189 Make a parser using any given 'name' or otherwise using the 190 "input_format" setting which will be replaced by any given 'name'. 191 """ 192 193 cls = get_parser(self.get_update("input_format", name)) 194 195 parser = self.make_object("parser", cls) 196 parser.parsers = parsers 197 return parser 198 199 def get_serialiser(self, name=None): 200 201 """ 202 Make a serialiser using any given 'name' or otherwise using the 203 "output_format" setting which will be replaced by any given 'name'. 204 """ 205 206 cls = get_serialiser(self.get_update("output_format", name), 207 self.get("input_format")) 208 209 serialiser = self.make_object("serialiser", cls) 210 serialiser.serialisers = serialisers 211 return serialiser 212 213 def get_theme(self, name=None): 214 215 """ 216 Make a theme using any given 'name' or otherwise using the "theme_name" 217 setting which will be replaced by any given 'name'. 218 """ 219 220 cls = get_theme(self.get_update("theme_name", name)) 221 222 return self.make_object("theme", cls) 223 224 # vim: tabstop=4 expandtab shiftwidth=4