1 : <?php
2 : /**
3 : * Roman de Renart
4 : *
5 : * PHP version 5
6 : *
7 : * @category Rdr
8 : * @package Edit
9 : * @author Michel Corne <mcorne@yahoo.com>
10 : * @copyright 2010 Michel Corne
11 : * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
12 : * @link http://roman-de-renart.blogspot.com/
13 : * @version SVN: $Id$
14 : */
15 :
16 : require_once 'Episode.php';
17 :
18 : /**
19 : * Mapping the lines of the texts of Meon and Martin and the old French text of reference of an episode
20 : *
21 : * @category Rdr
22 : * @package Edit
23 : * @author Michel Corne <mcorne@yahoo.com>
24 : * @copyright 2010 Michel Corne
25 : * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
26 : */
27 :
28 : class Mapping extends Episode
29 : {
30 : /**
31 : * The format of a Martin chapter and line number
32 : */
33 : const CHAPTER_FORMAT = '%s %s';
34 :
35 : /**
36 : * The error message reported when the mapping cannot be parsed
37 : */
38 : const ERR_PARSE_MAPPING = 'cannot parse mapping';
39 :
40 : /**
41 : * The format of the FHS part of the mapping
42 : */
43 : const FHS_FMT = 'FHS %s';
44 :
45 : /**
46 : * The format of the mapping
47 : */
48 : const MAPPING_FMT = '%s : %s';
49 :
50 : /**
51 : * The mapping separator
52 : */
53 : const MAPPING_SEPARATOR = ', ';
54 :
55 : /**
56 : * The format of the Martin part of the mapping
57 : */
58 : const MARTIN_FMT = 'Martin %s';
59 :
60 : /**
61 : * The format of the Meon part of the mapping
62 : */
63 : const MEON_FMT = 'Méon %s';
64 :
65 : /**
66 : * The message stating that a Meon line is ignored
67 : */
68 : const NO_TEXT_STRING = 'ignoré';
69 :
70 : /**
71 : * The format of a single line number
72 : */
73 : const NUMBER1_FMT = '%s';
74 :
75 : /**
76 : * The format of a line number range
77 : */
78 : const NUMBER2_FMT = '%s-%s'; // keep in sync with NUMBER_SEPARATOR
79 :
80 : /**
81 : * The number range separator
82 : */
83 : const NUMBER_SEPARATOR = '-'; // keep in sync with NUMBER2_FMT
84 :
85 :
86 : /**
87 : * The keys of the columns used for a mapping
88 : * @var array
89 : */
90 : public static $mappingKeys = array(
91 : Episode::COL_MAPPING,
92 : Episode::COL_FRO_NUMBERS,
93 : );
94 :
95 : /**
96 : * Converts a chapter and a line number to a string
97 : *
98 : * @param array $chapterNumber the chapter and the line number
99 : * @return array the chapter and the line number together and the line number
100 : */
101 : public function chapterToString($chapterNumber)
102 : {
103 5 : list($chapter, $number) = $chapterNumber;
104 5 : $string = sprintf(self::CHAPTER_FORMAT, $chapter, $number);
105 :
106 5 : return array($string, $number);
107 : }
108 :
109 : /**
110 : * Converts a mapping part into a string
111 : *
112 : * @param string $begin the begin line number
113 : * @param string $end the end line number
114 : * @param string $format the mapping format to apply
115 : * @return string the mapping part in a string
116 : */
117 : public function lineMappingToString($begin, $end, $format)
118 : {
119 3 : $numbers = $this->numbersToString($begin, $end);
120 :
121 3 : return sprintf($format, $numbers);
122 : }
123 :
124 : /**
125 : * Maps the episode texts of Meon, Martin and the old French text of reference
126 : *
127 : * @return string the mapping of the texts.
128 : */
129 : public function mappingEpisode()
130 : {
131 1 : $this->readSheet();
132 1 : $rows = $this->readRows();
133 :
134 1 : $mapping = $this->mapLines($rows);
135 1 : $mapping = $this->arrayMap('mappingToString', $mapping);
136 :
137 1 : $this->writeColumn($mapping, Episode::COL_MAPPING);
138 1 : $this->writeSheet();
139 :
140 1 : return $this->arrayToString($mapping);
141 : }
142 :
143 : /**
144 : * Maps texts lines
145 : *
146 : * @param array $row an episode rows
147 : * @return array the mapping
148 : */
149 : public function mapLine($row, $begin, $prev)
150 : {
151 3 : if (!isset($begin)) {
152 : // this is the first line, captures numbers
153 3 : $begin = $row;
154 3 : $mapping = array();
155 :
156 3 : } else if (!$row[Episode::COL_MEON_TEXT_FIXED] and
157 3 : !$row[Episode::COL_MARTIN_TEXT_ORIG] and
158 2 : !$row[Episode::COL_FRO_TEXT]
159 3 : ){
160 : // this is an empty row
161 1 : $mapping = array();
162 1 : } else if (
163 3 : (bool)$row[Episode::COL_MEON_TEXT_FIXED] != (bool)$prev[Episode::COL_MEON_TEXT_FIXED] or
164 3 : (bool)$row[Episode::COL_MARTIN_TEXT_ORIG] != (bool)$prev[Episode::COL_MARTIN_TEXT_ORIG] or
165 3 : (bool)$row[Episode::COL_FRO_TEXT] != (bool)$prev[Episode::COL_FRO_TEXT] or
166 3 : $row[Episode::COL_MARTIN_CHAPTERS] != $prev[Episode::COL_MARTIN_CHAPTERS]
167 3 : ) {
168 : // the current line of a text is empty (not empty) and
169 : // the previous line was not empty (empty) or
170 : // the martin chapter is different :
171 : // that is the begining of a new section
172 : // captures the previous section begin and end numbers
173 3 : $mapping = array($begin, $prev);
174 : // captures the current section begin numbers
175 3 : $begin = $row;
176 :
177 3 : } else {
178 : // no change, within a contigous sections of all texts
179 3 : $mapping = array();
180 : }
181 :
182 3 : return array($mapping, $begin);
183 : }
184 :
185 : /**
186 : * Maps the episode texts lines
187 : *
188 : * @param array $rows the episode rows
189 : * @return array the mapping
190 : */
191 : public function mapLines($rows)
192 : {
193 2 : $mapping = array();
194 2 : $begin = null;
195 2 : $prev = array();
196 :
197 2 : foreach($rows as $row) {
198 2 : list($mapping[], $begin) = $this->mapLine($row, $begin, $prev);
199 2 : $prev = $row;
200 2 : }
201 :
202 : // captures the last section begin and end line numbers
203 2 : $mapping[] = array($begin, $row);
204 : // removes the first entry that corresponds to no line
205 2 : array_shift($mapping);
206 :
207 2 : return $mapping;
208 : }
209 :
210 : /**
211 : * Converts a section mapping into a string
212 : *
213 : * @param array $values the mapping details
214 : * @return string the mapping in a string
215 : */
216 : public function mappingToString($values)
217 : {
218 2 : if ($values) {
219 : // extracts the section begin and end line numbers
220 2 : list($begin, $end) = $values;
221 :
222 : // gets the section reference begin and end line numbers
223 2 : $numbers = $begin[Episode::COL_FRO_TEXT]?
224 2 : $this->numbersToString($begin[Episode::COL_FRO_NUMBERS], $end[Episode::COL_FRO_NUMBERS]) :
225 2 : self::NO_TEXT_STRING;
226 :
227 : // converts the corresponding Meon section mapping to a string
228 2 : $begin[Episode::COL_MEON_TEXT_FIXED] and $mapping[] = $this->lineMappingToString(
229 2 : $begin[Episode::COL_MEON_NUMBERS],
230 2 : $end[Episode::COL_MEON_NUMBERS],
231 2 : self::MEON_FMT);
232 :
233 : // converts the corresponding Martin section mapping to a string
234 2 : $begin[Episode::COL_MARTIN_TEXT_ORIG] and $mapping[] = $this->lineMappingToString(
235 2 : array($begin[Episode::COL_MARTIN_CHAPTERS], $begin[Episode::COL_MARTIN_NUMBERS]),
236 2 : $end[Episode::COL_MARTIN_NUMBERS],
237 2 : self::MARTIN_FMT);
238 :
239 : // converts the corresponding old French text section mapping to a string
240 2 : $begin[Episode::COL_FRO_TEXT] and $mapping[] = $this->lineMappingToString(
241 2 : $begin[Episode::COL_FRO_NUMBERS],
242 2 : $end[Episode::COL_FRO_NUMBERS],
243 2 : self::FHS_FMT);
244 :
245 2 : $mapping = implode(self::MAPPING_SEPARATOR, $mapping);
246 2 : $string = sprintf(self::MAPPING_FMT, $numbers, $mapping);
247 :
248 2 : } else {
249 2 : $string = '';
250 : }
251 :
252 2 : return $string;
253 : }
254 :
255 : /**
256 : * Converts numbers to a string
257 : *
258 : * @param string $begin the begin line number
259 : * @param string $end the end line number
260 : * @return string the line numbers in a string
261 : */
262 : public function numbersToString($begin, $end)
263 : {
264 4 : list($begin, $beginNumber) = $this->numberToString($begin);
265 4 : list($end, $endNumber) = $this->numberToString($end);
266 :
267 4 : return $beginNumber == $endNumber?
268 2 : sprintf(self::NUMBER1_FMT, $begin) :
269 4 : sprintf(self::NUMBER2_FMT, $begin, $end);
270 : }
271 :
272 : /**
273 : * Converts a line number with a possible chapter to a string
274 : *
275 : * @param array $chapterNumber the line number with a possible chapter
276 : * @return string the chapter and the line number together and the line number
277 : */
278 : public function numberToString($chapterNumber)
279 : {
280 5 : if (is_array($chapterNumber)) {
281 : // this is a chapter and a line number
282 4 : list($chapterNumber, $number) = $this->chapterToString($chapterNumber);
283 4 : } else {
284 : // this is a number without a chapter
285 5 : $number = $chapterNumber;
286 : }
287 :
288 5 : return array($chapterNumber, $number);
289 : }
290 :
291 : /**
292 : * Parses a mapping
293 : *
294 : * @param string $mapping the mapping to parse
295 : * @return array The mapping details
296 : */
297 : public function toCsv($mapping)
298 : {
299 3 : $base = new Base;
300 :
301 3 : $pattern = $base->completePattern(self::MAPPING_FMT, '(.*)');
302 3 : $cells = $base->match($pattern, $mapping, self::ERR_PARSE_MAPPING, Mapping::$mappingKeys, true);
303 3 : $cells = array_map('trim', $cells);
304 :
305 : // extracts the current line number that is the last number of the mapping
306 3 : $numbers = explode(self::NUMBER_SEPARATOR, $cells[Episode::COL_FRO_NUMBERS]);
307 3 : $cells[Episode::COL_FRO_NUMBERS] = trim(end($numbers));
308 :
309 3 : return $cells;
310 : }
|