--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/util/JsValueStringifier.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,193 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.SortedMap;
+
+import org.chromium.sdk.JsArray;
+import org.chromium.sdk.JsObject;
+import org.chromium.sdk.JsValue;
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.JsValue.Type;
+
+/**
+ * A converter of JsValues into human-readable strings used in various contexts.
+ */
+public class JsValueStringifier {
+
+ /**
+ * A configuration class
+ */
+ public static class Config {
+
+ /**
+ * The maximum length of the text to render. If the value length exceeds
+ * the limit, an ellipsis will be used to truncate the value.
+ * The default is 80 characters.
+ */
+ public int maxLength = 80;
+ }
+
+ private static final String ELLIPSIS = "..."; //$NON-NLS-1$
+
+ private static final String UNKNOWN_VALUE = "<null>"; //$NON-NLS-1$
+
+ private final Config config;
+
+
+ /**
+ * Constructs a visible string for the given {@code value} (without exposing
+ * the value structure). Encloses JavaScript string values in double quotes.
+ *
+ * @param value to build a visible string for
+ * @return {@code value.getValueString()} (enclosed in double quotes if
+ * {@code value.getType() == TYPE_STRING}), or {@code null} if
+ * {@code value==null}
+ */
+ public static String toVisibleString(JsValue value) {
+ return possiblyQuoteValueString(value);
+ }
+
+ private static String possiblyQuoteValueString(JsValue value) {
+ if (value == null) {
+ return UNKNOWN_VALUE;
+ }
+ String valueString = value.getValueString();
+ return value.getType() == JsValue.Type.TYPE_STRING
+ ? "\"" + valueString + "\"" //$NON-NLS-1$ //$NON-NLS-2$
+ : valueString;
+ }
+
+ /**
+ * Use the default config values.
+ */
+ public JsValueStringifier() {
+ this.config = new Config();
+ }
+
+ /**
+ * Use the specified {@code config} data.
+ * @param config to use when rendering values.
+ */
+ public JsValueStringifier(Config config) {
+ this.config = config;
+ }
+
+ public String render(JsValue value) {
+ if (value == null) {
+ return UNKNOWN_VALUE;
+ }
+ StringBuilder output = new StringBuilder();
+ renderInternal(value, config.maxLength, true, output);
+ return output.toString();
+ }
+
+ /**
+ * @param value to render
+ * @param maxLength the maximum length of the {@code output}
+ * @param descend whether to descend into the object contents
+ * @param output to render into
+ */
+ private void renderInternal(JsValue value, int maxLength, boolean descend, StringBuilder output) {
+ if (!descend) {
+ renderPrimitive(value, maxLength, output);
+ return;
+ }
+ Type type = value.getType();
+ // TODO(apavlov): implement good stringification of other types?
+ switch (type) {
+ case TYPE_ARRAY:
+ renderArray(value.asObject().asArray(), maxLength, output);
+ break;
+ case TYPE_OBJECT:
+ renderObject(value.asObject(), maxLength, output);
+ break;
+ default:
+ renderPrimitive(value, maxLength, output);
+ break;
+ }
+ }
+
+ private void renderPrimitive(JsValue value, int maxLength, StringBuilder output) {
+ output.append(possiblyQuoteValueString(value));
+ truncate(output, maxLength, ELLIPSIS);
+ }
+
+ private void truncate(StringBuilder valueBuilder, int maxLength, String suffix) {
+ int length = valueBuilder.length();
+ if (length > maxLength) {
+ valueBuilder.setLength(maxLength);
+ valueBuilder.replace(
+ maxLength - suffix.length(), maxLength, suffix);
+ }
+ }
+
+ private StringBuilder renderArray(JsArray value, int maxLength, StringBuilder output) {
+ output.append('[');
+ SortedMap<Integer, ? extends JsVariable> indexToElement = value.toSparseArray();
+ boolean isFirst = true;
+ int maxLengthWithoutLastBracket = maxLength - 1;
+ StringBuilder elementBuilder = new StringBuilder();
+ int entriesWritten = 0;
+ for (Map.Entry<Integer, ? extends JsVariable> entry : indexToElement.entrySet()) {
+ Integer index = entry.getKey();
+ JsVariable var = entry.getValue();
+ if (!isFirst) {
+ output.append(',');
+ } else {
+ isFirst = false;
+ }
+ elementBuilder.setLength(0);
+ elementBuilder.append(index).append('=');
+ renderInternal(var.getValue(), maxLengthWithoutLastBracket /* essentially, no limit */,
+ false, elementBuilder);
+ if (output.length() + elementBuilder.length() >= maxLengthWithoutLastBracket) {
+ // reached max length
+ appendNMore(output, indexToElement.size() - entriesWritten);
+ break;
+ } else {
+ output.append(elementBuilder.toString());
+ entriesWritten++;
+ }
+ }
+ return output.append(']');
+ }
+
+ private StringBuilder renderObject(JsObject value, int maxLength, StringBuilder output) {
+ output.append('[');
+ Collection<? extends JsVariable> properties = value.getProperties();
+ boolean isFirst = true;
+ int maxLengthWithoutLastBracket = maxLength - 1;
+ StringBuilder elementBuilder = new StringBuilder();
+ int entriesWritten = 0;
+ for (JsVariable property : properties) {
+ String name = property.getName();
+ if (!isFirst) {
+ output.append(',');
+ } else {
+ isFirst = false;
+ }
+ elementBuilder.setLength(0);
+ elementBuilder.append(name).append('=');
+ renderInternal(property.getValue(), maxLengthWithoutLastBracket /* essentially, no limit */,
+ false, elementBuilder);
+ if (output.length() + elementBuilder.length() >= maxLengthWithoutLastBracket) {
+ // reached max length
+ appendNMore(output, properties.size() - entriesWritten);
+ break;
+ } else {
+ output.append(elementBuilder.toString());
+ entriesWritten++;
+ }
+ }
+ return output.append(']');
+ }
+
+ private StringBuilder appendNMore(StringBuilder output, int n) {
+ return output.append(" +").append(n).append(ELLIPSIS); //$NON-NLS-1$
+ }
+}
\ No newline at end of file