View Javadoc
1   /*
2    * ===========================================================================
3    * Copyright (C) 2010 by the Okapi Framework contributors
4    * -----------------------------------------------------------------------------
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    * 
9    * http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   * ===========================================================================
17   */
18  
19  package net.sf.okapi.common.resource;
20  
21  import java.util.Iterator;
22  import java.util.LinkedList;
23  import java.util.List;
24  import java.util.NoSuchElementException;
25  
26  import net.sf.okapi.common.IResource;
27  import net.sf.okapi.common.ISegmenter;
28  import net.sf.okapi.common.LocaleId;
29  import net.sf.okapi.common.exceptions.OkapiMisAlignmentException;
30  
31  /**
32   * Provides a standard implementation of the IAlignedSegments interface that
33   * works with variant sources.
34   * <p>
35   * Currently tightly coupled to ITextUnit.
36   * 
37   * @version 1.47.0
38   */
39  public class AlignedSegments implements IAlignedSegments {
40    private ITextUnit parent;
41  
42    public AlignedSegments(ITextUnit parent) {
43      this.parent = parent;
44    }
45  
46    @Override
47    public void append(Segment srcSeg, Segment trgSeg, LocaleId trgLoc) {
48      if (srcSeg == null && trgSeg == null)
49        throw new IllegalArgumentException("srcSeg and trgSeg cannot both be null");
50      insertOrAppend(true, -1, srcSeg, trgSeg, trgLoc);
51    }
52  
53    @Override
54    public void insert(int index, Segment srcSeg, Segment trgSeg, LocaleId trgLoc) {
55      if (srcSeg == null)
56        throw new IllegalArgumentException("srcSeg cannot be null");
57      insertOrAppend(false, index, srcSeg, trgSeg, trgLoc);
58    }
59  
60    private void insertOrAppend(boolean append, int index, Segment srcSeg, Segment trgSeg,
61        LocaleId trgLoc) {
62      Segment sourceSeg = (srcSeg != null ? srcSeg : new Segment(null, new TextFragment("")));
63      Segment targetSeg = (trgSeg != null ? trgSeg : new Segment(null, new TextFragment("")));
64  
65      String originalId = null;
66      String insertedId = null;
67  
68      ContainerIterator ci = new ContainerIterator(trgLoc);
69  
70      if (append) {
71        targetSeg.id = sourceSeg.id;
72        if (ci.hasSource())
73          ci.getSource().getSegments().append(sourceSeg);
74        if (ci.hasTarget())
75          ci.getTarget().getSegments().append(targetSeg);
76      } else {
77        originalId = getSource(trgLoc).getSegments().get(index).id;
78        if (ci.hasSource())
79          insertedId = doInsert(ci.getSource(), index, null, null, sourceSeg);
80        if (ci.hasTarget())
81          insertedId = doInsert(ci.getTarget(), index, originalId, insertedId, targetSeg);
82      }
83    }
84  
85    private String doInsert(TextContainer container, int index, String originalId, String insertedId,
86        Segment seg) {
87      ISegments segs = container.getSegments();
88      Segment currentSeg;
89  
90      if (originalId == null) {
91        segs.insert(index, seg);
92        return seg.id;
93      }
94      currentSeg = segs.get(originalId);
95      if (currentSeg != null) {
96        segs.insert(segs.getIndex(originalId), seg);
97        if (insertedId != null)
98          seg.id = insertedId;
99        return seg.id;
100     }
101     segs.append(seg);
102     return insertedId;
103   }
104 
105   @Override
106   public void setSegment(int index, Segment seg, LocaleId trgLoc) {
107     ISegments segs = getSource(trgLoc).getSegments();
108     String oldId = segs.get(index).id;
109     String newId = seg.id;
110     boolean idChanged = !newId.equals(oldId);
111 
112     Segment tempSeg;
113     ContainerIterator ci = new ContainerIterator(trgLoc);
114 
115     if (ci.hasSource())
116       ci.getSource().getSegments().set(index, seg.clone());
117     if (ci.hasTarget()) {
118       segs = ci.getTarget().getSegments();
119       segs.set(segs.getIndex(oldId), seg.clone());
120     }
121 
122     if (idChanged) {
123       ci = new ContainerIterator(trgLoc);
124       if (ci.hasSource()) {
125         tempSeg = ci.getSource().getSegments().get(oldId);
126         if (tempSeg != null)
127           tempSeg.id = newId;
128       }
129       if (ci.hasTarget()) {
130         tempSeg = ci.getTarget().getSegments().get(oldId);
131         if (tempSeg != null)
132           tempSeg.id = newId;
133       }
134     }
135   }
136 
137   @Override
138   public boolean remove(Segment seg, LocaleId trgLoc) {
139     int count = 0;
140     ContainerIterator ci = new ContainerIterator(trgLoc);
141     if (ci.hasSource())
142       count += removeSegment(ci.getSource(), seg.id);
143     if (ci.hasTarget())
144       count += removeSegment(ci.getTarget(), seg.id);
145     return (count > 0);
146   }
147 
148   private int removeSegment(TextContainer container, String segId) {
149     ISegments segs = container.getSegments();
150     int segIndex = segs.getIndex(segId);
151     if (segIndex > -1) {
152       container.remove(segs.getPartIndex(segIndex));
153       return 1;
154     }
155     return 0;
156   }
157 
158   @Override
159   public Segment getSource(int index, LocaleId trgLoc) {
160     return getSource(trgLoc).getSegments().get(index);
161   }
162 
163   @Override
164   public Segment getCorrespondingTarget(Segment srcSeg, LocaleId trgLoc) {
165     ISegments trgSegs = parent.getTargetSegments(trgLoc);
166     return trgSegs.get(srcSeg.id);
167   }
168 
169   @Override
170   public Segment getCorrespondingSource(Segment trgSeg, LocaleId trgLoc) {
171     ISegments srcSegs = getSource(trgLoc).getSegments();
172     return srcSegs.get(trgSeg.id);
173   }
174 
175   @Override
176   public void align(List<AlignedPair> alignedSegmentPairs, LocaleId trgLoc) {
177     parent.createTarget(trgLoc, false, IResource.CREATE_EMPTY);
178 
179     TextContainer src = getSource(trgLoc);
180     TextContainer trg = parent.getTarget(trgLoc);
181 
182     // DO NOT clear — preserve existing content
183     // src.clear();
184     // trg.clear();
185 
186     for (AlignedPair pair : alignedSegmentPairs) {
187       List<TextPart> srcParts = pair.getSourceParts();
188       List<TextPart> trgParts = pair.getTargetParts();
189 
190       if (srcParts != null && !srcParts.isEmpty()) {
191         for (TextPart part : srcParts) {
192           src.append(part.clone());
193         }
194       }
195       if (trgParts != null && !trgParts.isEmpty()) {
196         for (TextPart part : trgParts) {
197           trg.append(part.clone());
198         }
199       }
200     }
201 
202     src.setHasBeenSegmentedFlag(true);
203     trg.setHasBeenSegmentedFlag(true);
204     trg.getSegments().setAlignmentStatus(AlignmentStatus.ALIGNED);
205   }
206 
207   @Override
208   public void align(LocaleId trgLoc) {
209     Iterator<Segment> srcSegsIt = getSource(trgLoc).getSegments().iterator();
210     Iterator<Segment> trgSegsIt = parent.createTarget(trgLoc, false, IResource.COPY_SEGMENTATION)
211         .getSegments().iterator();
212     while (srcSegsIt.hasNext()) {
213       try {
214         Segment srcSeg = srcSegsIt.next();
215         Segment trgSeg = trgSegsIt.next();
216         trgSeg.id = srcSeg.id;
217       } catch (NoSuchElementException e) {
218         throw new OkapiMisAlignmentException("Different number of source and target segments", e);
219       }
220     }
221     parent.getTargetSegments(trgLoc).setAlignmentStatus(AlignmentStatus.ALIGNED);
222   }
223 
224   @Override
225   public void alignCollapseAll(LocaleId trgLoc) {
226     ContainerIterator ci = new ContainerIterator(trgLoc);
227     LinkedList<TextContainer> collapsed = new LinkedList<>();
228 
229     if (ci.hasSource()) {
230       ci.getSource().joinAll();
231       ci.getSource().setHasBeenSegmentedFlag(false);
232       collapsed.add(ci.getSource());
233     }
234     if (ci.hasTarget()) {
235       ci.getTarget().joinAll();
236       ci.getTarget().setHasBeenSegmentedFlag(false);
237       collapsed.add(ci.getTarget());
238     }
239 
240     for (LocaleId loc : parent.getTargetLocales()) {
241       TextContainer src = getSource(loc);
242       if (collapsed.contains(src)) {
243         TextContainer trg = parent.getTarget(loc);
244         if (collapsed.contains(trg)) {
245           trg.getSegments().setAlignmentStatus(AlignmentStatus.ALIGNED);
246           trg.getFirstSegment().id = src.getFirstSegment().getId();
247         }
248       }
249     }
250   }
251 
252   @Override
253   public Segment splitSource(LocaleId trgLoc, Segment srcSeg, int splitPos) {
254     TextContainer theSource = getSource(trgLoc);
255     ISegments srcSegs = theSource.getSegments();
256     int segIndex = srcSegs.getIndex(srcSeg.id);
257     if (segIndex == -1)
258       return null;
259     int partIndex = srcSegs.getPartIndex(segIndex);
260     theSource.split(partIndex, splitPos, splitPos, false);
261     Segment newSeg = srcSegs.get(segIndex + 1);
262 
263     ContainerIterator ci = new ContainerIterator(trgLoc);
264     if (ci.hasTarget()) {
265       ISegments currentSegs = ci.getTarget().getSegments();
266       Segment currentSeg = currentSegs.get(srcSeg.id);
267       if (currentSeg != null)
268         currentSegs.insert(currentSegs.getIndex(srcSeg.id) + 1,
269             new Segment(srcSeg.id, new TextFragment("")));
270     }
271     return newSeg;
272   }
273 
274   @Override
275   public Segment splitTarget(LocaleId trgLoc, Segment trgSeg, int splitPos) {
276     TextContainer theTarget = parent.createTarget(trgLoc, false, IResource.COPY_SEGMENTATION);
277     ISegments trgSegs = theTarget.getSegments();
278     int segIndex = trgSegs.getIndex(trgSeg.id);
279     if (segIndex == -1)
280       return null;
281     int partIndex = trgSegs.getPartIndex(segIndex);
282     theTarget.split(partIndex, splitPos, splitPos, false);
283     Segment newSeg = trgSegs.get(segIndex + 1);
284 
285     ContainerIterator ci = new ContainerIterator(trgLoc);
286     if (ci.hasSource()) {
287       ISegments currentSegs = ci.getSource().getSegments();
288       Segment currentSeg = currentSegs.get(trgSeg.id);
289       if (currentSeg != null)
290         currentSegs.insert(currentSegs.getIndex(trgSeg.id) + 1,
291             new Segment(trgSeg.id, new TextFragment("")));
292     }
293     return newSeg;
294   }
295 
296   @Override
297   public void joinWithNext(Segment seg, LocaleId trgLoc) {
298     ContainerIterator ci = new ContainerIterator(trgLoc);
299     if (ci.hasSource())
300       doJoinWithNext(ci.getSource(), seg.id);
301     if (ci.hasTarget())
302       doJoinWithNext(ci.getTarget(), seg.id);
303   }
304 
305   private void doJoinWithNext(TextContainer cont, String segId) {
306     int segIndex = cont.getSegments().getIndex(segId);
307     if (segIndex != -1)
308       cont.getSegments().joinWithNext(segIndex);
309   }
310 
311   @Override
312   public void joinAll(LocaleId trgLoc) {
313     ContainerIterator ci = new ContainerIterator(trgLoc);
314     if (ci.hasSource())
315       ci.getSource().joinAll();
316     if (ci.hasTarget())
317       ci.getTarget().joinAll();
318   }
319 
320   @Override
321   public AlignmentStatus getAlignmentStatus() {
322     for (LocaleId loc : parent.getTargetLocales()) {
323       ISegments trgSegs = parent.getTargetSegments(loc);
324       if (trgSegs.getAlignmentStatus() == AlignmentStatus.NOT_ALIGNED) {
325         return AlignmentStatus.NOT_ALIGNED;
326       }
327     }
328     return AlignmentStatus.ALIGNED;
329   }
330 
331   @Override
332   public AlignmentStatus getAlignmentStatus(LocaleId trgLoc) {
333     return parent.getTargetSegments(trgLoc).getAlignmentStatus();
334   }
335 
336   @Override
337   public void segmentSource(ISegmenter segmenter, LocaleId targetLocale) {
338     TextContainer theSource = getSource(targetLocale);
339     segmenter.computeSegments(theSource);
340     theSource.getSegments().create(segmenter.getRanges());
341   }
342 
343   @Override
344   public void segmentTarget(ISegmenter segmenter, LocaleId targetLocale) {
345     TextContainer theTarget = parent.createTarget(targetLocale, false, IResource.COPY_SEGMENTATION);
346     segmenter.computeSegments(theTarget);
347     theTarget.getSegments().create(segmenter.getRanges());
348   }
349 
350   @Override
351   public Iterator<Segment> iterator() {
352     return parent.getSource().getSegments().iterator();
353   }
354 
355   @Override
356   public Iterator<Segment> iterator(LocaleId trgLoc) {
357     return getSource(trgLoc).getSegments().iterator();
358   }
359 
360   private TextContainer getSource(LocaleId loc) {
361     return parent.getSource();
362   }
363 
364   private class ContainerIterator {
365     private TextContainer theSource = null;
366     private TextContainer theTarget = null;
367 
368     public ContainerIterator(LocaleId trgLoc) {
369       theSource = AlignedSegments.this.getSource(trgLoc);
370       theTarget = parent.getTarget(trgLoc);
371     }
372 
373     public boolean hasSource() {
374       return theSource != null;
375     }
376 
377     public TextContainer getSource() {
378       if (theSource == null)
379         throw new IllegalStateException(
380             "this method can only be called after hasSource() returns true");
381       return theSource;
382     }
383 
384     public boolean hasTarget() {
385       return theTarget != null;
386     }
387 
388     public TextContainer getTarget() {
389       if (theTarget == null)
390         throw new IllegalStateException(
391             "this method can only be called after hasTarget() returns true");
392       return theTarget;
393     }
394   }
395 
396   public final ITextUnit getParent() {
397     return parent;
398   }
399 }