// Copyright 2012 Google Inc. All Rights Reserved. // // 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 com.google.collide.client.code.debugging; import com.google.collide.client.code.debugging.DebuggerApiTypes.RemoteObject; import com.google.collide.client.code.debugging.DebuggerApiTypes.RemoteObjectType; import com.google.collide.client.ui.tree.NodeRenderer; import com.google.collide.client.ui.tree.TreeNodeElement; import com.google.collide.client.util.CssUtils; import com.google.collide.client.util.Elements; import com.google.collide.client.util.dom.DomUtils; import com.google.collide.shared.util.StringUtils; import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import elemental.html.Element; import elemental.html.SpanElement; /** * Renders the {@link RemoteObjectNode} in the {@link RemoteObjectTree}. * */ public class RemoteObjectNodeRenderer implements NodeRenderer<RemoteObjectNode> { private static final RegExp LINE_BREAK_REGEXP = RegExp.compile("\\r?\\n", "g"); private static final String LINE_BREAK_SUBSTITUTE = "\u21B5"; public interface Css extends CssResource { String root(); String propertyName(); String separator(); String propertyValue(); String wasThrown(); String noPropertyValue(); String notEnumerable(); String tokenArray(); String tokenBoolean(); String tokenDate(); String tokenFunction(); String tokenNode(); String tokenNull(); String tokenNumber(); String tokenObject(); String tokenRegexp(); String tokenString(); String tokenUndefined(); String inPropertyValueMutation(); } interface Resources extends ClientBundle { @Source("RemoteObjectNodeRenderer.css") Css remoteObjectNodeRendererCss(); } private final Css css; RemoteObjectNodeRenderer(Resources resources) { css = resources.remoteObjectNodeRendererCss(); } @Override public Element getNodeKeyTextContainer(SpanElement treeNodeLabel) { return treeNodeLabel.getFirstChildElement(); } @Override public SpanElement renderNodeContents(RemoteObjectNode data) { SpanElement root = Elements.createSpanElement(css.root()); if (data.wasThrown()) { root.addClassName(css.wasThrown()); } Element propertyNameElement = Elements.createDivElement(css.propertyName()); if (data.getRemoteObject() == null) { propertyNameElement.addClassName(css.noPropertyValue()); } if (!data.isEnumerable()) { propertyNameElement.addClassName(css.notEnumerable()); } propertyNameElement.setTextContent(data.getName()); root.appendChild(propertyNameElement); String propertyValue = getPropertyValueAsString(data); if (!StringUtils.isNullOrEmpty(propertyValue)) { if (!StringUtils.isNullOrEmpty(data.getName())) { Element separator = Elements.createDivElement(css.separator()); separator.setTextContent(":"); root.appendChild(separator); } Element propertyValueElement = Elements.createDivElement(css.propertyValue(), getTokenClassName(data.getRemoteObject())); propertyValueElement.setTextContent(propertyValue); root.appendChild(propertyValueElement); } return root; } @Override public void updateNodeContents(TreeNodeElement<RemoteObjectNode> treeNode) { // Not implemented. } Element getPropertyValueElement(SpanElement treeNodeLabel) { return DomUtils.getFirstElementByClassName(treeNodeLabel, css.propertyValue()); } Element getAncestorPropertyNameElement(Element element) { return CssUtils.getAncestorOrSelfWithClassName(element, css.propertyName()); } Element getAncestorPropertyValueElement(Element element) { return CssUtils.getAncestorOrSelfWithClassName(element, css.propertyValue()); } void enterPropertyValueMutation(SpanElement treeNodeLabel) { treeNodeLabel.addClassName(css.inPropertyValueMutation()); } void exitPropertyValueMutation(SpanElement treeNodeLabel, String newLabel) { treeNodeLabel.removeClassName(css.inPropertyValueMutation()); Element propertyValueElement = getPropertyValueElement(treeNodeLabel); if (propertyValueElement != null) { propertyValueElement.setTextContent(StringUtils.nullToEmpty(newLabel)); } } void removeTokenClassName(RemoteObjectNode data, SpanElement treeNodeLabel) { Element propertyValueElement = getPropertyValueElement(treeNodeLabel); if (propertyValueElement != null) { String tokenClassName = getTokenClassName(data.getRemoteObject()); if (!StringUtils.isNullOrEmpty(tokenClassName)) { propertyValueElement.removeClassName(tokenClassName); } } } public String getTokenClassName(RemoteObject remoteObject) { if (remoteObject == null) { return ""; } if (remoteObject.getSubType() != null) { switch (remoteObject.getSubType()) { case ARRAY: return css.tokenArray(); case DATE: return css.tokenDate(); case NODE: return css.tokenNode(); case NULL: return css.tokenNull(); case REGEXP: return css.tokenRegexp(); } } if (remoteObject.getType() != null) { switch (remoteObject.getType()) { case BOOLEAN: return css.tokenBoolean(); case FUNCTION: return css.tokenFunction(); case NUMBER: return css.tokenNumber(); case OBJECT: return css.tokenObject(); case STRING: return css.tokenString(); case UNDEFINED: return css.tokenUndefined(); } } return ""; } private static String getPropertyValueAsString(RemoteObjectNode node) { RemoteObject remoteObject = node.getRemoteObject(); if (remoteObject == null) { return ""; } if (node.wasThrown()) { return "[Exception: " + remoteObject.getDescription() + "]"; } if (node.isTransient()) { // Just put some UI difference for the transient objects. return ""; } if (RemoteObjectType.STRING.equals(remoteObject.getType())) { return "\"" + LINE_BREAK_REGEXP.replace(remoteObject.getDescription(), LINE_BREAK_SUBSTITUTE) + "\""; } return remoteObject.getDescription(); } }