content: /*
   * Copyright 2016 HuntBugs contributors
   * 
   * 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 one.util.huntbugs.warning;
  
  import com.strobel.assembler.metadata.TypeReference;
  import com.strobel.core.StringUtilities;
  
  import one.util.huntbugs.warning.WarningAnnotation.MemberInfo;
  import one.util.huntbugs.warning.WarningAnnotation.TypeInfo;
  
  /**
   * @author Tagir Valeev
   *
   */
  public class Formatter {
      public static final String FORMAT_PLAIN = "plain";
      public static final String FORMAT_TYPE_PLAIN = "type.plain";
      public static final String FORMAT_HTML = "html";
      public static final String FORMAT_TYPE_HTML = "type.html";
      public static final String FORMAT_NAME = "name";
      public static final String FORMAT_HEX = "hex";
      public static final String FORMAT_DEC = "dec";
      public static final String FORMAT_CONST = "const";
  
      private final Messages msgs;
  
      public Formatter() {
          this(Messages.load());
      }
  
      public Formatter(Messages msgs) {
          this.msgs = msgs;
      }
  
      public String getTitle(Warning warning) {
          return msgs.getMessagesForType(warning.getType()).getTitle();
      }
  
      public String getDescription(Warning warning) {
          return format(msgs.getMessagesForType(warning.getType()).getDescription(), warning, FORMAT_PLAIN);
      }
  
      public String getLongDescription(Warning warning) {
          return format(msgs.getMessagesForType(warning.getType()).getLongDescription(), warning, FORMAT_HTML);
      }
  
      private String format(String description, Warning warning, String format) {
          String[] fields = description.split("\\$", -1);
          if (fields.length == 1)
              return description;
          StringBuilder result = new StringBuilder(fields[0]);
          for (int i = 1; i < fields.length; i++) {
              if (i % 2 == 0) {
                  result.append(fields[i]);
              } else {
                  String key = fields[i];
                  String f = format;
                  int pos = key.indexOf(':');
                  if (pos > 0) {
                      f = key.substring(pos + 1);
                      key = key.substring(0, pos);
                  }
                  WarningAnnotation<?> anno = warning.getAnnotation(key);
                  if (anno == null) {
                      result.append('(').append(key).append(')');
                  } else {
                      result.append(formatValue(anno.getValue(), f));
                  }
              }
          }
          return result.toString();
      }
  
      public static String formatValue(Object value, String format) {
          if (value instanceof MemberInfo) {
              return formatMemberInfo((MemberInfo) value, format);
          }
          if (value instanceof TypeInfo) {
              return formatTypeInfo((TypeInfo) value, format);
          }
          if (value instanceof Double) {
              return formatDouble((Double) value);
          }
          if (value instanceof Float) {
              return formatFloat((Float) value);
          }
          if (value instanceof Integer) {
              return formatInteger((Integer) value, format);
          }
          if (value instanceof Long) {
              return formatLong((Long) value, format);
          }
          if (value instanceof String) {
              return formatString((String) value, format);
          }
          return String.valueOf(value);
      }
  
      private static String formatString(String value, String format) {
          if (format.equals("const")) {
              return StringUtilities.escape(value, true);
          }
          return value;
      }
  
      private static String formatLong(long value, String format) {
          if (format.equals(FORMAT_HEX)) {
              return "0x" + Long.toHexString(value);
          }
          if (format.equals(FORMAT_DEC)) {
              return Long.toString(value);
          }
          if (value == Long.MIN_VALUE) {
              return "Long.MIN_VALUE";
          }
          if (value == Long.MAX_VALUE) {
              return "Long.MAX_VALUE";
          }
          if (value == Integer.MIN_VALUE) {
              return "Integer.MIN_VALUE";
          }
          if (value == Integer.MAX_VALUE) {
              return "Integer.MAX_VALUE";
          }
          if (Math.abs(value) >= 0xFF
              && (Long.bitCount(value)+1 < Long.toString(value).length() || 66 - Long.numberOfLeadingZeros(value)
                  - Long.bitCount(value) < Long.toString(value).length())) {
              return "0x" + Long.toHexString(value);
          }
          return Long.toString(value);
      }
  
      private static String formatInteger(int value, String format) {
          if (format.equals(FORMAT_HEX)) {
              return "0x" + Integer.toHexString(value);
          }
          if (format.equals(FORMAT_DEC)) {
              return Integer.toString(value);
          }
          if (value == Integer.MIN_VALUE) {
              return "Integer.MIN_VALUE";
          }
          if (value == Integer.MAX_VALUE) {
              return "Integer.MAX_VALUE";
          }
          return Integer.toString(value);
      }
  
      private static String formatFloat(float val) {
          if (Float.isNaN(val))
              return "Float.NaN";
          if (val == Float.POSITIVE_INFINITY)
              return "Float.POSITIVE_INFINITY";
          if (val == Float.NEGATIVE_INFINITY)
              return "Float.NEGATIVE_INFINITY";
          if (val == Float.MIN_VALUE)
              return "Float.MIN_VALUE";
          if (val == Float.MAX_VALUE)
              return "Float.MAX_VALUE";
          if (val == -Float.MIN_VALUE)
              return "-Float.MIN_VALUE";
          if (val == -Float.MAX_VALUE)
              return "-Float.MAX_VALUE";
          return Float.toString(val);
      }
  
      private static String formatDouble(double val) {
          if (Double.isNaN(val))
              return "Double.NaN";
          if (val == Double.POSITIVE_INFINITY)
              return "Double.POSITIVE_INFINITY";
          if (val == Double.NEGATIVE_INFINITY)
              return "Double.NEGATIVE_INFINITY";
          if (val == Double.MIN_VALUE)
              return "Double.MIN_VALUE";
          if (val == Double.MAX_VALUE)
              return "Double.MAX_VALUE";
          if (val == -Double.MIN_VALUE)
              return "-Double.MIN_VALUE";
          if (val == -Double.MAX_VALUE)
              return "-Double.MAX_VALUE";
          return Double.toString(val);
      }
  
      private static String formatTypeInfo(TypeInfo ti, String format) {
          String simpleName = ti.getSimpleName();
          String result = simpleName;
          if (format.equals(FORMAT_HTML))
              return "<code class=\"Member\" title=\"" + ti + "\">" + result + "</code>";
          return result;
      }
  
      private static String formatMemberInfo(MemberInfo mi, String format) {
          if (format.equals(FORMAT_NAME))
              return mi.getName();
          if(format.startsWith("type."))
              return formatTypeInfo(mi.getType(), format.substring("type.".length()));
          if(format.startsWith("return_type."))
              return formatTypeInfo(mi.getReturnType(), format.substring("return_type.".length()));
          String type = mi.getTypeName();
          int pos = type.lastIndexOf('/');
          if (pos > -1)
              type = type.substring(pos + 1).replace('$', '.');
          String result;
          if (mi.isMethod()) {
              if (mi.getName().equals("<init>"))
                  result = "new " + type + "()";
              else if (mi.getName().equals("<clinit>"))
                  result = type + " static {}";
              else
                  result = type + "." + mi.getName() + "()";
          } else {
              result = type + "." + mi.getName();
          }
          if (format.equals(FORMAT_HTML))
              return "<code class=\"Member\" title=\"" + mi + "\">" + result + "</code>";
          return result;
      }
  
      public static String formatConstant(Object constant) {
          if(constant instanceof String) {
              return StringUtilities.escape((String)constant, true);
          }
          if(constant instanceof TypeReference) {
              return ((TypeReference)constant).getSimpleName()+".class";
          }
          if(constant instanceof Number) {
              return formatValue(constant, FORMAT_PLAIN);
          }
          return String.valueOf(constant);
      }
  }
  