Add CLDR import code, and external projects for CLDR data/tools.

Patch by: jat
Review by: pdr, fabbott


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9037 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/build.xml b/dev/build.xml
index 00a5c2b..fd43dde 100755
--- a/dev/build.xml
+++ b/dev/build.xml
@@ -59,7 +59,7 @@
           <include name="eclipse/jdt-3.4.2.jar" />
           <include name="guava/guava-r06/guava-r06-rebased.jar" />
           <include name="jetty/jetty-6.1.11.jar" />
-          <include name="icu4j/icu4j-4_4_1.jar" />
+          <include name="icu4j/4.4.2/icu4j.jar" />
           <include name="protobuf/protobuf-2.2.0/protobuf-java-rebased-2.2.0.jar" />
           <include name="tomcat/ant-launcher-1.6.5.jar" />
           <include name="tomcat/catalina-1.0.jar" />
@@ -122,7 +122,7 @@
           <zipfileset src="${gwt.tools.lib}/eclipse/jdt-3.4.2.jar" />
           <zipfileset src="${gwt.tools.lib}/guava/guava-r06/guava-r06-rebased.jar" />
           <zipfileset src="${gwt.tools.lib}/jetty/jetty-6.1.11.jar" />
-          <zipfileset src="${gwt.tools.lib}/icu4j/icu4j-4_4_1.jar" />
+          <zipfileset src="${gwt.tools.lib}/icu4j/4.4.2/icu4j.jar" />
           <zipfileset src="${gwt.tools.lib}/protobuf/protobuf-2.2.0/protobuf-java-rebased-2.2.0.jar" />
           <zipfileset src="${gwt.tools.lib}/streamhtmlparser/streamhtmlparser-jsilver-r10/streamhtmlparser-jsilver-r10-1.5-rebased.jar" />
           <zipfileset src="${gwt.tools.lib}/tomcat/ant-launcher-1.6.5.jar" />
diff --git a/eclipse/README-CLDR.txt b/eclipse/README-CLDR.txt
new file mode 100644
index 0000000..1a99f3d
--- /dev/null
+++ b/eclipse/README-CLDR.txt
@@ -0,0 +1,16 @@
+Steps to process CLDR data using Eclipse:
+
+1) Get the CLDR data available locally on your system, such as by:
+  svn co http://unicode.org/repos/cldr/tags/release-1-8-1 <cldrdir>
+2) Set a linked resource variable pointing to this in Eclipse:
+    Window -> Preferences -> General -> Workspace -> Linked Resources
+  Add a variable CLDR_ROOT pointing to <cldrdir> above
+3) Import the cldr-data, cldr-tools, and cldr-import projects (note that
+  CLDR_ROOT must be defined as above, or you will have to delete and
+  reimport these projects).
+    File -> Import -> General -> Existing Projects in Workspace
+  Browse to $GWT_ROOT/eclipse and select the cldr-data, cldr-tools,
+  and cldr-import projects.
+4) Run the GenerateGwtCldrData launch config -- by default, it will
+  overwrite files in the GWT distribution; edit the --outdir argument
+  if you want it to go somewhere else.
diff --git a/eclipse/external/cldr-data/.project b/eclipse/external/cldr-data/.project
new file mode 100644
index 0000000..17e74af
--- /dev/null
+++ b/eclipse/external/cldr-data/.project
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>cldr-data</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+	<linkedResources>
+		<link>
+			<name>common</name>
+			<type>2</type>
+			<locationURI>CLDR_ROOT/common</locationURI>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/eclipse/external/cldr-tools/.classpath b/eclipse/external/cldr-tools/.classpath
new file mode 100644
index 0000000..19e47de
--- /dev/null
+++ b/eclipse/external/cldr-tools/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry excluding="org/unicode/cldr/web/|org/unicode/cldr/tool/templates/" kind="src" path="java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/xalan/xalan-2.7.1.jar"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.7.1.jar"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/icu4j/4.4.2/icu4j.jar"/ sourcepath="/GWT_TOOLS/lib/icu4j/4.4.2/icu4jsrc.jar"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/icu4j/4.4.2/utilities.jar" sourcepath="/GWT_TOOLS/lib/icu4j/4.4.2/icu4jsrc.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/eclipse/external/cldr-tools/.project b/eclipse/external/cldr-tools/.project
new file mode 100644
index 0000000..c7109f6
--- /dev/null
+++ b/eclipse/external/cldr-tools/.project
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>cldr-tools</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+	<linkedResources>
+		<link>
+			<name>java</name>
+			<type>2</type>
+			<locationURI>CLDR_ROOT/tools/java</locationURI>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/eclipse/external/cldr-tools/.settings/org.eclipse.core.resources.prefs b/eclipse/external/cldr-tools/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..7abc8fb
--- /dev/null
+++ b/eclipse/external/cldr-tools/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Feb 02 11:09:17 EST 2010
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/eclipse/external/cldr-tools/.settings/org.eclipse.jdt.core.prefs b/eclipse/external/cldr-tools/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..ee81b53
--- /dev/null
+++ b/eclipse/external/cldr-tools/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,332 @@
+#Mon Oct 04 15:17:42 EDT 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=ignore
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=ignore
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=ignore
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=ignore
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=ignore
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=ignore
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=ignore
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=1
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/eclipse/external/cldr-tools/.settings/org.eclipse.jdt.ui.prefs b/eclipse/external/cldr-tools/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..29ae99b
--- /dev/null
+++ b/eclipse/external/cldr-tools/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,8 @@
+#Tue Feb 02 11:38:32 EST 2010
+eclipse.preferences.version=1
+formatter_profile=_cldr
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
diff --git a/eclipse/tools/cldr-import/.checkstyle b/eclipse/tools/cldr-import/.checkstyle
new file mode 100644
index 0000000..0f1cbd9
--- /dev/null
+++ b/eclipse/tools/cldr-import/.checkstyle
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fileset-config file-format-version="1.2.0" simple-config="true">
+    <fileset name="all" enabled="true" check-config-name="GWT Checks" local="false">
+        <file-match-pattern match-pattern="." include-pattern="true"/>
+    </fileset>
+    <filter name="NonSrcDirs" enabled="true"/>
+</fileset-config>
diff --git a/eclipse/tools/cldr-import/.classpath b/eclipse/tools/cldr-import/.classpath
new file mode 100644
index 0000000..a6f8e0c
--- /dev/null
+++ b/eclipse/tools/cldr-import/.classpath
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="core/src"/>
+	<classpathentry kind="src" path="core/test"/>
+	<classpathentry kind="src" path="generated"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/gwt-user"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/cldr-tools"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/gwt-dev"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/icu4j/4.4.2/utilities.jar" sourcepath="/GWT_TOOLS/lib/icu4j/4.4.2/icu4jsrc.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/eclipse/tools/cldr-import/.project b/eclipse/tools/cldr-import/.project
new file mode 100644
index 0000000..2c5d2ce
--- /dev/null
+++ b/eclipse/tools/cldr-import/.project
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>cldr-import</name>
+	<comment>CLDR import tools</comment>
+	<projects>
+		<project>cldr-data</project>
+		<project>cldr-tools</project>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</nature>
+	</natures>
+	<linkedResources>
+		<link>
+			<name>core</name>
+			<type>2</type>
+			<locationURI>GWT_ROOT/tools/cldr-import</locationURI>
+		</link>
+		<link>
+			<name>generated</name>
+			<type>2</type>
+			<location>/tmp/cldr-gen/user/src</location>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/eclipse/tools/cldr-import/GenerateGwtCldrData.launch b/eclipse/tools/cldr-import/GenerateGwtCldrData.launch
new file mode 100644
index 0000000..9c6d71c
--- /dev/null
+++ b/eclipse/tools/cldr-import/GenerateGwtCldrData.launch
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<stringAttribute key="bad_container_name" value="/cldr-import/G"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/cldr-import/core/src/com/google/gwt/tools/cldr/GenerateGwtCldrData.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.tools.cldr.GenerateGwtCldrData"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="--sourcedir ${resource_loc:cldr-data/common}/main&#10;--outdir ${resource_loc:gwt-user/core}/.."/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="cldr-import"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx1024m"/>
+</launchConfiguration>
diff --git a/eclipse/user/.classpath b/eclipse/user/.classpath
index 5075ee1..39d4509 100644
--- a/eclipse/user/.classpath
+++ b/eclipse/user/.classpath
@@ -19,7 +19,6 @@
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/commons-logging-1.0.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/commons/commons-io-1.4.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/cssparser/cssparser-0.9.5.jar"/>
-	<classpathentry kind="var" path="GWT_TOOLS/lib/icu4j/icu4j-4_4_1.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/nekohtml/nekohtml-1.9.13.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/selenium/selenium-java-client-driver.jar" sourcepath="/GWT_TOOLS/lib/selenium/selenium-java-client-driver-sources.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/xalan/xalan-2.7.1.jar"/>
@@ -46,5 +45,6 @@
 	<classpathentry kind="var" path="GWT_TOOLS/lib/slf4j/slf4j-log4j12/slf4j-log4j12-1.6.1.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/guava/guava-r06/guava-r06-rebased.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/streamhtmlparser/streamhtmlparser-jsilver-r10/streamhtmlparser-jsilver-r10-1.5-rebased.jar"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/icu4j/4.4.2/icu4j.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/build.xml b/tools/build.xml
index 104f13b..cc6c10a 100755
--- a/tools/build.xml
+++ b/tools/build.xml
@@ -5,20 +5,27 @@
 
         <!-- "build" is the default when subprojects are directly targeted -->
         <property name="target" value="build" />
-      
-        <target name="benchmark-viewer" depends="" description="Run benchmark-viewer">
+
+        <target name="benchmark-viewer" depends=""
+          description="Run benchmark-viewer">
                 <gwt.ant dir="benchmark-viewer" />
         </target>
 
-        <target name="api-checker" depends="" description="Compile api-checker">
+        <target name="api-checker" depends=""
+          description="Compile api-checker">
                 <gwt.ant dir="api-checker" />
         </target>
 
         <target name="soyc-vis" depends="" description="Compile SOYC dashboard">
                 <gwt.ant dir="soyc-vis" />
         </target>
-     
-  <target name="-do" depends="benchmark-viewer,api-checker,soyc-vis" description="Run all subfolders" /> 
+
+        <target name="cldr-import" depends="" description="Compile CLDR import">
+                <gwt.ant dir="cldr-import" />
+        </target>
+
+  <target name="-do" depends="benchmark-viewer,api-checker,soyc-vis,cldr-import"
+    description="Run all subfolders that don't have additional dependencies" />
 
   <target name="build" description="Build each subfolder">
     <antcall target="-do">
@@ -26,7 +33,8 @@
     </antcall>
   </target>
 
-  <target name="checkstyle" description="Static analysis of source for each subfolder">
+  <target name="checkstyle"
+    description="Static analysis of source for each subfolder">
     <antcall target="-do">
       <param name="target" value="checkstyle" />
     </antcall>
diff --git a/tools/cldr-import/build.xml b/tools/cldr-import/build.xml
new file mode 100644
index 0000000..24aa636
--- /dev/null
+++ b/tools/cldr-import/build.xml
@@ -0,0 +1,138 @@
+<project name="cldr-import" default="build" basedir=".">
+
+  <property name="gwt.root" location="../.." />
+  <property name="project.tail" value="tools/cldr-import" />
+  <import file="${gwt.root}/common.ant.xml" />
+
+  <property.ensure name="gwt.dev.jar" location="${gwt.build.lib}/gwt-dev.jar" />
+  <property.ensure name="gwt.user.jar" location="${gwt.build.lib}/gwt-user.jar" />
+  <property.ensure name="gwt.servlet.jar" location="${gwt.build.lib}/gwt-servlet.jar" />
+
+  <property name="ICU" location="${gwt.tools}/lib/icu4j/4.4.2" />
+  <property name="CLDR_TOOLS" location="${gwt.tools}/lib/cldr/1.8.1" />
+
+  <property.ensure name="ICU_CORE_PATH" location="${ICU}/icu4j.jar" />
+
+  <condition property="cldr.root.check" value="${env.CLDR_ROOT}"
+      else="${gwt.root}/../CLDR">
+    <isset property="env.CLDR_ROOT" />
+  </condition>
+  <property name="cldr.root" location="${cldr.root.check}" />
+
+  <condition property="tmpdir" value="${env.CLDR_TEMP}"
+      else="/tmp/cldr-import">
+    <isset property="env.CLDR_TEMP" />
+  </condition>
+
+  <path id="project.class.path">
+    <pathelement location="${gwt.user.jar}"/>
+    <pathelement location="${gwt.dev.jar}"/>
+    <pathelement location="${CLDR_TOOLS}/cldr.jar"/>
+    <pathelement location="${ICU}/icu4j.jar"/>
+    <pathelement location="${ICU}/utilities.jar"/>
+  </path>
+
+  <target name="compile" description="Compile java source">
+    <mkdir dir="${javac.out}" />
+    <javac srcdir="src" destdir="${javac.out}"
+        debug="${javac.debug}" debuglevel="${javac.debuglevel}"
+        source="${javac.source}" target="${javac.target}"
+        nowarn="${javac.nowarn}" encoding="${javac.encoding}">
+      <classpath refid="project.class.path"/>
+    </javac>
+    <copy todir="${javac.out}">
+      <fileset dir="src" excludes="**/*.java"/>
+    </copy>
+  </target>
+
+  <target name="compile.tests" description="Compile java source">
+    <mkdir dir="${javac.junit.out}" />
+    <javac srcdir="test" destdir="${javac.junit.out}"
+        debug="${javac.debug}" debuglevel="${javac.debuglevel}"
+        source="${javac.source}" target="${javac.target}"
+        nowarn="${javac.nowarn}" encoding="${javac.encoding}">
+      <classpath>
+        <pathelement location="${gwt.dev.jar}" />
+        <pathelement location="${gwt.user.jar}" />
+        <pathelement location="${gwt.tools.lib}/junit/junit-3.8.1.jar" />
+        <pathelement location="${javac.out}"/>
+        <pathelement location="${CLDR_TOOLS}/cldr.jar"/>
+        <pathelement location="${ICU}/icu4j.jar"/>
+        <pathelement location="${ICU}/utilities.jar"/>
+      </classpath>
+    </javac>
+    <copy todir="${javac.junit.out}">
+      <fileset dir="src" excludes="**/*.java"/>
+    </copy>
+  </target>
+
+  <target name="build" depends="compile" description="Build this project" />
+
+  <target name="clean" description="Cleans this project">
+    <delete dir="${javac.out}" failonerror="false" />
+    <delete dir="${javac.junit.out}" failonerror="false" />
+  </target>
+
+  <target name="checkstyle" description="Static analysis of source">
+    <gwt.checkstyle>
+      <fileset dir="src"/>
+    </gwt.checkstyle>
+  </target>
+
+  <target name="test" depends="build, compile.tests" description="Run tests">
+    <mkdir dir="${junit.out}/tools-cldr-import" />
+    <fileset id="tests" dir="${javac.junit.out}"
+        includes="**/*Test.class" />
+    <gwt.junit test.name="test"
+        test.args=""
+        test.jvmargs="-ea"
+        test.out="${junit.out}/tools-cldr-import"
+        test.cases="tests">
+      <extraclasspaths>
+        <pathelement location="${gwt.dev.jar}" />
+        <pathelement location="${gwt.user.jar}" />
+        <pathelement location="${CLDR_TOOLS}/cldr.jar"/>
+        <pathelement location="${ICU}/icu4j.jar"/>
+        <pathelement location="${ICU}/utilities.jar"/>
+      </extraclasspaths>
+    </gwt.junit>
+  </target>
+
+  <target name="cldrdata" description="Verifies CLDR data is present">
+    <property.ensure name="cldr.data" location="${cldr.root}/common/main"
+      message="common/main missing under ${cldr.root} (set using CLDR_ROOT)" />
+  </target>
+
+  <target name="gen.temp" depends="build, cldrdata"
+    description="Generate GWT classes/property files in a temporary directory">
+    <mkdir dir="${tmpdir}"/>
+    <java classname="com.google.gwt.tools.cldr.GenerateGwtCldrData"
+      fork="true" maxmemory="2048m" failonerror="true">
+      <arg line="--sourcedir ${cldr.data} --outdir ${tmpdir}"/>
+      <classpath>
+        <pathelement location="${javac.out}"/>
+        <pathelement location="${gwt.user.jar}"/>
+        <pathelement location="${gwt.dev.jar}"/>
+        <pathelement location="${ICU}/icu4j.jar"/>
+        <pathelement location="${ICU}/utilities.jar"/>
+        <pathelement location="${CLDR_TOOLS}/cldr.jar"/>
+      </classpath>
+    </java>
+  </target>
+
+  <target name="gen" depends="build, cldrdata"
+      description="Generate GWT classes/property files">
+    <java classname="com.google.gwt.tools.cldr.GenerateGwtCldrData"
+      fork="true" maxmemory="2048m" failonerror="true">
+      <arg line="--sourcedir ${cldr.data} --outdir ${gwt.root}"/>
+      <classpath>
+        <pathelement location="${javac.out}"/>
+        <pathelement location="${gwt.user.jar}"/>
+        <pathelement location="${gwt.dev.jar}"/>
+        <pathelement location="${ICU}/icu4j.jar"/>
+        <pathelement location="${ICU}/utilities.jar"/>
+        <pathelement location="${CLDR_TOOLS}/cldr.jar"/>
+      </classpath>
+    </java>
+  </target>
+</project>
diff --git a/tools/cldr-import/src/com/google/gwt/tools/cldr/CurrencyDataProcessor.java b/tools/cldr-import/src/com/google/gwt/tools/cldr/CurrencyDataProcessor.java
new file mode 100644
index 0000000..92fb6af
--- /dev/null
+++ b/tools/cldr-import/src/com/google/gwt/tools/cldr/CurrencyDataProcessor.java
@@ -0,0 +1,149 @@
+/*
+ * 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.tools.cldr;
+
+import com.google.gwt.i18n.shared.GwtLocale;
+
+import org.unicode.cldr.util.CLDRFile;
+import org.unicode.cldr.util.XPathParts;
+import org.unicode.cldr.util.CLDRFile.DraftStatus;
+import org.unicode.cldr.util.CLDRFile.Factory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Loads data needed to produce DateTimeFormatInfo implementations.
+ */
+public class CurrencyDataProcessor extends Processor {
+
+  private Map<String, Integer> currencyFractions = new HashMap<String, Integer>();
+  private int defaultCurrencyFraction;
+
+  private Set<String> stillInUse = new HashSet<String>();
+
+  public CurrencyDataProcessor(File outputDir, Factory cldrFactory,
+      LocaleData localeData) {
+    super(outputDir, cldrFactory, localeData);
+  }
+
+  @Override
+  protected void cleanupData() {
+    localeData.removeDuplicates("currency");
+  }
+
+  @Override
+  protected void loadData() throws IOException {
+    System.out.println("Loading data for currencies");
+    loadLocaleIndependentCurrencyData();
+    localeData.addCurrencyEntries("currency", cldrFactory, currencyFractions,
+        defaultCurrencyFraction, stillInUse);
+  }
+  
+  @Override
+  protected void writeOutputFiles() throws IOException {
+    for (GwtLocale locale : localeData.getNonEmptyLocales()) {
+      String path = "client/impl/cldr/CurrencyData";
+      PrintWriter pw = createOutputFile(path + Processor.localeSuffix(locale)
+          + ".properties");
+      printHeader(pw);
+      Map<String, String> map = localeData.getEntries("currency", locale);
+      String[] keys = new String[map.size()];
+      map.keySet().toArray(keys);
+      Arrays.sort(keys);
+      
+      for (String key : keys) {
+        pw.print(key);
+        pw.print(" = ");
+        pw.println(map.get(key));
+      }
+      pw.close();
+    }
+  }
+
+  private void loadLocaleIndependentCurrencyData() {
+    CLDRFile supp = cldrFactory.make("supplementalData", true,
+        DraftStatus.approved);
+    
+    // load the table of default # of decimal places for each currency
+    currencyFractions = new HashMap<String, Integer>();
+    defaultCurrencyFraction = 0;
+    XPathParts parts = new XPathParts();
+    Iterator<String> iterator = supp.iterator(
+        "//supplementalData/currencyData/fractions/info");
+    while (iterator.hasNext()) {
+      String path = iterator.next();
+      parts.set(supp.getFullXPath(path));
+      Map<String, String> attr = parts.findAttributes("info");
+      if (attr == null) {
+        continue;
+      }
+      String curCode = attr.get("iso4217");
+      int digits = Integer.valueOf(attr.get("digits"));
+      // TODO(jat): make use of the "rounding" attribute, currently only on CHF
+      if ("DEFAULT".equalsIgnoreCase(curCode)) {
+        defaultCurrencyFraction = digits; 
+      } else {
+        currencyFractions.put(curCode, digits);
+      }
+    }
+    
+    // find which currencies are still in use in some region, everything else
+    // should be marked as deprecated
+    iterator = supp.iterator(
+        "//supplementalData/currencyData/region");
+    while (iterator.hasNext()) {
+      String path = iterator.next();
+      parts.set(supp.getFullXPath(path));
+      Map<String, String> attr = parts.findAttributes("currency");
+      if (attr == null) {
+        continue;
+      }
+      String region = parts.findAttributeValue("region", "iso3166");
+      String curCode = attr.get("iso4217");
+      if ("ZZ".equals(region) || "false".equals(attr.get("tender"))
+          || "XXX".equals(curCode)) {
+        // ZZ is an undefined region, XXX is an unknown currency code (and needs
+        // to be special-cased because it is listed as used in Anartica!)
+        continue;
+      }
+      String to = attr.get("to");
+      if (to == null) {
+        stillInUse.add(curCode);
+      }
+    }
+  }
+
+  private void printHeader(PrintWriter pw) {
+    pw.println("# Do not edit - generated from Unicode CLDR data");
+    pw.println("#");
+    pw.println("# The key is an ISO4217 currency code, and the value is of the "
+        + "form:");
+    pw.println("#   display name|symbol|decimal digits|not-used-flag");
+    pw.println("# If a symbol is not supplied, the currency code will be used");
+    pw.println("# If # of decimal digits is omitted, 2 is used");
+    pw.println("# If a currency is not generally used, not-used-flag=1");
+    pw.println("# Trailing empty fields can be omitted");
+    pw.println();
+  }
+}
diff --git a/tools/cldr-import/src/com/google/gwt/tools/cldr/DateTimeFormatInfoProcessor.java b/tools/cldr-import/src/com/google/gwt/tools/cldr/DateTimeFormatInfoProcessor.java
new file mode 100644
index 0000000..42edc54
--- /dev/null
+++ b/tools/cldr-import/src/com/google/gwt/tools/cldr/DateTimeFormatInfoProcessor.java
@@ -0,0 +1,656 @@
+/*
+ * 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.tools.cldr;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.i18n.client.DateTimeFormatInfo;
+import com.google.gwt.i18n.client.impl.cldr.DateTimeFormatInfoImpl;
+import com.google.gwt.i18n.rebind.DateTimePatternGenerator;
+import com.google.gwt.i18n.rebind.MessageFormatParser;
+import com.google.gwt.i18n.rebind.StringGenerator;
+import com.google.gwt.i18n.rebind.MessageFormatParser.ArgumentChunk;
+import com.google.gwt.i18n.rebind.MessageFormatParser.DefaultTemplateChunkVisitor;
+import com.google.gwt.i18n.rebind.MessageFormatParser.StringChunk;
+import com.google.gwt.i18n.rebind.MessageFormatParser.TemplateChunk;
+import com.google.gwt.i18n.shared.GwtLocale;
+
+import org.unicode.cldr.util.CLDRFile.Factory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Loads data needed to produce DateTimeFormatInfo implementations.
+ */
+public class DateTimeFormatInfoProcessor extends Processor {
+
+  private static final String[] DAYS = new String[] {
+    "sun", "mon", "tue", "wed", "thu", "fri", "sat"
+  };
+
+  /**
+   * Map of skeleton format patterns and the method name suffix that uses them. 
+   */
+  private static final Map<String, String> FORMATS;
+
+
+  /**
+   * Index of the formats, ordered by the method name. 
+   */
+  private static final SortedMap<String, String> FORMAT_BY_METHOD;
+
+  static {
+    FORMATS = new HashMap<String, String>();
+    FORMATS.put("d", "Day");
+    FORMATS.put("hmm", "Hour12Minute");
+    FORMATS.put("hmmss", "Hour12MinuteSecond");
+    FORMATS.put("Hmm", "Hour24Minute");
+    FORMATS.put("Hmmss", "Hour24MinuteSecond");
+    FORMATS.put("mss", "MinuteSecond");
+    FORMATS.put("MMM", "MonthAbbrev");
+    FORMATS.put("MMMd", "MonthAbbrevDay");
+    FORMATS.put("MMMM", "MonthFull");
+    FORMATS.put("MMMMd", "MonthFullDay");
+    FORMATS.put("MMMMEEEEd", "MonthFullWeekdayDay");
+    FORMATS.put("Md", "MonthNumDay");
+    FORMATS.put("y", "Year");
+    FORMATS.put("yMMM", "YearMonthAbbrev");
+    FORMATS.put("yMMMd", "YearMonthAbbrevDay");
+    FORMATS.put("yMMMM", "YearMonthFull");
+    FORMATS.put("yMMMMd", "YearMonthFullDay");
+    FORMATS.put("yM", "YearMonthNum");
+    FORMATS.put("yMd", "YearMonthNumDay");
+    FORMATS.put("yMMMEEEd", "YearMonthWeekdayDay");
+    FORMATS.put("yQQQQ", "YearQuarterFull");
+    FORMATS.put("yQ", "YearQuarterShort");
+
+    FORMAT_BY_METHOD = new TreeMap<String, String>();
+    for (Map.Entry<String, String> entry : FORMATS.entrySet()) {
+      FORMAT_BY_METHOD.put(entry.getValue(), entry.getKey());
+    }
+  }
+
+  /**
+   * Convert the unlocalized name of a day ("sun".."sat") into a day number of
+   * the week, ie 0-6.
+   * 
+   * @param day abbreviated, unlocalized name of the day ("sun".."sat")
+   * @return the day number, 0-6
+   * @throws IllegalArgumentException if the day name is not found
+   */
+  private static int getDayNumber(String day) {
+    for (int i = 0; i < DAYS.length; ++i) {
+      if (DAYS[i].equals(day)) {
+        return i;
+      }
+    }
+    throw new IllegalArgumentException();
+  }
+
+  private final RegionLanguageData regionLanguageData;
+
+  public DateTimeFormatInfoProcessor(File outputDir, Factory cldrFactory,
+      LocaleData localeData) {
+    super(outputDir, cldrFactory, localeData);
+    regionLanguageData = new RegionLanguageData(cldrFactory);
+  }
+
+  @Override
+  protected void cleanupData() {
+    System.out.println("Removing duplicates from date/time formats");
+    localeData.copyLocaleData("en", "default", "era-wide", "era-abbrev",
+        "quarter-wide", "quarter-abbrev", "day-wide", "day-sa-wide",
+        "day-narrow", "day-sa-narrow", "day-abbrev", "day-sa-abbrev",
+        "month-wide", "month-sa-wide", "month-narrow", "month-sa-narrow",
+        "month-abbrev", "month-sa-abbrev");
+    removeUnusedFormats();
+    localeData.removeDuplicates("predef");
+    localeData.removeDuplicates("weekdata");
+    localeData.removeDuplicates("date");
+    localeData.removeDuplicates("time");
+    localeData.removeDuplicates("dateTime");
+    localeData.removeCompleteDuplicates("dayPeriod-abbrev");
+    computePeriodRedirects("day");
+    computePeriodRedirects("month");
+    computePeriodRedirects("day");
+    removePeriodDuplicates("day");
+    removePeriodDuplicates("month");
+    removePeriodDuplicates("quarter");
+    removePeriodDuplicates("era");
+  }
+
+  /**
+   * Generate an override for a method which takes String arguments, which
+   * simply redirect to another method based on a default value.
+   *
+   * @param pw
+   * @param category
+   * @param locale
+   * @param method
+   * @param args
+   */
+  protected void generateArgMethod(PrintWriter pw, String category,
+      GwtLocale locale, String method, String... args) {
+    String value = localeData.getEntry(category, locale, "default");
+    if (value != null && value.length() > 0) {
+      pw.println();
+      if (getOverrides()) {
+        pw.println("  @Override");
+      }
+      pw.print("  public String " + method + "(");
+      String prefix = "";
+      for (String arg : args) {
+        pw.print(prefix + "String " + arg);
+        prefix = ", ";
+      }
+      pw.println(") {");
+      pw.print("    return " + method + Character.toTitleCase(value.charAt(0))
+          + value.substring(1) + "(");
+      prefix = "";
+      for (String arg : args) {
+        pw.print(prefix + arg);
+        prefix = ", ";
+      }
+      pw.println(");");
+      pw.println("  }");
+    }
+  }
+
+  /**
+   * Generate an override for a method which takes String arguments.
+   *
+   * @param pw
+   * @param category
+   * @param locale
+   * @param key
+   * @param method
+   * @param args
+   */
+  protected void generateArgMethodRedirect(PrintWriter pw, String category,
+      GwtLocale locale, String key, String method, final String... args) {
+    String value = localeData.getEntry(category, locale, key);
+    if (value != null) {
+      pw.println();
+      if (getOverrides()) {
+        pw.println("  @Override");
+      }
+      pw.print("  public String " + method + "(");
+      String prefix = "";
+      for (String arg : args) {
+        pw.print(prefix + "String " + arg);
+        prefix = ", ";
+      }
+      pw.println(") {");
+      final StringBuffer buf = new StringBuffer();
+      final StringGenerator gen = new StringGenerator(buf, false);
+      try {
+        List<TemplateChunk> chunks = MessageFormatParser.parse(value);
+        for (TemplateChunk chunk : chunks) {
+          chunk.accept(new DefaultTemplateChunkVisitor() {
+            @Override
+            public void visit(ArgumentChunk argChunk)
+                throws UnableToCompleteException {
+              gen.appendStringValuedExpression(args[argChunk.getArgumentNumber()]);
+            }
+
+            @Override
+            public void visit(StringChunk stringChunk)
+                throws UnableToCompleteException {
+              gen.appendStringLiteral(stringChunk.getString());
+            }
+          });
+        }
+      } catch (ParseException e) {
+        throw new RuntimeException("Unable to parse pattern '" + value
+            + "' for locale " + locale + " key " + category + "/" + key, e);
+      } catch (UnableToCompleteException e) {
+        throw new RuntimeException("Unable to parse pattern '" + value
+            + "' for locale " + locale + " key " + category + "/" + key, e);
+      }
+      gen.completeString();
+      pw.println("    return " + buf.toString() + ";");
+      pw.println("  }");
+    }
+  }
+
+  /**
+   * Generate a method which returns a day number as an integer.
+   * 
+   * @param pw
+   * @param locale
+   * @param key
+   * @param method
+   */
+  protected void generateDayNumber(PrintWriter pw, GwtLocale locale,
+      String key, String method) {
+    String day = localeData.getEntry("weekdata", locale, key);
+    if (day != null) {
+      int value = getDayNumber(day);
+      pw.println();
+      if (getOverrides()) {
+        pw.println("  @Override");
+      }
+      pw.println("  public int " + method + "() {");
+      pw.println("    return " + value + ";");
+      pw.println("  }");
+    }
+  }
+
+  /**
+   * Generate a method which returns a format string for a given predefined
+   * skeleton pattern.
+   * 
+   * @param locale
+   * @param pw
+   * @param skeleton
+   * @param methodSuffix
+   */
+  protected void generateFormat(GwtLocale locale, PrintWriter pw,
+      String skeleton, String methodSuffix) {
+    String pattern = localeData.getEntry("predef", locale, skeleton);
+    generateStringValue(pw, "format" + methodSuffix, pattern);
+  }
+
+  /**
+   * Generate a series of methods which returns names in wide, narrow, and
+   * abbreviated lengths plus their standalone versions.
+   * 
+   * @param pw
+   * @param group
+   * @param locale
+   * @param methodPrefix
+   * @param keys
+   */
+  protected void generateFullStringList(PrintWriter pw, String group,
+      GwtLocale locale, String methodPrefix, String... keys) {
+    generateStringListPair(pw, group, locale, methodPrefix, "Full", "wide",
+        keys);
+    generateStringListPair(pw, group, locale, methodPrefix, "Narrow", "narrow",
+        keys);
+    generateStringListPair(pw, group, locale, methodPrefix, "Short", "abbrev",
+        keys);
+  }
+
+  /**
+   * Generate an override of a standalone names list that simply redirects to
+   * the non-standalone version.
+   * 
+   * @param pw
+   * @param methodPrefix
+   */
+  protected void generateStandaloneRedirect(PrintWriter pw,
+      String methodPrefix) {
+    pw.println();
+    if (getOverrides()) {
+      pw.println("  @Override");
+    }
+    pw.println("  public String[] " + methodPrefix
+        + "Standalone" + "() {");
+    pw.println("    return " + methodPrefix + "();");
+    pw.println("  }");
+  }
+
+  /**
+   * Generate a method which returns a list of strings.
+   * 
+   * @param pw
+   * @param category
+   * @param fallbackCategory
+   * @param locale
+   * @param method
+   * @param keys
+   * @return true if the method was skipped as identical to its ancestor
+   */
+  protected boolean generateStringList(PrintWriter pw, String category,
+      String fallbackCategory, GwtLocale locale, String method,
+      String... keys) {
+    Map<String, String> map = localeData.getEntries(category, locale);
+    Map<String, String> fallback = fallbackCategory == null ?
+        Collections.<String, String>emptyMap()
+        : localeData.getEntries(fallbackCategory, locale);
+    if (map == null || map.isEmpty() && fallback != null
+        && !fallback.isEmpty()) {
+      return true;
+    }
+    if (map != null && !map.isEmpty()) {
+      if (fallbackCategory != null) {
+        // see if the entry is the same as the fallback
+        boolean different = false; 
+        for (String key : keys) {
+          String value = map.get(key);
+          if (value != null && !value.equals(fallback.get(key))) {
+            different = true;
+            break;
+          }
+        }
+        if (!different) {
+          return true;
+        }
+      }
+      pw.println();
+      if (getOverrides()) {
+        pw.println("  @Override");
+      }
+      pw.println("  public String[] " + method + "() {");
+      pw.print("    return new String[] {");
+      boolean first = true;
+      for (String key : keys) {
+        String value = map.get(key);
+        if (value == null) {
+          value = fallback.get(key);
+        }
+        if (value == null) {
+          System.err.println("Missing \"" + key + "\" in " + locale + "/"
+              + category);
+          value = "";
+        }
+        if (first) {
+          first = false;
+        } else {
+          pw.print(",");
+        }
+        pw.print("\n        \"" + value.replace("\"", "\\\"") + "\"");
+      }
+      pw.println("\n    };");
+      pw.println("  }");
+    }
+    return false;
+  }
+
+  protected void generateStringListPair(PrintWriter pw, String group,
+      GwtLocale locale, String methodPrefix, String width, String categorySuffix,
+      String... keys) {
+    generateStringList(pw, group + "-" + categorySuffix, null, locale,
+        methodPrefix + width, keys);
+    String redirect = localeData.getEntry(group + "-sa-" + categorySuffix
+        + "-redirect", locale, "redirect");
+    if ("yes".equals(redirect)) {
+      generateStandaloneRedirect(pw, methodPrefix + width);
+    } else {
+      generateStringList(pw, group + "-sa-" + categorySuffix, group + "-"
+        + categorySuffix, locale, methodPrefix + width + "Standalone", keys);
+    }
+  }
+
+  @Override
+  protected void loadData() throws IOException {
+    System.out.println("Loading data for date/time formats");
+    localeData.addEntries("predef", cldrFactory,
+        "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/"
+            + "availableFormats", "dateFormatItem", "id");
+    localeData.addNameEntries("month", cldrFactory);
+    localeData.addNameEntries("day", cldrFactory);
+    localeData.addNameEntries("quarter", cldrFactory);
+
+    // only add the entries we will use to avoid overriding a parent for
+    // differences that don't matter.
+    localeData.addEntries("dayPeriod-abbrev", cldrFactory,
+        "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dayPeriods/"
+        + "dayPeriodContext[@type=\"format\"]/"
+        + "dayPeriodWidth[@type=\"abbreviated\"]/dayPeriod[@type=\"am\"]",
+        "dayPeriod", "type");
+    localeData.addEntries("dayPeriod-abbrev", cldrFactory,
+        "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dayPeriods/"
+        + "dayPeriodContext[@type=\"format\"]/"
+        + "dayPeriodWidth[@type=\"abbreviated\"]/dayPeriod[@type=\"pm\"]",
+        "dayPeriod", "type");
+
+    localeData.addEntries("era-abbrev", cldrFactory,
+        "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/eras/eraAbbr",
+        "era", "type");
+    localeData.addEntries("era-wide", cldrFactory,
+        "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/eras/eraNames",
+        "era", "type");
+    localeData.addDateTimeFormatEntries("date", cldrFactory);
+    localeData.addDateTimeFormatEntries("time", cldrFactory);
+    localeData.addDateTimeFormatEntries("dateTime", cldrFactory);
+    loadWeekData();
+    loadFormatPatterns();
+  }
+
+  @Override
+  protected void writeOutputFiles() throws IOException {
+    // TODO(jat): make uz_UZ inherit from uz_Cyrl rather than uz, for example
+    System.out.println("Writing output for date/time formats");
+    for (GwtLocale locale : localeData.getNonEmptyLocales()) {
+      String myClass;
+      String path = "client/";
+      String pathSuffix = "";
+      if (locale.isDefault()) {
+        myClass = "DefaultDateTimeFormatInfo";
+      } else {
+        myClass = "DateTimeFormatInfoImpl" + localeSuffix(locale);
+        pathSuffix = "impl/cldr/";
+      }
+      GwtLocale parent = localeData.inheritsFrom(locale);
+      PrintWriter pw = createOutputFile(path + pathSuffix + myClass + ".java");
+      printHeader(pw);
+      pw.print("package com.google.gwt.i18n.client");
+      if (!locale.isDefault()) {
+        pw.print(".impl.cldr");
+        setOverrides(true);
+      } else {
+        setOverrides(false);
+      }
+      pw.println(";");
+      pw.println();
+      pw.println("// DO NOT EDIT - GENERATED FROM CLDR AND ICU DATA");
+      pw.println();
+      if (locale.isDefault()) {
+        pw.println("/**"); 
+        pw.println(" * Default implementation of DateTimeFormatInfo interface, "
+            + "using values from");   
+        pw.println(" * the CLDR root locale.");
+        pw.println(" * <p>");
+        pw.println(" * Users who need to create their own DateTimeFormatInfo "
+            + "implementation are");    
+        pw.println(" * encouraged to extend this class so their implementation "
+            + "won't break when   "); 
+        pw.println(" * new methods are added.");
+        pw.println(" */");
+      } else {
+        pw.println("/**"); 
+        pw.println(" * Implementation of DateTimeFormatInfo for the \""
+            + locale + "\" locale.");   
+        pw.println(" */");
+      }
+      pw.print("public class " + myClass);
+      if (locale.isDefault()) {
+        pw.print(" implements " + DateTimeFormatInfo.class.getSimpleName());
+      } else {
+        pw.print(" extends ");
+        pw.print(DateTimeFormatInfoImpl.class.getSimpleName());
+        if (!parent.isDefault()) {
+          pw.print('_');
+          pw.print(parent.getAsString());
+        }
+      }
+      pw.println(" {");
+
+      // write AM/PM names
+      generateStringList(pw, "dayPeriod-abbrev", null, locale, "ampms", "am",
+          "pm");
+
+      // write standard date formats
+      generateArgMethod(pw, "date",    locale, "dateFormat");
+      generateStringMethod(pw, "date", locale, "full", "dateFormatFull");
+      generateStringMethod(pw, "date", locale, "long", "dateFormatLong");
+      generateStringMethod(pw, "date", locale, "medium", "dateFormatMedium");
+      generateStringMethod(pw, "date", locale, "short", "dateFormatShort");
+
+      // write methods for assembling date/time formats
+      generateArgMethod(pw, "dateTime", locale, "dateTime", "timePattern",
+          "datePattern");
+      generateArgMethodRedirect(pw, "dateTime", locale, "full", "dateTimeFull",
+          "timePattern", "datePattern");
+      generateArgMethodRedirect(pw, "dateTime", locale, "long", "dateTimeLong",
+          "timePattern", "datePattern");
+      generateArgMethodRedirect(pw, "dateTime", locale, "medium",
+          "dateTimeMedium", "timePattern", "datePattern");
+      generateArgMethodRedirect(pw, "dateTime", locale, "short",
+          "dateTimeShort", "timePattern", "datePattern");
+
+      // write era names
+      generateStringList(pw, "era-wide", null, locale, "erasFull", "0", "1");
+      generateStringList(pw, "era-abbrev", null, locale, "erasShort", "0", "1");
+
+      // write firstDayOfTheWeek
+      generateDayNumber(pw, locale, "firstDay", "firstDayOfTheWeek");
+
+      // write predefined date/time formats
+      for (Map.Entry<String, String> entry : FORMAT_BY_METHOD.entrySet()) {
+        generateFormat(locale, pw, entry.getValue(), entry.getKey());
+      }
+
+      // write month names
+      generateFullStringList(pw, "month", locale, "months", "1", "2", "3", "4",
+          "5", "6", "7", "8", "9", "10", "11", "12");
+      
+      // write quarter names
+      generateStringList(pw, "quarter-wide", null, locale, "quartersFull", "1",
+          "2", "3", "4");
+      generateStringList(pw, "quarter-abbrev", null, locale, "quartersShort",
+          "1", "2", "3", "4");
+      
+      // write standard time formats
+      generateArgMethod(pw, "time",    locale, "timeFormat");
+      generateStringMethod(pw, "time", locale, "full", "timeFormatFull");
+      generateStringMethod(pw, "time", locale, "long", "timeFormatLong");
+      generateStringMethod(pw, "time", locale, "medium",
+          "timeFormatMedium");
+      generateStringMethod(pw, "time", locale, "short",
+          "timeFormatShort");
+      
+      // write weekday names
+      generateFullStringList(pw, "day", locale, "weekdays", DAYS);
+
+      // write weekend boundaries
+      generateDayNumber(pw, locale, "weekendEnd", "weekendEnd");
+      generateDayNumber(pw, locale, "weekendStart", "weekendStart");
+
+      pw.println("}");
+      pw.close();
+    }
+  }
+
+  /**
+   * @param period
+   */
+  private void computePeriodRedirects(String period) {
+    localeData.computeRedirects(period + "-abbrev", period + "-sa-abbrev");
+    localeData.computeRedirects(period + "-narrow", period + "-sa-narrow");
+    localeData.computeRedirects(period + "-wide", period + "-sa-wide");
+  }
+
+  private void loadFormatPatterns() {
+    localeData.addEntries("predef", cldrFactory,
+        "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/dateTimeFormats/"
+            + "availableFormats", "dateFormatItem", "id");
+    for (GwtLocale locale : localeData.getAllLocales()) {
+      DateTimePatternGenerator dtpg = new DateTimePatternGenerator(locale);
+      for (Map.Entry<String, String> entry : FORMATS.entrySet()) {
+        String skeleton = entry.getKey();
+        String cldrPattern = localeData.getEntry("predef", locale, skeleton);
+        String pattern = dtpg.getBestPattern(skeleton);
+        if (cldrPattern != null && !cldrPattern.equals(pattern)) {
+          System.err.println("Mismatch on skeleton pattern in locale " + locale
+              + " for skeleton '" + skeleton + "': icu='" + pattern
+              + "', cldr='" + cldrPattern + "'");
+        }
+        localeData.addEntry("predef", locale, skeleton, pattern);
+      }
+    }
+  }
+
+  /**
+   * Load the week start and weekend range values from CLDR.
+   */
+  private void loadWeekData() {
+    localeData.addTerritoryEntries("weekdata", cldrFactory,
+        regionLanguageData, "//supplementalData/weekData/firstDay", "firstDay", "day");
+    localeData.addTerritoryEntries("weekdata", cldrFactory,
+        regionLanguageData, "//supplementalData/weekData/weekendStart", "weekendStart", "day");
+    localeData.addTerritoryEntries("weekdata", cldrFactory,
+        regionLanguageData, "//supplementalData/weekData/weekendEnd", "weekendEnd", "day");
+    localeData.addTerritoryEntries("weekdata", cldrFactory,
+        regionLanguageData, "//supplementalData/weekData/minDays", "minDays", "count");
+  }
+
+  private void printHeader(PrintWriter pw) {
+    pw.println("/*");
+    pw.println(" * Copyright 2010 Google Inc.");
+    pw.println(" * ");
+    pw.println(" * Licensed under the Apache License, Version 2.0 (the "
+        + "\"License\"); you may not");
+    pw.println(" * use this file except in compliance with the License. You "
+        + "may obtain a copy of");
+    pw.println(" * the License at");
+    pw.println(" * ");
+    pw.println(" * http://www.apache.org/licenses/LICENSE-2.0");
+    pw.println(" * ");
+    pw.println(" * Unless required by applicable law or agreed to in writing, "
+        + "software");
+    pw.println(" * distributed under the License is distributed on an \"AS "
+        + "IS\" BASIS, WITHOUT");
+    pw.println(" * WARRANTIES OR CONDITIONS OF ANY KIND, either express or "
+        + "implied. See the");
+    pw.println(" * License for the specific language governing permissions and "
+        + "limitations under");
+    pw.println(" * the License.");
+    pw.println(" */");
+  }
+
+  /**
+   * Remove duplicates from period names.
+   * 
+   * @param group
+   */
+  private void removePeriodDuplicates(String group) {
+    removePeriodWidthDuplicates(group, "wide");
+    removePeriodWidthDuplicates(group, "abbrev");
+    removePeriodWidthDuplicates(group, "narrow");
+  }
+
+  private void removePeriodWidthDuplicates(String group, String width) {
+    localeData.removeCompleteDuplicates(group + "-" + width);
+    localeData.removeCompleteDuplicates(group + "-sa-" + width);
+    localeData.removeCompleteDuplicates(group + "-sa-" + width + "-redirect");
+  }
+
+  private void removeUnusedFormats() {
+    for (GwtLocale locale : localeData.getAllLocales()) {
+      Set<String> toRemove = new HashSet<String>();
+      Map<String, String> map = localeData.getEntries("predef", locale);
+      for (Entry<String, String> entry : map.entrySet()) {
+        if (!FORMATS.containsKey(entry.getKey())) {
+          toRemove.add(entry.getKey());
+        }
+      }
+      localeData.removeEntries("predef", locale, toRemove);
+    }
+  }
+}
diff --git a/tools/cldr-import/src/com/google/gwt/tools/cldr/GenerateGwtCldrData.java b/tools/cldr-import/src/com/google/gwt/tools/cldr/GenerateGwtCldrData.java
new file mode 100644
index 0000000..cea2fe6
--- /dev/null
+++ b/tools/cldr-import/src/com/google/gwt/tools/cldr/GenerateGwtCldrData.java
@@ -0,0 +1,97 @@
+/*
+ * 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.tools.cldr;
+
+import com.google.gwt.i18n.server.GwtLocaleFactoryImpl;
+import com.google.gwt.i18n.shared.GwtLocaleFactory;
+
+import com.ibm.icu.dev.tool.UOption;
+
+import org.unicode.cldr.util.CldrUtility;
+import org.unicode.cldr.util.CLDRFile.Factory;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Generate a country list for each locale, taking into account the literate
+ * population of each country speaking the language.
+ */
+@SuppressWarnings("unchecked")
+public class GenerateGwtCldrData {
+
+  private static final GwtLocaleFactory factory = new GwtLocaleFactoryImpl();
+
+  private static boolean DEBUG = false;
+
+  private static final Class<? extends Processor>[] PROCESSORS;
+  
+  static {
+    PROCESSORS = new Class[] {
+        CurrencyDataProcessor.class,
+        DateTimeFormatInfoProcessor.class,
+        ListFormattingProcessor.class,
+    };
+  }
+
+  public static void main(String[] args) throws IOException, SecurityException,
+      NoSuchMethodException, IllegalArgumentException, InstantiationException,
+      IllegalAccessException, InvocationTargetException {
+    System.out.println("Starting to generate from CLDR data (ignore -D lines "
+        + "produced by cldr-tools)");
+    UOption[] options = {
+        UOption.HELP_H(),
+        UOption.HELP_QUESTION_MARK(),
+        UOption.SOURCEDIR().setDefault(CldrUtility.MAIN_DIRECTORY), // C:\cvsdata/unicode\cldr\diff\summary
+        outputDir().setDefault("./"),
+        restrictLocales(),
+      };
+    UOption.parseArgs(args, options);
+    String sourceDir = options[2].value; // SOURCEDIR
+    String targetDir = options[3].value; // outputDir
+    DEBUG = options[4].doesOccur; // --restrictLocales
+    Factory cldrFactory = Factory.make(sourceDir, ".*");
+    Set<String> locales = cldrFactory.getAvailable();
+    if (DEBUG) {
+      locales = new HashSet<String>(Arrays.asList(new String[] {
+          "root", "en", "en_US", "en_CA", "ar", "ar_IQ"}));
+    }
+    System.out.println("Processing " + locales.size() + " locales");
+    File outputDir = new File(targetDir);
+    LocaleData localeData = new LocaleData(factory, locales);
+    for (Class<? extends Processor> processorClass : PROCESSORS) {
+      Constructor<? extends Processor> ctor = processorClass.getConstructor(
+          File.class, Factory.class, LocaleData.class);
+      Processor processor = ctor.newInstance(outputDir, cldrFactory,
+          localeData);
+      processor.run();
+    }
+    System.out.println("Finished.");
+  }
+
+  private static UOption outputDir() {
+    return UOption.create("outdir", 'o', UOption.REQUIRES_ARG);
+  }
+
+  private static UOption restrictLocales() {
+    return UOption.create("restrictLocales", 'r', UOption.NO_ARG);
+  }
+}
diff --git a/tools/cldr-import/src/com/google/gwt/tools/cldr/ListFormattingProcessor.java b/tools/cldr-import/src/com/google/gwt/tools/cldr/ListFormattingProcessor.java
new file mode 100644
index 0000000..91bea10
--- /dev/null
+++ b/tools/cldr-import/src/com/google/gwt/tools/cldr/ListFormattingProcessor.java
@@ -0,0 +1,66 @@
+/*
+ * 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.tools.cldr;
+
+import com.google.gwt.i18n.shared.GwtLocale;
+
+import org.unicode.cldr.util.CLDRFile.Factory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
+
+/**
+ * Extract list formatting information from CLDR data.
+ */
+public class ListFormattingProcessor extends Processor {
+
+  public ListFormattingProcessor(File outputDir, Factory cldrFactory,
+      LocaleData localeData) {
+    super(outputDir, cldrFactory, localeData);
+  }
+
+  @Override
+  protected void cleanupData() {
+    localeData.removeCompleteDuplicates();
+  }
+
+  @Override
+  protected void loadData() throws IOException {
+    System.out.println("Loading data for list formatting");
+    localeData.addEntries("list", cldrFactory,
+        "//ldml/listPatterns/listPattern", "listPatternPart", "type");
+  }
+
+  @Override
+  protected void writeOutputFiles() throws IOException {
+    for (GwtLocale locale : localeData.getNonEmptyLocales("list")) {
+      PrintWriter pw = null;
+      for (Map.Entry<String, String> entry : localeData.getEntries("list",
+          locale).entrySet()) {
+        if (pw == null) {
+          pw = createOutputFile("rebind/cldr/ListPatterns_"
+              + locale.getAsString() + ".properties");
+        }
+        pw.println(entry.getKey() + "=" + entry.getValue());
+      }
+      if (pw != null) {
+        pw.close();
+      }
+    }
+  }
+}
diff --git a/tools/cldr-import/src/com/google/gwt/tools/cldr/LocaleData.java b/tools/cldr-import/src/com/google/gwt/tools/cldr/LocaleData.java
new file mode 100644
index 0000000..db3cfba
--- /dev/null
+++ b/tools/cldr-import/src/com/google/gwt/tools/cldr/LocaleData.java
@@ -0,0 +1,1048 @@
+/*
+ * 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.tools.cldr;
+
+import com.google.gwt.i18n.shared.GwtLocale;
+import com.google.gwt.i18n.shared.GwtLocaleFactory;
+
+import org.unicode.cldr.util.CLDRFile;
+import org.unicode.cldr.util.CLDRFile.Factory;
+import org.unicode.cldr.util.XPathParts;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Collects all the locale data from CLDR, grouping it by how it will be used
+ * and removing equivalent values that could be inherited.
+ */
+public class LocaleData {
+
+  /**
+   * Represents data about a single currency in a particular locale from CLDR.
+   */
+  public static class Currency {
+
+    private static boolean equalsNullCheck(Object a, Object b) {
+      if (a == null) {
+        return b == null;
+      }
+      return a.equals(b);
+    }
+
+    private static int hashCodeNullCheck(Object obj) {
+      return obj == null ? 0 : obj.hashCode();
+    }
+
+    private final String code;
+
+    private String symbol;
+
+    private String displayName;
+
+    private String pattern;
+
+    private String decimalSeparator;
+
+    private String groupingSeparator;
+
+    private int decimalDigits;
+
+    private boolean inUse;
+
+    public Currency(String code) {
+      this.code = code;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof Currency)) {
+        return false;
+      }
+      Currency other = (Currency) obj;
+      return code.equals(other.code)
+          && equalsNullCheck(displayName, other.displayName)
+          && equalsNullCheck(symbol, other.symbol)
+          && equalsNullCheck(pattern, other.pattern)
+          && equalsNullCheck(decimalSeparator, other.decimalSeparator)
+          && equalsNullCheck(groupingSeparator, other.groupingSeparator)
+          && decimalDigits == other.decimalDigits && inUse == other.inUse;
+    }
+
+    public String getCode() {
+      return code;
+    }
+
+    /**
+     * @return the number of decimal digits this currency is commonly displayed
+     *         with.
+     */
+    public int getDecimalDigits() {
+      return decimalDigits;
+    }
+
+    public String getDecimalSeparator() {
+      return decimalSeparator;
+    }
+
+    public String getDisplayName() {
+      return displayName;
+    }
+
+    public String getGroupingSeparator() {
+      return groupingSeparator;
+    }
+
+    public String getPattern() {
+      return pattern;
+    }
+
+    public String getSymbol() {
+      return symbol;
+    }
+
+    @Override
+    public int hashCode() {
+      return code.hashCode() + 17 * hashCodeNullCheck(displayName) + 19
+          * hashCodeNullCheck(symbol) + 23 * hashCodeNullCheck(pattern) + 29
+          * hashCodeNullCheck(decimalSeparator) + 31
+          * hashCodeNullCheck(groupingSeparator) + 37 * decimalDigits
+          + (inUse ? 41 : 0);
+    }
+
+    /**
+     * @return true if this currency is still in regular use.
+     */
+    public boolean isInUse() {
+      return inUse;
+    }
+
+    public void setDecimalDigits(int decimalDigits) {
+      this.decimalDigits = decimalDigits;
+    }
+
+    public void setDecimalSeparator(String decimalSeparator) {
+      this.decimalSeparator = decimalSeparator;
+    }
+
+    public void setDisplayName(String displayName) {
+      this.displayName = displayName;
+    }
+
+    public void setGroupingSeparator(String groupingSeparator) {
+      this.groupingSeparator = groupingSeparator;
+    }
+
+    public void setInUse(boolean inUse) {
+      this.inUse = inUse;
+    }
+
+    public void setPattern(String pattern) {
+      this.pattern = pattern;
+    }
+
+    public void setSymbol(String symbol) {
+      this.symbol = symbol;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder buf = new StringBuilder();
+      buf.append(code);
+      if (displayName != null || symbol != null) {
+        buf.append(" (");
+      }
+      if (displayName != null) {
+        buf.append(displayName);
+      }
+      if (symbol != null) {
+        if (displayName != null) {
+          buf.append("; ");
+        }
+        buf.append(symbol);
+      }
+      if (displayName != null || symbol != null) {
+        buf.append(")");
+      }
+      return buf.toString();
+    }
+  }
+
+  /**
+   * Comparator that orders locales based the inheritance depth.
+   */
+  private class LocaleComparator implements Comparator<GwtLocale> {
+    public int compare(GwtLocale a, GwtLocale b) {
+      int depthA = localeDepth.get(a);
+      int depthB = localeDepth.get(b);
+      int c = depthB - depthA;
+      if (c == 0) {
+        c = a.compareTo(b);
+      }
+      return c;
+    }
+  }
+
+  /**
+   * Encapsulates the key for lookup values, comprising a locale and a category.
+   */
+  private static class MapKey {
+    private final GwtLocale locale;
+    private final String category;
+
+    public MapKey(String category, GwtLocale locale) {
+      this.category = category;
+      this.locale = locale;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj == null || getClass() != obj.getClass()) {
+        return false;
+      }
+      MapKey other = (MapKey) obj;
+      return locale.equals(other.locale) && category.equals(other.category);
+    }
+
+    public String getCategory() {
+      return category;
+    }
+
+    public GwtLocale getLocale() {
+      return locale;
+    }
+
+    @Override
+    public int hashCode() {
+      return category.hashCode() + 31 * locale.hashCode();
+    }
+
+    public MapKey inNewLocale(GwtLocale newLocale) {
+      return new MapKey(category, newLocale); 
+    }
+
+    @Override
+    public String toString() {
+      return "[cat=" + category + ", locale=" + locale + "]";
+    }
+  }
+
+  /**
+   * Return the CLDR locale name for a GWT locale.
+   * 
+   * @param locale
+   * @return CLDR locale name for GWT locale
+   */
+  public static String getCldrLocale(GwtLocale locale) {
+    return locale.isDefault() ? "root" : locale.toString();
+  }
+  
+  /**
+   * @param factory 
+   * @param localeName
+   * @return GwtLocale instance for CLDR locale
+   */
+  public static GwtLocale getGwtLocale(GwtLocaleFactory factory,
+      String localeName) {
+    return "root".equals(localeName) ? factory.getDefault()
+        : factory.fromString(localeName).getCanonicalForm();
+  }
+  
+  /**
+   * Get the value of a given category of territory data inherited by a locale.
+   * 
+   * @param locale the locale to search for
+   * @param map the map containing territory=>value data
+   * @return the requested value from the closest ancestor of the specified
+   *     locale, or null if not found
+   */
+  private static String getTerritoryData(GwtLocale locale,
+      Map<String, String> map) {
+    if (map == null) {
+      return null;
+    }
+    for (GwtLocale search : locale.getCompleteSearchList()) {
+      String region = search.getRegion();
+      if (region == null) {
+        region = "001";
+      }
+      String value = map.get(region);
+      if (value != null) {
+        return value;
+      }
+    }
+    return null;
+  }
+
+  private Map<MapKey, Map<String, String>> maps;
+
+  private final HashMap<GwtLocale, GwtLocale> inheritsFrom;
+  
+  private final Map<GwtLocale, String> allLocales;
+
+  private final Map<GwtLocale, Integer> localeDepth;
+
+  private final GwtLocale defaultLocale;
+
+  private final GwtLocaleFactory localeFactory;
+
+  /**
+   * Construct a LocaleData object.
+   * 
+   * @param localeFactory
+   * @param localeNames
+   */
+  public LocaleData(GwtLocaleFactory localeFactory,
+      Collection<String> localeNames) {
+    this.localeFactory = localeFactory;
+    defaultLocale = localeFactory.getDefault();
+    allLocales = new HashMap<GwtLocale, String>();
+    for (String localeName : localeNames) {
+      allLocales.put(getGwtLocale(localeFactory, localeName), localeName);
+    }
+    inheritsFrom = new HashMap<GwtLocale, GwtLocale>();
+    buildInheritsFrom();
+    localeDepth = new HashMap<GwtLocale, Integer>();
+    maps = new HashMap<MapKey, Map<String, String>>();
+    buildLocaleDepth();
+  }
+
+  /**
+   * Add a single entry from an attribute on a CLDR node.
+   * 
+   * @param category
+   * @param locale
+   * @param cldrFactory
+   * @param path
+   * @param tag
+   * @param key
+   * @param attribute
+   */
+  public void addAttributeEntry(String category, GwtLocale locale,
+      Factory cldrFactory, String path, String tag, String key,
+      String attribute) {
+    Map<String, String> map = getMap(category, locale);
+    CLDRFile cldr = cldrFactory.make(allLocales.get(locale), true);
+    XPathParts parts = new XPathParts();
+    parts.set(cldr.getFullXPath(path));
+    Map<String, String> attr = parts.findAttributes(tag);
+    if (attr == null) {
+      return;
+    }
+    String value = attr.get(attribute);
+    map.put(key, value);
+  }
+
+  /**
+   * Add currency entries for all locales.
+   * 
+   * @param category
+   * @param cldrFactory
+   * @param currencyFractions map of currency fraction data extracted from
+   *     locale-independent data
+   * @param defaultCurrencyFraction
+   * @param stillInUse
+   */
+  public void addCurrencyEntries(String category, Factory cldrFactory,
+      Map<String, Integer> currencyFractions, int defaultCurrencyFraction,
+      Set<String> stillInUse) {
+    for (GwtLocale locale : allLocales.keySet()) {
+      // skip the "default" locale for now
+      if (locale.isDefault()) {
+        continue;
+      }
+      addCurrencyEntries(category, locale, cldrFactory, currencyFractions,
+          defaultCurrencyFraction, stillInUse);
+    }
+    // run the "default" locale last, to override inherited entries
+    GwtLocale locale = localeFactory.getDefault();
+    addCurrencyEntries(category, locale, cldrFactory, currencyFractions,
+        defaultCurrencyFraction, stillInUse);
+  }
+
+  public void addDateTimeFormatEntries(String group, Factory cldrFactory) {
+    addAttributeEntries(group, cldrFactory,
+        "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + group
+        + "Formats/default", "default", "default", "choice");
+    addDateTimeFormatEntries(group, "full", cldrFactory);
+    addDateTimeFormatEntries(group, "long", cldrFactory);
+    addDateTimeFormatEntries(group, "medium", cldrFactory);
+    addDateTimeFormatEntries(group, "short", cldrFactory);
+  }
+
+  public void addEntries(String category, Factory cldrFactory, String prefix,
+      String tag, String keyAttribute) {
+    for (GwtLocale locale : allLocales.keySet()) {
+      addEntries(category, locale, cldrFactory, prefix, tag, keyAttribute);
+    }
+  }
+
+  public void addEntries(String category, GwtLocale locale, Factory cldrFactory,
+      String prefix, String tag, String keyAttribute) {
+    Map<String, String> map = getMap(category, locale);
+    CLDRFile cldr = cldrFactory.make(allLocales.get(locale), true);
+    XPathParts parts = new XPathParts();
+    Iterator<String> iterator = cldr.iterator(prefix);
+    while (iterator.hasNext()) {
+      String path = iterator.next();
+      String fullXPath = cldr.getFullXPath(path);
+      if (fullXPath == null) {
+        fullXPath = path;
+      }
+      parts.set(fullXPath);
+      if (parts.containsAttribute("alt")) {
+        // ignore alternate strings
+        continue;
+      }
+      Map<String, String> attr = parts.findAttributes(tag);
+      if (attr == null) {
+        continue;
+      }
+      String key = attr.get(keyAttribute);
+      String value = cldr.getStringValue(path);
+      boolean draft = parts.containsAttribute("draft");
+      if (!draft || !map.containsKey(key)) {
+        map.put(key, value);
+      }
+    }
+  }
+
+  public void addEntry(String category, GwtLocale locale, String key,
+      String value) {
+    Map<String, String> map = getMap(category, locale);
+    map.put(key, value);
+  }
+
+  /**
+   * @param period "month", "day", "quarter", "dayPeriod", 
+   * @param cldrFactory
+   */
+  public void addNameEntries(String period, Factory cldrFactory) {
+    addEntries(period + "-abbrev", cldrFactory, "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period
+        + "s/" + period + "Context[@type=\"format\"]/" + period
+        + "Width[@type=\"abbreviated\"]", period, "type");
+    addEntries(period + "-narrow", cldrFactory, "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period
+        + "s/" + period + "Context[@type=\"format\"]/" + period
+        + "Width[@type=\"narrow\"]", period, "type");
+    addEntries(period + "-wide", cldrFactory, "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period
+        + "s/" + period + "Context[@type=\"format\"]/" + period
+        + "Width[@type=\"wide\"]", period, "type");
+    addEntries(period + "-sa-abbrev", cldrFactory, "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period
+        + "s/" + period + "Context[@type=\"stand-alone\"]/" + period
+        + "Width[@type=\"abbreviated\"]", period, "type");
+    addEntries(period + "-sa-narrow", cldrFactory, "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period
+        + "s/" + period + "Context[@type=\"stand-alone\"]/" + period
+        + "Width[@type=\"narrow\"]", period, "type");
+    addEntries(period + "-sa-wide", cldrFactory, "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + period
+        + "s/" + period + "Context[@type=\"stand-alone\"]/" + period
+        + "Width[@type=\"wide\"]", period, "type");
+  }
+
+  /**
+   * Add entries from territory-oriented CLDR data.
+   * 
+   * @param category category to store resulting data under
+   * @param cldrFactory
+   * @param regionLanguageData
+   * @param prefix the XPath prefix to iterate through
+   * @param tag the tag to load
+   * @param keyAttribute the attribute in the tag to use as the key
+   */
+  public void addTerritoryEntries(String category, Factory cldrFactory,
+      RegionLanguageData regionLanguageData, String prefix, String tag,
+      String keyAttribute) {
+    CLDRFile supp = cldrFactory.make("supplementalData", true);
+    Map<String, String> map = new HashMap<String, String>();
+    XPathParts parts = new XPathParts();
+    Iterator<String> iterator = supp.iterator(prefix);
+    while (iterator.hasNext()) {
+      String path = iterator.next();
+      parts.set(supp.getFullXPath(path));
+      Map<String, String> attr = parts.findAttributes(tag);
+      if (attr == null || attr.get("alt") != null) {
+        continue;
+      }
+      String key = attr.get(keyAttribute);
+      String territories = attr.get("territories");
+      String draft = attr.get("draft");
+      for (String territory : territories.split(" ")) {
+        if (draft == null || !map.containsKey(territory)) {
+          map.put(territory, key);
+        }
+      }
+    }
+
+    if (regionLanguageData != null) {
+      // find the choice used by most literate speakers of each language
+      // based on region-based preferences.
+      for (GwtLocale locale : allLocales.keySet()) {
+        if (locale.getRegion() != null || locale.getLanguage() == null) {
+          // skip any that have a region or don't have a language
+          continue;
+        }
+        String language = locale.getAsString();
+        Map<String, Double> langMap = new HashMap<String, Double>();
+        for (RegionLanguageData.RegionPopulation langData
+            : regionLanguageData.getRegions(language)) {
+          String region = langData.getRegion();
+          GwtLocale regionLocale = localeFactory.fromString(language + "_"
+              + region);
+          String day = getTerritoryData(regionLocale, map);
+          if (day != null) {
+            Double pop = langMap.get(day);
+            if (pop == null) {
+              pop = 0.0;
+            }
+            pop += langData.getLiteratePopulation();
+            langMap.put(day, pop);
+          }
+        }
+        double max = 0;
+        String maxDay = null;
+        for (Map.Entry<String, Double> entry : langMap.entrySet()) {
+          if (entry.getValue() > max) {
+            max = entry.getValue();
+            maxDay = entry.getKey();
+          }
+        }
+        if (maxDay != null) {
+          addEntry(category, locale, tag, maxDay);
+        }
+      }
+    }
+
+    // map locales to territory data
+    for (GwtLocale locale : allLocales.keySet()) {
+      if (getEntry(category, locale, tag) != null) {
+        // don't override what we set above
+        continue;
+      }
+      String day = getTerritoryData(locale, map);
+      if (day != null) {
+        addEntry(category, locale, tag, day);
+      }
+    }
+  }
+
+  /**
+   * Add a redirect entry for each locale where all entries in the standalone
+   * category match those in the base category.
+   * 
+   * @param baseCategory
+   * @param standaloneCategory
+   */
+  public void computeRedirects(String baseCategory,
+      String standaloneCategory) {
+    for (GwtLocale locale : allLocales.keySet()) {
+      MapKey baseKey = new MapKey(baseCategory, locale);
+      MapKey standaloneKey = new MapKey(standaloneCategory, locale);
+      Map<String, String> baseMap = maps.get(baseKey);
+      Map<String, String> standaloneMap = maps.get(standaloneKey);
+      if (baseMap != null && standaloneMap != null && (standaloneMap.isEmpty()
+          || baseMap.equals(standaloneMap))) {
+        addEntry(standaloneCategory + "-redirect", locale, "redirect", "yes");
+      }
+    }
+  }
+
+  /**
+   * Copy data from one locale to another.
+   * 
+   * @param srcLocaleName source locale name
+   * @param destLocaleName destination locale name
+   * @param categories list of categories to copy
+   */
+  public void copyLocaleData(String srcLocaleName, String destLocaleName,
+      String... categories) {
+    GwtLocale src = localeFactory.fromString(srcLocaleName);
+    GwtLocale dest = localeFactory.fromString(destLocaleName);
+    for (String category : categories) {
+      Map<String, String> srcMap = maps.get(new MapKey(category, src));
+      if (srcMap == null || srcMap.isEmpty()) {
+        continue;
+      }
+      Map<String, String> destMap = getMap(category, dest);
+      destMap.putAll(srcMap);
+    }
+  }
+
+  public Map<String, Map<String, String>> getAllEntries(String localeName) {
+    GwtLocale locale = localeFactory.fromString(localeName);
+    Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
+    for (Map.Entry<MapKey, Map<String, String>> entry : maps.entrySet()) {
+      Map<String, String> map = entry.getValue();
+      if (entry.getKey().getLocale().equals(locale) && !map.isEmpty()) {
+        result.put(entry.getKey().getCategory(),
+            Collections.unmodifiableMap(entry.getValue()));
+      }
+    }
+    return result;
+  }
+
+  /**
+   * @return all locales present in the CLDR data.
+   */
+  public Set<GwtLocale> getAllLocales() {
+    return Collections.unmodifiableSet(allLocales.keySet());
+  }
+
+  /**
+   * Return all entries in a given category and locale.
+   * 
+   * @param category
+   * @param locale
+   * @return map of keys to localized values
+   */
+  public Map<String, String> getEntries(String category, GwtLocale locale) {
+    MapKey mapKey = new MapKey(category, locale);
+    Map<String, String> map = maps.get(mapKey);
+    if (map == null) {
+      return Collections.emptyMap();
+    }
+    return Collections.unmodifiableMap(map);
+  }
+
+  /**
+   * Return a single value.
+   * 
+   * @param category
+   * @param locale
+   * @param key
+   * @return the requested value, or null if not present
+   */
+  public String getEntry(String category, GwtLocale locale, String key) {
+    MapKey mapKey = new MapKey(category, locale);
+    Map<String, String> map = maps.get(mapKey);
+    if (map == null) {
+      return null;
+    }
+    return map.get(key);
+  }
+
+  /**
+   * @return all locales that have some data associated with them.
+   */
+  public Set<GwtLocale> getNonEmptyLocales() {
+    Set<GwtLocale> result = new HashSet<GwtLocale>();
+    for (Map.Entry<MapKey, Map<String, String>> entry : maps.entrySet()) {
+      Map<String, String> map = entry.getValue();
+      if (map.isEmpty()) {
+        continue;
+      }
+      result.add(entry.getKey().getLocale());
+    }
+    return result;
+  }
+
+  /**
+   * @return all locales that have some data associated with them in the
+   *     specified category.
+   */
+  public Set<GwtLocale> getNonEmptyLocales(String category) {
+    Set<GwtLocale> result = new HashSet<GwtLocale>();
+    for (Map.Entry<MapKey, Map<String, String>> entry : maps.entrySet()) {
+      Map<String, String> map = entry.getValue();
+      if (!category.equals(entry.getKey().category) || map.isEmpty()) {
+      continue;
+      }
+      result.add(entry.getKey().getLocale());
+    }
+    return result;
+  }
+
+  /**
+   * Return the nearest ancestor locale of the supplied locale which has any
+   * values present.
+   *
+   * @param locale
+   * @return GwtLocale of nearest ancestor
+   */
+  public GwtLocale inheritsFrom(GwtLocale locale) {
+    GwtLocale parent = inheritsFrom.get(locale); 
+    while (parent != null && parent != defaultLocale) {
+      for (Map.Entry<MapKey, Map<String, String>> entry : maps.entrySet()) {
+        if (entry.getKey().getLocale().equals(parent)) {
+          Map<String, String> map = entry.getValue();
+          if (!map.isEmpty()) {
+            return parent;
+          }
+        }
+      }
+      parent = inheritsFrom.get(parent);
+    }
+    return parent;
+  }
+
+  /**
+   * Return the nearest ancestor locale of the supplied locale which has any
+   * values present in the specified category.
+   *
+   * @param category
+   * @param locale
+   * @return GwtLocale of nearest ancestor with the specified category
+   */
+  public GwtLocale inheritsFrom(String category, GwtLocale locale) {
+    GwtLocale parent = inheritsFrom.get(locale); 
+    while (parent != null && parent != defaultLocale) {
+      Map<String, String> map = getMap(category, parent);
+      if (!map.isEmpty()) {
+            return parent;
+      }
+      parent = inheritsFrom.get(parent);
+    }
+    return parent;
+  }
+
+  /**
+   * Remove locale entries that completely duplicate their parent.
+   */
+  public void removeCompleteDuplicates() {
+    removeCompleteDuplicates(null);
+  }
+
+  /**
+   * Remove locale entries that completely duplicate their parent.
+   *
+   * @param matchCategory
+   */
+  public void removeCompleteDuplicates(String matchCategory) {
+    MapKey[] keys = getSortedMapKeys();
+    for (MapKey key : keys) {
+      String category = key.getCategory();
+      if (matchCategory != null && !matchCategory.equals(category)) {
+        continue;
+      }
+      GwtLocale locale = key.getLocale();
+      GwtLocale parent = inheritsFrom(category, locale);
+      if (parent == null) {
+        continue;
+      }
+      MapKey parentKey = key.inNewLocale(parent);
+      Map<String, String> parentMap = maps.get(parentKey);
+      if (parentMap == null) {
+        continue;
+      }
+      Map<String, String> map = maps.get(key);
+      boolean allMatch = true;
+      for (Map.Entry<String, String> entry : map.entrySet()) {
+        if (!entry.getValue().equals(parentMap.get(entry.getKey()))) {
+          allMatch = false;
+          break;
+        }
+      }
+      if (allMatch) {
+        maps.remove(key);
+      }
+    }
+  }
+
+  /**
+   * Remove entries that are duplicates of the entries in the parent locale.
+   */
+  public void removeDuplicates() {
+    removeDuplicates(null);
+  }
+
+  /**
+   * Remove entries that are duplicates of the entries in the parent locale.
+   *
+   * @param matchCategory
+   */
+  public void removeDuplicates(String matchCategory) {
+    MapKey[] keys = getSortedMapKeys();
+    for (MapKey key : keys) {
+      String category = key.getCategory();
+      if (matchCategory != null && !matchCategory.equals(category)) {
+        continue;
+      }
+      GwtLocale locale = key.getLocale();
+      GwtLocale parent = inheritsFrom(category, locale);
+      if (parent == null) {
+        continue;
+      }
+      MapKey parentKey = key.inNewLocale(parent);
+      Map<String, String> parentMap = maps.get(parentKey);
+      if (parentMap == null) {
+        continue;
+      }
+      Map<String, String> map = maps.get(key);
+      Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
+      while (it.hasNext()) {
+        Map.Entry<String, String> entry = it.next();
+        String value = entry.getValue();
+        if (value == null || value.equals(parentMap.get(entry.getKey()))) {
+          it.remove();
+        }
+      }
+    }
+  }
+
+  /**
+   * Remove entries in the specified category and locale which match any of
+   * the supplied keys.
+   * 
+   * @param category
+   * @param locale
+   * @param keys
+   */
+  public void removeEntries(String category, GwtLocale locale,
+      Collection<String> keys) {
+    Map<String, String> map = getMap(category, locale);
+    map.keySet().removeAll(keys);
+  }
+
+  /**
+   * Remove a single entry, if present.
+   * 
+   * @param category
+   * @param locale
+   * @param key
+   */
+  public void removeEntry(String category, GwtLocale locale, String key) {
+    Map<String, String> map = getMap(category, locale);
+    map.remove(key);
+  }
+
+  /**
+   * Reset state, forgetting any cached values.
+   */
+  public void reset() {
+    maps.clear();
+  }
+
+  private void addAttributeEntries(String category, Factory cldrFactory,
+      String prefix, String tag, String key, String attribute) {
+    for (GwtLocale locale : allLocales.keySet()) {
+      addAttributeEntry(category, locale, cldrFactory, prefix, tag, key,
+          attribute);
+    }
+  }
+
+  /**
+   * Add currency entries for the specified locale.  If this locale is not the
+   * default locale, also add default entries into the default locale to make
+   * sure it has entries for any currency present in any locale.  Note that
+   * this means that the default locale must be processed last.
+   * 
+   * @param category
+   * @param locale
+   * @param cldrFactory
+   * @param currencyFractions map of currency fraction data extracted from
+   *     locale-independent data
+   * @param defaultCurrencyFraction
+   * @param stillInUse
+   */
+  private void addCurrencyEntries(String category, GwtLocale locale,
+      Factory cldrFactory, Map<String, Integer> currencyFractions,
+      int defaultCurrencyFraction, Set<String> stillInUse) {
+    Map<String, String> outputMap = getMap(category, locale);
+    Map<String, String> defaultMap = null;
+    if (!locale.isDefault()) {
+      defaultMap = getMap(category, localeFactory.getDefault());
+    }
+    Map<String, Currency> tempMap = new HashMap<String, Currency>();
+    CLDRFile cldr = cldrFactory.make(allLocales.get(locale), true);
+    XPathParts parts = new XPathParts();
+    Iterator<String> iterator = cldr.iterator(
+        "//ldml/numbers/currencies");
+    while (iterator.hasNext()) {
+      String path = iterator.next();
+      path = cldr.getFullXPath(path);
+      parts.set(path);
+      Map<String, String> attr = parts.findAttributes("currency");
+      if (attr == null) {
+        continue;
+      }
+      String currencyCode = attr.get("type");
+      Currency currency = tempMap.get(currencyCode);
+      if (currency == null) {
+        currency = new Currency(currencyCode);
+        if (currencyFractions.containsKey(currencyCode)) {
+          currency.setDecimalDigits(currencyFractions.get(currencyCode));
+        } else {
+          currency.setDecimalDigits(defaultCurrencyFraction);
+        }
+        currency.setInUse(stillInUse.contains(currencyCode));
+        tempMap.put(currencyCode, currency);
+      }
+      String field = parts.getElement(4);
+      String value = cldr.getStringValue(path);
+      attr = parts.findAttributes(field);
+      if (attr == null) {
+        attr = Collections.emptyMap();
+      }
+      String draft = attr.get("draft");
+      if ("symbol".equalsIgnoreCase(field)) {
+        currency.setSymbol(value);
+      } else if ("displayName".equalsIgnoreCase(field)) {
+        if (attr.get("count") != null) {
+          // We don't care about currency "count" names
+          continue;
+        }
+        if (draft == null || currency.getDisplayName() == null) {
+          // don't override non-draft name with draft name
+          currency.setDisplayName(value);
+        }
+      } else if ("pattern".equalsIgnoreCase(field)) {
+        currency.setPattern(value);
+      } else if ("decimal".equalsIgnoreCase(field)) {
+        currency.setDecimalSeparator(value);
+      } else if ("group".equalsIgnoreCase(field)) {
+        currency.setGroupingSeparator(value);
+      } else {
+        System.err.println("Ignoring unknown field \"" + field
+            + "\" on currency data for \"" + currencyCode + "\"");
+      }
+    }
+    for (Currency currency : tempMap.values()) {
+      String code = currency.getCode();
+      outputMap.put(code, encodeCurrencyData(currency));
+      if (defaultMap != null) {
+        // Don't copy language-specific things to default
+        currency.setDisplayName(code);
+        currency.setSymbol(null);
+        defaultMap.put(code, encodeCurrencyData(currency));
+      }
+    }
+  }
+
+  private void addDateTimeFormatEntries(String group, String length,
+      Factory cldrFactory) {
+    addEntries(group, cldrFactory,
+        "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/" + group
+        + "Formats/" + group + "FormatLength"
+        + "[@type=\"" + length + "\"]/" + group + "Format[@type=\"standard\"]"
+        + "/pattern[@type=\"standard\"]", group + "FormatLength", "type");
+  }
+
+  private void buildInheritsFrom() {
+    for (GwtLocale locale : allLocales.keySet()) {
+      GwtLocale parent = null;
+      for (GwtLocale search : locale.getInheritanceChain()) {
+        if (!search.equals(locale) && allLocales.containsKey(search)) {
+          parent = search;
+          break;
+        }
+      }
+      inheritsFrom.put(locale, parent);
+    }
+  }
+
+  /**
+   * Build a depth map which is used to sort locales such that more derived
+   * locales are processed before less derived locales.
+   */
+  private void buildLocaleDepth() {
+    Set<GwtLocale> remaining = new HashSet<GwtLocale>(allLocales.keySet());
+    localeDepth.put(defaultLocale, 0);
+    remaining.remove(defaultLocale);
+    while (!remaining.isEmpty()) {
+      Set<GwtLocale> nextPass = new HashSet<GwtLocale>();
+      for (GwtLocale locale : remaining) {
+        GwtLocale parent = inheritsFrom.get(locale);
+        if (localeDepth.containsKey(parent)) {
+          int depth = localeDepth.get(parent);
+          localeDepth.put(locale, depth + 1);
+        } else {
+          nextPass.add(locale);
+        }
+      }
+      remaining = nextPass;
+    }
+  }
+
+  /**
+   * Encode the currency data as needed by CurrencyListGenerator.
+   * 
+   * @param currency
+   * @return a string containing the property file entry for the specified
+   *     currency
+   */
+  private String encodeCurrencyData(Currency currency) {
+    StringBuilder buf = new StringBuilder();
+    String skipped = "";
+    String displayName = currency.getDisplayName();
+    if (displayName == null) {
+      displayName = currency.getCode();
+    }
+    buf.append(displayName);
+    String symbol = currency.getSymbol();
+    if (symbol != null && !currency.getCode().equals(symbol)) {
+      buf.append('|');
+      buf.append(symbol);
+      skipped = "";
+    } else {
+      skipped = "|";
+    }
+    if (currency.getDecimalDigits() != 2) {
+      buf.append(skipped).append('|');
+      buf.append(currency.getDecimalDigits());
+      skipped = "";
+    } else {
+      skipped += "|";
+    }
+    if (!currency.isInUse()) {
+      buf.append(skipped).append("|1");
+    }
+    return buf.toString();
+  }
+
+  /**
+   * Get a map for a given class/locale combination.
+   * @param category 
+   * @param locale
+   * 
+   * @return map for the specified class/locale
+   */
+  private Map<String, String> getMap(String category, GwtLocale locale) {
+    MapKey mapKey = new MapKey(category, locale);
+    Map<String, String> map = maps.get(mapKey);
+    if (map == null) {
+      map = new HashMap<String, String>();
+      maps.put(mapKey, map);
+    }
+    return map;
+  }
+
+  /**
+   * @return an array of map keys, ordered from most derived to least derived.
+   */
+  private MapKey[] getSortedMapKeys() {
+    Set<MapKey> keySet = maps.keySet();
+    MapKey[] keys = keySet.toArray(new MapKey[keySet.size()]);
+    Arrays.sort(keys, new Comparator<MapKey>() {
+      private final Comparator<GwtLocale> depthComparator = new LocaleComparator();
+      
+      public int compare(MapKey a, MapKey b) {
+        return depthComparator.compare(a.getLocale(), b.getLocale());
+      }
+    });
+    return keys;
+  }
+}
diff --git a/tools/cldr-import/src/com/google/gwt/tools/cldr/Processor.java b/tools/cldr-import/src/com/google/gwt/tools/cldr/Processor.java
new file mode 100644
index 0000000..7ad1b75
--- /dev/null
+++ b/tools/cldr-import/src/com/google/gwt/tools/cldr/Processor.java
@@ -0,0 +1,202 @@
+/*
+ * 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.tools.cldr;
+
+import com.google.gwt.i18n.shared.GwtLocale;
+
+import org.unicode.cldr.util.CLDRFile.Factory;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+/**
+ * Base class for CLDR processors that generate GWT i18n resources.
+ */
+public abstract class Processor {
+
+  protected static final String I18N_PACKAGE_PATH = "user/src/com/google/gwt/i18n/";
+
+  protected static <T> String join(String joiner, Iterable<T> objects) {
+    StringBuilder buf = new StringBuilder();
+    for (Object obj : objects) {
+      if (buf.length() > 0) {
+        buf.append(joiner);
+      }
+      buf.append(obj.toString());
+    }
+    return buf.toString();
+  }
+
+  protected static String localeSuffix(GwtLocale locale) {
+    return (locale.isDefault() ? "" : "_") + locale.getAsString();
+  }
+  
+  protected final Factory cldrFactory;
+
+  protected final LocaleData localeData;  
+
+  protected final File outputDir;
+
+  private boolean useOverride;
+
+  /**
+   * Initialize the shared portion of a Processor.
+   * 
+   * @param outputDir output directory for created files
+   * @param cldrFactory CLDR factory used to create new CLDRFile instances
+   * @param localeData LocaleData instance to collect data from CLDR files
+   */
+  protected Processor(File outputDir, Factory cldrFactory,
+      LocaleData localeData) {
+    this.outputDir = outputDir;
+    this.cldrFactory = cldrFactory;
+    this.localeData = localeData;
+    useOverride = true;
+  }
+
+  /**
+   * Execute this processor.
+   * 
+   * It will call loadData, cleanupData, writeOutputFiles, and then reset on
+   * its localeData instance.
+   *  
+   * @throws IOException
+   */
+  public final void run() throws IOException {
+    try {
+      loadData();
+      cleanupData();
+      writeOutputFiles();
+    } finally {
+      localeData.reset();
+    }
+  }
+
+  /**
+   * Override hook for subclasses to implement any cleanup needed, such as
+   * removing values which duplicate those from ancestors.
+   */
+  protected void cleanupData() {
+    // do nothing by default
+  }
+
+  /**
+   * Create an output file including any parent directories.
+   * 
+   * @param name name of file, which will be prefixed by
+   *     user/src/com/google/gwt/i18n/client/impl/cldr
+   * @param ext extension for file
+   * @param locale locale name or null if not localized
+   * @return a PrintWriter instance
+   * @throws IOException
+   */
+  protected PrintWriter createFile(String name, String ext, String locale)
+      throws IOException {
+    if (locale == null || locale.length() == 0) {
+      locale = "";
+    } else {
+      locale = "_" + locale;
+    }
+    return createOutputFile("client/impl/cldr/" + name + locale + "." + ext);
+  }
+
+  protected PrintWriter createOutputFile(String suffix)
+      throws IOException, FileNotFoundException {
+    return createOutputFile(I18N_PACKAGE_PATH, suffix);
+  }
+
+  protected PrintWriter createOutputFile(String prefix, String suffix)
+      throws IOException, FileNotFoundException {
+    PrintWriter pw;
+    File f = new File(outputDir, prefix + suffix);
+    File parent = f.getParentFile();
+    if (parent != null) {
+      parent.mkdirs();
+    }
+    f.createNewFile();
+    pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
+        new FileOutputStream(f), "UTF-8")), false);
+    return pw;
+  }
+
+  protected void generateIntMethod(PrintWriter pw, String category,
+      GwtLocale locale, String key, String method) {
+    String value = localeData.getEntry(category, locale, key);
+    if (value != null) {
+      pw.println();
+      if (useOverride) {
+        pw.println("  @Override");
+      }
+      pw.println("  public int " + method + "() {");
+      pw.println("    return " + value + ";");
+      pw.println("  }");
+    }
+  }
+
+  protected void generateStringMethod(PrintWriter pw, String category,
+      GwtLocale locale, String key, String method) {
+    String value = localeData.getEntry(category, locale, key);
+    generateStringValue(pw, method, value);
+  }
+
+  protected void generateStringValue(PrintWriter pw, String method,
+      String value) {
+    if (value != null) {
+      pw.println();
+      if (useOverride) {
+        pw.println("  @Override");
+      }
+      pw.println("  public String " + method + "() {");
+      pw.println("    return \"" + value.replace("\"", "\\\"") + "\";");
+      pw.println("  }");
+    }
+  }
+
+  /**
+   * @return true if generated methods should use @Override.
+   */
+  protected boolean getOverrides() {
+    return useOverride;
+  }
+
+  /**
+   * Load data needed by this processor.
+   *
+   * @throws IOException
+   */
+  protected abstract void loadData() throws IOException;
+
+  /**
+   * Set whether method definitions should use @Override.
+   * 
+   * @param useOverride
+   */
+  protected void setOverrides(boolean useOverride) {
+    this.useOverride = useOverride;
+  }
+
+  /**
+   * Write output files produced by this processor.
+   *
+   * @throws IOException
+   */
+  protected abstract void writeOutputFiles() throws IOException;
+}
diff --git a/tools/cldr-import/src/com/google/gwt/tools/cldr/RegionLanguageData.java b/tools/cldr-import/src/com/google/gwt/tools/cldr/RegionLanguageData.java
new file mode 100644
index 0000000..bdab309
--- /dev/null
+++ b/tools/cldr-import/src/com/google/gwt/tools/cldr/RegionLanguageData.java
@@ -0,0 +1,232 @@
+/*
+ * 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.tools.cldr;
+
+import org.unicode.cldr.util.CLDRFile;
+import org.unicode.cldr.util.XPathParts;
+import org.unicode.cldr.util.CLDRFile.DraftStatus;
+import org.unicode.cldr.util.CLDRFile.Factory;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Encapsulates region/language literacy data from CLDR.
+ */
+public class RegionLanguageData {
+
+  /**
+   * Stores the populate literate in a given language.
+   */
+  public static class LanguagePopulation implements
+      Comparable<LanguagePopulation> {
+    private final String language;
+    private final double literatePopulation;
+    private final boolean official;
+
+    public LanguagePopulation(String language, double literatePopulation,
+        boolean official) {
+      this.language = language;
+      this.literatePopulation = literatePopulation;
+      this.official = official;
+    }
+
+    public int compareTo(LanguagePopulation other) {
+      int c = -Double.compare(literatePopulation, other.literatePopulation);
+      if (c == 0) {
+        c = language.compareTo(other.language);
+      }
+      return c;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj == null || getClass() != obj.getClass()) {
+        return false;
+      }
+      LanguagePopulation other = (LanguagePopulation) obj;
+      return language.equals(other.language)
+          && (Double.compare(literatePopulation, other.literatePopulation) == 0);
+    }
+
+    public String getLanguage() {
+      return language;
+    }
+
+    public double getLiteratePopulation() {
+      return literatePopulation;
+    }
+
+    @Override
+    public int hashCode() {
+      long temp = Double.doubleToLongBits(literatePopulation);
+      return language.hashCode() + 31 * (int) (temp ^ (temp >>> 32));
+    }
+
+    public boolean isOfficial() {
+      return official;
+    }
+
+    @Override
+    public String toString() {
+      return "[lang=" + language + " pop=" + literatePopulation + "]";
+    }
+  }
+
+  /**
+   * Stores the region populations literate in a given language.
+   */
+  public static class RegionPopulation implements Comparable<RegionPopulation> {
+    private final String region;
+    private final double literatePopulation;
+    private final boolean official;
+
+    public RegionPopulation(String region, double literatePopulation,
+        boolean official) {
+      this.region = region;
+      this.literatePopulation = literatePopulation;
+      this.official = official;
+    }
+
+    public int compareTo(RegionPopulation other) {
+      int c = -Double.compare(literatePopulation, other.literatePopulation);
+      if (c == 0) {
+        c = region.compareTo(other.region);
+      }
+      return c;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj == null || getClass() != obj.getClass()) {
+        return false;
+      }
+      RegionPopulation other = (RegionPopulation) obj;
+      return region.equals(other.region)
+          && (Double.compare(literatePopulation, other.literatePopulation) == 0);
+    }
+
+    public double getLiteratePopulation() {
+      return literatePopulation;
+    }
+
+    public String getRegion() {
+      return region;
+    }
+
+    @Override
+    public int hashCode() {
+      long temp = Double.doubleToLongBits(literatePopulation);
+      return region.hashCode() + 31 * (int) (temp ^ (temp >>> 32));
+    }
+
+    public boolean isOfficial() {
+      return official;
+    }
+
+    @Override
+    public String toString() {
+      return "[region=" + region + " pop=" + literatePopulation + "]";
+    }
+  }
+
+  private final Factory cldrFactory;
+
+  private Map<String, SortedSet<LanguagePopulation>> regionMap;
+  private Map<String, SortedSet<RegionPopulation>> languageMap;
+
+  public RegionLanguageData(Factory cldrFactory) {
+    this.cldrFactory = cldrFactory;
+  }
+
+  public Set<LanguagePopulation> getLanguages(String region) {
+    ensureMaps();
+    Set<LanguagePopulation> languageSet = regionMap.get(region);
+    if (languageSet == null) {
+      languageSet = Collections.emptySet();
+    }
+    return languageSet;
+  }
+
+  public Set<RegionPopulation> getRegions(String language) {
+    ensureMaps();
+    Set<RegionPopulation> regionSet = languageMap.get(language);
+    if (regionSet == null) {
+      regionSet = Collections.emptySet();
+    }
+    return regionSet;
+  }
+
+  private void ensureMaps() {
+    if (regionMap != null) {
+      return;
+    }
+    regionMap = new HashMap<String, SortedSet<LanguagePopulation>>();
+    languageMap = new HashMap<String, SortedSet<RegionPopulation>>();
+    CLDRFile supp = cldrFactory.make("supplementalData", true,
+        DraftStatus.approved);
+    XPathParts parts = new XPathParts();
+    Iterator<String> iterator = supp.iterator(
+        "//supplementalData/territoryInfo/territory");
+    while (iterator.hasNext()) {
+      String path = iterator.next();
+      parts.set(supp.getFullXPath(path));
+      String language = parts.findAttributeValue("languagePopulation", "type");
+      if (language == null) {
+        continue;
+      }
+      String territory = parts.findAttributeValue("territory", "type");
+      String literacyPercentStr = parts.findAttributeValue("territory",
+          "literacyPercent");
+      String populationStr = parts.findAttributeValue("territory",
+          "population");
+      String populationPercentStr = parts.findAttributeValue(
+          "languagePopulation", "populationPercent");
+      String statusStr = parts.findAttributeValue("languagePopulation",
+          "officialStatus");
+      double literacyPercent = Double.parseDouble(literacyPercentStr) * .01;
+      double population = Double.parseDouble(populationStr);
+      double populationPercent = Double.parseDouble(populationPercentStr) * .01;
+      double literatePopulation = population * populationPercent
+          * literacyPercent;
+      boolean official = "official".equals(statusStr);
+      SortedSet<RegionPopulation> regPop = languageMap.get(language);
+      if (regPop == null) {
+        regPop = new TreeSet<RegionPopulation>();
+        languageMap.put(language, regPop);
+      }
+      regPop.add(new RegionPopulation(territory, literatePopulation, official));
+      SortedSet<LanguagePopulation> langPop = regionMap.get(territory);
+      if (langPop == null) {
+        langPop = new TreeSet<LanguagePopulation>();
+        regionMap.put(territory, langPop);
+      }
+      langPop.add(new LanguagePopulation(language, literatePopulation,
+          official));
+    }
+  }
+}
diff --git a/tools/cldr-import/test/com/google/gwt/tools/cldr/LocaleDataTest.java b/tools/cldr-import/test/com/google/gwt/tools/cldr/LocaleDataTest.java
new file mode 100644
index 0000000..817139c
--- /dev/null
+++ b/tools/cldr-import/test/com/google/gwt/tools/cldr/LocaleDataTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.tools.cldr;
+
+import com.google.gwt.i18n.server.GwtLocaleFactoryImpl;
+import com.google.gwt.i18n.shared.GwtLocale;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Test the LocaleData class.
+ */
+public class LocaleDataTest extends TestCase {
+
+  private static final GwtLocaleFactoryImpl localeFactory;
+  
+  static {
+    localeFactory = new GwtLocaleFactoryImpl();
+  }
+
+  /**
+   * Test method for {@link LocaleData#getAllLocales()}.
+   */
+  public void testGetAllLocales() {
+    LocaleData localeData = new LocaleData(localeFactory, Arrays.asList(
+        "root", "en", "en_US", "ar", "ar_IQ"));
+    Set<GwtLocale> locales = localeData.getAllLocales();
+    assertEquals(5, locales.size());
+    GwtLocale localeEn = localeFactory.fromString("en");
+    localeData.addEntry("foo", localeEn, "k1", "v1");
+    locales = localeData.getAllLocales();
+    assertEquals(5, locales.size());
+    GwtLocale localeAr = localeFactory.fromString("ar");
+    localeData.addEntry("bar", localeAr, "k1", "v1");
+    locales = localeData.getAllLocales();
+    assertEquals(5, locales.size());
+  }
+
+  /**
+   * Test method for {@link LocaleData#getNonEmptyLocales()}.
+   */
+  public void testGetNonEmptyLocales() {
+    LocaleData localeData = new LocaleData(localeFactory, Arrays.asList(
+        "root", "en", "en_US", "ar", "ar_IQ"));
+    Iterator<GwtLocale> it = localeData.getNonEmptyLocales().iterator();
+    assertFalse(it.hasNext());
+    GwtLocale localeEn = localeFactory.fromString("en");
+    localeData.addEntry("foo", localeEn, "k1", "v1");
+    it = localeData.getNonEmptyLocales().iterator();
+    assertTrue(it.hasNext());
+    assertEquals(localeEn, it.next());
+    assertFalse(it.hasNext());
+    GwtLocale localeAr = localeFactory.fromString("ar");
+    localeData.addEntry("bar", localeAr, "k1", "v1");
+    it = localeData.getNonEmptyLocales().iterator();
+    assertTrue(it.hasNext());
+    assertNotNull(it.next());
+    assertTrue(it.hasNext());
+    assertNotNull(it.next());
+    assertFalse(it.hasNext());
+  }
+
+  /**
+   * Test method for {@link LocaleData#getNonEmptyLocales(java.lang.String)}.
+   */
+  public void testGetNonEmptyLocalesString() {
+    LocaleData localeData = new LocaleData(localeFactory, Arrays.asList(
+        "root", "en", "en_US", "ar", "ar_IQ"));
+    Iterator<GwtLocale> it = localeData.getNonEmptyLocales("foo").iterator();
+    assertFalse(it.hasNext());
+    GwtLocale localeEn = localeFactory.fromString("en");
+    localeData.addEntry("foo", localeEn, "k1", "v1");
+    it = localeData.getNonEmptyLocales("foo").iterator();
+    assertTrue(it.hasNext());
+    assertEquals(localeEn, it.next());
+    assertFalse(it.hasNext());
+    it = localeData.getNonEmptyLocales("bar").iterator();
+    assertFalse(it.hasNext());
+    GwtLocale localeAr = localeFactory.fromString("ar");
+    localeData.addEntry("bar", localeAr, "k1", "v1");
+    it = localeData.getNonEmptyLocales("foo").iterator();
+    assertTrue(it.hasNext());
+    assertEquals(localeEn, it.next());
+    assertFalse(it.hasNext());
+    it = localeData.getNonEmptyLocales("bar").iterator();
+    assertTrue(it.hasNext());
+    assertEquals(localeAr, it.next());
+    assertFalse(it.hasNext());
+    it = localeData.getNonEmptyLocales("baz").iterator();
+    assertFalse(it.hasNext());
+  }
+
+  /**
+   * Test method for {@link LocaleData#inheritsFrom(com.google.gwt.i18n.shared.GwtLocale)}.
+   */
+  public void testInheritsFrom() {
+    LocaleData localeData = new LocaleData(localeFactory, Arrays.asList(
+        "root", "en", "en_US", "en_US_VARIANT", "ar", "ar_IQ"));
+    GwtLocale localeEn = localeFactory.fromString("en");
+    localeData.addEntry("foo", localeEn, "k1", "v1");
+    GwtLocale localeEnUs = localeFactory.fromString("en_us");
+    GwtLocale localeEnUsVariant = localeFactory.fromString("en_us_variant");
+    GwtLocale localeDefault = localeFactory.getDefault();
+    assertEquals(null, localeData.inheritsFrom(localeDefault));
+    assertEquals(localeEn, localeData.inheritsFrom(localeEnUs));
+    assertEquals(localeEn, localeData.inheritsFrom(localeEnUsVariant));
+    assertEquals(localeDefault, localeData.inheritsFrom(localeEn));
+    GwtLocale localeAr = localeFactory.fromString("ar");
+    assertEquals(localeDefault, localeData.inheritsFrom(localeAr));
+  }
+
+  /**
+   * Test method for {@link LocaleData#inheritsFrom(java.lang.String, com.google.gwt.i18n.shared.GwtLocale)}.
+   */
+  public void testInheritsFromWithCategory() {
+    LocaleData localeData = new LocaleData(localeFactory, Arrays.asList(
+        "root", "en", "en_US", "en_US_VARIANT", "ar", "ar_IQ"));
+    GwtLocale localeEn = localeFactory.fromString("en");
+    localeData.addEntry("foo", localeEn, "k1", "v1");
+    GwtLocale localeEnUs = localeFactory.fromString("en_us");
+    GwtLocale localeEnUsVariant = localeFactory.fromString("en_us_variant");
+    GwtLocale localeDefault = localeFactory.getDefault();
+    assertEquals(localeEn, localeData.inheritsFrom("foo", localeEnUs));
+    assertEquals(localeEn, localeData.inheritsFrom("foo", localeEnUsVariant));
+    assertEquals(localeDefault, localeData.inheritsFrom("foo", localeEn));
+    assertEquals(localeDefault, localeData.inheritsFrom("bar", localeEnUs));
+    assertEquals(localeDefault, localeData.inheritsFrom("bar", localeEnUsVariant));
+    GwtLocale localeAr = localeFactory.fromString("ar");
+    assertEquals(localeDefault, localeData.inheritsFrom("foo", localeAr));
+    assertEquals(localeDefault, localeData.inheritsFrom("bar", localeAr));
+  }
+
+  /**
+   * Test method for {@link LocaleData#removeCompleteDuplicates()}.
+   */
+  public void testRemoveCompleteDuplicates() {
+    LocaleData localeData = new LocaleData(localeFactory, Arrays.asList(
+        "root", "en", "en_US", "ar", "ar_IQ"));
+    GwtLocale localeEn = localeFactory.fromString("en");
+    GwtLocale localeEnUs = localeFactory.fromString("en_us");
+    localeData.addEntry("foo", localeEn, "k1", "v1");
+    localeData.addEntry("foo", localeEn, "k2", "v2");
+    localeData.addEntry("foo", localeEnUs, "k1", "v1");
+    localeData.addEntry("foo", localeEnUs, "k2", "v2");
+    localeData.removeCompleteDuplicates();
+    Set<GwtLocale> locales = localeData.getNonEmptyLocales();
+    assertEquals(1, locales.size());
+    localeData.addEntry("foo", localeEnUs, "k2", "v2a");
+    locales = localeData.getNonEmptyLocales();
+    assertEquals(2, locales.size());
+  }
+
+  /**
+   * Test method for {@link LocaleData#removeDuplicates()}.
+   */
+  public void testRemoveDuplicates() {
+    LocaleData localeData = new LocaleData(localeFactory, Arrays.asList(
+        "root", "en", "en_US", "ar", "ar_IQ"));
+    GwtLocale localeEn = localeFactory.fromString("en");
+    GwtLocale localeEnUs = localeFactory.fromString("en_us");
+    localeData.addEntry("foo", localeEn, "k1", "v1");
+    localeData.addEntry("foo", localeEn, "k2", "v2");
+    localeData.addEntry("foo", localeEnUs, "k1", "v1");
+    localeData.addEntry("foo", localeEnUs, "k2", "v2a");
+    localeData.removeDuplicates();
+    Set<GwtLocale> locales = localeData.getNonEmptyLocales();
+    assertEquals(2, locales.size());
+    assertEquals("v2a", localeData.getEntry("foo", localeEnUs, "k2"));
+    localeData.addEntry("foo", localeEnUs, "k2", "v2");
+    localeData.removeDuplicates();
+    locales = localeData.getNonEmptyLocales();
+    assertEquals(1, locales.size());
+    assertNull(localeData.getEntry("foo", localeEnUs, "k2"));
+  }
+}
diff --git a/user/src/com/google/gwt/i18n/rebind/DateTimePatternGenerator.java b/user/src/com/google/gwt/i18n/rebind/DateTimePatternGenerator.java
index b84d04a..66269a2 100644
--- a/user/src/com/google/gwt/i18n/rebind/DateTimePatternGenerator.java
+++ b/user/src/com/google/gwt/i18n/rebind/DateTimePatternGenerator.java
@@ -23,7 +23,7 @@
  * Helper class to create a localized date/time pattern based on a pattern
  * skeleton.
  */
-class DateTimePatternGenerator {
+public class DateTimePatternGenerator {
   // TODO(jat): Currently uses ICU4J's DateTimePatternGenerator, but should
   // probably be rewritten to avoid that dependency.
 
diff --git a/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
index b5e1729..0168f07 100644
--- a/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
@@ -403,130 +403,6 @@
   }
 
   /**
-   * Helper class to produce string expressions consisting of literals and
-   * computed values.
-   */
-  private static class StringGenerator {
-
-    /**
-     * Output string buffer.
-     */
-    private StringBuffer buf;
-
-    /**
-     * True if we are in the middle of a string literal.
-     */
-    private boolean inString;
-
-    /**
-     * True if the method's return type is SafeHtml (and SafeHtmlBuilder is to
-     * be used to generate the expression); otherwise a String expression is
-     * generated.
-     */
-    private final boolean returnsSafeHtml;
-
-    /**
-     * Initialize the StringGenerator with an output buffer.
-     *
-     * @param buf output buffer
-     * @param returnsSafeHtml if true, an expression of type {@link SafeHtml} is
-     *          being generated, otherwise a {@link String}-valued expression is
-     *          generated
-     */
-    public StringGenerator(StringBuffer buf, boolean returnsSafeHtml) {
-      this.buf = buf;
-      inString = false;
-      this.returnsSafeHtml = returnsSafeHtml;
-      if (returnsSafeHtml) {
-        buf.append("new " + SAFE_HTML_BUILDER_FQCN + "()");
-      } else {
-        buf.append("new java.lang.StringBuffer()");
-      }
-    }
-
-    /**
-     * Append an expression to this string expression.
-     *
-     * @param expression to add
-     * @param isSafeHtmlTyped true if the expression is known to be of type
-     *          {@link SafeHtml}; only relevant if this generator has been
-     *          initialized to generate a {@link SafeHtml}-valued expression
-     * @param isPrimititiveTyped true if the expression is of a primitive type;
-     *          only relevant if this generator has been initialized to generate
-     *          a {@link SafeHtml}-valued expression
-     * @param needsConversionToString true if the expression is not known to be
-     *          of type String and needs to be converted
-     */
-    public void appendExpression(String expression, boolean isSafeHtmlTyped,
-        boolean isPrimititiveTyped, boolean needsConversionToString) {
-      if (inString) {
-        buf.append("\")");
-        inString = false;
-      }
-      /*
-       * SafeHtmlBuilder has append() methods for primitive types as well as for
-       * SafeHtml-valued expressions. For all other expression types, use
-       * appendEscaped(). In addition, if the expression is not known to be of
-       * type String, covert to String.
-       */
-      if (returnsSafeHtml && !isSafeHtmlTyped && !isPrimititiveTyped) {
-        buf.append(".appendEscaped(");
-        if (needsConversionToString) {
-          buf.append("String.valueOf(");
-        }
-      } else {
-        buf.append(".append(");
-      }
-      buf.append(expression);
-      buf.append(")");
-      if (returnsSafeHtml && !isSafeHtmlTyped && !isPrimititiveTyped
-          && needsConversionToString) {
-        buf.append(")");
-      }
-    }
-
-    /**
-     * Append part of a string literal.
-     *
-     * @param str part of string literal
-     */
-    public void appendStringLiteral(String str) {
-      if (!inString) {
-        if (returnsSafeHtml) {
-          buf.append(".appendHtmlConstant(\"");
-        } else {
-          buf.append(".append(\"");
-        }
-        inString = true;
-      }
-      buf.append(str);
-    }
-
-    /**
-     * Append an expression to this string expression.
-     *
-     * @param expression to add, which the caller asserts is String-valued
-     */
-    public void appendStringValuedExpression(String expression) {
-      appendExpression(expression, false, false, false);
-    }
-
-    /**
-     * Complete the string, closing an open quote and handling empty strings.
-     */
-    public void completeString() {
-      if (inString) {
-        buf.append("\")");
-      }
-      if (returnsSafeHtml) {
-        buf.append(".toSafeHtml()");
-      } else {
-        buf.append(".toString()");
-      }
-    }
-  }
-
-  /**
    * Implements {x,time...} references in MessageFormat.
    */
   private static class TimeFormatter implements ValueFormatter {
diff --git a/user/src/com/google/gwt/i18n/rebind/StringGenerator.java b/user/src/com/google/gwt/i18n/rebind/StringGenerator.java
new file mode 100644
index 0000000..1200220
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/rebind/StringGenerator.java
@@ -0,0 +1,140 @@
+/*
+ * 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.rebind;
+
+/**
+ * Helper class to produce string expressions consisting of literals and
+ * computed values.
+ */
+public class StringGenerator {
+
+  /**
+   * Output string buffer.
+   */
+  private StringBuffer buf;
+
+  /**
+   * True if we are in the middle of a string literal.
+   */
+  private boolean inString;
+
+  /**
+   * True if the method's return type is SafeHtml (and SafeHtmlBuilder is to
+   * be used to generate the expression); otherwise a String expression is
+   * generated.
+   */
+  private final boolean returnsSafeHtml;
+
+  /**
+   * Initialize the StringGenerator with an output buffer.
+   *
+   * @param buf output buffer
+   * @param returnsSafeHtml if true, an expression of type {@link SafeHtml} is
+   *          being generated, otherwise a {@link String}-valued expression is
+   *          generated
+   */
+  public StringGenerator(StringBuffer buf, boolean returnsSafeHtml) {
+    this.buf = buf;
+    inString = false;
+    this.returnsSafeHtml = returnsSafeHtml;
+    if (returnsSafeHtml) {
+      buf.append("new " + MessagesMethodCreator.SAFE_HTML_BUILDER_FQCN + "()");
+    } else {
+      buf.append("new java.lang.StringBuffer()");
+    }
+  }
+
+  /**
+   * Append an expression to this string expression.
+   *
+   * @param expression to add
+   * @param isSafeHtmlTyped true if the expression is known to be of type
+   *          {@link SafeHtml}; only relevant if this generator has been
+   *          initialized to generate a {@link SafeHtml}-valued expression
+   * @param isPrimititiveTyped true if the expression is of a primitive type;
+   *          only relevant if this generator has been initialized to generate
+   *          a {@link SafeHtml}-valued expression
+   * @param needsConversionToString true if the expression is not known to be
+   *          of type String and needs to be converted
+   */
+  public void appendExpression(String expression, boolean isSafeHtmlTyped,
+      boolean isPrimititiveTyped, boolean needsConversionToString) {
+    if (inString) {
+      buf.append("\")");
+      inString = false;
+    }
+    /*
+     * SafeHtmlBuilder has append() methods for primitive types as well as for
+     * SafeHtml-valued expressions. For all other expression types, use
+     * appendEscaped(). In addition, if the expression is not known to be of
+     * type String, covert to String.
+     */
+    if (returnsSafeHtml && !isSafeHtmlTyped && !isPrimititiveTyped) {
+      buf.append(".appendEscaped(");
+      if (needsConversionToString) {
+        buf.append("String.valueOf(");
+      }
+    } else {
+      buf.append(".append(");
+    }
+    buf.append(expression);
+    buf.append(")");
+    if (returnsSafeHtml && !isSafeHtmlTyped && !isPrimititiveTyped
+        && needsConversionToString) {
+      buf.append(")");
+    }
+  }
+
+  /**
+   * Append part of a string literal.
+   *
+   * @param str part of string literal
+   */
+  public void appendStringLiteral(String str) {
+    if (!inString) {
+      if (returnsSafeHtml) {
+        buf.append(".appendHtmlConstant(\"");
+      } else {
+        buf.append(".append(\"");
+      }
+      inString = true;
+    }
+    buf.append(str);
+  }
+
+  /**
+   * Append an expression to this string expression.
+   *
+   * @param expression to add, which the caller asserts is String-valued
+   */
+  public void appendStringValuedExpression(String expression) {
+    appendExpression(expression, false, false, false);
+  }
+
+  /**
+   * Complete the string, closing an open quote and handling empty strings.
+   */
+  public void completeString() {
+    if (inString) {
+      buf.append("\")");
+    }
+    if (returnsSafeHtml) {
+      buf.append(".toSafeHtml()");
+    } else {
+      buf.append(".toString()");
+    }
+  }
+}