Direction estimators are simple classes which estimate a string's direction using various heuristics.
This is useful for automatically setting the direction of an element that contains text.
Review at http://gwt-code-reviews.appspot.com/338801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7973 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/i18n/shared/AnyRtlDirectionEstimator.java b/user/src/com/google/gwt/i18n/shared/AnyRtlDirectionEstimator.java
new file mode 100644
index 0000000..e3855d4
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/AnyRtlDirectionEstimator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.HasDirection.Direction;
+
+/**
+ * Direction estimator that uses the "any RTL" heuristic.
+ */
+public class AnyRtlDirectionEstimator extends DirectionEstimator {
+
+ /**
+ * An instance of AnyRtlDirectionEstimator, to be returned by {@link #get}.
+ */
+ private static final AnyRtlDirectionEstimator instance =
+ new AnyRtlDirectionEstimator();
+
+ /**
+ * Get an instance of AnyRtlDirectionEstimator.
+ *
+ * @return An instance of AnyRtlDirectionEstimator.
+ */
+ public static AnyRtlDirectionEstimator get() {
+ return instance;
+ }
+
+ /**
+ * Estimates the direction of a given string using the "any RTL" heuristic:
+ * the return value is RTL if the string contains at least one RTL character.
+ * Otherwise, it is LTR.
+ *
+ * @param str Input string.
+ * @return Direction The estimated direction of {@code str}.
+ */
+ @Override
+ public Direction estimateDirection(String str) {
+ return BidiUtils.get().hasAnyRtl(str) ? Direction.RTL : Direction.LTR;
+ }
+}
diff --git a/user/src/com/google/gwt/i18n/shared/DirectionEstimator.java b/user/src/com/google/gwt/i18n/shared/DirectionEstimator.java
new file mode 100644
index 0000000..9488e54
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/DirectionEstimator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.HasDirection.Direction;
+
+/**
+ * Interface for direction estimators.
+ */
+public abstract class DirectionEstimator {
+
+ /**
+ * Estimates the direction of a plain-text string.
+ *
+ * @param str The string to check.
+ * @return {@code str}'s estimated direction.
+ */
+ public abstract Direction estimateDirection(String str);
+
+ /**
+ * Estimates the direction of a string.
+ *
+ * @param str The string to check.
+ * @param isHtml Whether {@code str} is HTML / HTML-escaped. {@code false}
+ * means that {@code str} is plain-text.
+ * @return {@code str}'s estimated direction.
+ */
+ public Direction estimateDirection(String str, boolean isHtml) {
+ return estimateDirection(BidiUtils.get().stripHtmlIfNeeded(str, isHtml));
+ }
+}
diff --git a/user/src/com/google/gwt/i18n/shared/FirstStrongDirectionEstimator.java b/user/src/com/google/gwt/i18n/shared/FirstStrongDirectionEstimator.java
new file mode 100644
index 0000000..ca596a3
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/FirstStrongDirectionEstimator.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.HasDirection.Direction;
+
+/**
+ * Direction estimator that uses the "first strong" heuristic.
+ */
+public class FirstStrongDirectionEstimator extends DirectionEstimator {
+
+ /**
+ * An instance of FirstStrongDirectionEstimator, to be returned by
+ * {@link #get}.
+ */
+ private static final FirstStrongDirectionEstimator instance =
+ new FirstStrongDirectionEstimator();
+
+ /**
+ * Get an instance of FirstStrongDirectionEstimator.
+ *
+ * @return An instance of FirstStrongDirectionEstimator.
+ */
+ public static FirstStrongDirectionEstimator get() {
+ return instance;
+ }
+
+ /**
+ * Estimates the direction of a given string using the "first strong"
+ * heuristic: The return value is determined by the first character in the
+ * string with strong directionality. If there is no such character, the
+ * return value is DEFAULT.
+ *
+ * @param str Input string.
+ * @return Direction The estimated direction of {@code str}.
+ */
+ @Override
+ public Direction estimateDirection(String str) {
+ return BidiUtils.get().startsWithRtl(str) ? Direction.RTL :
+ BidiUtils.get().startsWithLtr(str) ? Direction.LTR : Direction.DEFAULT;
+ }
+}
diff --git a/user/src/com/google/gwt/i18n/shared/HasDirectionEstimator.java b/user/src/com/google/gwt/i18n/shared/HasDirectionEstimator.java
new file mode 100644
index 0000000..df86681
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/HasDirectionEstimator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+/**
+ * Interface for objects that have a direction estimator.
+ */
+public interface HasDirectionEstimator {
+
+ /**
+ * @return the {@code DirectionEstimator} object.
+ */
+ DirectionEstimator getDirectionEstimator();
+
+ /**
+ * Toggles on / off direction estimation.
+ *
+ * @param enabled Whether to enable direction estimation. If {@true}, sets the
+ * DirectionEstimator object to a default {@DirectionEstimator}.
+ */
+ void setDirectionEstimator(boolean enabled);
+
+ /**
+ * Sets the DirectionEstimator object.
+ *
+ * @param directionEstimator The {@code directionEstimator} to be set. {@code
+ * null} means turning off direction estimation.
+ */
+ void setDirectionEstimator(DirectionEstimator directionEstimator);
+}
diff --git a/user/src/com/google/gwt/i18n/shared/WordCountDirectionEstimator.java b/user/src/com/google/gwt/i18n/shared/WordCountDirectionEstimator.java
new file mode 100644
index 0000000..0333609
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/WordCountDirectionEstimator.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.HasDirection.Direction;
+
+/**
+ * Direction estimator that uses the "word count" heuristic.
+ *
+ * <p> Note: this is probably the recommended estimator for most use cases.
+ */
+public class WordCountDirectionEstimator extends DirectionEstimator {
+
+ /**
+ * An instance of WordCountDirectionEstimator, to be returned by {@link #get}.
+ */
+ private static final WordCountDirectionEstimator instance =
+ new WordCountDirectionEstimator();
+
+ /**
+ * Get an instance of WordCountDirectionEstimator.
+ *
+ * @return An instance of WordCountDirectionEstimator.
+ */
+ public static WordCountDirectionEstimator get() {
+ return instance;
+ }
+
+ /**
+ * Estimates the direction of a given string using the "word count" heuristic,
+ * as defined at {@link BidiUtils#estimateDirection}.
+ *
+ * @param str Input string.
+ * @return Direction The estimated direction of {@code str}.
+ */
+ @Override
+ public Direction estimateDirection(String str) {
+ return BidiUtils.get().estimateDirection(str);
+ }
+}
diff --git a/user/test/com/google/gwt/i18n/I18NSuite.java b/user/test/com/google/gwt/i18n/I18NSuite.java
index 0088aad..a1357fc 100644
--- a/user/test/com/google/gwt/i18n/I18NSuite.java
+++ b/user/test/com/google/gwt/i18n/I18NSuite.java
@@ -44,9 +44,12 @@
import com.google.gwt.i18n.rebind.MessageFormatParserTest;
import com.google.gwt.i18n.server.GwtLocaleTest;
import com.google.gwt.i18n.server.RegionInheritanceTest;
+import com.google.gwt.i18n.shared.AnyRtlDirectionEstimatorTest;
import com.google.gwt.i18n.shared.BidiFormatterTest;
import com.google.gwt.i18n.shared.BidiUtilsTest;
+import com.google.gwt.i18n.shared.FirstStrongDirectionEstimatorTest;
import com.google.gwt.i18n.shared.GwtBidiUtilsTest;
+import com.google.gwt.i18n.shared.WordCountDirectionEstimatorTest;
import com.google.gwt.junit.tools.GWTTestSuite;
import junit.framework.Test;
@@ -61,6 +64,7 @@
// $JUnit-BEGIN$
suite.addTestSuite(ArabicPluralsTest.class);
suite.addTestSuite(AnnotationsTest.class);
+ suite.addTestSuite(AnyRtlDirectionEstimatorTest.class);
suite.addTestSuite(BidiFormatterTest.class);
suite.addTestSuite(BidiUtilsTest.class);
suite.addTestSuite(ConstantMapTest.class);
@@ -72,6 +76,7 @@
suite.addTestSuite(DateTimeFormat_pl_Test.class);
suite.addTestSuite(DateTimeParse_en_Test.class);
suite.addTestSuite(DateTimeParse_zh_CN_Test.class);
+ suite.addTestSuite(FirstStrongDirectionEstimatorTest.class);
suite.addTestSuite(GwtBidiUtilsTest.class);
suite.addTestSuite(GwtLocaleTest.class);
suite.addTestSuite(I18NTest.class);
@@ -92,6 +97,7 @@
suite.addTestSuite(RuntimeLocalesTest.class);
suite.addTestSuite(TimeZoneInfoTest.class);
suite.addTestSuite(TimeZoneTest.class);
+ suite.addTestSuite(WordCountDirectionEstimatorTest.class);
// $JUnit-END$
return suite;
diff --git a/user/test/com/google/gwt/i18n/shared/AnyRtlDirectionEstimatorTest.java b/user/test/com/google/gwt/i18n/shared/AnyRtlDirectionEstimatorTest.java
new file mode 100644
index 0000000..66d7687
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/shared/AnyRtlDirectionEstimatorTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.HasDirection.Direction;
+
+/**
+ * Unit tests for {@link AnyRtlDirectionEstimator}.
+ */
+public class AnyRtlDirectionEstimatorTest extends DirectionEstimatorTestBase {
+
+ @Override
+ protected void assertDirectionEstimation(Direction expectedDirection,
+ String str, boolean isHtml) {
+ assertDirectionEstimation(expectedDirection, AnyRtlDirectionEstimator.get(),
+ str, isHtml);
+ }
+
+ private final String containsRtlChar = EN_WORD + DIGITS_WORD +
+ WORD_WITH_ONE_RTL_CHAR;
+ private final String noRtlChars = EN_WORD + DIGITS_WORD + NEUTRAL_WORD;
+ private final String noRtlCharsHtml = LONG_MIXED_TAG + EN_WORD + DIGITS_WORD;
+
+ public void testEstimateDirection() {
+ assertDirectionEstimation(Direction.RTL, containsRtlChar);
+ assertDirectionEstimation(Direction.LTR, noRtlChars);
+
+ assertDirectionEstimation(Direction.RTL, noRtlCharsHtml);
+ assertDirectionEstimationHtml(Direction.LTR, noRtlCharsHtml);
+ }
+}
diff --git a/user/test/com/google/gwt/i18n/shared/DirectionEstimatorTestBase.java b/user/test/com/google/gwt/i18n/shared/DirectionEstimatorTestBase.java
new file mode 100644
index 0000000..7960906
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/shared/DirectionEstimatorTestBase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.HasDirection.Direction;
+
+import junit.framework.TestCase;
+
+/**
+ * TestCase extension that includes some useful variables for estimation tests.
+ */
+public abstract class DirectionEstimatorTestBase extends TestCase {
+
+ protected final String DIGITS_WORD = " 012";
+ protected final String EN_WORD = " abc";
+ protected final String IW_WORD = " \u05e0\u05e1\u05e2";
+ protected final String LONG_LTR_TAG = "<some nasty tag>";
+
+ protected final String LONG_MIXED_TAG = "<some nasty tag" + IW_WORD + ">";
+ protected final String NEUTRAL_WORD = " ___";
+ protected final String WORD_WITH_ONE_RTL_CHAR = " ab\u05e0cd";
+
+ /**
+ * Asserts that the estimated direction of a given String matches the expected
+ * direction.
+ *
+ * @param expectedDirection The expected direction.
+ * @param directionEstimator A direction estimator object.
+ * @param str A String to estimate its direction.
+ * @param isHtml Whether {@code str} is HTML / HTML-escaped.
+ */
+ protected void assertDirectionEstimation(Direction expectedDirection,
+ DirectionEstimator directionEstimator, String str, boolean isHtml) {
+ assertEquals(expectedDirection,
+ directionEstimator.estimateDirection(str, isHtml));
+ }
+
+ /**
+ * Asserts that the estimated direction of a given String matches the expected
+ * direction.
+ * The implementation will usually call {@link #assertDirectionEstimation(
+ * Direction, DirectionEstimator, String, boolean)} with the {@code
+ * DirectionEstimator} object to be tested.
+ *
+ * @param expectedDirection The expected direction.
+ * @param str A String whose direction is estimated.
+ * @param isHtml Whether {@code str} is HTML / HTML-escaped.
+ */
+ protected abstract void assertDirectionEstimation(Direction expectedDirection,
+ String str, boolean isHtml);
+
+ /**
+ * Operates like {@link #assertDirectionEstimation(Direction, String,
+ * boolean)}, but assuming {@code str} is not HTML / HTML-escaped.
+ *
+ * @param expectedDirection The expected direction.
+ * @param str A String whose direction is estimated.
+ */
+ protected void assertDirectionEstimation(Direction expectedDirection,
+ String str) {
+ assertDirectionEstimation(expectedDirection, str, false);
+ }
+
+ /**
+ * Operates like {@link #assertDirectionEstimation(Direction, String,
+ * boolean)}, but assuming {@code str} is HTML / HTML-escaped.
+ *
+ * @param expectedDirection The expected direction.
+ * @param str A String whose direction is estimated.
+ */
+ protected void assertDirectionEstimationHtml(Direction expectedDirection,
+ String str) {
+ assertDirectionEstimation(expectedDirection, str, true);
+ }
+}
diff --git a/user/test/com/google/gwt/i18n/shared/FirstStrongDirectionEstimatorTest.java b/user/test/com/google/gwt/i18n/shared/FirstStrongDirectionEstimatorTest.java
new file mode 100644
index 0000000..efb0d2d
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/shared/FirstStrongDirectionEstimatorTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.HasDirection.Direction;
+
+/**
+ * Unit tests for {@link FirstStrongDirectionEstimator}.
+ */
+public class FirstStrongDirectionEstimatorTest extends DirectionEstimatorTestBase {
+
+ @Override
+ protected void assertDirectionEstimation(Direction expectedDirection,
+ String str, boolean isHtml) {
+ assertDirectionEstimation(expectedDirection,
+ FirstStrongDirectionEstimator.get(), str, isHtml);
+ }
+
+ private String firstStrongLtr = DIGITS_WORD + EN_WORD + IW_WORD + IW_WORD;
+ private String firstStrongRtl = DIGITS_WORD + IW_WORD + EN_WORD + EN_WORD;
+ private String firstStrongRtlHtml = DIGITS_WORD + LONG_LTR_TAG + IW_WORD +
+ EN_WORD;
+ private String noStrongChars = NEUTRAL_WORD + DIGITS_WORD;
+
+ public void testEstimateDirection() {
+ assertDirectionEstimation(Direction.LTR, firstStrongLtr);
+ assertDirectionEstimation(Direction.RTL, firstStrongRtl);
+ assertDirectionEstimation(Direction.DEFAULT, noStrongChars);
+
+ assertDirectionEstimation(Direction.LTR, firstStrongRtlHtml);
+ assertDirectionEstimationHtml(Direction.RTL, firstStrongRtlHtml);
+ }
+}
diff --git a/user/test/com/google/gwt/i18n/shared/WordCountDirectionEstimatorTest.java b/user/test/com/google/gwt/i18n/shared/WordCountDirectionEstimatorTest.java
new file mode 100644
index 0000000..dfb6561
--- /dev/null
+++ b/user/test/com/google/gwt/i18n/shared/WordCountDirectionEstimatorTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.HasDirection.Direction;
+
+/**
+ * Unit tests for {@link WordCountDirectionEstimator}.
+ */
+public class WordCountDirectionEstimatorTest extends DirectionEstimatorTestBase {
+
+ @Override
+ protected void assertDirectionEstimation(Direction expectedDirection,
+ String str, boolean isHtml) {
+ assertDirectionEstimation(expectedDirection,
+ WordCountDirectionEstimator.get(), str, isHtml);
+ }
+
+ private String pureNeutral = NEUTRAL_WORD;
+ private String rtlAboveThreshold = EN_WORD + IW_WORD;
+ private String rtlBelowThreshold = IW_WORD + EN_WORD + EN_WORD;
+ private String rtlHtml = LONG_LTR_TAG + IW_WORD;
+ private String weaklyLtr = NEUTRAL_WORD + DIGITS_WORD;
+
+ public void testEstimateDirection() {
+ assertDirectionEstimation(Direction.RTL, rtlAboveThreshold);
+ assertDirectionEstimation(Direction.LTR, rtlBelowThreshold);
+ assertDirectionEstimation(Direction.LTR, weaklyLtr);
+ assertDirectionEstimation(Direction.DEFAULT, pureNeutral);
+
+ assertDirectionEstimation(Direction.LTR, rtlHtml);
+ assertDirectionEstimationHtml(Direction.RTL, rtlHtml);
+ }
+}