private static void generateStepHelp(StepDescriptor d, PrintWriter pw) throws Exception {
   pw.println("<h2><code>" + d.getFunctionName() + "</code>: " + d.getDisplayName() + "</h2>");
   try {
     generateHelp(DescribableHelper.schemaFor(d.clazz), pw, 3);
   } catch (Exception x) {
     pw.println("<pre><code>" + /*Util.escape(Functions.printThrowable(x))*/ x + "</code></pre>");
   }
 }
 static String object2Groovy(Object o) throws UnsupportedOperationException {
   Class<? extends Object> clazz = o.getClass();
   for (StepDescriptor d : StepDescriptor.all()) {
     if (d.clazz.equals(clazz)) {
       StringBuilder b = new StringBuilder(d.getFunctionName());
       Step step = (Step) o;
       Map<String, Object> args = new TreeMap<String, Object>(d.defineArguments(step));
       boolean first = true;
       boolean singleMap = args.size() == 1 && args.values().iterator().next() instanceof Map;
       for (Map.Entry<String, Object> entry : args.entrySet()) {
         if (first) {
           first = false;
           if (d.takesImplicitBlockArgument() || singleMap) {
             b.append('(');
           } else {
             b.append(' ');
           }
         } else {
           b.append(", ");
         }
         String key = entry.getKey();
         if (args.size() > 1 || !isDefaultKey(step, key)) {
           b.append(key).append(": ");
         }
         render(b, entry.getValue());
       }
       if (d.takesImplicitBlockArgument()) {
         if (!args.isEmpty()) {
           b.append(')');
         }
         b.append(" {\n    // some block\n}");
       } else if (singleMap) {
         b.append(')');
       } else if (args.isEmpty()) {
         b.append("()");
       }
       return b.toString();
     }
   }
   throw new UnsupportedOperationException("Unknown step " + clazz);
 }
 @Override
 public int compare(StepDescriptor o1, StepDescriptor o2) {
   return o1.getFunctionName().compareTo(o2.getFunctionName());
 }