[513632] Stack overflow when validating reversed type parameters
diff --git a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EcoreValidator.java b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EcoreValidator.java
index 827a78f..ef309eb 100644
--- a/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EcoreValidator.java
+++ b/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/util/EcoreValidator.java
@@ -19,10 +19,12 @@
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.eclipse.emf.common.util.BasicDiagnostic;
 import org.eclipse.emf.common.util.Diagnostic;
@@ -3788,7 +3790,7 @@
           {
             return true;
           }
-          else if (substitution != null && substitution.getEUpperBound() != eGenericType && substitution.getELowerBound() != eGenericType)
+          else if (substitution != null && substitution.getEUpperBound() != eGenericType && substitution.getELowerBound() != eGenericType && !isCircularSubstitution(eTypeParameter, substitution, substitutions))
           {
             return isBounded(substitution, eBound, substitutions);
           }
@@ -3923,6 +3925,33 @@
     }
   }
 
+  private static boolean isCircularSubstitution(ETypeParameter eTypeParameter, EGenericType substitution, Map<? extends ETypeParameter, ? extends EGenericType> substitutions)
+  {
+    Set<ETypeParameter> visited = new HashSet<ETypeParameter>();
+    for (ETypeParameter otherETypeParameter = substitution.getETypeParameter(); otherETypeParameter != null; )
+    {
+      if (otherETypeParameter == eTypeParameter)
+      {
+        return true;
+      }
+
+      if (!visited.add(otherETypeParameter))
+      {
+        return false;
+      }
+
+      EGenericType otherSubstitution = substitutions.get(otherETypeParameter);
+      if (otherSubstitution == null)
+      {
+        return false;
+      }
+
+      otherETypeParameter = otherSubstitution.getETypeParameter();
+    }
+
+    return false;
+  }
+
   public static boolean matchingTypeArguments
     (EList<EGenericType> eTypeArguments1, EList<EGenericType> eTypeArguments2, Map<? extends ETypeParameter, ? extends EGenericType> substitutions)
   {