repository restructure

git-svn-id: http://dev.eclipse.org/svnroot/technology/org.eclipse.stem/trunk/data@2198 92a21009-5b66-0410-b83a-dc787c41c6e9
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/.classpath b/geography/org.eclipse.stem.internal.data.geography.models/.classpath
new file mode 100644
index 0000000..304e861
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/.cvsignore b/geography/org.eclipse.stem.internal.data.geography.models/.cvsignore
new file mode 100644
index 0000000..9c595a6
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/.cvsignore
@@ -0,0 +1 @@
+temp
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/.project b/geography/org.eclipse.stem.internal.data.geography.models/.project
new file mode 100644
index 0000000..779bb51
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.stem.internal.data.geography.models</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/.settings/org.eclipse.jdt.core.prefs b/geography/org.eclipse.stem.internal.data.geography.models/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..db3f3a9
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,15 @@
+#Tue Sep 01 17:02:11 EDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.doc.comment.support=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/META-INF/MANIFEST.MF b/geography/org.eclipse.stem.internal.data.geography.models/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..ebdbceb
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: STEM Internal Geography Models
+Bundle-SymbolicName: org.eclipse.stem.internal.data.geography.models
+Bundle-Version: 1.2.0
+Require-Bundle: org.eclipse.emf.ecore,
+ org.eclipse.emf.ecore.xmi,
+ org.eclipse.stem.core,
+ org.eclipse.stem.internal.data,
+ org.eclipse.stem.internal.data.geography,
+ org.eclipse.stem.data.geography,
+ org.eclipse.stem.definitions,
+ org.eclipse.stem.data.geography.models
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: org.eclipse.stem.internal.data.geography.models,
+ org.eclipse.stem.internal.data.specifications
+Bundle-Vendor: Eclipse.org
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/build.properties b/geography/org.eclipse.stem.internal.data.geography.models/build.properties
new file mode 100644
index 0000000..34d2e4d
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/src/org/eclipse/stem/internal/data/geography/models/GeographyModelGenerator.java b/geography/org.eclipse.stem.internal.data.geography.models/src/org/eclipse/stem/internal/data/geography/models/GeographyModelGenerator.java
new file mode 100644
index 0000000..529dffe
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/src/org/eclipse/stem/internal/data/geography/models/GeographyModelGenerator.java
@@ -0,0 +1,771 @@
+// GeographyModelGenerator.java
+package org.eclipse.stem.internal.data.geography.models;
+
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.stem.core.Constants;
+import org.eclipse.stem.core.graph.GraphPackage;
+import org.eclipse.stem.data.geography.ISOKey;
+import org.eclipse.stem.definitions.nodes.Region;
+import org.eclipse.stem.internal.data.ModelGenerator;
+import org.eclipse.stem.internal.data.PluginFileGenerator;
+import org.eclipse.stem.internal.data.generatedplugin.DublinCore;
+import org.eclipse.stem.internal.data.generatedplugin.Extension;
+import org.eclipse.stem.internal.data.generatedplugin.GeneratedpluginFactory;
+import org.eclipse.stem.internal.data.generatedplugin.Plugin;
+import org.eclipse.stem.internal.data.generatedplugin.StemCategory;
+import org.eclipse.stem.internal.data.geography.GeographyPluginFileGenerator;
+import org.eclipse.stem.internal.data.geography.specifications.AdminLevel;
+import org.eclipse.stem.internal.data.geography.specifications.AdminLevelSet;
+import org.eclipse.stem.internal.data.geography.specifications.CommonBorderGeographicRelationshipPropertyFileSpecification;
+import org.eclipse.stem.internal.data.geography.specifications.CountryAreaLabelPropertyFileSpecification;
+import org.eclipse.stem.internal.data.geography.specifications.CountryGraphPropertyFileSpecification;
+import org.eclipse.stem.internal.data.geography.specifications.RelativePhysicalGeographicRelationshipPropertyFileSpecification;
+import org.eclipse.stem.internal.data.specifications.GeographicModelSpecification;
+import org.eclipse.stem.internal.data.specifications.GraphPropertyFileSpecification;
+import org.eclipse.stem.internal.data.specifications.ModelSpecification;
+
+/**
+ * This class processes the generated Geography Graph files to create Geography
+ * Models.
+ */
+public class GeographyModelGenerator extends ModelGenerator {
+
+	/**
+	 * The id of the Geography category
+	 */
+	public static final String ID_MODEL_GEOGRAPHY_CATEGORY = PluginFileGenerator.ID_MODEL_CATEGORY
+			+ ".geography"; //$NON-NLS-1$
+
+	/**
+	 * The id of the Political category
+	 */
+	public static final String ID_MODEL_GEOGRAPHY_POLITICAL_CATEGORY = ID_MODEL_GEOGRAPHY_CATEGORY
+			+ ".political"; //$NON-NLS-1$
+
+	/**
+	 * The id of the Countries category
+	 */
+	public static final String ID_MODEL_GEOGRAPHY_POLITICAL_COUNTRIES_CATEGORY = ID_MODEL_GEOGRAPHY_POLITICAL_CATEGORY
+			+ ".countries"; //$NON-NLS-1$
+
+	private static final Subject REGION_SUBJECT = new Subject(
+			Region.DUBLIN_CORE_SUBJECT);
+	private static final Subject AREA_SUBJECT = new Subject(
+			CountryAreaLabelPropertyFileSpecification.AREA_LABEL_NAME);
+	private static final Subject COMMON_BORDER_SUBJECT = new Subject(
+			CommonBorderGeographicRelationshipPropertyFileSpecification.COMMON_BORDER_EDGE_NAME);
+	private static final Subject RELATIVE_PHYSICAL_EDGE_NAME_SUBJECT = new Subject(
+			RelativePhysicalGeographicRelationshipPropertyFileSpecification.RELATIVE_PHYSICAL_EDGE_NAME);
+
+	/**
+	 * @param args
+	 *            the URI of the generated geography plugin.xml file
+	 */
+	public static void main(final String[] args) {
+		// Is the plugin file specified?
+		if (args.length == 0) {
+			// Yes
+			System.err.println("Missing specification of the plugin file"); //$NON-NLS-1$
+		} // if
+		else {
+
+			final String GENERATED_FILES_PATH = args[0];
+
+			final String sourceProjectName = args[1];
+
+			final String GENERATED_MODELS_PATH = GENERATED_FILES_PATH
+					+ File.separator + "resources" + File.separator + "data"; //$NON-NLS-1$ //$NON-NLS-2$
+
+			final File file = new File(
+					".."	+ File.separator + sourceProjectName + File.separator + PluginFileGenerator.PLUGIN_XML_FILE_NAME); //$NON-NLS-1$
+			final URI pluginFileURI = URI.createFileURI(file.getAbsolutePath());
+
+			final GeographyModelGenerator dmg = new GeographyModelGenerator();
+			final Map<ISOKey, List<GeographicModelSpecification>> modelSpecifications = dmg
+					.processFiles(pluginFileURI);
+
+			for (final ISOKey isoKey : sortISOKeys(modelSpecifications)) {
+				// Now serialize the Identifiables
+				for (final ModelSpecification gms : modelSpecifications
+						.get(isoKey)) {
+					try {
+						gms.serialize(GENERATED_MODELS_PATH);
+					} catch (final IOException e) {
+						e.printStackTrace();
+					}
+				} // for gms
+			} // for isoKey
+
+			// Create the instance of plugin.xml that we'll serialize later
+			final Plugin pluginxml = GeneratedpluginFactory.eINSTANCE
+					.createPlugin();
+			final Extension extension = GeneratedpluginFactory.eINSTANCE
+					.createExtension();
+
+			// Add the "model" extension point
+			extension.setPoint(Constants.ID_MODEL_EXTENSION_POINT);
+
+			// Add the categories to the extension
+			addCatagoriesToExtension(extension);
+
+			pluginxml.getExtensionelement().add(extension);
+
+			// Now add the dublin core entries to the plugin.xml file for each
+			// of the models
+			for (final ISOKey isoKey : sortISOKeys(modelSpecifications)) {
+				final StemCategory countryCategory = GeneratedpluginFactory.eINSTANCE
+						.createStemCategory();
+				final String COUNTRY_CATAGORY_STRING = ID_MODEL_GEOGRAPHY_POLITICAL_COUNTRIES_CATEGORY
+						+ "." + isoKey.toString().toLowerCase(); //$NON-NLS-1$
+				countryCategory.setId(COUNTRY_CATAGORY_STRING);
+				countryCategory.setName(isoKey.toString());
+				countryCategory
+						.setParentId(ID_MODEL_GEOGRAPHY_POLITICAL_COUNTRIES_CATEGORY);
+				extension.getCategories().add(countryCategory);
+				for (final ModelSpecification gms : modelSpecifications
+						.get(isoKey)) {
+					final DublinCore dc = GeneratedpluginFactory.eINSTANCE
+							.createDublinCore();
+					dc.setCategoryId(COUNTRY_CATAGORY_STRING);
+					extension.getDublinCores().add(
+							populateGeneratedDC(dc, gms.getDublinCore()));
+				} // for gms
+			} // for isoKey
+
+			final URI TEMP_PLUGINXML_URI = URI
+					.createFileURI(GENERATED_FILES_PATH + File.separator
+							+ PluginFileGenerator.PLUGIN_XML_FILE_NAME);
+
+			// Serialize the plugin.xml file.
+			ModelGenerator.writePluginxml(pluginxml, TEMP_PLUGINXML_URI);
+
+			// Create the plugin.properties file
+			createPluginPropertties(GENERATED_FILES_PATH);
+
+		} // else
+	} // main
+
+	/**
+	 * @param path
+	 */
+	public static void createPluginPropertties(final String path) {
+		final File pluginProperties = new File(path + File.separator
+				+ PluginFileGenerator.PLUGIN_PROPERTIES_FILE_NAME);
+		try {
+			final BufferedOutputStream pluginPropertiesOS = new BufferedOutputStream(
+					new FileOutputStream(pluginProperties));
+			System.out.println();
+			final PrintStream ps = new PrintStream(pluginPropertiesOS);
+			ps
+					.println("pluginName = STEM Geographic Model Definitions"); //$NON-NLS-1$
+			ps.println("providerName = " + PluginFileGenerator.PROVIDER_NAME); //$NON-NLS-1$
+			ps.println(PluginFileGenerator.UI_STEM_CATEGORY_NAME
+					+ " = " + PluginFileGenerator.STEM_CATEGORY_NAME); //$NON-NLS-1$
+			ps
+					.println(GeographyPluginFileGenerator.UI_GEOGRAPHY_CATEGORY_NAME
+							+ " = " + GeographyPluginFileGenerator.GEOGRAPHY_CATEGORY_NAME); //$NON-NLS-1$
+			ps
+					.println(GeographyPluginFileGenerator.UI_POLITICAL_CATEGORY_NAME
+							+ " = " + GeographyPluginFileGenerator.POLITITCAL_CATEGORY_NAME); //$NON-NLS-1$
+			ps
+					.println(GeographyPluginFileGenerator.UI_COUNTRIES_CATEGORY_NAME
+							+ " = " + GeographyPluginFileGenerator.COUNTRIES_CATEGORY_NAME); //$NON-NLS-1$
+			ps.flush();
+			ps.close();
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+	} // createPluginPropertties
+
+	private static void addCatagoriesToExtension(final Extension extension) {
+
+		final StemCategory modelCatagory = GeneratedpluginFactory.eINSTANCE
+				.createStemCategory();
+		modelCatagory.setId(PluginFileGenerator.ID_MODEL_CATEGORY);
+		modelCatagory.setName("%" + PluginFileGenerator.UI_STEM_CATEGORY_NAME); //$NON-NLS-1$
+		modelCatagory.setParentId("/"); //$NON-NLS-1$
+		extension.getCategories().add(modelCatagory);
+
+		final StemCategory geographyCatagory = GeneratedpluginFactory.eINSTANCE
+				.createStemCategory();
+		geographyCatagory.setId(ID_MODEL_GEOGRAPHY_CATEGORY);
+		geographyCatagory
+				.setName("%" + GeographyPluginFileGenerator.UI_GEOGRAPHY_CATEGORY_NAME); //$NON-NLS-1$
+		geographyCatagory.setParentId(PluginFileGenerator.ID_MODEL_CATEGORY);
+		extension.getCategories().add(geographyCatagory);
+
+		final StemCategory politicalCatagory = GeneratedpluginFactory.eINSTANCE
+				.createStemCategory();
+		politicalCatagory.setId(ID_MODEL_GEOGRAPHY_POLITICAL_CATEGORY);
+		politicalCatagory
+				.setName("%" + GeographyPluginFileGenerator.UI_POLITICAL_CATEGORY_NAME); //$NON-NLS-1$
+		politicalCatagory.setParentId(ID_MODEL_GEOGRAPHY_CATEGORY);
+		extension.getCategories().add(politicalCatagory);
+
+		final StemCategory countriesCatagory = GeneratedpluginFactory.eINSTANCE
+				.createStemCategory();
+		countriesCatagory
+				.setId(ID_MODEL_GEOGRAPHY_POLITICAL_COUNTRIES_CATEGORY);
+		countriesCatagory
+				.setName("%" + GeographyPluginFileGenerator.UI_COUNTRIES_CATEGORY_NAME); //$NON-NLS-1$
+		countriesCatagory.setParentId(ID_MODEL_GEOGRAPHY_POLITICAL_CATEGORY);
+		extension.getCategories().add(countriesCatagory);
+	} // addCatagoriesToExtension
+
+	/**
+	 * @param map
+	 *            a mapping between {@link ISOKey} and Object
+	 * @return a list of the {@link ISOKey}s sorted in descending alphabetical
+	 *         order
+	 */
+	public static List<ISOKey> sortISOKeys(
+			final Map<ISOKey, ? extends Object> map) {
+		final List<ISOKey> retValue = new ArrayList<ISOKey>();
+
+		retValue.addAll(map.keySet());
+		Collections.sort(retValue);
+		return retValue;
+	} // sortISOKeys
+
+	@SuppressWarnings("unused")
+	private static void printModelSpecifications(
+			final Map<ISOKey, List<GeographicModelSpecification>> modelSpecifications) {
+		final List<ISOKey> sortedISOKeys = sortISOKeys(modelSpecifications);
+		for (final ISOKey isoKey : sortedISOKeys) {
+			final List<GeographicModelSpecification> temp = modelSpecifications
+					.get(isoKey);
+			System.out.println(isoKey + " : " + temp); //$NON-NLS-1$
+		} // for isoKey
+
+	} // printModelSpecifications
+
+	protected Map<ISOKey, List<GeographicModelSpecification>> processFiles(
+			final URI pluginFileURI) {
+		final Map<ISOKey, List<GeographicModelSpecification>> retValue = new HashMap<ISOKey, List<GeographicModelSpecification>>();
+
+		final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> nodeMap = new HashMap<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>();
+		final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> labelMap = new HashMap<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>();
+		final Map<ISOKey, Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>> edgeMap = new HashMap<ISOKey, Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>>();
+
+		// Get the contents of the plug.xml file
+		final Plugin plugin = getPluginxml(pluginFileURI);
+
+		for (final Extension extension : plugin.getExtensionelement()) {
+			for (final DublinCore dc : extension.getDublinCores()) {
+				final String[] typeSubject = GraphPropertyFileSpecification
+						.parseDublinCoreSubjectString(dc.getSubject());
+
+				final Subject subject = new Subject(typeSubject[1]);
+
+				final Map<ISOKey, AdminLevelSet> isoKeyAdminLevelSetMap = CountryGraphPropertyFileSpecification
+						.parseDublinCoreCoverageString(dc.getCoverage());
+
+				// Edge?
+				if (typeSubject[0].equals(GraphPackage.Literals.EDGE.getName())) {
+					populateEdgeMap(isoKeyAdminLevelSetMap, subject, dc,
+							edgeMap);
+				} // if Edge
+				else {
+					// No
+					final ISOKey isoKey = (ISOKey) isoKeyAdminLevelSetMap
+							.keySet().toArray()[0];
+					final AdminLevel adminLevel = isoKeyAdminLevelSetMap.get(
+							isoKey).getMaxAdminLevel();
+
+					poulateMap(isoKey, adminLevel, subject, dc,
+							typeSubject[0].equals(GraphPackage.Literals.NODE
+									.getName()) ? nodeMap : labelMap);
+				} // if Node
+			} // else not Edge
+		} // for each extension
+
+		final Map<ISOKey, AdminLevelSet> isoKeyAdminMap = extractISOKeyAdminLevels(nodeMap);
+		for (final ISOKey isoKey : isoKeyAdminMap.keySet()) {
+			retValue.put(isoKey, makeAllModels(isoKey, isoKeyAdminMap
+					.get(isoKey), nodeMap, labelMap, edgeMap));
+		} // for each isoKey
+
+		// printMapAdminLevels(nodeMap);
+
+		// printMap(nodeMap);
+		//
+		// printMap(labelMap);
+		//
+		// printEdgeMap(edgeMap);
+
+		return retValue;
+	} // processFiles
+
+	private List<GeographicModelSpecification> makeAllModels(
+			final ISOKey isoKey,
+			final AdminLevelSet adminLevelSet,
+			final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> nodeMap,
+			final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> labelMap,
+			final Map<ISOKey, Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>> edgeMap) {
+		final List<GeographicModelSpecification> retValue = new ArrayList<GeographicModelSpecification>();
+
+		for (final AdminLevelSet levelSet : adminLevelSet.makeAllLevelSets()) {
+			final GeographicModelSpecification temp = new GeographicModelSpecification(
+					isoKey, levelSet);
+
+			temp.addNodeDCs(findDCs(REGION_SUBJECT, levelSet, nodeMap
+					.get(isoKey)));
+
+			temp.addAreaDCs(findDCs(AREA_SUBJECT, levelSet, labelMap
+					.get(isoKey)));
+
+			final List<DublinCore> commonBorderEdgeDCs = findEdgeDCs(
+					COMMON_BORDER_SUBJECT, isoKey, levelSet, isoKey, levelSet,
+					edgeMap);
+
+			temp.addCommonBorderEdgeDCs(commonBorderEdgeDCs);
+
+			final List<DublinCore> containmentEdgeDCs = findEdgeDCs(
+					RELATIVE_PHYSICAL_EDGE_NAME_SUBJECT, isoKey, levelSet,
+					isoKey, levelSet, edgeMap);
+			temp.addContainmentEdgeDCs(containmentEdgeDCs);
+			retValue.add(temp);
+		} // for each admin level set
+
+		Collections.sort(retValue);
+		return retValue;
+	} // makeAllModels
+
+	private List<DublinCore> findDCs(
+			final Subject subject,
+			final AdminLevelSet levelSet,
+			final Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>> adminMap) {
+		final List<DublinCore> retValue = new ArrayList<DublinCore>();
+		for (final AdminLevel adminLevel : levelSet.getAdminLevels()) {
+			final Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap = adminMap
+					.get(adminLevel);
+			final Map<ValidDateRange, DublinCore> dateRangeMap = subjectMap
+					.get(subject);
+			// Should be just one
+			final DublinCore dc = (DublinCore) dateRangeMap.values().toArray()[0];
+			retValue.add(dc);
+		} // for adminLevel
+
+		return retValue;
+	} // findNodeDCs
+
+	private List<DublinCore> findEdgeDCs(
+			final Subject subject,
+			final ISOKey isoKey0,
+			final AdminLevelSet levelSet0,
+			final ISOKey isoKey1,
+			final AdminLevelSet levelSet1,
+			final Map<ISOKey, Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>> edgeMap) {
+		final List<DublinCore> retValue = new ArrayList<DublinCore>();
+
+		// Same ISOKey?
+		if (isoKey0.equals(isoKey1)) {
+			// Yes
+			// Common Border?
+			if (subject.equals(COMMON_BORDER_SUBJECT)) {
+				// Yes
+				for (final AdminLevel adminLevel : levelSet0.getAdminLevels()) {
+					final DublinCore tempDC = getEdgeDC(subject, isoKey0,
+							adminLevel, isoKey0, adminLevel, edgeMap);
+					if (tempDC != null) {
+						retValue.add(tempDC);
+					}
+				} // for
+			} // if common border
+			else {
+				// No
+				for (final AdminLevel adminLevel0 : levelSet0.getAdminLevels()) {
+					for (final AdminLevel adminLevel1 : levelSet1
+							.getAdminLevels()) {
+						final DublinCore tempDC = getEdgeDC(subject, isoKey0,
+								adminLevel0, isoKey0, adminLevel1, edgeMap);
+						if (tempDC != null) {
+							retValue.add(tempDC);
+						}
+					} // for adminLevel1
+				} // for adminLevel0
+			} // else
+
+		} // if same ISOKey
+		else {
+			// No
+			// Different ISOKeys
+			final DublinCore tempDC = getEdgeDC(subject, isoKey0, levelSet0
+					.getMaxAdminLevel(), isoKey1, levelSet1.getMaxAdminLevel(),
+					edgeMap);
+			if (tempDC != null) {
+				retValue.add(tempDC);
+			}
+		} // else
+
+		return retValue;
+	} // findEdgeDCs
+
+	private DublinCore getEdgeDC(
+			final Subject subject,
+			final ISOKey isoKey0,
+			final AdminLevel adminLevel0,
+			final ISOKey isoKey1,
+			final AdminLevel adminLevel1,
+			final Map<ISOKey, Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>> edgeMap) {
+		DublinCore retValue = null;
+
+		final Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>> adminMap0 = edgeMap
+				.get(isoKey0);
+		if (adminMap0 != null) {
+			final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> isoMap0 = adminMap0
+					.get(adminLevel0);
+			if (isoMap0 != null) {
+				final Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>> adminMap1 = isoMap0
+						.get(isoKey1);
+				if (adminMap1 != null) {
+					final Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap = adminMap1
+							.get(adminLevel1);
+					if (subjectMap != null) {
+						final Map<ValidDateRange, DublinCore> dateRangeMap = subjectMap
+								.get(subject);
+						if (dateRangeMap != null) {
+							retValue = (DublinCore) dateRangeMap.values()
+									.toArray()[0];
+						}
+					} // if subjectMap
+
+				} // if adminMap1
+			}
+		} // if adminMap0
+		return retValue;
+	} // getEdgeDC
+
+	private void populateEdgeMap(
+			final Map<ISOKey, AdminLevelSet> isoKeyAdminLevelSetMap,
+			final Subject subject,
+			final DublinCore dc,
+			final Map<ISOKey, Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>> edgeMap) {
+
+		final List<ISOKey> sortedISOKeys = sortISOKeys(isoKeyAdminLevelSetMap);
+
+		// How many keys?
+		switch (sortedISOKeys.size()) {
+		case 1:
+			final AdminLevelSet adminLevelSet = isoKeyAdminLevelSetMap
+					.get(sortedISOKeys.get(0));
+
+			// How many levels?
+			switch (adminLevelSet.getNumAdminLevels()) {
+			case 1:
+				// Within the same level
+				final AdminLevel adminLevel = adminLevelSet.getMinAdminLevel();
+
+				final Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap = getSubjectMap(
+						sortedISOKeys.get(0), adminLevel, sortedISOKeys.get(0),
+						adminLevel, edgeMap);
+				getDateRangeMap(subject, subjectMap).put(
+						new ValidDateRange(dc.getValid()), dc);
+				break;
+			case 2:
+				// Between administrative levels
+				final AdminLevel adminLevel0 = adminLevelSet.getMinAdminLevel();
+				final AdminLevel adminLevel1 = adminLevelSet.getMaxAdminLevel();
+
+				final Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap2 = getSubjectMap(
+						sortedISOKeys.get(0), adminLevel0,
+						sortedISOKeys.get(0), adminLevel1, edgeMap);
+				getDateRangeMap(subject, subjectMap2).put(
+						new ValidDateRange(dc.getValid()), dc);
+
+				break;
+			default:
+				System.out.println("Problem!"); //$NON-NLS-1$
+				break;
+			}
+			break;
+
+		case 2:
+			// Between countries
+			final AdminLevelSet adminLevelSet0 = isoKeyAdminLevelSetMap
+					.get(sortedISOKeys.get(0));
+			final AdminLevelSet adminLevelSet1 = isoKeyAdminLevelSetMap
+					.get(sortedISOKeys.get(1));
+
+			final Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap = getSubjectMap(
+					sortedISOKeys.get(0), adminLevelSet0.getMinAdminLevel(),
+					sortedISOKeys.get(1), adminLevelSet1.getMinAdminLevel(),
+					edgeMap);
+			getDateRangeMap(subject, subjectMap).put(
+					new ValidDateRange(dc.getValid()), dc);
+			break;
+		default:
+			System.out.println("Problem!!"); //$NON-NLS-1$
+			break;
+		} // switch
+
+	} // populateEdgeMap
+
+	private void poulateMap(
+			final ISOKey isoKey,
+			final AdminLevel adminLevel,
+			final Subject subject,
+			final DublinCore dc,
+			final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> map) {
+		Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>> adminMap = map
+				.get(isoKey);
+		// Got a adminMap map for this ISO Key?
+		if (adminMap == null) {
+			// NO
+			adminMap = new HashMap<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>();
+			map.put(isoKey, adminMap);
+		}
+		Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap = adminMap
+				.get(adminLevel);
+
+		// Got an subjectMap map?
+		if (subjectMap == null) {
+			// No
+			subjectMap = new HashMap<Subject, Map<ValidDateRange, DublinCore>>();
+			adminMap.put(adminLevel, subjectMap);
+		}
+
+		getDateRangeMap(subject, subjectMap).put(
+				new ValidDateRange(dc.getValid()), dc);
+	} // poulateMap
+
+	private Map<Subject, Map<ValidDateRange, DublinCore>> getSubjectMap(
+			final ISOKey isoKey1,
+			final AdminLevel adminLevel1,
+			final ISOKey isoKey2,
+			final AdminLevel adminLevel2,
+			final Map<ISOKey, Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>> edgeMap) {
+		Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>> adminMap1 = edgeMap
+				.get(isoKey1);
+		if (adminMap1 == null) {
+			adminMap1 = new HashMap<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>();
+			edgeMap.put(isoKey1, adminMap1);
+		}
+		Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> isoMap1 = adminMap1
+				.get(adminLevel1);
+		if (isoMap1 == null) {
+			isoMap1 = new HashMap<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>();
+			adminMap1.put(adminLevel1, isoMap1);
+		}
+		Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>> adminMap2 = isoMap1
+				.get(isoKey2);
+		if (adminMap2 == null) {
+			adminMap2 = new HashMap<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>();
+			isoMap1.put(isoKey2, adminMap2);
+		}
+		Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap = adminMap2
+				.get(adminLevel2);
+		if (subjectMap == null) {
+			subjectMap = new HashMap<Subject, Map<ValidDateRange, DublinCore>>();
+			adminMap2.put(adminLevel2, subjectMap);
+		}
+
+		return subjectMap;
+	} // getSubjectMap
+
+	private Map<ValidDateRange, DublinCore> getDateRangeMap(
+			final Subject subject,
+			final Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap) {
+		Map<ValidDateRange, DublinCore> dateRangeMap = subjectMap.get(subject);
+
+		// Got a valid date range map?
+		if (dateRangeMap == null) {
+			// No
+			dateRangeMap = new HashMap<ValidDateRange, DublinCore>();
+			subjectMap.put(subject, dateRangeMap);
+		}
+		return dateRangeMap;
+	} // getDateRangeMap
+
+	private Map<ISOKey, AdminLevelSet> extractISOKeyAdminLevels(
+			final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> map) {
+		final Map<ISOKey, AdminLevelSet> retValue = new HashMap<ISOKey, AdminLevelSet>();
+
+		for (final ISOKey isoKey : sortISOKeys(map)) {
+			final Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>> adminMap = map
+					.get(isoKey);
+			final AdminLevelSet temp = new AdminLevelSet();
+			for (final AdminLevel adminLevel : adminMap.keySet()) {
+				temp.addAdminLevel(adminLevel);
+			} // for adminLevel
+			retValue.put(isoKey, temp);
+		} // for isoKey
+		return retValue;
+	} // extractISOKeyAdminLevels
+
+	@SuppressWarnings("unused")
+	private void printMap(
+			final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> map) {
+		System.out
+				.println("****************************************************************"); //$NON-NLS-1$
+
+		for (final ISOKey isoKey : sortISOKeys(map)) {
+			System.out.println(isoKey);
+			final Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>> adminMap = map
+					.get(isoKey);
+			for (final AdminLevel adminLevel : adminMap.keySet()) {
+				System.out.println("\t" + adminLevel); //$NON-NLS-1$
+				printSubjectMap(adminMap.get(adminLevel));
+			} // for adminLevel
+		} // for nodeMap isoKey
+	} // printMap
+
+	@SuppressWarnings("unused")
+	private void printEdgeMap(
+			final Map<ISOKey, Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>>> edgeMap) {
+		System.out
+				.println("****************************************************************"); //$NON-NLS-1$
+
+		for (final ISOKey isoKey0 : sortISOKeys(edgeMap)) {
+			System.out.println(isoKey0);
+			final Map<AdminLevel, Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>>> adminMap0 = edgeMap
+					.get(isoKey0);
+			for (final AdminLevel adminLeve0 : adminMap0.keySet()) {
+				System.out.println("\t" + adminLeve0); //$NON-NLS-1$
+				final Map<ISOKey, Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>>> isoKeyMap1 = adminMap0
+						.get(adminLeve0);
+
+				for (final ISOKey isoKey1 : sortISOKeys(isoKeyMap1)) {
+					System.out.println("\t\t" + isoKey1); //$NON-NLS-1$
+					final Map<AdminLevel, Map<Subject, Map<ValidDateRange, DublinCore>>> adminMap1 = isoKeyMap1
+							.get(isoKey1);
+					for (final AdminLevel adminLevel1 : adminMap1.keySet()) {
+						System.out.println("\t\t\t" + adminLevel1); //$NON-NLS-1$
+						printSubjectMap(adminMap1.get(adminLevel1));
+					} // for adminLevel
+				} // for isoKey1
+			} // for each adminLevel
+		} // for each ISO Key
+
+	} // printEdgeMap
+
+	private void printSubjectMap(
+			final Map<Subject, Map<ValidDateRange, DublinCore>> subjectMap) {
+		for (final Subject subject : subjectMap.keySet()) {
+			System.out.println("\t\t\t\t" + subject); //$NON-NLS-1$
+			final Map<ValidDateRange, DublinCore> dateRangeMap = subjectMap
+					.get(subject);
+			for (final ValidDateRange validDateRange : dateRangeMap.keySet()) {
+				final DublinCore dc = dateRangeMap.get(validDateRange);
+				System.out
+						.println("\t\t\t\t\t" + validDateRange + " : " + dc.getIdentifier()); //$NON-NLS-1$ //$NON-NLS-2$
+			} // for validDateRange
+		} // for subject
+	}
+
+	private static class ValidDateRange {
+		String dcValidString;
+
+		/**
+		 * @param dcValidString
+		 */
+		public ValidDateRange(final String dcValidString) {
+			this.dcValidString = dcValidString;
+		}
+
+		/**
+		 * @see java.lang.Object#hashCode()
+		 */
+		@Override
+		public int hashCode() {
+			return dcValidString.hashCode();
+		}
+
+		/**
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		@Override
+		public boolean equals(final Object obj) {
+			if (obj instanceof ValidDateRange) {
+				return dcValidString.equals(((ValidDateRange)obj).dcValidString);
+			}
+			return false;
+		}
+
+		/**
+		 * @see java.lang.Object#toString()
+		 */
+		@Override
+		public String toString() {
+			return dcValidString;
+		}
+
+	} // ValidDateRange
+
+	private static class Subject {
+		String subject;
+
+		/**
+		 * @param subject
+		 */
+		public Subject(final String subject) {
+			this.subject = subject;
+		}
+
+		/**
+		 * @see java.lang.Object#hashCode()
+		 */
+		@Override
+		public int hashCode() {
+			return subject.hashCode();
+		}
+
+		/**
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		@Override
+		public boolean equals(final Object obj) {
+			if (this == obj) {
+				return true;
+			}
+			if (obj == null) {
+				return false;
+			}
+			if (getClass() != obj.getClass()) {
+				return false;
+			}
+			final Subject other = (Subject) obj;
+			if (subject == null) {
+				if (other.subject != null) {
+					return false;
+				}
+			} else if (!subject.equals(other.subject)) {
+				return false;
+			}
+			return true;
+		}
+
+		/**
+		 * @see java.lang.Object#toString()
+		 */
+		@Override
+		public String toString() {
+			return subject;
+		}
+
+	} // Subject
+
+} // GeographyModelGenerator
\ No newline at end of file
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/src/org/eclipse/stem/internal/data/specifications/GeographicModelSpecification.java b/geography/org.eclipse.stem.internal.data.geography.models/src/org/eclipse/stem/internal/data/specifications/GeographicModelSpecification.java
new file mode 100644
index 0000000..fc5e3f8
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/src/org/eclipse/stem/internal/data/specifications/GeographicModelSpecification.java
@@ -0,0 +1,313 @@
+// GeographicModelSpecification.java
+package org.eclipse.stem.internal.data.specifications;
+
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.plugin.EcorePlugin;
+import org.eclipse.stem.core.Utility;
+import org.eclipse.stem.core.graph.Graph;
+import org.eclipse.stem.core.model.Model;
+import org.eclipse.stem.core.model.ModelPackage;
+import org.eclipse.stem.data.geography.ISOKey;
+import org.eclipse.stem.definitions.nodes.NodesPackage;
+import org.eclipse.stem.internal.data.generatedplugin.DublinCore;
+import org.eclipse.stem.internal.data.geography.specifications.AdminLevelSet;
+import org.eclipse.stem.internal.data.geography.specifications.CountryGraphPropertyFileSpecification;
+
+/**
+ * This class represents a {@link ModelSpecification} for a Geographic model.
+ */
+public class GeographicModelSpecification extends ModelSpecification implements
+		Comparable<GeographicModelSpecification> {
+
+	/**
+	 * This is referenced to cause the {@link NodesPackage} to register itself
+	 * as a package so that {@link org.eclipse.stem.definitions.nodes.Region}s in the {@link Graph}'s we're
+	 * deserializing can be created. Without it, the package will not be found.
+	 */
+	@SuppressWarnings("unused")
+	private final NodesPackage nodesPackage = NodesPackage.eINSTANCE;
+
+	/**
+	 * This is a map between the {@link ISOKey} of the countries in the
+	 * specification and the administrative levels of the graphs included in the
+	 * model. If the model is for a single country, there will be only one
+	 * entry, with the ISOKey of the country as the key. If there are more than
+	 * one, for instance in the case of a model that represents a continent,
+	 * then there will be an entry for each country.
+	 */
+	private final Map<ISOKey, AdminLevelSet> countryLevelMap = new HashMap<ISOKey, AdminLevelSet>();
+
+	private final List<DublinCore> nodeDublinCores = new ArrayList<DublinCore>();
+	private final List<DublinCore> areaDublinCores = new ArrayList<DublinCore>();
+	private final List<DublinCore> commonBorderEdgeDublinCores = new ArrayList<DublinCore>();
+	private final List<DublinCore> containmentEdgeDublinCores = new ArrayList<DublinCore>();
+
+	static {
+		// This sets up the mapping between the "platform" URI's and the
+		// projects in the file system.
+
+		// CWD is the current working directory
+		final String CWD = System.getProperty("user.dir"); //$NON-NLS-1$
+		// WORKSPACE is the directory of the parent workspace
+		final String WORKSPACE = CWD.substring(0, CWD
+				.lastIndexOf(File.separator));
+
+		// The path to the generated geography data project
+		final String GEOGRAPHY_PROJECT_ROOT = WORKSPACE + File.separator
+				+ org.eclipse.stem.data.geography.Activator.PLUGIN_ID
+				+ File.separator;
+
+		final Map<String, URI> platformResourceMap = EcorePlugin
+				.getPlatformResourceMap();
+
+		// Register the mapping between the project id and the absolute path to
+		// the project in the file system. This will be found later when
+		// deserializing when the platform URI is mapped.
+		platformResourceMap.put(
+				org.eclipse.stem.data.geography.Activator.PLUGIN_ID, URI
+						.createFileURI(GEOGRAPHY_PROJECT_ROOT));
+	} // static
+
+	/**
+	 * @param isoKey
+	 *            the {@link ISOKey} for a single region
+	 * @param levelSet
+	 *            the {@link AdminLevelSet} covered
+	 */
+	public GeographicModelSpecification(ISOKey isoKey, AdminLevelSet levelSet) {
+		countryLevelMap.put(isoKey, levelSet);
+	} // GeographicModelSpecification
+
+	/**
+	 * @param nodeDCs
+	 */
+	public void addNodeDCs(List<DublinCore> nodeDCs) {
+		nodeDublinCores.addAll(nodeDCs);
+	}
+
+	/**
+	 * @param areaDCs
+	 */
+	public void addAreaDCs(List<DublinCore> areaDCs) {
+		areaDublinCores.addAll(areaDCs);
+	}
+
+	/**
+	 * @param commonBorderEdgeDCs
+	 */
+	public void addCommonBorderEdgeDCs(List<DublinCore> commonBorderEdgeDCs) {
+		commonBorderEdgeDublinCores.addAll(commonBorderEdgeDCs);
+	}
+
+	/**
+	 * @param containmentEdgeDCs
+	 */
+	public void addContainmentEdgeDCs(List<DublinCore> containmentEdgeDCs) {
+		containmentEdgeDublinCores.addAll(containmentEdgeDCs);
+	}
+
+	/**
+	 * @see org.eclipse.stem.internal.data.specifications.ModelSpecification#createIdentifiableInstance()
+	 */
+	@Override
+	protected Model createIdentifiableInstance() {
+		final Model retValue = super.createIdentifiableInstance();
+		// The generated Graphs etc., all have dublin core that references
+		// them as plugins. This is as it should be when STEM is running,
+		// but right now we're running stand-alone and want to access the
+		// plugins as projects (they aren't plugged into our running version
+		// of Eclipse). Substitute "resource" for "plugin" in the URI's we
+		// use and all will be well.
+
+		for (DublinCore dc : nodeDublinCores) {
+			retValue.getGraphs().add(
+					(Graph) Utility
+							.getIdentifiable(getIdentifiableProjectURI(dc)));
+		} // for each node dc
+
+		for (DublinCore dc : areaDublinCores) {
+			retValue.getGraphs().add(
+					(Graph) Utility
+							.getIdentifiable(getIdentifiableProjectURI(dc)));
+		} // for each area dc
+
+		for (DublinCore dc : commonBorderEdgeDublinCores) {
+			retValue.getGraphs().add(
+					(Graph) Utility
+							.getIdentifiable(getIdentifiableProjectURI(dc)));
+		} // for each common border dc
+
+		for (DublinCore dc : containmentEdgeDublinCores) {
+			retValue.getGraphs().add(
+					(Graph) Utility
+							.getIdentifiable(getIdentifiableProjectURI(dc)));
+		} // for each containment border dc
+
+		return retValue;
+	} // createIdentifiableInstance
+
+	@Override
+	protected String getRelativeSerializationPath() {
+		final StringBuilder sb = new StringBuilder();
+		sb.append(CountryGraphPropertyFileSpecification.COUNTRY_DIR);
+		sb.append(File.separatorChar);
+		sb.append(getISOKey().toString());
+		return sb.toString();
+	}
+
+	@Override
+	protected String getSerializationFileNameRoot() {
+		final StringBuilder sb = new StringBuilder(getISOKey().toString());
+		sb.append("_"); //$NON-NLS-1$
+		sb.append(countryLevelMap.get(getISOKey()).toString());
+		return sb.toString();
+	}
+
+	@Override
+	protected String getTargetPluginId() {
+		return org.eclipse.stem.data.geography.models.Activator.PLUGIN_ID;
+	}
+
+	@Override
+	protected String getTitleDescriptor() {
+		final StringBuilder sb = new StringBuilder(getISOKey().toString());
+		sb.append(" Geography"); //$NON-NLS-1$
+
+		final String levelString = countryLevelMap.get(getISOKey()).toString();
+		final AdminLevelSet als = new AdminLevelSet(levelString);
+
+		sb.append(als.getNumAdminLevels() > 1 ? " (Levels: " : " (Level: "); //$NON-NLS-1$ //$NON-NLS-2$
+		sb.append(als.toString(",")); //$NON-NLS-1$
+		sb.append(")"); //$NON-NLS-1$
+
+		// Common Border Edges?
+		if (commonBorderEdgeDublinCores.size() > 0) {
+			sb.append(", Common Border("); //$NON-NLS-1$
+			for (Iterator<DublinCore> dcIterator = commonBorderEdgeDublinCores
+					.iterator(); dcIterator.hasNext();) {
+				DublinCore dc = dcIterator.next();
+				final Map<ISOKey, AdminLevelSet> coverageMap = CountryGraphPropertyFileSpecification
+						.parseDublinCoreCoverageString(dc.getCoverage());
+
+				for (Iterator<AdminLevelSet> alsIterator = coverageMap.values()
+						.iterator(); alsIterator.hasNext();) {
+					sb.append(alsIterator.next().toString("-")); //$NON-NLS-1$
+					sb.append(dcIterator.hasNext() ? ", " : ""); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+			} // foreach dc
+			sb.append(")"); //$NON-NLS-1$
+		} // if common border edges
+
+		// Containment?
+		if (containmentEdgeDublinCores.size() > 0) {
+			sb.append(", Containment("); //$NON-NLS-1$
+			for (Iterator<DublinCore> dcIterator = containmentEdgeDublinCores
+					.iterator(); dcIterator.hasNext();) {
+				DublinCore dc = dcIterator.next();
+
+				final Map<ISOKey, AdminLevelSet> coverageMap = CountryGraphPropertyFileSpecification
+						.parseDublinCoreCoverageString(dc.getCoverage());
+				for (Iterator<AdminLevelSet> iterator = coverageMap.values()
+						.iterator(); iterator.hasNext();) {
+					sb.append(iterator.next().toString("->")); //$NON-NLS-1$
+					sb.append(dcIterator.hasNext() ? ", " : ""); //$NON-NLS-1$ //$NON-NLS-2$
+				}
+
+			} // foreach dc
+			sb.append(")"); //$NON-NLS-1$
+		}
+
+		return sb.toString();
+	} // getTitleDescriptor
+
+	/**
+	 * @see org.eclipse.stem.internal.data.specifications.IdentifiableSpecification#createDubinCoreCoverage()
+	 */
+	@Override
+	protected String createDubinCoreCoverage() {
+		return CountryGraphPropertyFileSpecification
+				.createDublinCoreCoverageString(countryLevelMap);
+	}
+
+	/**
+	 * @see org.eclipse.stem.internal.data.specifications.IdentifiableSpecification#createDubinCoreSubject()
+	 */
+	@Override
+	protected String createDubinCoreSubject() {
+		return GraphPropertyFileSpecification.createDublinCoreSubjectString(
+				ModelPackage.Literals.MODEL.getName(), "geography"); //$NON-NLS-1$
+	}
+
+	private ISOKey getISOKey() {
+		final ISOKey isoKey = (ISOKey) countryLevelMap.keySet().toArray()[0];
+		return isoKey;
+	}
+
+	public int compareTo(GeographicModelSpecification gms) {
+		int retValue = 0;
+		// Maps same size?
+		if (countryLevelMap.size() == gms.countryLevelMap.size()) {
+			// Yes
+			// Just one?
+			if (countryLevelMap.size() == 1) {
+				// Yes
+				final AdminLevelSet adminLevelSet0 = (AdminLevelSet) countryLevelMap
+						.values().toArray()[0];
+				final AdminLevelSet adminLevelSet1 = (AdminLevelSet) gms.countryLevelMap
+						.values().toArray()[0];
+				if (adminLevelSet0.getNumAdminLevels() == adminLevelSet1
+						.getNumAdminLevels()) {
+					retValue = adminLevelSet0.getMinAdminLevel().compareTo(
+							adminLevelSet1.getMinAdminLevel());
+				} else {
+					// Different number of levels
+					retValue = adminLevelSet0.getNumAdminLevels() > adminLevelSet1
+							.getNumAdminLevels() ? 1 : -1;
+				}
+			} // if
+			// else retValue = 0;
+		} // if maps same size
+		else {
+			retValue = countryLevelMap.size() > gms.countryLevelMap.size() ? 1
+					: -1;
+		}
+		return retValue;
+	} // compareTo
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	@Override
+	public String toString() {
+		final StringBuilder sb = new StringBuilder();
+		for (ISOKey isoKey : countryLevelMap.keySet()) {
+			sb.append(isoKey);
+			sb.append(" : "); //$NON-NLS-1$
+			sb.append(countryLevelMap.get(isoKey));
+			sb.append("(" + nodeDublinCores.size() + "/" //$NON-NLS-1$//$NON-NLS-2$
+					+ areaDublinCores.size() + "/" //$NON-NLS-1$
+					+ commonBorderEdgeDublinCores.size() + "/" //$NON-NLS-1$
+					+ containmentEdgeDublinCores.size() + ")"); //$NON-NLS-1$
+		} // foreach isoKey
+		return sb.toString();
+	} // toString
+
+} // GeographicModelSpecification
\ No newline at end of file
diff --git a/geography/org.eclipse.stem.internal.data.geography.models/update.xml b/geography/org.eclipse.stem.internal.data.geography.models/update.xml
new file mode 100644
index 0000000..c3ff030
--- /dev/null
+++ b/geography/org.eclipse.stem.internal.data.geography.models/update.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0"?>
+<!-- ====================================================================== 
+     ANT script to build org.eclispe.ohf.stem.geography project 
+     and the initial set of STEM scenarios.  
+     This must be run after installing the STEM workspace and after changes
+     to code generated by EMF models.
+                                                                    
+     ====================================================================== -->
+<project name="STEM Internal Geographic Models" default="update_all">
+	<description>
+     Use the properties files to populate the org.eclipse.stem.data.geography.models plugin
+    </description>
+
+	<!-- P r o j e c t  S p e c i f i c a t i o n s -->
+
+	<!-- The name of the project -->
+	<property name="project.name" value="stem" />
+
+	
+	<!-- Are we running headless? -->
+	<condition property="runningHeadless" >
+		<isset property="buildDirectory" />
+	</condition>
+	
+	<!-- The buildDirectory property is set in a headless build -->
+	<condition property="pluginRootDirectory" value="${buildDirectory}/plugins" else="..">
+		<isset property="runningHeadless" />
+	</condition>
+
+	<!-- The baseLocation property is the location of the eclipse home installation -->
+	<!-- It is used to find eclipse plugins                                         -->
+	<!-- It is set in a headless build, but not one started manually from Eclipse   -->
+	<condition property="baseLocation" value="${eclipse.pdebuild.home}../..">
+		<not>
+			<isset property="runningHeadless" />
+		</not>
+	</condition>
+
+	<!-- This is the location of the class files of the built plug-ins     -->
+	<!-- If this is a manual build started from Eclipse then the directory -->
+	<!-- is "bin", if its a headless build then the directory is "@dot"    -->
+	<!-- The property baseLocation is set in a headless build              -->
+	<condition property="targetBin" value="@dot" else="bin">
+		<isset property="runningHeadless" />
+	</condition>
+	
+	
+	<!-- The prefix for fully qualified names -->
+	<property name="ID_ROOT" value="org.eclipse.stem" />
+
+	<!-- The name of the plugin that contains the plugin.xml file to scan -->
+	<property name="scan.plugin" value="${ID_ROOT}.data.geography" />
+	
+	<!-- The name of the source plugin (i.e., the one that contains this file -->
+	<property name="source.plugin" value="${ID_ROOT}.internal.data.geography.models" />
+
+	<!-- The name of the target plugin -->
+	<property name="target.plugin" value="${ID_ROOT}.data.geography.models" />
+
+	<!-- The path to the root directory of the source plugin -->
+	<property name="path.sourceplugin" location="${pluginRootDirectory}/${source.plugin}" />
+
+	<!-- The path to the root directory of the plugin that will be augmented with generated content -->
+	<property name="path.targetplugin" location="${pluginRootDirectory}/${target.plugin}" />
+
+	<!-- The path to the root directory of the resource files -->
+	<property name="path.resource.files" value="${path.sourceplugin}/resources/data/country" />
+
+	<!-- The path to the root directory of the plugin that will be augmented with generated content-->
+	<property name="path.generated.files" value="${path.sourceplugin}/temp" />
+
+	<!-- The path to the root directory of geography source code -->
+	<property name="path.geography.src" value="${path.targetplugin}/src/org/eclipse/stem/data/geography/models" />
+
+	<!-- The name of the plugin.xml file that will be augmented with generated content-->
+	<property name="file.target.pluginxml" value="${path.targetplugin}/plugin.xml" />
+
+	<!-- The name of the plugin.properties file that will be augmented with generated content-->
+	<property name="file.target.pluginproperties" value="${path.targetplugin}/plugin.properties" />
+
+	<!-- The name of the plugin.xml file that that is the aggregation of the generated content-->
+	<property name="file.util.pluginxml" value="${path.generated.files}/plugin.xml" />
+
+	<!-- The name of the plugin.properties file that that is the aggregation of the generated content-->
+	<property name="file.util.pluginproperties" value="${path.generated.files}/plugin.properties" />
+
+    <!-- The name of the plugin.xml file that that is the aggregation of the generated content-->
+    <property name="file.target.pluginproperties" value="${path.generated.files}/plugin.properties" />
+
+	<!-- The class path to use -->
+	<path id="classpath.runtime"> 
+		<pathelement location="${path.sourceplugin}/${targetBin}" />
+		<pathelement location="${pluginRootDirectory}/${ID_ROOT}.core/${targetBin}" />
+		<pathelement location="${pluginRootDirectory}/${ID_ROOT}.definitions/${targetBin}" />
+	    <pathelement location="${pluginRootDirectory}/${ID_ROOT}.internal.data/${targetBin}" />
+		 <pathelement location="${pluginRootDirectory}/${ID_ROOT}.internal.data.geography/${targetBin}" />
+		<pathelement location="${pluginRootDirectory}/${ID_ROOT}.data.geography/${targetBin}" />
+		<fileset dir="${baseLocation}/plugins/">
+			<include name="*emf*.jar" />
+		</fileset>
+		<fileset dir="${pluginRootDirectory}/${ID_ROOT}.internal.data/lib/">
+			<include name="*emf*.jar" />
+		</fileset>
+	</path>
+
+	<target name="update_all" depends="update_geography" />
+
+	<!-- Update the geography plugin with the latest definitions -->
+	<target name="update_geography" depends="update_geography_models, update_geography_plugin_xml, update_geography_plugin_properties" />
+
+	<target name="update_geography_plugin_xml" depends="create_candidates">
+		<copy tofile="${file.target.pluginxml}" file="${file.util.pluginxml}" overwrite="true" />
+	</target>
+
+	<target name="update_geography_plugin_properties" depends="create_candidates">
+		<copy tofile="${file.target.pluginproperties}" file="${file.util.pluginproperties}" overwrite="true" />
+	</target>
+
+	<!-- Update the graph files in the geography plugin -->
+	<target name="update_geography_models" depends="create_candidates">
+		<copy todir="${path.targetplugin}/resources/data/" overwrite="true">
+			<fileset dir="${path.generated.files}/resources/data">
+				<include name="**/*.model" />
+			</fileset>
+		</copy>
+	</target>
+	
+	<!-- Create the model files for countries and the plugin.xml and plugin.properties fragments -->
+	<target name="create_candidates" depends="clean">
+		<!-- There is a problem here with specifying fork="true"  maxmemory="768m".  It causes an IOException on some machines -->
+		<java classname="${ID_ROOT}.internal.data.geography.models.GeographyModelGenerator" fork="true" maxmemory="1024m">
+			<classpath refid="classpath.runtime" />
+			<arg value="${path.generated.files}" />
+			<arg value="${scan.plugin}" />
+			<jvmarg value="-Xms1024m"/>
+			<jvmarg value="-Xmx1024m"/>
+		</java>
+	</target>
+
+	<!-- clean out the .../temp/data directories -->
+	<!-- clean out the target directories -->
+	<target name="clean" depends="create_generated_files_dir">
+		<delete includeEmptyDirs="true" quiet="true">
+			<fileset dir="${path.generated.files}/">
+				<include name="**/*" />
+			</fileset>
+			<fileset dir="${path.targetplugin}/resources/data/">
+				<include name="country/**/*" />
+			</fileset>
+		</delete>
+	</target>
+
+	<target name="create_generated_files_dir">
+		<mkdir dir="${path.generated.files}/resources/data"/>		
+	</target>
+</project>