| // Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | List sorted(Iterable input, [compare, key]) { | 
 |   comparator(compare, key) { | 
 |     if (compare == null && key == null) | 
 |       return (a, b) => a.compareTo(b); | 
 |     if (compare == null) | 
 |       return (a, b) => key(a).compareTo(key(b)); | 
 |     if (key == null) | 
 |       return compare; | 
 |     return (a, b) => compare(key(a), key(b)); | 
 |   } | 
 |   List copy = new List.from(input); | 
 |   copy.sort(comparator(compare, key)); | 
 |   return copy; | 
 | } | 
 |  | 
 | render(idl_node, [indent_str='  ']) { | 
 |   var output = ['']; | 
 |   var indent_stack = []; | 
 |  | 
 |   indented(action) {  // TODO: revert to  indented(action()) { | 
 |     indent_stack.add(indent_str); | 
 |     action(); | 
 |     indent_stack.removeLast(); | 
 |   } | 
 |  | 
 |   sort(nodes) => sorted(nodes, key: (a) => a.id); | 
 |  | 
 |   var w; // For some reason mutually recursive local functions don't work. | 
 |  | 
 |   wln([node]) { | 
 |     w(node); | 
 |     output.add('\n'); | 
 |   } | 
 |  | 
 |   w = (node, [list_separator]) { | 
 |     /* | 
 |      Writes the given node. | 
 |  | 
 |     Args: | 
 |       node -- a string, IDLNode instance or a list of such. | 
 |       list_separator -- if provided, and node is a list, | 
 |         list_separator will be written between the list items. | 
 |     */ | 
 |     if (node == null) { | 
 |       return; | 
 |     } else if (node is String) { | 
 |       if (output.last().endsWith('\n')) | 
 |         output.addAll(indent_stack); | 
 |       output.add(node); | 
 |     } else if (node is List) { | 
 |       var separator = null; | 
 |       for (var element in node) { | 
 |         w(separator); | 
 |         separator = list_separator; | 
 |         w(element); | 
 |       } | 
 |     } else if (node is IDLFile) { | 
 |       w(node.modules); | 
 |       w(node.interfaces); | 
 |     } else if (node is IDLModule) { | 
 |       w(node.annotations); | 
 |       w(node.extAttrs); | 
 |       wln('module ${node.id} {'); | 
 |       indented(() { | 
 |           w(node.interfaces); | 
 |           w(node.typedefs); | 
 |         }); | 
 |       wln('};'); | 
 |     } else if (node is IDLInterface) { | 
 |       w(node.annotations); | 
 |       w(node.extAttrs); | 
 |       w('interface ${node.id}'); | 
 |       indented(() { | 
 |           if (!node.parents.isEmpty()) { | 
 |             wln(' :'); | 
 |             w(node.parents, ',\n'); | 
 |           } | 
 |           wln(' {'); | 
 |           section(list, comment) { | 
 |             if (list != null && !list.isEmpty()) { | 
 |               wln(); | 
 |               wln(comment); | 
 |               w(sort(list)); | 
 |             } | 
 |           } | 
 |           section(node.constants, '/* Constants */'); | 
 |           section(node.attributes, '/* Attributes */'); | 
 |           section(node.operations, '/* Operations */'); | 
 |           section(node.snippets, '/* Snippets */'); | 
 |         }); | 
 |       wln('};'); | 
 |     } else if (node is IDLParentInterface) { | 
 |       w(node.annotations); | 
 |       w(node.type.id); | 
 |     } else if (node is IDLAnnotations) { | 
 |       for (var name in sorted(node.map.getKeys())) { | 
 |         IDLAnnotation annotation = node.map[name]; | 
 |         var args = annotation.map; | 
 |         if (args.isEmpty()) { | 
 |           w('@$name'); | 
 |         } else { | 
 |           var formattedArgs = []; | 
 |           for (var argName in sorted(args.getKeys())) { | 
 |             var argValue = args[argName]; | 
 |             if (argValue == null) | 
 |               formattedArgs.add(argName); | 
 |             else | 
 |               formattedArgs.add('$argName=$argValue'); | 
 |           } | 
 |           w('@$name(${Strings.join(formattedArgs,',')})'); | 
 |         } | 
 |         w(' '); | 
 |       } | 
 |     } else if (node is IDLExtAttrs) { | 
 |       if(!node.map.isEmpty()) { | 
 |         w('['); | 
 |         var sep = null; | 
 |         for (var name in sorted(node.map.getKeys())) { | 
 |           w(sep); | 
 |           sep = ', '; | 
 |           w(name); | 
 |           var value = node.map[name]; | 
 |           if (value != null) { | 
 |             w('='); | 
 |             w(value); | 
 |           } | 
 |         } | 
 |         w('] '); | 
 |       } | 
 |     } else if (node is IDLAttribute) { | 
 |       w(node.annotations); | 
 |       w(node.extAttrs); | 
 |       //if (node.isFcGetter) | 
 |       //  w('getter '); | 
 |       //if (node.isFcSetter) | 
 |       //  w('setter '); | 
 |       wln('attribute ${node.type.id} ${node.id};'); | 
 |     } else if (node is IDLConstant) { | 
 |       w(node.annotations); | 
 |       w(node.extAttrs); | 
 |       wln('const ${node.type.id} ${node.id} = ${node.value};'); | 
 |     } else if (node is IDLSnippet) { | 
 |       w(node.annotations); | 
 |       wln('snippet {${node.text}};'); | 
 |     } else if (node is IDLOperation) { | 
 |       w(node.annotations); | 
 |       w(node.extAttrs); | 
 |       if (node.specials != null && !node.specials.isEmpty()) { | 
 |         w(node.specials, ' '); | 
 |         w(' '); | 
 |       } | 
 |       w('${node.type.id} ${node.id}'); | 
 |       w('('); | 
 |       w(node.arguments, ', '); | 
 |       wln(');'); | 
 |     } else if (node is IDLArgument) { | 
 |       w(node.extAttrs); | 
 |       w('in '); | 
 |       if (node.isOptional) | 
 |         w('optional '); | 
 |       w('${node.type.id} ${node.id}'); | 
 |     } else if (node is IDLExtAttrFunctionValue) { | 
 |       w(node.name); | 
 |       w('('); | 
 |       w(node.arguments, ', '); | 
 |       w(')'); | 
 |     } else if (node is IDLTypeDef) { | 
 |       wln('typedef ${node.type.id} ${node.id};'); | 
 |     } else { | 
 |       w('// $node\n'); | 
 |     } | 
 |   }; | 
 |  | 
 |   w(idl_node); | 
 |   return Strings.concatAll(output); | 
 | } |