Rick

Rick
Rick

Sunday, June 10, 2012

Implemented Python style formatting in Java

This is pretty early, and well... probably full of bugs... It is just a proof of concept at this point, but seems to work.

Python style template/formatting in Java

  // >>> '{0}, {1}, {2}'.format('a', 'b', 'c')
  // 'a, b, c'
  String str = format("{0}, {1}, {2}", "a", "b", "c");
  print("fmt1", str);
  //fmt is the short form of format
  str = fmt("{0}, {1}, {2}", "a", "b", "c");
  print("fmt2", str);
  // >>> '{}, {}, {}'.format('a', 'b', 'c') # 2.7+ only
  // 'a, b, c'
  str = fmt("{}, {}, {}", "a", "b", "c");
  print("fmt3", str);

  // >>> '{2}, {1}, {0}'.format('a', 'b', 'c')
  // 'c, b, a'
  str = fmt("{2}, {1}, {0}", "a", "b", "c");
  print("fmt4", str);

  // >>> '{2}, {1}, {0}'.format(*'abc') # unpacking argument sequence
  // 'c, b, a'
  str = fmt("{2}, {1}, {0}", array("abc"));
  print("fmt5", str);

  str = fmt("{2}, {1}, {0}", (Object[]) array(1, 2, 3));
  print("fmt6", str);

  str = fmt("{2}, {1}, {0}", oarray(1, 2, 3));
  print("fmt7", str);

  str = fmt("{2}, {1}, {0}", oar(1, 2, 3));
  print("fmt8", str);

  // >>> '{0}{1}{0}'.format('abra', 'cad') # arguments' indices can be
  // repeated
  // 'abracadabra'
  str = fmt("{0}{1}{0}", "abra", "cad");
  print("fmt9", str);
  // >>> 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N',
  // longitude='-115.81W')
  // 'Coordinates: 37.24N, -115.81W'
  str = fmt("Coordinates: {latitude}, {longitude}",
    mp("latitude", "37.24N", "longitude", "-115.81W"));
  print("fmt10", str);
  // >>> "{1:0>+10.3f},{1}".format(2.2,3.0)
  // '0000+3.000,3.0'
  str = fmt("{1:2.2f},{1}", 2.2, 3.0);
  print("fmt11", str);

  str = fmt("Coordinates: {latitude:2.2f}, {longitude}",
    mp("latitude", 37.24, "longitude", -115.81));
  print("fmt12", str);

  str = fmt("{1[0]:2.2f},{1}", 2.2, ls(2.2, 3.0));
  print("fmt13", str);

  str = fmt("0={0},1={1},1.my2={1.my2:2.2f}", 2.2,
    mp("my", 2.2, "my2", 3.0));
  print("fmt14", str);

  str = fmt("{0}, fn={0.firstName}, salary={0.salary:2.2f}",
    emp("Rick", "Hightower", 1000.0));
  print("fmt15", str);

  str = fmt(
    "{0}, fn={emps[0].firstName}, salary={emps[0].salary:2.2f}",
    mp("emps",
      ls(emp("Rick", "Hightower", 1000.0),
        emp("Bob", "Hightower", 7000.0))));
  print("fmt16", str);

  print();
  print();
  str = fmt(
    "a={test}\n" + "b={test.emps}\n" + "c={test.emps[0]}\n"
      + "d={test.emps[0].firstName}\n"
      + "e={test.emps[0].salary:2.2f}",
    mp("test",
      mp("emps",
        ls(emp("Rick", "Hightower", 1000.0),
          emp("Bob", "Hightower", 7000.0)))));
  print("fmt17", str);

...
 static Employee emp(String firstName, String lastName, double salary) {
  return new Employee(firstName, lastName, salary);
 }

 public static class Employee {
  String firstName;
  String lastName;

  public String getFirstName() {
   return firstName;
  }

  public String getLastName() {
   return lastName;
  }

  public void setSalary(double salary) {
   this.salary = salary;
  }

  double salary;

  public Employee(String firstName, String lastName, double salary) {
   super();
   this.firstName = firstName;
   this.lastName = lastName;
   this.salary = salary;
  }

  @Override
  public String toString() {
   return "Employee [firstName=" + firstName + ", lastName="
     + lastName + ", salary=" + salary + "]";
  }

 }

Output

fmt1 a, b, c 
fmt2 a, b, c 
fmt3 a, b, c 
fmt4 c, b, a 
fmt5 c, b, a 
fmt6 3, 2, 1 
fmt7 3, 2, 1 
fmt8 3, 2, 1 
fmt9 abracadabra 
fmt10 Coordinates: 37.24N, -115.81W 
fmt11 3.00,3.0 
fmt12 Coordinates: 37.24, -115.81 
fmt13 2.20,[2.2, 3.0] 
fmt14 0=2.2,1={my2=3.0, my=2.2},1.my2=3.00 
fmt15 Employee [firstName=Rick, lastName=Hightower, salary=1000.0], fn=Rick, salary=1000.00 
fmt16 null, fn=Rick, salary=1000.00 
fmt17 a={emps=[Employee [firstName=Rick, lastName=Hightower, salary=1000.0], Employee [firstName=Bob, lastName=Hightower, salary=7000.0]]}
b=[Employee [firstName=Rick, lastName=Hightower, salary=1000.0], Employee [firstName=Bob, lastName=Hightower, salary=7000.0]]
c=Employee [firstName=Rick, lastName=Hightower, salary=1000.0]
d=Rick
e=1000.00 

Implementation

 public static String mfmt(String str, Map<?, ?> map) {
  return mapFormat(str, map);
 }
 public static String mapFormat(String str, Map<?, ?> map) {

  StringBuilder builder = new StringBuilder(str.length());
  StringBuilder contents = new StringBuilder(32);
  StringBuilder formatRulesContents = new StringBuilder(32);

  
  char[] charArray = str.toCharArray();
  for (int index=0; index<charArray.length; index++) {
   char c = charArray[index];
   if (c=='{') {
    contents.setLength(0);
    formatRulesContents.setLength(0);
    index++;
    boolean formatRules=false;
    
    for (;index<charArray.length && c!='}'; index++) {
     c = charArray[index];
     if (c=='[' ||  c=='!' || c==':' || c=='.') {
      formatRules=true;
     }
     if (c!='}') {
      if (!formatRules) {
       contents.append(c);
      } else {
       formatRulesContents.append(c);
      }
     }
    }
     if (!formatRules) {
         builder.append(map.get(contents.toString()));
     } else {
      builder.append(formatRule(map.get(contents.toString()), formatRulesContents.toString()));
    }

    index--;
   } else {
    builder.append(c);
   }
  }
  return builder.toString();
 }

 public static String fmt(String str, Object... args) {
  return format(str, args);
 }
 public static String format(String str, Object... args) {
  
  if (args.length==1) {
   if (args[0] instanceof char[]) {
    return cformat(str, (char[]) args[0]);
   } else if (args[0] instanceof Map) {
    return mapFormat(str, (Map<?,?>)args[0]);
   } else {
    return arrayFormat(str, args);
   }
  } else {
   return arrayFormat(str, args);
  }
 }

 public static String arfmt(String str, Object... args) {
  return format(str, args);
 }
 public static String arrayFormat(String str, Object... args) {

  StringBuilder builder = new StringBuilder(str.length());
  StringBuilder contents = new StringBuilder(32);
  StringBuilder formatRulesContents = new StringBuilder(32);
  
  int argIndexKeeper = 0;
  int argIndex=-666;
  
  char[] charArray = str.toCharArray();
  for (int index=0; index<charArray.length; index++) {
   char c = charArray[index];
   if (c=='{') {
    contents.setLength(0);
    formatRulesContents.setLength(0);
    index++;
    boolean formatRules=false;
    
    for (;index<charArray.length && c!='}'; index++) {
     c = charArray[index];
     if (c=='[' ||  c=='!' || c==':' || c=='.') {
      formatRules=true;
     }
     if (c!='}') {
      if (!formatRules) {
       contents.append(c);
      } else {
       formatRulesContents.append(c);
      }
     }
    }
    
    String sindex = contents.toString().trim();
    if (sindex.isEmpty()) {
     argIndex = argIndexKeeper;
     if (!formatRules) {
      builder.append(args[argIndex].toString());
     } else {
      builder.append(formatRule(args[argIndex], formatRulesContents.toString()));
     }
     argIndexKeeper++;
    } else {
      argIndex = Integer.parseInt(sindex);
      if (!formatRules) {
       builder.append(args[argIndex].toString());
      } else {
       builder.append(formatRule(args[argIndex], formatRulesContents.toString()));
     }
    }
    index--;
   } else {
    builder.append(c);
   }
  }
  return builder.toString();
 }

 
 private static String formatRule(Object object, String rule) {
  StringBuilder format = new StringBuilder(16);
  StringBuilder idx = new StringBuilder(16);
  List <Object> idxs  = new ArrayList<Object>(16);
  char[] charArray = rule.toCharArray();
  
  boolean hasFormat = false;
  boolean hasIndex = false;
  boolean hasDotIndex = false;
  
  for (int index=0; index< charArray.length; index++) {
   char c = charArray[index];
   
   
   if(hasFormat) {
    format.append(c);
   } else if (hasIndex) {
    if (c == ']') {
     idxs.add(Integer.parseInt(idx.toString()));
     hasIndex = false;
    } else {
     idx.append(c);
    }
   } else if (hasDotIndex) {
    if (c=='['  || c == '.' || c == ':') {
     idxs.add(idx.toString());
     hasDotIndex = false;
    } else {
     idx.append(c);
     if  (index+1 == charArray.length) {
      idxs.add(idx.toString());
      hasDotIndex = false;      
     }
    }
   }

   if (c == ':') {
    hasFormat = true;
    format.setLength(0);
   } else if (c == '[') {
    hasIndex = true;
    idx.setLength(0);   
   } else if (c == '.') {
    hasDotIndex = true;
    idx.setLength(0);   
   }

  }
  if (hasFormat) {
   format.insert(0, '%');
  }
  
  for (Object oindex : idxs) {
   if (oindex instanceof Integer) {
    int index = (Integer)oindex;
    if (object.getClass().isArray()) {
     object = Array.get(object, index);
    } else if (object instanceof List) {
     object = index(((List<?>)object), index);
    } else {
     object = null;
    }
   } else {
    String key = (String)oindex;
    if (object instanceof Map) {
     @SuppressWarnings("unchecked")
     Map <String,?> map = (Map<String, ?>) object;
     object = map.get(key);
    } else {
     object.getClass().getDeclaredMethods();
     Class<? extends Object> clz = object.getClass();
     outer:
     while (clz!=Object.class) {
      Method[] methods = clz.getDeclaredMethods();
      for (Method method : methods) {
       method.setAccessible(true);
       if (method.getParameterTypes().length==0 && method.getName().toLowerCase().endsWith(key.toLowerCase())
         && (method.getName().startsWith("is") || method.getName().startsWith("get") || 
           method.getName().length()==key.length())
         ) {
        try {
         object = method.invoke(object, (Object[])null);
         break outer;
        } catch (Exception ex) {
         continue;
        }
       }
      }
      Field[] declaredFields = clz.getDeclaredFields();
      for (Field field : declaredFields) {
       field.setAccessible(true);
       if (field.getName().equals(key)) {
        try {
         object = field.get(object);
         break outer;
        } catch (Exception ex) {
         break;
        }
       }
      }
      
      clz = clz.getSuperclass();
     }
    }
   }
  }
  if (object==null) {
   return "";
  } else if (hasFormat) {
   return String.format(format.toString(), object);
  } else {
   return object.toString();
  }
  
 }

 public static String cfmt(String str, char... args) {
  return cformat(str, args);
 }
 public static String cformat(String str, char... args) {
  StringBuilder builder = new StringBuilder(str.length());
  StringBuilder contents = new StringBuilder(32);
  
  int argIndexKeeper = 0;
  int argIndex=-666;
  
  char[] charArray = str.toCharArray();
  for (int index=0; index<charArray.length; index++) {
   char c = charArray[index];
   if (c=='{') {
    contents.setLength(0);
    index++;
    
    for (;index<charArray.length && c!='}'; index++) {
     c = charArray[index];
     if (c!='}') {
      contents.append(c);
     }
    }
    
    String sindex = contents.toString().trim();
    if (sindex.isEmpty()) {
     argIndex = argIndexKeeper;
     builder.append(args[argIndex]);
     argIndexKeeper++;
    } else {
      argIndex = Integer.parseInt(sindex);
      builder.append(args[argIndex]);
    }
    index--;
   } else {
    builder.append(c);
   }
  }
  return builder.toString();
 }

No comments:

Post a Comment

Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training