/* * 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.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; 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.SegmentTextCell; import com.vistatec.ocelot.segment.view.SegmentVariantSelection; import net.sf.okapi.common.resource.Code; import net.sf.okapi.common.resource.TextContainer; import net.sf.okapi.common.resource.TextFragment; import net.sf.okapi.common.resource.TextFragment.TagType; public class TestTextContainerSegmentVariant { private TextContainerVariant tcv, plainTextTCV, plainCodeTCV; @Before public void beforeTest() { tcv = sampleText(); plainTextTCV = plainText(); plainCodeTCV = plainCode(); } private TextContainerVariant sampleText() { TextContainer tc = new TextContainer(); TextFragment tf = tc.getFirstContent(); tf.append("A"); tf.append(new Code(TagType.OPENING, "b", "<b id=\"1\">")); tf.append("B"); tf.append(new Code(TagType.CLOSING, "b", "</b>")); return new TextContainerVariant(tc); } private TextContainerVariant plainText() { TextContainer tc = new TextContainer(); TextFragment tf = tc.getFirstContent(); tf.append("Plain text"); return new TextContainerVariant(tc); } private TextContainerVariant plainCode() { TextContainer tc = new TextContainer(); TextFragment tf = tc.getFirstContent(); tf.append(new Code(TagType.OPENING, "b", "<b id=\"1\">")); tf.append(new Code(TagType.CLOSING, "b", "</b>")); return new TextContainerVariant(tc); } @Test public void testGetAtoms() { assertEquals(Lists.newArrayList(new TextAtom("A"), new CodeAtom("0", "<b1>", "<b id=\"1\">"), new TextAtom("B"), new CodeAtom("1", "</b1>", "</b>")), tcv.getAtoms()); } @Test public void testSetAtoms() { List<SegmentAtom> atoms = tcv.getAtoms(); atoms.add(new TextAtom("X")); tcv.setAtoms(atoms); assertEquals(Lists.newArrayList(new TextAtom("A"), new CodeAtom("0", "<b1>", "<b id=\"1\">"), new TextAtom("B"), new CodeAtom("1", "</b1>", "</b>"), new TextAtom("X")), tcv.getAtoms()); } @Test public void testReplaceSelection() { // A<b1>B</b1> // replace <b1>B with B<b1> TextContainer tc = new TextContainer(); TextFragment tf = tc.getFirstContent(); tf.append("AB"); tf.append(new Code(TagType.OPENING, "b", "<b id=\"1\">")); tf.append(new Code(TagType.CLOSING, "b", "</b>")); TextContainerVariant replacement = new TextContainerVariant(tc); // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 // - - - // A B < b 1 > < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 tcv.replaceSelection(1, 6, new SegmentVariantSelection("0", replacement, 1, 6)); assertEquals("AB<b1></b1>", tcv.getDisplayText()); } @Test public void testGetDisplayText() { assertEquals("A<b1>B</b1>", tcv.getDisplayText()); } @Test public void testGetStyleData() { List<String> expected = new ArrayList<String>(); expected.add("A"); expected.add(SegmentTextCell.regularStyle); expected.add("<b1>"); expected.add(SegmentTextCell.tagStyle); expected.add("B"); expected.add(SegmentTextCell.regularStyle); expected.add("</b1>"); expected.add(SegmentTextCell.tagStyle); assertEquals(expected, tcv.getStyleData(false)); } @Test public void testGetStyleDataVerbose() { List<String> expected = new ArrayList<String>(); expected.add("A"); expected.add(SegmentTextCell.regularStyle); expected.add("<b id=\"1\">"); expected.add(SegmentTextCell.tagStyle); expected.add("B"); expected.add(SegmentTextCell.regularStyle); expected.add("</b>"); expected.add(SegmentTextCell.tagStyle); assertEquals(expected, tcv.getStyleData(true)); } @Test public void testCanInsertAt() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 // Y Y N N N Y Y N N N N assertTrue(tcv.canInsertAt(0)); assertTrue(tcv.canInsertAt(1)); assertFalse(tcv.canInsertAt(2)); assertFalse(tcv.canInsertAt(3)); assertFalse(tcv.canInsertAt(4)); assertTrue(tcv.canInsertAt(5)); assertTrue(tcv.canInsertAt(6)); assertFalse(tcv.canInsertAt(7)); assertFalse(tcv.canInsertAt(8)); assertFalse(tcv.canInsertAt(9)); assertFalse(tcv.canInsertAt(10)); } @Test public void testContainsTag() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 assertFalse(tcv.containsTag(0, 1)); assertTrue(tcv.containsTag(0, 2)); assertTrue(tcv.containsTag(1, 2)); assertTrue(tcv.containsTag(2, 2)); assertTrue(tcv.containsTag(3, 2)); assertTrue(tcv.containsTag(4, 1)); assertFalse(tcv.containsTag(5, 1)); assertTrue(tcv.containsTag(4, 2)); assertTrue(tcv.containsTag(5, 2)); assertTrue(tcv.containsTag(6, 2)); } @Test public void testGetAtomAt() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 assertEquals("A", tcv.getAtomAt(0).getData()); assertEquals("<b1>", tcv.getAtomAt(1).getData()); assertEquals("<b1>", tcv.getAtomAt(2).getData()); assertEquals("<b1>", tcv.getAtomAt(3).getData()); assertEquals("<b1>", tcv.getAtomAt(4).getData()); assertEquals("B", tcv.getAtomAt(5).getData()); assertEquals("</b1>", tcv.getAtomAt(6).getData()); assertEquals("</b1>", tcv.getAtomAt(7).getData()); assertEquals("</b1>", tcv.getAtomAt(8).getData()); assertEquals("</b1>", tcv.getAtomAt(9).getData()); assertEquals("</b1>", tcv.getAtomAt(10).getData()); assertNull(tcv.getAtomAt(11)); } @Test public void testModifyChars() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 tcv.modifyChars(0, 0, "X"); assertEquals("XA<b1>B</b1>", tcv.getDisplayText()); tcv = sampleText(); tcv.modifyChars(1, 0, "X"); assertEquals("AX<b1>B</b1>", tcv.getDisplayText()); tcv = sampleText(); tcv.modifyChars(5, 0, "X"); assertEquals("A<b1>XB</b1>", tcv.getDisplayText()); tcv.modifyChars(6, 0, "N"); assertEquals("A<b1>XNB</b1>", tcv.getDisplayText()); tcv = sampleText(); tcv.modifyChars(6, 0, "X"); assertEquals("A<b1>BX</b1>", tcv.getDisplayText()); tcv = sampleText(); tcv.modifyChars(0, 1, "X"); assertEquals("X<b1>B</b1>", tcv.getDisplayText()); tcv = sampleText(); tcv.modifyChars(5, 1, "X"); assertEquals("A<b1>X</b1>", tcv.getDisplayText()); tcv = sampleText(); tcv.modifyChars(0, 1, null); // delete assertEquals("<b1>B</b1>", tcv.getDisplayText()); tcv = sampleText(); tcv.modifyChars(11, 0, "X"); assertEquals("A<b1>B</b1>X", tcv.getDisplayText()); } @Test public void testInsertPlainCode() { plainCodeTCV.modifyChars(0, 0, "ABC"); assertEquals("ABC<b1></b1>", plainCodeTCV.getDisplayText()); plainCodeTCV = plainCode(); plainCodeTCV.modifyChars(9, 0, "ABC"); assertEquals("<b1></b1>ABC", plainCodeTCV.getDisplayText()); plainCodeTCV = plainCode(); plainCodeTCV.modifyChars(4, 0, "ABC"); assertEquals("<b1>ABC</b1>", plainCodeTCV.getDisplayText()); // These test calls should never be called in real usage, as modifyChars // should not be called unless it has already been verified that you are // not modifying within a CodeAtom. plainCodeTCV = plainCode(); plainCodeTCV.modifyChars(1, 0, "ABC"); assertEquals("ABC<b1></b1>", plainCodeTCV.getDisplayText()); plainCodeTCV = plainCode(); plainCodeTCV.modifyChars(2, 0, "ABC"); assertEquals("ABC<b1></b1>", plainCodeTCV.getDisplayText()); plainCodeTCV = plainCode(); plainCodeTCV.modifyChars(4, 0, "ABC"); assertEquals("<b1>ABC</b1>", plainCodeTCV.getDisplayText()); plainCodeTCV = plainCode(); plainCodeTCV.modifyChars(5, 0, "ABC"); assertEquals("<b1>ABC</b1>", plainCodeTCV.getDisplayText()); plainCodeTCV = plainCode(); plainCodeTCV.modifyChars(6, 0, "ABC"); assertEquals("<b1>ABC</b1>", plainCodeTCV.getDisplayText()); } @Test public void testRemoveCharsFromPlainText() { plainTextTCV.modifyChars(5, 5, null); assertEquals("Plain", plainTextTCV.getDisplayText()); plainTextTCV = plainText(); plainTextTCV.modifyChars(5, 1, null); assertEquals("Plaintext", plainTextTCV.getDisplayText()); plainTextTCV = plainText(); plainTextTCV.modifyChars(0, 6, null); assertEquals("text", plainTextTCV.getDisplayText()); } @Test public void testDoesThisCrash() { List<String> l = Lists.newArrayList("A", "B", "C"); assertEquals(Lists.newArrayList("C"), l.subList(2, 3)); assertEquals(Lists.newArrayList(), l.subList(3, 3)); } @Test public void testReplaceWithAtoms() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 // Insert with zero-length range. tcv.replaceSelection(0, 0, Arrays.asList(new TextAtom("C"), new TextAtom("D"))); assertEquals("CDA<b1>B</b1>", tcv.toString()); tcv.replaceSelection(0, 3, Arrays.asList(new TextAtom("E"))); assertEquals("E<b1>B</b1>", tcv.toString()); // Range must include entire CodeAtom in order to replace it. tcv.replaceSelection(0, 2, Arrays.asList(new TextAtom("F"))); assertEquals("F<b1>B</b1>", tcv.toString()); tcv.replaceSelection(0, 3, Arrays.asList(new TextAtom("F"))); assertEquals("F<b1>B</b1>", tcv.toString()); tcv.replaceSelection(0, 4, Arrays.asList(new TextAtom("F"))); assertEquals("F<b1>B</b1>", tcv.toString()); tcv.replaceSelection(0, 5, Arrays.asList(new TextAtom("F"))); assertEquals("FB</b1>", tcv.toString()); // Replace with empty list to clear. tcv.replaceSelection(0, 3, Collections.<SegmentAtom> emptyList()); assertEquals("</b1>", tcv.toString()); } @Test public void testSelection() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 // SegmentVariantSelection represents a selection of the textual // representation, not the atomic representation. SegmentVariantSelection sel = new SegmentVariantSelection("", tcv, 0, 1); assertEquals("A", sel.getDisplayText()); sel = new SegmentVariantSelection("", tcv, 0, 2); assertEquals("A<", sel.getDisplayText()); sel = new SegmentVariantSelection("", tcv, 0, 3); assertEquals("A<b", sel.getDisplayText()); sel = new SegmentVariantSelection("", tcv, 0, 4); assertEquals("A<b1", sel.getDisplayText()); sel = new SegmentVariantSelection("", tcv, 0, 5); assertEquals("A<b1>", sel.getDisplayText()); // Selection refers to source SegmentVariant, will become invalid if // source is mutated. tcv.clearSelection(0, tcv.getLength()); try { assertEquals("A<b1>", sel.getDisplayText()); fail("Selection should no longer be valid"); } catch (IndexOutOfBoundsException e) { // OK } } @Test public void testReplaceWithSelection() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 SegmentVariantSelection sel = new SegmentVariantSelection("", tcv.createCopy(), 1, 6); assertEquals("<b1>B", sel.getDisplayText()); tcv.replaceSelection(0, 0, sel); assertEquals("<b1>BA<b1>B</b1>", tcv.toString()); tcv.replaceSelection(tcv.getLength(), tcv.getLength(), sel); assertEquals("<b1>BA<b1>B</b1><b1>B", tcv.toString()); tcv.replaceSelection(0, 11, sel); assertEquals("<b1>B</b1><b1>B", tcv.toString()); } @Test public void testClearSelection() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 tcv.clearSelection(0, 0); assertEquals("A<b1>B</b1>", tcv.getDisplayText()); tcv.clearSelection(0, 1); assertEquals("<b1>B</b1>", tcv.getDisplayText()); // Range must cover entire CodeAtom to remove it. tcv.clearSelection(0, 1); assertEquals("<b1>B</b1>", tcv.getDisplayText()); tcv.clearSelection(0, 2); assertEquals("<b1>B</b1>", tcv.getDisplayText()); tcv.clearSelection(0, 3); assertEquals("<b1>B</b1>", tcv.getDisplayText()); tcv.clearSelection(0, 4); assertEquals("B</b1>", tcv.getDisplayText()); } @Test public void testValidation() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 TextContainerVariant copy = tcv.createCopy(); assertFalse(tcv.needsValidation()); // Replacing empty selection does not dirty the variant. tcv.replaceSelection(0, 0, Collections.<SegmentAtom> emptyList()); assertFalse(tcv.needsValidation()); // Replacing atoms dirties the variant... tcv.replaceSelection(0, 1, Collections.<SegmentAtom> emptyList()); assertTrue(tcv.needsValidation()); // ...but tags are still OK here. assertTrue(tcv.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(tcv.needsValidation()); tcv = copy.createCopy(); assertFalse(tcv.needsValidation()); // Removing a tag will make the variant fail validation. tcv.replaceSelection(0, 5, Arrays.asList(new TextAtom("Z"))); assertTrue(tcv.needsValidation()); assertFalse(tcv.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 = tcv.getMissingTags(copy); assertEquals(1, missing.size()); assertEquals("<b1>", missing.get(0).getData()); tcv.replaceSelection(tcv.getLength(), tcv.getLength(), missing); assertTrue(tcv.needsValidation()); assertTrue(tcv.validateAgainst(copy)); } @Test public void testPositions() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 PositionAtom start = tcv.createPosition(0); assertTrue(start.getData().isEmpty()); assertEquals(0, start.getLength()); assertEquals(0, start.getPosition()); PositionAtom end = tcv.createPosition(6); assertEquals(6, end.getPosition()); tcv.modifyChars(0, 0, "Z"); assertEquals("ZA<b1>B</b1>", tcv.getDisplayText()); assertEquals(0, start.getPosition()); assertEquals(7, end.getPosition()); tcv.replaceSelection(start.getPosition(), end.getPosition(), Arrays.asList(new TextAtom("ABCD"))); assertEquals("ABCD</b1>", tcv.getDisplayText()); assertEquals(0, start.getPosition()); assertEquals(4, end.getPosition()); PositionAtom middle = tcv.createPosition(2); tcv.clearSelection(start.getPosition(), end.getPosition()); assertEquals("</b1>", tcv.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 = tcv.createPosition(2); assertEquals(5, inCode.getPosition()); } @Test public void testCopy() { // A < b 1 > B < / b 1 > // 0 1 2 3 4 5 6 7 8 9 10 TextContainerVariant copy = tcv.createCopy(); // We *currently* do not provide our own notion of equality other than // identity. assertNotEquals(tcv, copy); // We *currently* only care that the serialized representation is // correct assertEquals(tcv.getDisplayText(), copy.getDisplayText()); // Even the underlying TextContainer does not have its own notion of // equality separate from identity. Note that some assumptions in our // code might have to change if this situation ever changes. assertNotEquals(tcv.getTextContainer(), copy.getTextContainer()); // The number of atoms should be the same as long as the original did // not have PositionAtoms. assertEquals(tcv.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(tcv.getAtoms(), copy.getAtoms()); SegmentVariantSelection sel = new SegmentVariantSelection("", tcv.createCopy(), 1, 5); tcv.replaceSelection(tcv.getLength(), tcv.getLength(), sel); assertEquals("A<b1>B</b1><b1>", tcv.getDisplayText()); // Copy should not be affected by change to original. assertNotEquals(tcv.getDisplayText(), copy.getDisplayText()); tcv.createPosition(1); TextContainerVariant newCopy = tcv.createCopy(); // Copy should not retain the PositionAtom in the original. assertNotEquals(tcv.getAtoms().size(), newCopy.getAtoms().size()); for (SegmentAtom atom : newCopy.getAtoms()) { if (atom instanceof PositionAtom) { fail("Copy should not contain PositionAtoms"); } } } }