/* * Copyright (C) 2015, VistaTEC or third-party contributors as indicated * by the @author tags or express copyright attribution statements applied by * the authors. All third-party contributions are distributed under license by * VistaTEC. * * This file is part of Ocelot. * * Ocelot is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Ocelot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, write to: * * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 * USA * * Also, see the full LGPL text here: <http://www.gnu.org/copyleft/lesser.html> */ package com.vistatec.ocelot.segment.model.okapi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.Before; import org.junit.Test; import com.google.common.collect.Lists; import com.vistatec.ocelot.segment.model.CodeAtom; import com.vistatec.ocelot.segment.model.PositionAtom; import com.vistatec.ocelot.segment.model.SegmentAtom; import com.vistatec.ocelot.segment.model.TextAtom; import com.vistatec.ocelot.segment.view.SegmentVariantSelection; import net.sf.okapi.lib.xliff2.core.Directionality; import net.sf.okapi.lib.xliff2.core.Fragment; import net.sf.okapi.lib.xliff2.core.IWithStore; import net.sf.okapi.lib.xliff2.core.Segment; import net.sf.okapi.lib.xliff2.core.Store; import net.sf.okapi.lib.xliff2.core.TagType; public class TestFragmentVariant { private FragmentVariant sampleFV, plainTextFV, plainCodeFV; @Before public void beforeTest() { sampleFV = sampleText(false); plainTextFV = plainText(); plainCodeFV = plainCode(); } @Test public void testPlainTextAtoms() { assertEquals(Lists.newArrayList(new TextAtom("Plain Text")), plainTextFV.getAtoms()); } @Test public void testPlainCodeAtoms() { assertEquals(Lists.newArrayList(new CodeAtom("id1", "<pcid1>", "<pc id=\"id1\">"), new CodeAtom("id1", "</pcid1>", "</pc>")), plainCodeFV.getAtoms()); } @Test public void testGetAtoms() { assertEquals(Lists.newArrayList(new TextAtom("A"), new CodeAtom("id1", "<pcid1>", "<pc id=\"id1\">"), new TextAtom("B"), new CodeAtom("id1", "</pcid1>", "</pc>")), sampleFV.getAtoms()); } @Test public void testGetAtomsForTarget() { assertEquals(Lists.newArrayList(new TextAtom("A"), new CodeAtom("id1", "<pcid1>", "<pc id=\"id1\">"), new TextAtom("B"), new CodeAtom("id1", "</pcid1>", "</pc>")), sampleText(true).getAtoms()); } @Test public void testCreateEmptyTarget() { FragmentVariant fv = plainCodeFV.createEmptyTarget(); assertEquals(Collections.emptyList(), fv.getAtoms()); assertEquals(true, fv.isTarget()); } @Test public void testNullXLIFFTarget() { Store store = new Store(new DummyWithStore()); Segment segment = new Segment(store); segment.setSource(new Fragment(store, false).append("Hello world")); FragmentVariant v = new FragmentVariant(segment, true); assertEquals("", v.getDisplayText()); assertNotNull(segment.getTarget()); } @Test public void testCreateCopy() { FragmentVariant copy = sampleFV.createCopy(); assertEquals(Lists.newArrayList(new TextAtom("A"), new CodeAtom("id1", "<pcid1>", "<pc id=\"id1\">"), new TextAtom("B"), new CodeAtom("id1", "</pcid1>", "</pc>")), copy.getAtoms()); } @Test public void testReplaceWithAtoms() { // A < p c i d 1 > B < / p c i d 1 > // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // Insert with zero-length range. sampleFV.replaceSelection(0, 0, Arrays.asList(new TextAtom("C"), new TextAtom("D"))); assertEquals("CDA<pcid1>B</pcid1>", sampleFV.toString()); sampleFV.replaceSelection(0, 3, Arrays.asList(new TextAtom("E"))); assertEquals("E<pcid1>B</pcid1>", sampleFV.toString()); // Range must include entire CodeAtom in order to replace it. sampleFV.replaceSelection(0, 2, Arrays.asList(new TextAtom("F"))); assertEquals("F<pcid1>B</pcid1>", sampleFV.toString()); sampleFV.replaceSelection(0, 3, Arrays.asList(new TextAtom("F"))); assertEquals("F<pcid1>B</pcid1>", sampleFV.toString()); sampleFV.replaceSelection(0, 4, Arrays.asList(new TextAtom("F"))); assertEquals("F<pcid1>B</pcid1>", sampleFV.toString()); sampleFV.replaceSelection(0, 5, Arrays.asList(new TextAtom("F"))); assertEquals("F<pcid1>B</pcid1>", sampleFV.toString()); sampleFV.replaceSelection(0, 6, Arrays.asList(new TextAtom("F"))); assertEquals("F<pcid1>B</pcid1>", sampleFV.toString()); sampleFV.replaceSelection(0, 7, Arrays.asList(new TextAtom("F"))); assertEquals("F<pcid1>B</pcid1>", sampleFV.toString()); sampleFV.replaceSelection(0, 8, Arrays.asList(new TextAtom("F"))); assertEquals("FB</pcid1>", sampleFV.toString()); // Replace with empty list to clear. sampleFV.replaceSelection(0, 3, Collections.<SegmentAtom> emptyList()); assertEquals("</pcid1>", sampleFV.toString()); } @Test public void testSelection() { // A < p c i d 1 > B < / p c i d 1 > // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // SegmentVariantSelection represents a selection of the textual // representation, not the atomic representation. SegmentVariantSelection sel = new SegmentVariantSelection("", sampleFV, 0, 1); assertEquals("A", sel.getDisplayText()); sel = new SegmentVariantSelection("", sampleFV, 0, 2); assertEquals("A<", sel.getDisplayText()); sel = new SegmentVariantSelection("", sampleFV, 0, 3); assertEquals("A<p", sel.getDisplayText()); sel = new SegmentVariantSelection("", sampleFV, 0, 4); assertEquals("A<pc", sel.getDisplayText()); sel = new SegmentVariantSelection("", sampleFV, 0, 5); assertEquals("A<pci", sel.getDisplayText()); // Selection refers to source SegmentVariant, will become invalid if // source is mutated. sampleFV.clearSelection(0, sampleFV.getLength()); try { assertEquals("A<pci", sel.getDisplayText()); fail("Selection should no longer be valid"); } catch (IndexOutOfBoundsException e) { // OK } } @Test public void testReplaceWithSelection() { // A < p c i d 1 > B < / p c i d 1 > // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 SegmentVariantSelection sel = new SegmentVariantSelection("", sampleFV.createCopy(), 1, 9); assertEquals("<pcid1>B", sel.getDisplayText()); sampleFV.replaceSelection(0, 0, sel); assertEquals("<pcid1>BA<pcid1>B</pcid1>", sampleFV.toString()); sampleFV.replaceSelection(sampleFV.getLength(), sampleFV.getLength(), sel); assertEquals("<pcid1>BA<pcid1>B</pcid1><pcid1>B", sampleFV.toString()); sampleFV.replaceSelection(0, 17, sel); assertEquals("<pcid1>B</pcid1><pcid1>B", sampleFV.toString()); } @Test public void testClearSelection() { // A < p c i d 1 > B < / p c i d 1 > // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 sampleFV.clearSelection(0, 0); assertEquals("A<pcid1>B</pcid1>", sampleFV.getDisplayText()); sampleFV.clearSelection(0, 1); assertEquals("<pcid1>B</pcid1>", sampleFV.getDisplayText()); // Range must cover entire CodeAtom to remove it. sampleFV.clearSelection(0, 1); assertEquals("<pcid1>B</pcid1>", sampleFV.getDisplayText()); sampleFV.clearSelection(0, 2); assertEquals("<pcid1>B</pcid1>", sampleFV.getDisplayText()); sampleFV.clearSelection(0, 3); assertEquals("<pcid1>B</pcid1>", sampleFV.getDisplayText()); sampleFV.clearSelection(0, 4); assertEquals("<pcid1>B</pcid1>", sampleFV.getDisplayText()); sampleFV.clearSelection(0, 5); assertEquals("<pcid1>B</pcid1>", sampleFV.getDisplayText()); sampleFV.clearSelection(0, 6); assertEquals("<pcid1>B</pcid1>", sampleFV.getDisplayText()); sampleFV.clearSelection(0, 7); assertEquals("B</pcid1>", sampleFV.getDisplayText()); } @Test public void testValidation() { // A < p c i d 1 > B < / p c i d 1 > // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 FragmentVariant copy = sampleFV.createCopy(); assertFalse(sampleFV.needsValidation()); // Replacing empty selection does not dirty the variant. sampleFV.replaceSelection(0, 0, Collections.<SegmentAtom> emptyList()); assertFalse(sampleFV.needsValidation()); // Replacing atoms dirties the variant... sampleFV.replaceSelection(0, 1, Collections.<SegmentAtom> emptyList()); assertTrue(sampleFV.needsValidation()); // ...but tags are still OK here. assertTrue(sampleFV.validateAgainst(copy)); // ...but the variant is still considered dirty because it's up to the // caller to provide the correct original to validate against. assertTrue(sampleFV.needsValidation()); sampleFV = copy.createCopy(); assertFalse(sampleFV.needsValidation()); // Removing a tag will make the variant fail validation. sampleFV.replaceSelection(0, 8, Arrays.asList(new TextAtom("Z"))); assertTrue(sampleFV.needsValidation()); assertFalse(sampleFV.validateAgainst(copy)); // Restore the missing tag to get it to validate. // Note that validation does not handle ordering or nesting issues; it // only cares that the codes in the reference variant are present in the // same number. List<CodeAtom> missing = sampleFV.getMissingTags(copy); assertEquals(1, missing.size()); assertEquals("<pcid1>", missing.get(0).getData()); sampleFV.replaceSelection(sampleFV.getLength(), sampleFV.getLength(), missing); assertTrue(sampleFV.needsValidation()); assertTrue(sampleFV.validateAgainst(copy)); } @Test public void testPositions() { // A < p c i d 1 > B < / p c i d 1 > // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 PositionAtom start = sampleFV.createPosition(0); assertTrue(start.getData().isEmpty()); assertEquals(0, start.getLength()); assertEquals(0, start.getPosition()); PositionAtom end = sampleFV.createPosition(9); assertEquals(9, end.getPosition()); sampleFV.modifyChars(0, 0, "Z"); assertEquals("ZA<pcid1>B</pcid1>", sampleFV.getDisplayText()); assertEquals(0, start.getPosition()); assertEquals(10, end.getPosition()); sampleFV.replaceSelection(start.getPosition(), end.getPosition(), Arrays.asList(new TextAtom("ABCD"))); assertEquals("ABCD</pcid1>", sampleFV.getDisplayText()); assertEquals(0, start.getPosition()); assertEquals(4, end.getPosition()); PositionAtom middle = sampleFV.createPosition(2); sampleFV.clearSelection(start.getPosition(), end.getPosition()); assertEquals("</pcid1>", sampleFV.getDisplayText()); try { middle.getPosition(); fail("Positions removed from parent segment should throw an exception"); } catch (IllegalStateException e) { // OK } assertEquals(0, start.getPosition()); assertEquals(0, end.getPosition()); // Attempting to place a position inside a code atom will result in the // position sliding to the end of the code. PositionAtom inCode = sampleFV.createPosition(2); assertEquals(8, inCode.getPosition()); } @Test public void testCopy() { // A < p c i d 1 > B < / p c i d 1 > // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 FragmentVariant copy = sampleFV.createCopy(); // We *currently* do not provide our own notion of equality other than // identity. assertNotEquals(sampleFV, copy); // We *currently* only care that the serialized representation is // correct assertEquals(sampleFV.getDisplayText(), copy.getDisplayText()); // Okapi Fragments have a concept of equality that is preserved through // copying. This differs from TextContainerVariant. assertEquals(sampleFV.getUpdatedOkapiFragment(newFragment()), copy.getUpdatedOkapiFragment(newFragment())); // The number of atoms should be the same as long as the original did // not have PositionAtoms. assertEquals(sampleFV.getAtoms().size(), copy.getAtoms().size()); // The atoms themselves are the same in terms of the equals() methods on // the Ocelot base classes. This ignores the fact that the underlying // Okapi Codes do not have a notion of equality. TODO: Is this OK? assertEquals(sampleFV.getAtoms(), copy.getAtoms()); SegmentVariantSelection sel = new SegmentVariantSelection("", sampleFV.createCopy(), 1, 5); sampleFV.replaceSelection(sampleFV.getLength(), sampleFV.getLength(), sel); assertEquals("A<pcid1>B</pcid1><pcid1>", sampleFV.getDisplayText()); // Copy should not be affected by change to original. assertNotEquals(sampleFV.getDisplayText(), copy.getDisplayText()); sampleFV.createPosition(1); FragmentVariant newCopy = sampleFV.createCopy(); // Copy should not retain the PositionAtom in the original. assertNotEquals(sampleFV.getAtoms().size(), newCopy.getAtoms().size()); for (SegmentAtom atom : newCopy.getAtoms()) { if (atom instanceof PositionAtom) { fail("Copy should not contain PositionAtoms"); } } } private FragmentVariant sampleText(boolean isTarget) { Store store = new Store(new DummyWithStore()); Segment segment = new Segment(store); Fragment fragment = new Fragment(store, isTarget); fragment.append("A"); fragment.append(TagType.OPENING, "id1", "<b>", false); fragment.append("B"); fragment.append(TagType.CLOSING, "id1", "</b>", false); if (isTarget) { segment.setTarget(fragment); } else { segment.setSource(fragment); } return new FragmentVariant(segment, fragment.isTarget()); } private FragmentVariant plainText() { Store store = new Store(new DummyWithStore()); Segment segment = new Segment(store); segment.setSource("Plain Text"); return new FragmentVariant(segment, false); } private FragmentVariant plainCode() { Store store = new Store(new DummyWithStore()); Segment segment = new Segment(store); Fragment fragment = new Fragment(store, false); fragment.append(TagType.OPENING, "id1", "<b>", false); fragment.append(TagType.CLOSING, "id1", "</b>", false); segment.setSource(fragment); return new FragmentVariant(segment, false); } private Fragment newFragment() { Store store = new Store(new DummyWithStore()); return new Fragment(store, false); } public static class DummyWithStore implements IWithStore { @Override public Directionality getSourceDir() { return Directionality.AUTO; } @Override public Directionality getTargetDir() { return Directionality.AUTO; } @Override public boolean isIdUsed(String id) { return false; } @Override public void setSourceDir(Directionality arg0) { } @Override public void setTargetDir(Directionality arg0) { } } }