Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
update tag order algorithm to report minimal tags to reorder
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmason committed Jan 19, 2012
1 parent e69ee68 commit 1852aad
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 34 deletions.
Expand Up @@ -21,7 +21,7 @@
package org.zanata.webtrans.shared.validation.action;

import java.util.ArrayList;
import java.util.List;
//import java.util.List;

import org.zanata.webtrans.client.resources.ValidationMessages;
import org.zanata.webtrans.shared.validation.ValidationUtils;
Expand Down Expand Up @@ -52,52 +52,108 @@ public void validate(String source, String target)
{
if (!ValidationUtils.isEmpty(target))
{
List<String> error = runValidation(source, target);
ArrayList<String> sourceTags = getTagList(source);
ArrayList<String> targetTags = getTagList(target);

ArrayList<String> error = listMissing(source, target);
if (!error.isEmpty())
{

addError(getMessages().tagsMissing(error));
}

error = runValidation(target, source);
boolean noError = error.isEmpty();

error = listMissing(target, source);
if (!error.isEmpty())
{
addError(getMessages().tagsAdded(error));
}

if (getError().isEmpty())
noError &= error.isEmpty();

if (noError)
{
orderValidation(source, target);
orderValidation(sourceTags, targetTags);
}
}
}

private void orderValidation(String source, String target)
private void orderValidation(ArrayList<String> srcTags, ArrayList<String> trgTags)
{
// TODO improve for cases such as first node moved to end and last node
// moved to start. Currently reports every node in these cases, should
// only report the one moved node.
List<String> from = getTagList(source);
List<String> to = getTagList(target);
List<String> outOfOrder = new ArrayList<String>();

for (int i = 0; i < from.size(); i++)
ArrayList<String> longestRun = null;
ArrayList<String> currentRun = new ArrayList<String>();

String[] src = srcTags.toArray(new String[srcTags.size()]);
String[] trg = trgTags.toArray(new String[trgTags.size()]);

for (int i = 0; i < src.length; i++)
{
if (!to.get(i).equals(from.get(i)))
String token = src[i];
int srcIndex = i;
int trgIndex = trgTags.indexOf(token);

if (trgIndex > -1)
{
outOfOrder.add(from.get(i));
currentRun = new ArrayList<String>();
currentRun.add(token);

int j = trgIndex + 1;

while (j < trg.length && srcIndex < src.length - 1)
{
int nextIndexInSrc = findInTail(trg[j], src, srcIndex + 1);
if (nextIndexInSrc > -1)
{
srcIndex = nextIndexInSrc;
currentRun.add(src[srcIndex]);
}
j++;
}

if (currentRun.size() == srcTags.size())
{
// must all match
return;
}

if (longestRun == null || longestRun.size() < currentRun.size())
{
longestRun = currentRun;
}
}
}

if (!outOfOrder.isEmpty())
if (longestRun != null && longestRun.size() > 0)
{
ArrayList<String> outOfOrder = new ArrayList<String>();

for (int i = 0; i < src.length; i++)
{
if (!longestRun.contains(src[i]))
{
outOfOrder.add(src[i]);
}
}

addError(getMessages().tagsWrongOrder(outOfOrder));
}
}

private List<String> getTagList(String src)
private int findInTail(String toFind, String[] findIn, int startIndex)
{
for (int i = startIndex; i < findIn.length; i++)
{
if (findIn[i].equals(toFind))
{
return i;
}
}
return -1;
}

private ArrayList<String> getTagList(String src)
{
List<String> list = new ArrayList<String>();
ArrayList<String> list = new ArrayList<String>();
MatchResult result = regExp.exec(src);
while (result != null)
{
Expand All @@ -108,10 +164,10 @@ private List<String> getTagList(String src)
return list;
}

private List<String> runValidation(String compareFrom, String compareTo)
private ArrayList<String> listMissing(String compareFrom, String compareTo)
{
String tmp = compareTo;
List<String> unmatched = new ArrayList<String>();
ArrayList<String> unmatched = new ArrayList<String>();
MatchResult result = regExp.exec(compareFrom);

while (result != null)
Expand Down
Expand Up @@ -194,10 +194,128 @@ public void missingTagsError()
assertThat(capturedTagsMissing.getValue().size(), is(3));
}

// TODO update algorithm, this test will fail so will need updating
@Test
public void orderOnlyValidatedWithSameTags()
{
htmlXmlTagValidation = new HtmlXmlTagValidation(mockMessages);
String source = "<one><two><three></four></five>";
String target = "<two></five></four><three><six>";
htmlXmlTagValidation.validate(source, target);

assertThat(htmlXmlTagValidation.hasError(), is(true));
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_MISSING_MESSAGE));
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_ADDED_MESSAGE));
assertThat(htmlXmlTagValidation.getError().size(), is(2));

assertThat(capturedTagsMissing.getValue(), hasItem("<one>"));
assertThat(capturedTagsMissing.getValue().size(), is(1));
assertThat(capturedTagsAdded.getValue(), hasItem("<six>"));
assertThat(capturedTagsAdded.getValue().size(), is(1));
assertThat(capturedTagsOutOfOrder.hasCaptured(), is(false));
}

@Test
public void lastTagMovedToFirstError()
{
htmlXmlTagValidation = new HtmlXmlTagValidation(mockMessages);
String source = "<one><two><three></four></five><six>";
String target = "<six><one><two><three></four></five>";
htmlXmlTagValidation.validate(source, target);

assertThat(htmlXmlTagValidation.hasError(), is(true));
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_OUT_OF_ORDER_MESSAGE));
assertThat(htmlXmlTagValidation.getError().size(), is(1));

assertThat(capturedTagsOutOfOrder.getValue(), hasItem("<six>"));
assertThat("when one tag has moved, only that tag should be reported out of order", capturedTagsOutOfOrder.getValue().size(), is(1));
}

@Test
public void firstTagMovedToLastError()
{
htmlXmlTagValidation = new HtmlXmlTagValidation(mockMessages);
String source = "<one><two><three></four></five><six>";
String target = "<two><three></four></five><six><one>";
htmlXmlTagValidation.validate(source, target);

assertThat(htmlXmlTagValidation.hasError(), is(true));
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_OUT_OF_ORDER_MESSAGE));
assertThat(htmlXmlTagValidation.getError().size(), is(1));

assertThat(capturedTagsOutOfOrder.getValue(), hasItem("<one>"));
assertThat("when one tag has moved, only that tag should be reported out of order", capturedTagsOutOfOrder.getValue().size(), is(1));
}

@Test
public void tagMovedToMiddleError()
{
htmlXmlTagValidation = new HtmlXmlTagValidation(mockMessages);
String source = "<one><two><three></four></five><six>";
String target = "<two><three><one></four></five><six>";
htmlXmlTagValidation.validate(source, target);

assertThat(htmlXmlTagValidation.hasError(), is(true));
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_OUT_OF_ORDER_MESSAGE));
assertThat(htmlXmlTagValidation.getError().size(), is(1));

assertThat(capturedTagsOutOfOrder.getValue(), hasItem("<one>"));
assertThat("when one tag has moved, only that tag should be reported out of order", capturedTagsOutOfOrder.getValue().size(), is(1));
}

@Test
public void reversedTagsError()
{
htmlXmlTagValidation = new HtmlXmlTagValidation(mockMessages);
String source = "<one><two><three></four></five><six>";
String target = "<six></five></four><three><two><one>";
htmlXmlTagValidation.validate(source, target);

assertThat(htmlXmlTagValidation.hasError(), is(true));
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_OUT_OF_ORDER_MESSAGE));
assertThat(htmlXmlTagValidation.getError().size(), is(1));

// <one> is the first in-order tag, so is not reported
assertThat(capturedTagsOutOfOrder.getValue(), hasItems("<six>", "</five>", "</four>", "<three>", "<two>"));
assertThat(capturedTagsOutOfOrder.getValue().size(), is(5));
}

@Test
public void reportFirstTagsOutOfOrder()
{
htmlXmlTagValidation = new HtmlXmlTagValidation(mockMessages);
String source = "<one><two><three></four></five><six>";
String target = "</four></five><six><one><two><three>";
htmlXmlTagValidation.validate(source, target);

assertThat(htmlXmlTagValidation.hasError(), is(true));
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_OUT_OF_ORDER_MESSAGE));
assertThat(htmlXmlTagValidation.getError().size(), is(1));

assertThat(capturedTagsOutOfOrder.getValue(), hasItems("</four>", "</five>", "<six>"));
assertThat(capturedTagsOutOfOrder.getValue().size(), is(3));
}

@Test
public void reportLeastTagsOutOfOrder()
{
htmlXmlTagValidation = new HtmlXmlTagValidation(mockMessages);
String source = "<one><two><three></four></five><six>";
String target = "<six></four></five><one><two><three>";
htmlXmlTagValidation.validate(source, target);

assertThat(htmlXmlTagValidation.hasError(), is(true));
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_OUT_OF_ORDER_MESSAGE));
assertThat(htmlXmlTagValidation.getError().size(), is(1));

// <one><two><three> in order
// should not use </four></five> as there are less tags
assertThat("should report the least number of tags to move to restore order", capturedTagsOutOfOrder.getValue(), hasItems("</four>", "</five>", "<six>"));
assertThat(capturedTagsOutOfOrder.getValue().size(), is(3));
}

@SuppressWarnings("unchecked")
@Test
public void wrongOrderTagError()
public void swapSomeTagsError()
{
htmlXmlTagValidation = new HtmlXmlTagValidation(mockMessages);
String source = "<one><two><three></three></two><four></four></one>";
Expand All @@ -208,17 +326,10 @@ public void wrongOrderTagError()
assertThat(htmlXmlTagValidation.getError(), hasItem(MOCK_TAGS_OUT_OF_ORDER_MESSAGE));
assertThat(htmlXmlTagValidation.getError().size(), is(1));

// Note: only <three> and </three> have been moved in this string, but the
// algorithm just looks for changed index at the moment.
assertThat(capturedTagsOutOfOrder.getValue(), hasItems("<three>", "</three>", "</two>", "<four>"));
assertThat(capturedTagsOutOfOrder.getValue(), not(anyOf(hasItem("<one>"), hasItem("<two>"), hasItem("</four>"), hasItem("</one>"))));
assertThat(capturedTagsOutOfOrder.getValue().size(), is(4));
assertThat(capturedTagsOutOfOrder.getValue(), hasItems("<three>", "</three>"));
assertThat(capturedTagsOutOfOrder.getValue(), not(anyOf(hasItem("<one>"), hasItem("<two>"), hasItem("</two>"), hasItem("<four>"), hasItem("</four>"), hasItem("</one>"))));
assertThat(capturedTagsOutOfOrder.getValue().size(), is(2));
}

// TODO test cases for moving one tag a long way (when the algorithm
// handles that better). e.g.
// String source = "<one><two><three></three></two><four></four></one>";
// String target = "</one><one><two><three></three></two><four></four>";
}


Expand Down

0 comments on commit 1852aad

Please sign in to comment.