Passed
Push — master ( cd421c...e31be6 )
by Domenico
02:52
created

Xliff20::tagClose()   C

Complexity

Conditions 17
Paths 50

Size

Total Lines 119
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 17
eloc 51
c 1
b 0
f 0
nc 50
nop 2
dl 0
loc 119
rs 5.2166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * @author hashashiyyin [email protected] / [email protected]
5
 * Date: 02/08/24
6
 * Time: 17:51
7
 *
8
 */
9
10
namespace Matecat\XliffParser\XliffReplacer;
11
12
use Matecat\XliffParser\Utils\Strings;
13
14
class Xliff20 extends AbstractXliffReplacer {
15
16
    /**
17
     * @var int
18
     */
19
    private int $mdaGroupCounter = 0;
20
    /**
21
     * @var bool
22
     */
23
    protected bool $unitContainsMda = false;   // check if <unit> already contains a <mda:metadata> (forXliff v 2.*)
24
    /**
25
     * @var array
26
     */
27
    protected array $nodesToBuffer = [
28
            'source',
29
            'mda:metadata',
30
            'memsource:additionalTagData',
31
            'originalData',
32
            'note'
33
    ];
34
35
    /**
36
     * @inheritDoc
37
     */
38
    protected function tagOpen( $parser, string $name, array $attr ) {
39
40
        $this->handleOpenUnit( $name, $attr );
41
42
        if ( 'mda:metadata' === $name ) {
43
            $this->unitContainsMda = true;
44
        }
45
46
        $this->checkSetInTarget( $name );
47
48
        // open buffer
49
        $this->setInBuffer( $name );
50
51
        // check if we are inside a <target>, obviously this happen only if there are targets inside the trans-unit
52
        // <target> must be stripped to be replaced, so this check avoids <target> reconstruction
53
        if ( !$this->inTarget ) {
54
55
            $tag = '';
56
57
            //
58
            // ============================================
59
            // only for Xliff 2.*
60
            // ============================================
61
            //
62
            // In xliff v2 we MUST add <mda:metadata> BEFORE <notes>/<originalData>/<segment>/<ignorable>
63
            //
64
            // As documentation says, <unit> contains:
65
            //
66
            // - elements from other namespaces, OPTIONAL
67
            // - Zero or one <notes> elements followed by
68
            // - Zero or one <originalData> element followed by
69
            // - One or more <segment> or <ignorable> elements in any order.
70
            //
71
            // For more info please refer to:
72
            //
73
            // http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html#unit
74
            //
75
            if ( in_array( $name, [ 'notes', 'originalData', 'segment', 'ignorable' ] ) &&
76
                    $this->unitContainsMda === false &&
77
                    !empty( $this->transUnits[ $this->currentTransUnitId ] ) &&
78
                    !$this->hasWrittenCounts
79
            ) {
80
                // we need to update counts here
81
                $this->updateCounts();
82
                $this->hasWrittenCounts = true;
83
                $tag                    .= $this->getWordCountGroupForXliffV2();
84
                $this->unitContainsMda  = true;
85
            }
86
87
            // construct tag
88
            $tag .= "<$name ";
89
90
            foreach ( $attr as $k => $v ) {
91
                //normal tag flux, put attributes in it but skip for translation state and set the right value for the attribute
92
                if ( $k != 'state' ) {
93
                    $tag .= "$k=\"$v\" ";
94
                }
95
            }
96
97
            $seg = $this->getCurrentSegment();
98
99
            if ( $name === $this->tuTagName && !empty( $seg ) && isset( $seg[ 'sid' ] ) ) {
100
101
                // add `mtc:segment-id` to xliff v.2*
102
                if ( strpos( $tag, 'mtc:segment-id' ) === false ) {
103
                    $tag .= "mtc:segment-id=\"{$seg[ 'sid' ]}\" ";
104
                }
105
106
            }
107
108
            // replace state for xliff v2
109
            if ( 'segment' === $name ) { // add state to segment in Xliff v2
110
                [ $stateProp, ] = StatusToStateAttribute::getState( $this->xliffVersion, $seg[ 'status' ] );
111
                $tag .= $stateProp;
112
            }
113
114
            $tag = $this->handleOpenXliffTag( $name, $attr, $tag );
115
116
            $this->checkForSelfClosedTagAndFlush( $parser, $tag );
117
118
        }
119
120
    }
121
122
    /**
123
     * @param string $name
124
     * @param array  $attr
125
     * @param string $tag
126
     *
127
     * @return string
128
     */
129
    protected function handleOpenXliffTag( string $name, array $attr, string $tag ): string {
130
        $tag = parent::handleOpenXliffTag( $name, $attr, $tag );
131
        // add oasis xliff 20 namespace
132
        if ( $name === 'xliff' && !array_key_exists( 'xmlns:mda', $attr ) ) {
133
            $tag .= 'xmlns:mda="urn:oasis:names:tc:xliff:metadata:2.0"';
134
        }
135
136
        return $tag;
137
    }
138
139
    /**
140
     * @inheritDoc
141
     */
142
    protected function tagClose( $parser, string $name ) {
143
        $tag = '';
144
145
        /**
146
         * if is a tag within <target> or
147
         * if it is an empty tag, do not add closing tag because we have already closed it in
148
         *
149
         * self::tagOpen method
150
         */
151
        if ( !$this->isEmpty ) {
152
153
            if ( !$this->inTarget ) {
154
                $tag = "</$name>";
155
            }
156
157
            if ( 'target' == $name ) {
158
159
                if ( isset( $this->transUnits[ $this->currentTransUnitId ] ) ) {
160
161
                    $seg = $this->getCurrentSegment();
162
163
                    // update counts
164
                    if ( !$this->hasWrittenCounts && !empty( $seg ) ) {
165
                        $this->updateSegmentCounts( $seg );
166
                    }
167
168
                    // delete translations so the prepareSegment
169
                    // will put source content in target tag
170
                    if ( $this->sourceInTarget ) {
171
                        $seg[ 'translation' ] = '';
172
                        $this->resetCounts();
173
                    }
174
175
                    // append $translation
176
                    $translation = $this->prepareTranslation( $seg );
177
178
                    //append translation
179
                    $tag = "<target>$translation</target>";
180
181
                }
182
183
                // signal we are leaving a target
184
                $this->targetWasWritten = true;
185
                $this->inTarget         = false;
186
                $this->postProcAndFlush( $this->outputFP, $tag, true );
187
188
            } elseif ( in_array( $name, $this->nodesToBuffer ) ) { // we are closing a critical CDATA section
189
190
                $this->bufferIsActive = false;
191
192
                // only for Xliff 2.*
193
                // write here <mda:metaGroup> and <mda:meta> if already present in the <unit>
194
                if ( 'mda:metadata' === $name && $this->unitContainsMda && !$this->hasWrittenCounts ) {
195
196
                    // we need to update counts here
197
                    $this->updateCounts();
198
                    $this->hasWrittenCounts = true;
199
200
                    $tag = $this->CDATABuffer;
201
                    $tag .= $this->getWordCountGroupForXliffV2( false );
202
                    $tag .= "    </mda:metadata>";
203
204
                } else {
205
                    $tag = $this->CDATABuffer . "</$name>";
206
                }
207
208
                $this->CDATABuffer = "";
209
210
                //flush to the pointer
211
                $this->postProcAndFlush( $this->outputFP, $tag );
212
213
            } elseif ( 'segment' === $name ) {
214
215
                // only for Xliff 2.*
216
                // if segment has no <target> add it BEFORE </segment>
217
                if ( !$this->targetWasWritten ) {
218
219
                    $seg = $this->getCurrentSegment();
220
221
                    if ( isset( $seg[ 'translation' ] ) ) {
222
223
                        $translation = $this->prepareTranslation( $seg );
224
                        // replace the tag
225
                        $tag = "<target>$translation</target>";
226
227
                        $tag .= '</segment>';
228
229
                    }
230
231
                }
232
233
                // update segmentPositionInTu
234
                $this->segmentInUnitPosition++;
235
236
                $this->postProcAndFlush( $this->outputFP, $tag );
237
238
                // we are leaving <segment>, reset $segmentHasTarget
239
                $this->targetWasWritten = false;
240
241
            } elseif ( $this->bufferIsActive ) { // this is a tag ( <g | <mrk ) inside a seg or seg-source tag
242
                $this->CDATABuffer .= "</$name>";
243
                // Do NOT Flush
244
            } else { //generic tag closure do Nothing
245
                // flush to pointer
246
                $this->postProcAndFlush( $this->outputFP, $tag );
247
            }
248
        } else {
249
            //ok, nothing to be done; reset flag for next coming tag
250
            $this->isEmpty = false;
251
        }
252
253
        // check if we are leaving a <trans-unit> (xliff v1.*) or <unit> (xliff v2.*)
254
        if ( $this->tuTagName === $name ) {
255
            $this->currentTransUnitIsTranslatable = null;
256
            $this->inTU                           = false;
257
            $this->unitContainsMda                = false;
258
            $this->hasWrittenCounts               = false;
259
260
            $this->resetCounts();
261
        }
262
    }
263
264
    /**
265
     * Update counts
266
     */
267
    private function updateCounts() {
268
269
        $seg = $this->getCurrentSegment();
270
        if ( !empty( $seg ) ) {
271
            $this->updateSegmentCounts( $seg );
272
        }
273
274
    }
275
276
    /**
277
     * @param bool $withMetadataTag
278
     *
279
     * @return string
280
     */
281
    private function getWordCountGroupForXliffV2( bool $withMetadataTag = true ): string {
282
283
        $this->mdaGroupCounter++;
284
        $segments_count_array = $this->counts[ 'segments_count_array' ];
285
286
        $tag = '';
287
288
        if ( $withMetadataTag === true ) {
289
            $tag .= '<mda:metadata>';
290
        }
291
292
        $index = 0;
293
        foreach ( $segments_count_array as $segments_count_item ) {
294
295
            $id = 'word_count_tu[' . $this->currentTransUnitId . '][' . $index . ']';
296
            $index++;
297
298
            $tag .= "    <mda:metaGroup id=\"" . $id . "\" category=\"row_xml_attribute\">
299
                                <mda:meta type=\"x-matecat-raw\">" . $segments_count_item[ 'raw_word_count' ] . "</mda:meta>
300
                                <mda:meta type=\"x-matecat-weighted\">" . $segments_count_item[ 'eq_word_count' ] . "</mda:meta>
301
                            </mda:metaGroup>";
302
        }
303
304
        if ( $withMetadataTag === true ) {
305
            $tag .= '</mda:metadata>';
306
        }
307
308
        return $tag;
309
310
    }
311
312
    /**
313
     * prepare segment tagging for xliff insertion
314
     *
315
     * @param array $seg
316
     *
317
     * @return string
318
     */
319
    protected function prepareTranslation( array $seg ): string {
320
321
        $segment     = Strings::removeDangerousChars( $seg [ 'segment' ] );
322
        $translation = Strings::removeDangerousChars( $seg [ 'translation' ] );
323
        $dataRefMap  = ( isset( $seg[ 'data_ref_map' ] ) ) ? Strings::jsonToArray( $seg[ 'data_ref_map' ] ) : [];
324
325
        if ( $seg [ 'translation' ] == '' ) {
326
            $translation = $segment;
327
        } else {
328
            if ( $this->callback instanceof XliffReplacerCallbackInterface ) {
329
                $error = ( !empty( $seg[ 'error' ] ) ) ? $seg[ 'error' ] : null;
330
                if ( $this->callback->thereAreErrors( $seg[ 'sid' ], $segment, $translation, $dataRefMap, $error ) ) {
331
                    $translation = '|||UNTRANSLATED_CONTENT_START|||' . $segment . '|||UNTRANSLATED_CONTENT_END|||';
332
                }
333
            }
334
        }
335
336
        return $translation;
337
338
    }
339
340
}