/** * Copyright 2010 Google Inc. * * 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 org.waveprotocol.wave.client.wavepanel.block.js; import org.waveprotocol.wave.client.wavepanel.block.BlockStructure; import org.waveprotocol.wave.client.wavepanel.block.BlockStructure.Node; import org.waveprotocol.wave.client.wavepanel.block.pojo.PojoRenderer; import org.waveprotocol.wave.client.wavepanel.view.ViewIdMapper; import org.waveprotocol.wave.model.conversation.ConversationView; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.IdentityMap; /** * Renders a block tree as Javascript. Evaluating that javascript rebuilds an * identical block tree as JSOs. * * THIS RENDERER ASSUMES THAT BLOCK IDS ARE SAFE (i.e., [a-zA-Z_]*). IF THAT * ASSUMPTION IS INVALID, THEN THE GENERATED JS IS A SECURITY RISK. * */ public final class JsRenderer { /** * Renders the block structure of a collection of conversations as javascript. */ public static String render(ViewIdMapper viewIdMapper, ConversationView model) { BlockStructure v = PojoRenderer.render(viewIdMapper, model); final IdentityMap<Node, String> nodes = CollectionUtils.createIdentityMap(); final StringBuffer decl = new StringBuffer(); final StringBuffer hookup = new StringBuffer(); decl.append("var "); buildNames(nodes, 0, v.getRoot()); nodes.each(new IdentityMap.ProcV<Node, String>() { @Override public void apply(Node node, String name) { render(decl, hookup, nodes, node); } }); decl.append("n;"); decl.append(hookup); return decl.toString(); } private static int buildNames(IdentityMap<Node, String> names, int c, Node n) { names.put(n, "n" + c++); for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) { c = buildNames(names, c, child); } return c; } private static void render(StringBuffer declarations, StringBuffer hookup, IdentityMap<Node, String> names, Node node) { String var = names.get(node); declarations.append(var); declarations.append("={"); declareAttribute(declarations, "i", "" + node.getId()); declareAttribute(declarations, "t", "" + node.getType().ordinal()); declareReference(hookup, var, "f", names.get(node.getFirstChild())); declareReference(hookup, var, "l", names.get(node.getLastChild())); declareReference(hookup, var, "p", names.get(node.getPreviousSibling())); declareReference(hookup, var, "n", names.get(node.getNextSibling())); declareReference(hookup, var, "c", names.get(node.getParent())); hookup.append("\n"); declarations.append("},\n"); } private static void declareAttribute(StringBuffer decl, String name, String value) { decl.append(name); decl.append(":\""); decl.append(value); decl.append(":\";"); } private static void declareReference(StringBuffer hookup, String var, String name, String value) { if (value == null) { return; } hookup.append(var); hookup.append("."); hookup.append(name); hookup.append("="); hookup.append(value); hookup.append(";"); } }