content: package rabbitescape.engine.err;
  
  import static rabbitescape.engine.util.Util.*;
  
  import java.lang.reflect.Field;
  import java.util.Locale;
  import java.util.Map;
  import java.util.MissingResourceException;
  import java.util.ResourceBundle;
  import java.util.TreeMap;
  
  import rabbitescape.engine.util.NamedFieldFormatter;
  
  public class ExceptionTranslation
  {
      private static final String prefix = "rabbitescape.engine.";
  
      public static String translate(
          RabbitEscapeException exception, Locale locale )
      {
          String name = exception.getClass().getCanonicalName();
  
          if ( !name.startsWith( prefix ) )
          {
              return name + " " + extractFields( exception ).toString();
          }
  
          String key = stripPrefix( name );
  
          ResourceBundle bundle = ResourceBundle.getBundle(
              "rabbitescape.engine.err.exceptions", locale );
  
          try
          {
              return substituteFields( bundle.getString( key ), exception );
          }
          catch( MissingResourceException e )
          {
              return key + " " + extractFields( exception ).toString();
          }
      }
  
      private static String substituteFields(
          String format,
          RabbitEscapeException exception )
      {
          return NamedFieldFormatter.format( format, extractFields( exception ) );
      }
  
      private static Map<String, Object> extractFields(
          RabbitEscapeException exception )
      {
          // TreeMap for predictable ordering
          Map<String, Object> ret = new TreeMap<>();
  
          for ( Field field : exception.getClass().getFields() )
          {
              ret.put( field.getName(), fieldValue( field, exception ) );
          }
  
          return ret;
      }
  
      private static Object fieldValue(
          Field field,
          RabbitEscapeException exception )
      {
  
          try
          {
              return field.get( exception );
          }
          catch ( IllegalArgumentException e )
          {
              // Should never happen
              e.printStackTrace();
  
              return "<<ERROR FINDING VALUE>>";
          }
          catch ( IllegalAccessException e )
          {
              // Will happen if the field is private
  
              e.printStackTrace();
  
              return "<<ERROR FINDING VALUE - NOT PUBLIC IN EXCEPTION>>";
          }
      }
  
      private static String stripPrefix( String name )
      {
          reAssert( name.startsWith( prefix ) );
  
          return name.substring( prefix.length() );
      }
  }
  