diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/CliCommandCatalog.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/CliCommandCatalog.java index c324906d..3f110c98 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/CliCommandCatalog.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/CliCommandCatalog.java @@ -309,10 +309,10 @@ private static Map definitions() "Read basic heap metadata such as object counts and used heap.", //$NON-NLS-1$ "mat-cli summary [--format text|json]", true, heapArgument, FORMAT_OPTION, //$NON-NLS-1$ Arrays.asList(output("text", "summary", "Human-readable heap summary."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "summary", "Stable summary JSON envelope.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "summary", "Compact mat-cli/v1 summary JSON envelope.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("histogram ", "top-consumers ", //$NON-NLS-1$ //$NON-NLS-2$ "query --command \"thread_overview\""), //$NON-NLS-1$ - "summary object with snapshot-wide counters and heap metadata.", //$NON-NLS-1$ + "compact summary object with snapshot-wide counters and heap metadata, omitting absent strings.", //$NON-NLS-1$ Arrays.asList("summary.path", "summary.heapFormat", "summary.numberOfObjects", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "summary.numberOfClasses", "summary.usedHeapSize"))); //$NON-NLS-1$ //$NON-NLS-2$ definitions.put(CliCommand.THREADS, new CommandDefinition(CliCommand.THREADS, @@ -321,22 +321,22 @@ private static Map definitions() Arrays.asList(freeTextOption("--limit", "N", false, "Limit threads returned. Defaults to all threads."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ enumOption("--format", "text|json", false, "Select text or JSON output.", FORMAT_VALUES)), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList(output("text", "threads", "Thread report with overview plus per-thread stack sections."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "threads", "Stable thread report JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "threads", "Compact mat-cli/v1 thread report JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("histogram ", "top-consumers "), //$NON-NLS-1$ //$NON-NLS-2$ - "thread report payload with a summary, best-effort notice, and one entry per returned thread.", //$NON-NLS-1$ + "thread report payload with a summary, optional notice, and one compact entry per returned thread.", //$NON-NLS-1$ Arrays.asList("notice", "summary.totalThreads", "summary.returnedThreads", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "summary.stackAvailableThreads", "summary.stateAvailableThreads", "threads[]", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "threads[].name", "threads[].technicalName", "threads[].objectAddress", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "threads[].state", "threads[].retainedBytes", "threads[].stackAvailable", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "threads[].stackUnavailableReason", "threads[].stackFrames[]"))); //$NON-NLS-1$ //$NON-NLS-2$ + "threads[].stackUnavailableReason when unavailable", "threads[].stackFrames[] when non-empty"))); //$NON-NLS-1$ //$NON-NLS-2$ definitions.put(CliCommand.HISTOGRAM, new CommandDefinition(CliCommand.HISTOGRAM, "Group objects by class and report shallow heap plus approximate retained heap.", //$NON-NLS-1$ "mat-cli histogram [--limit N] [--format text|json]", true, heapArgument, //$NON-NLS-1$ FORMAT_AND_LIMIT_OPTIONS, Arrays.asList(output("text", "table", "Text table with formatted columns."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "table", "Stable keyed table JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "table", "Compact keyed table JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("top-consumers ", "query --command \"histogram\""), //$NON-NLS-1$ //$NON-NLS-2$ - "table payload with stable column ids, normalized byte values, optional row addresses, and per-cell metadata for approximate retained sizes.", //$NON-NLS-1$ + "table payload with normalized column ids, non-null cell values only, optional row addresses, and per-cell metadata for approximate retained sizes.", //$NON-NLS-1$ Arrays.asList("items[]", "items[]._address when no address column", //$NON-NLS-1$ //$NON-NLS-2$ "items[]._meta.retained_heap.kind when approximate"))); //$NON-NLS-1$ definitions.put(CliCommand.INSTANCES, new CommandDefinition(CliCommand.INSTANCES, @@ -344,9 +344,9 @@ private static Map definitions() "mat-cli instances [--class | --class-regex | --class-contains ] [--include-subclasses] [--limit N] [--format text|json]", //$NON-NLS-1$ true, heapArgument, FORMAT_LIMIT_CLASS_OPTIONS, Arrays.asList(output("text", "table", "Text table of matching objects."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "table", "Stable keyed table JSON for matching objects.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "table", "Compact keyed table JSON for matching objects.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("inspect-object --object 0x...", "path2gc --object 0x..."), //$NON-NLS-1$ //$NON-NLS-2$ - "table payload listing matching objects with addresses, previews, and heap sizes.", //$NON-NLS-1$ + "table payload listing matching objects with non-null fields, addresses, previews, and heap sizes.", //$NON-NLS-1$ Arrays.asList("items[]", "items[].object_address", "items[].class_name", "items[].preview"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ definitions.put(CliCommand.INSPECT_OBJECT, new CommandDefinition(CliCommand.INSPECT_OBJECT, "Inspect one object like MAT's object inspector, or jump directly to one or more field paths for targeted state checks.", //$NON-NLS-1$ @@ -363,12 +363,12 @@ private static Map definitions() "Limit nested object expansion depth. Defaults to 3 for inspect-object when omitted."), //$NON-NLS-1$ enumOption("--format", "text|json", false, "Select text or JSON output.", FORMAT_VALUES)), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList(output("text", "tree", "Indented object-inspector tree."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "tree", "Stable keyed object-inspector tree JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "tree", "Compact keyed object-inspector tree JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("path2gc --object 0x...", "oql --query \"SELECT * FROM OBJECTS 0x...\""), //$NON-NLS-1$ //$NON-NLS-2$ - "tree payload with field and element nodes, stable paths, value kinds, concrete values, targeted field-path roots, preview metadata, sparse child arrays, and optional row addresses.", //$NON-NLS-1$ - Arrays.asList("items[]", "items[].path", "items[].valueKind", "items[].hasChildren", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - "items[].kind", "items[].name", "items[].value", "items[].object_address", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + "tree payload with field and element nodes, concrete values, preview metadata, compact child arrays, sparse slot preservation, and optional row addresses.", //$NON-NLS-1$ + Arrays.asList("items[]", "items[].kind", "items[].name", "items[].value", "items[].object_address", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ "items[]._children[] when returned", "items[]._childrenTruncated when true", //$NON-NLS-1$ //$NON-NLS-2$ + "items[].slot when null siblings were compacted", //$NON-NLS-1$ "items[]._address when no address column", //$NON-NLS-1$ "items[]._meta.value.kind when previewed", //$NON-NLS-1$ "items[]._meta.value.length when previewed", //$NON-NLS-1$ @@ -379,13 +379,13 @@ private static Map definitions() heapArgument, FORMAT_LIMIT_AND_DEPTH_OPTIONS, Arrays.asList(output("text", "top-consumers", "Text report matching MAT top consumers."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ output("json", "top-consumers", //$NON-NLS-1$ //$NON-NLS-2$ - "Stable aggregated JSON without display-only duplicates.")), //$NON-NLS-1$ + "Compact aggregated JSON without display-only duplicates.")), //$NON-NLS-1$ Arrays.asList("histogram ", "path2gc --object 0x..."), //$NON-NLS-1$ //$NON-NLS-2$ - "aggregated payload with biggestObjects, classes, classLoaders, and packages.", //$NON-NLS-1$ + "aggregated payload with biggestObjects, classes, classLoaders, and optional packages.", //$NON-NLS-1$ Arrays.asList("totalRetainedHeap", "biggestObjects[]", "biggestObjects[].objectAddress", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "biggestObjectsTruncated", "classes[]", "classes[].objectAddress", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "classesTruncated", "classLoaders[]", "classLoaders[].objectAddress", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "classLoadersTruncated", "packages", "packagesTruncated"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "biggestObjectsTruncated when true", "classes[]", "classes[].objectAddress", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "classesTruncated when true", "classLoaders[]", "classLoaders[].objectAddress", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "classLoadersTruncated when true", "packages when available", "packagesTruncated when true"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ definitions.put(CliCommand.PATH2GC, new CommandDefinition(CliCommand.PATH2GC, "Find paths from an object to GC roots using MAT's native query.", //$NON-NLS-1$ "mat-cli path2gc --object 0x... [--limit N] [--depth N] [--format text|json]", true, //$NON-NLS-1$ @@ -395,12 +395,11 @@ private static Map definitions() freeTextOption("--depth", "N", false, "Limit nested tree depth."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ enumOption("--format", "text|json", false, "Select text or JSON output.", FORMAT_VALUES)), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList(output("text", "tree", "Indented tree view."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "tree", "Stable keyed tree JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "tree", "Compact keyed tree JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("inspect-object --object 0x...", "oql --query \"SELECT * FROM OBJECTS 0x...\""), //$NON-NLS-1$ //$NON-NLS-2$ - "tree payload with keyed nodes, stable paths, value kinds, sparse child arrays, and optional row addresses.", //$NON-NLS-1$ - Arrays.asList("items[]", "items[].path", "items[].valueKind", "items[].hasChildren", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - "items[]._children[] when returned", "items[]._childrenTruncated when true", //$NON-NLS-1$ //$NON-NLS-2$ - "items[]._address when no address column"))); //$NON-NLS-1$ + "tree payload with compact nodes, sparse child arrays, optional slot preservation, and optional row addresses.", //$NON-NLS-1$ + Arrays.asList("items[]", "items[]._children[] when returned", "items[]._childrenTruncated when true", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "items[].slot when null siblings were compacted", "items[]._address when no address column"))); //$NON-NLS-1$ //$NON-NLS-2$ definitions.put(CliCommand.OQL, new CommandDefinition(CliCommand.OQL, "Run a MAT OQL query directly against the snapshot.", //$NON-NLS-1$ "mat-cli oql --query \"...\" [--limit N] [--depth N] [--format text|json]", true, //$NON-NLS-1$ @@ -414,11 +413,11 @@ private static Map definitions() enumOption("--format", "text|json", false, "Select text or JSON output.", FORMAT_VALUES)), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList(output("text", "text|table|tree|pie", "Depends on the OQL result."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ output("json", "text|table|tree|pie", //$NON-NLS-1$ //$NON-NLS-2$ - "Stable JSON envelope around the resolved result kind.")), //$NON-NLS-1$ + "Compact mat-cli/v1 JSON envelope around the resolved result kind.")), //$NON-NLS-1$ Arrays.asList("histogram ", "query --command \"histogram\""), //$NON-NLS-1$ //$NON-NLS-2$ "same payload contract as the resolved result kind returned by the OQL query.", //$NON-NLS-1$ - Arrays.asList("resultKind", "items[] when table/tree", "items[].path/valueKind/hasChildren when tree", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "slices[] when pie", "content when text"))); //$NON-NLS-1$ //$NON-NLS-2$ + Arrays.asList("resultKind", "items[] when table/tree/pie", "items[].slot when tree siblings were compacted", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "content when text"))); //$NON-NLS-1$ definitions.put(CliCommand.QUERY, new CommandDefinition(CliCommand.QUERY, "Run a MAT query command string through SnapshotQuery.parse(...).", //$NON-NLS-1$ "mat-cli query --command \"...\" [--limit N] [--depth N] [--format text|json]", true, //$NON-NLS-1$ @@ -433,24 +432,24 @@ private static Map definitions() Arrays.asList(output("text", "text|table|tree|section|pie|top-consumers", //$NON-NLS-1$ //$NON-NLS-2$ "Depends on the resolved query result."), output("json", "text|table|tree|section|pie|top-consumers", //$NON-NLS-1$ //$NON-NLS-2$ - "Stable JSON envelope around the resolved result kind.")), //$NON-NLS-1$ + "Compact mat-cli/v1 JSON envelope around the resolved result kind.")), //$NON-NLS-1$ Arrays.asList("schema histogram", "describe top-consumers"), //$NON-NLS-1$ //$NON-NLS-2$ "same payload contract as the resolved result kind returned by the parsed query.", //$NON-NLS-1$ - Arrays.asList("resultKind", "items[] when table/tree", "items[].path/valueKind/hasChildren when tree", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "slices[] when pie", "sections[] when section"))); //$NON-NLS-1$ //$NON-NLS-2$ + Arrays.asList("resultKind", "items[] when table/tree/pie", "items[].slot when tree siblings were compacted", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "sections[] when section"))); //$NON-NLS-1$ //$NON-NLS-2$ definitions.put(CliCommand.DESCRIBE, new CommandDefinition(CliCommand.DESCRIBE, "Describe a CLI command, its options, and the result kinds it can return.", //$NON-NLS-1$ "mat-cli describe [--format text|json]", false, commandArgument, FORMAT_OPTION, //$NON-NLS-1$ Arrays.asList(output("text", "describe", "Human-readable command description."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "describe", "Stable command metadata JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "describe", "Compact command metadata JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("schema "), "metadata payload for one command definition.", //$NON-NLS-1$ //$NON-NLS-2$ Arrays.asList("name", "summary", "usage", "requiresSnapshot", "options[]", "outputs[]", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ "suggestedNextCommands via top-level envelope"))); //$NON-NLS-1$ definitions.put(CliCommand.SCHEMA, new CommandDefinition(CliCommand.SCHEMA, - "Describe the stable JSON contract for a CLI command.", //$NON-NLS-1$ + "Describe the compact mat-cli/v1 JSON contract for a CLI command.", //$NON-NLS-1$ "mat-cli schema [--format text|json]", false, commandArgument, FORMAT_OPTION, //$NON-NLS-1$ Arrays.asList(output("text", "schema", "Readable contract summary."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "schema", "Stable contract metadata JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "schema", "Compact contract metadata JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("describe "), "JSON envelope and payload contract summary.", //$NON-NLS-1$ //$NON-NLS-2$ Arrays.asList("jsonEnvelope", "payloadKind", "payloadFields[]", "outputs[]"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ definitions.put(CliCommand.LIST_QUERIES, new CommandDefinition(CliCommand.LIST_QUERIES, @@ -458,7 +457,7 @@ private static Map definitions() "mat-cli list-queries [--format text|json]", false, Collections.emptyList(), //$NON-NLS-1$ FORMAT_OPTION, Arrays.asList(output("text", "query-list", "List MAT query identifiers and summaries."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "query-list", "Stable query registry metadata JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "query-list", "Compact query registry metadata JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("describe-query histogram", "query --command \"histogram\""), //$NON-NLS-1$ //$NON-NLS-2$ "array of MAT query descriptors including identifiers, usage, and arguments.", //$NON-NLS-1$ Arrays.asList("queries[]", "queries[].identifier", "queries[].usage", "queries[].arguments[]"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ @@ -467,7 +466,7 @@ private static Map definitions() "mat-cli describe-query [--format text|json]", false, queryIdArgument, //$NON-NLS-1$ FORMAT_OPTION, Arrays.asList(output("text", "query-description", "Human-readable MAT query metadata."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "query-description", "Stable MAT query metadata JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "query-description", "Compact MAT query metadata JSON.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("list-queries", "query --command \"\""), //$NON-NLS-1$ //$NON-NLS-2$ "single MAT query descriptor with argument metadata, help, and subjects.", //$NON-NLS-1$ Arrays.asList("query.identifier", "query.usage", "query.arguments[]", "query.subjects[]"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ @@ -476,10 +475,10 @@ private static Map definitions() "mat-cli completion [--format text|json]", false, completionShellArgument, //$NON-NLS-1$ FORMAT_OPTION, Arrays.asList(output("text", "text", "Shell completion script."), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - output("json", "text", "Shell completion script in the standard text envelope.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + output("json", "text", "Shell completion script in the compact text envelope.")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ Arrays.asList("completion bash", "completion zsh"), //$NON-NLS-1$ //$NON-NLS-2$ "plain-text shell completion script.", //$NON-NLS-1$ - Arrays.asList("text"))); //$NON-NLS-1$ + Arrays.asList("content"))); //$NON-NLS-1$ return Collections.unmodifiableMap(definitions); } diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/CommandMetadataSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/CommandMetadataSerializer.java index f68e268e..13e8fadc 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/CommandMetadataSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/CommandMetadataSerializer.java @@ -30,8 +30,8 @@ public boolean writeJson(JsonWriter writer, CommandMetadataResult result) { CommandDefinition definition = result.getDefinition(); writer.name("name").value(definition.getCommand().getToken()); //$NON-NLS-1$ - writer.name("summary").value(definition.getSummary()); //$NON-NLS-1$ - writer.name("usage").value(definition.getUsage()); //$NON-NLS-1$ + writeStringField(writer, "summary", definition.getSummary()); //$NON-NLS-1$ + writeStringField(writer, "usage", definition.getUsage()); //$NON-NLS-1$ writer.name("requiresSnapshot").value(definition.requiresSnapshot()); //$NON-NLS-1$ writeStrings(writer, "positionalArguments", definition.getPositionalArguments()); //$NON-NLS-1$ writeOptions(writer, definition.getOptions()); @@ -46,7 +46,7 @@ public boolean writeJson(JsonWriter writer, CommandMetadataResult result) writer.value("suggestedNextCommands when present"); //$NON-NLS-1$ writer.endArray(); writer.name("payloadKind").value(jsonPayloadKind(definition)); //$NON-NLS-1$ - writer.name("payloadDescription").value(definition.getAgentPayloadDescription()); //$NON-NLS-1$ + writeStringField(writer, "payloadDescription", definition.getAgentPayloadDescription()); //$NON-NLS-1$ writeStrings(writer, "payloadFields", definition.getAgentPayloadFields()); //$NON-NLS-1$ } return false; @@ -70,14 +70,16 @@ private String jsonPayloadKind(CommandDefinition definition) private void writeOptions(JsonWriter writer, List options) { + if (options == null || options.isEmpty()) + return; writer.name("options").beginArray(); //$NON-NLS-1$ for (OptionDefinition option : options) { writer.beginObject(); writer.name("name").value(option.getName()); //$NON-NLS-1$ - writer.name("valueHint").value(option.getValueHint()); //$NON-NLS-1$ + writeStringField(writer, "valueHint", option.getValueHint()); //$NON-NLS-1$ writer.name("required").value(option.isRequired()); //$NON-NLS-1$ - writer.name("description").value(option.getDescription()); //$NON-NLS-1$ + writeStringField(writer, "description", option.getDescription()); //$NON-NLS-1$ writer.endObject(); } writer.endArray(); @@ -85,13 +87,15 @@ private void writeOptions(JsonWriter writer, List options) private void writeOutputs(JsonWriter writer, List outputs) { + if (outputs == null || outputs.isEmpty()) + return; writer.name("outputs").beginArray(); //$NON-NLS-1$ for (OutputDefinition output : uniqueOutputs(outputs)) { writer.beginObject(); writer.name("format").value(output.getFormat()); //$NON-NLS-1$ writer.name("resultKind").value(output.getResultKind()); //$NON-NLS-1$ - writer.name("description").value(output.getDescription()); //$NON-NLS-1$ + writeStringField(writer, "description", output.getDescription()); //$NON-NLS-1$ writer.endObject(); } writer.endArray(); @@ -109,6 +113,8 @@ private List uniqueOutputs(List outputs) private void writeStrings(JsonWriter writer, String name, List values) { + if (values == null || values.isEmpty()) + return; writer.name(name).beginArray(); for (String value : values) { @@ -117,6 +123,13 @@ private void writeStrings(JsonWriter writer, String name, List values) writer.endArray(); } + private void writeStringField(JsonWriter writer, String name, String value) + { + if (value == null || value.length() == 0) + return; + writer.name(name).value(value); + } + private void appendStrings(StringBuilder builder, String label, List values) { builder.append(label).append(":\n"); //$NON-NLS-1$ diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/PieResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/PieResultSerializer.java index f5ebea12..61c650c8 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/PieResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/PieResultSerializer.java @@ -23,17 +23,7 @@ public class PieResultSerializer { public boolean writeJson(JsonWriter writer, IResultPie pie, SerializationOptions options) { - List slices = pie.getSlices(); - int limit = Math.min(slices.size(), options.getLimit()); - boolean truncated = slices.size() > limit; - - writer.name("slices").beginArray(); //$NON-NLS-1$ - for (int ii = 0; ii < limit; ii++) - { - writeSlice(writer, slices.get(ii), false, options); - } - writer.endArray(); - return truncated; + return writeAgentJson(writer, pie, options); } public boolean writeAgentJson(JsonWriter writer, IResultPie pie, SerializationOptions options) @@ -45,7 +35,7 @@ public boolean writeAgentJson(JsonWriter writer, IResultPie pie, SerializationOp writer.name("items").beginArray(); //$NON-NLS-1$ for (int ii = 0; ii < limit; ii++) { - writeSlice(writer, slices.get(ii), true, options); + writeSlice(writer, slices.get(ii), options); } writer.endArray(); return truncated; @@ -72,45 +62,32 @@ public String toText(IResultPie pie, SerializationOptions options) return builder.toString(); } - private void writeSlice(JsonWriter writer, Slice slice, boolean agentProfile, SerializationOptions options) + private void writeSlice(JsonWriter writer, Slice slice, SerializationOptions options) { writer.beginObject(); writer.name("label").value(slice.getLabel()); //$NON-NLS-1$ writer.name("value").value(slice.getValue()); //$NON-NLS-1$ - writer.name("description").value(slice.getDescription()); //$NON-NLS-1$ - if (agentProfile) - writeAgentAddress(writer, slice.getContext(), options); - else - writeContext(writer, slice.getContext(), options); - writer.name("color").value(color(slice)); //$NON-NLS-1$ + writeStringField(writer, "description", slice.getDescription()); //$NON-NLS-1$ + writeAgentAddress(writer, slice.getContext(), options); + writeStringField(writer, "color", color(slice)); //$NON-NLS-1$ writer.endObject(); } - private void writeContext(JsonWriter writer, IContextObject context, SerializationOptions options) + private void writeAgentAddress(JsonWriter writer, IContextObject context, SerializationOptions options) { Integer objectId = contextObjectId(context); if (objectId == null) - { - writer.name("context").nullValue(); //$NON-NLS-1$ return; - } - - writer.name("context").beginObject(); //$NON-NLS-1$ - writer.name("objectId").value(objectId.intValue()); //$NON-NLS-1$ String objectAddress = options == null ? null : options.resolveObjectAddress(objectId.intValue()); if (objectAddress != null) - writer.name("objectAddress").value(objectAddress); //$NON-NLS-1$ - writer.endObject(); + writer.name("_address").value(objectAddress); //$NON-NLS-1$ } - private void writeAgentAddress(JsonWriter writer, IContextObject context, SerializationOptions options) + private void writeStringField(JsonWriter writer, String name, String value) { - Integer objectId = contextObjectId(context); - if (objectId == null) + if (value == null || value.length() == 0) return; - String objectAddress = options == null ? null : options.resolveObjectAddress(objectId.intValue()); - if (objectAddress != null) - writer.name("_address").value(objectAddress); //$NON-NLS-1$ + writer.name(name).value(value); } private Integer contextObjectId(IContextObject context) diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/QueryMetadataSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/QueryMetadataSerializer.java index 15d1cddd..7e74e663 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/QueryMetadataSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/QueryMetadataSerializer.java @@ -24,14 +24,17 @@ public boolean writeJson(JsonWriter writer, QueryMetadataResult result) { if (result.getKind() == QueryMetadataResult.Kind.LIST) { - writer.name("queries").beginArray(); //$NON-NLS-1$ - for (QueryMetadataResult.QueryDefinition query : result.getQueries()) + if (result.getQueries() != null && !result.getQueries().isEmpty()) { - writer.beginObject(); - writeQuery(writer, query); - writer.endObject(); + writer.name("queries").beginArray(); //$NON-NLS-1$ + for (QueryMetadataResult.QueryDefinition query : result.getQueries()) + { + writer.beginObject(); + writeQuery(writer, query); + writer.endObject(); + } + writer.endArray(); } - writer.endArray(); } else { @@ -75,37 +78,50 @@ public String toText(QueryMetadataResult result) private void writeQuery(JsonWriter writer, QueryMetadataResult.QueryDefinition query) { writer.name("identifier").value(query.getIdentifier()); //$NON-NLS-1$ - writer.name("name").value(query.getName()); //$NON-NLS-1$ - writer.name("category").value(query.getCategory()); //$NON-NLS-1$ - writer.name("usage").value(query.getUsage()); //$NON-NLS-1$ - writer.name("summary").value(query.getSummary()); //$NON-NLS-1$ - writer.name("help").value(query.getHelp()); //$NON-NLS-1$ - writer.name("helpUrl").value(query.getHelpUrl()); //$NON-NLS-1$ - writer.name("commandClass").value(query.getCommandClass()); //$NON-NLS-1$ + writeStringField(writer, "name", query.getName()); //$NON-NLS-1$ + writeStringField(writer, "category", query.getCategory()); //$NON-NLS-1$ + writeStringField(writer, "usage", query.getUsage()); //$NON-NLS-1$ + writeStringField(writer, "summary", query.getSummary()); //$NON-NLS-1$ + writeStringField(writer, "help", query.getHelp()); //$NON-NLS-1$ + writeStringField(writer, "helpUrl", query.getHelpUrl()); //$NON-NLS-1$ + writeStringField(writer, "commandClass", query.getCommandClass()); //$NON-NLS-1$ writer.name("shallow").value(query.isShallow()); //$NON-NLS-1$ - writer.name("subjects").beginArray(); //$NON-NLS-1$ - for (String subject : query.getSubjects()) + if (query.getSubjects() != null && !query.getSubjects().isEmpty()) { - writer.value(subject); + writer.name("subjects").beginArray(); //$NON-NLS-1$ + for (String subject : query.getSubjects()) + { + writer.value(subject); + } + writer.endArray(); } - writer.endArray(); - writer.name("arguments").beginArray(); //$NON-NLS-1$ - for (QueryMetadataResult.QueryArgument argument : query.getArguments()) + if (query.getArguments() != null && !query.getArguments().isEmpty()) { - writer.beginObject(); - writer.name("name").value(argument.getName()); //$NON-NLS-1$ - writer.name("flag").value(argument.getFlag()); //$NON-NLS-1$ - writer.name("type").value(argument.getType()); //$NON-NLS-1$ - writer.name("advice").value(argument.getAdvice()); //$NON-NLS-1$ - writer.name("mandatory").value(argument.isMandatory()); //$NON-NLS-1$ - writer.name("multiple").value(argument.isMultiple()); //$NON-NLS-1$ - writer.name("boolean").value(argument.isBoolean()); //$NON-NLS-1$ - writer.name("enum").value(argument.isEnumeration()); //$NON-NLS-1$ - writer.name("defaultValue").value(argument.getDefaultValue()); //$NON-NLS-1$ - writer.name("help").value(argument.getHelp()); //$NON-NLS-1$ - writer.endObject(); + writer.name("arguments").beginArray(); //$NON-NLS-1$ + for (QueryMetadataResult.QueryArgument argument : query.getArguments()) + { + writer.beginObject(); + writer.name("name").value(argument.getName()); //$NON-NLS-1$ + writeStringField(writer, "flag", argument.getFlag()); //$NON-NLS-1$ + writeStringField(writer, "type", argument.getType()); //$NON-NLS-1$ + writeStringField(writer, "advice", argument.getAdvice()); //$NON-NLS-1$ + writer.name("mandatory").value(argument.isMandatory()); //$NON-NLS-1$ + writer.name("multiple").value(argument.isMultiple()); //$NON-NLS-1$ + writer.name("boolean").value(argument.isBoolean()); //$NON-NLS-1$ + writer.name("enum").value(argument.isEnumeration()); //$NON-NLS-1$ + writeStringField(writer, "defaultValue", argument.getDefaultValue()); //$NON-NLS-1$ + writeStringField(writer, "help", argument.getHelp()); //$NON-NLS-1$ + writer.endObject(); + } + writer.endArray(); } - writer.endArray(); + } + + private void writeStringField(JsonWriter writer, String name, String value) + { + if (value == null || value.length() == 0) + return; + writer.name(name).value(value); } private void appendList(StringBuilder builder, String label, List values) diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/ResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/ResultSerializer.java index 32f9ace5..058066ac 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/ResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/ResultSerializer.java @@ -35,7 +35,7 @@ public class ResultSerializer { - private static final String JSON_SCHEMA_VERSION = "mat-cli/v2"; //$NON-NLS-1$ + private static final String JSON_SCHEMA_VERSION = "mat-cli/v1"; //$NON-NLS-1$ private final TableResultSerializer tableSerializer = new TableResultSerializer(); private final TreeResultSerializer treeSerializer = new TreeResultSerializer(); @@ -223,8 +223,7 @@ else if (result instanceof Spec) } writer.name("truncated").value(truncated); //$NON-NLS-1$ - if (execution != null && execution.getNote() != null) - writer.name("note").value(execution.getNote()); //$NON-NLS-1$ + writeStringField(writer, "note", execution == null ? null : execution.getNote()); //$NON-NLS-1$ if (shouldWriteSuggestedNextCommands(arguments, execution)) writeSuggestedNextCommands(writer, suggestedNextCommands(arguments, execution)); writer.endObject(); @@ -241,14 +240,14 @@ private void serializeJsonError(CliArguments arguments, int exitCode, Throwable writer.name("code").value(exitCode); //$NON-NLS-1$ writer.name("message").value(error.getMessage() == null ? error.getClass().getName() : error.getMessage()); //$NON-NLS-1$ writer.name("kind").value(errorKind(arguments, exitCode, error)); //$NON-NLS-1$ - writer.name("hint").value(errorHint(arguments, exitCode, error)); //$NON-NLS-1$ + writeStringField(writer, "hint", errorHint(arguments, exitCode, error)); //$NON-NLS-1$ writer.name("retryable").value(isRetryable(arguments, exitCode, error)); //$NON-NLS-1$ if (arguments != null && arguments.isVerbose()) { - writer.name("exceptionClass").value(error == null ? null : error.getClass().getName()); //$NON-NLS-1$ - writer.name("rootCauseClass").value(rootCause(error).getClass().getName()); //$NON-NLS-1$ - writer.name("rootCauseMessage").value(rootCauseMessage(error)); //$NON-NLS-1$ - writer.name("stackTrace").value(stackTrace(error)); //$NON-NLS-1$ + writeStringField(writer, "exceptionClass", error == null ? null : error.getClass().getName()); //$NON-NLS-1$ + writeStringField(writer, "rootCauseClass", rootCause(error).getClass().getName()); //$NON-NLS-1$ + writeStringField(writer, "rootCauseMessage", rootCauseMessage(error)); //$NON-NLS-1$ + writeStringField(writer, "stackTrace", stackTrace(error)); //$NON-NLS-1$ } writer.endObject(); writer.name("truncated").value(false); //$NON-NLS-1$ @@ -260,10 +259,10 @@ private void serializeJsonError(CliArguments arguments, int exitCode, Throwable private void writeSummary(JsonWriter writer, SnapshotSummary summary) { writer.name("summary").beginObject(); //$NON-NLS-1$ - writer.name("path").value(summary.getPath()); //$NON-NLS-1$ - writer.name("heapFormat").value(summary.getHeapFormat()); //$NON-NLS-1$ - writer.name("jvmInfo").value(summary.getJvmInfo()); //$NON-NLS-1$ - writer.name("creationDate").value(summary.getCreationDate()); //$NON-NLS-1$ + writeStringField(writer, "path", summary.getPath()); //$NON-NLS-1$ + writeStringField(writer, "heapFormat", summary.getHeapFormat()); //$NON-NLS-1$ + writeStringField(writer, "jvmInfo", summary.getJvmInfo()); //$NON-NLS-1$ + writeStringField(writer, "creationDate", summary.getCreationDate()); //$NON-NLS-1$ writer.name("identifierSize").value(summary.getIdentifierSize()); //$NON-NLS-1$ writer.name("numberOfObjects").value(summary.getNumberOfObjects()); //$NON-NLS-1$ writer.name("numberOfClasses").value(summary.getNumberOfClasses()); //$NON-NLS-1$ @@ -281,6 +280,8 @@ private CliException unsupported(IResult result) private void writeSuggestedNextCommands(JsonWriter writer, List commands) { + if (commands == null || commands.isEmpty()) + return; writer.name("suggestedNextCommands").beginArray(); //$NON-NLS-1$ for (String command : commands) { @@ -289,6 +290,13 @@ private void writeSuggestedNextCommands(JsonWriter writer, List commands writer.endArray(); } + private void writeStringField(JsonWriter writer, String name, String value) + { + if (value == null || value.length() == 0) + return; + writer.name(name).value(value); + } + private boolean shouldWriteSuggestedNextCommands(CliArguments arguments, CliExecution execution) { if (execution == null) diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/SpecResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/SpecResultSerializer.java index 251a1b45..834c34c3 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/SpecResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/SpecResultSerializer.java @@ -69,7 +69,6 @@ private boolean writeJson(JsonWriter writer, Spec spec, SerializationOptions opt return writeQuery(writer, (QuerySpec) spec, options, sectionDepth); writeSpecMetadata(writer, spec); - writer.name("sections").beginArray().endArray(); //$NON-NLS-1$ return false; } @@ -136,10 +135,7 @@ private boolean writeSection(JsonWriter writer, SectionSpec spec, SerializationO { writeSpecMetadata(writer, spec); if (sectionDepth >= options.getTreeDepthLimit()) - { - writer.name("sections").beginArray().endArray(); //$NON-NLS-1$ return !spec.getChildren().isEmpty(); - } return writeSectionContents(writer, spec, options, sectionDepth); } @@ -153,19 +149,22 @@ private boolean writeSectionContents(JsonWriter writer, SectionSpec spec, Serial if (children.size() > limit) truncated = true; - writer.name("sections").beginArray(); //$NON-NLS-1$ - for (int ii = 0; ii < limit; ii++) + if (limit > 0) { - Spec child = children.get(ii); - writer.beginObject(); - writer.name("kind").value(rootResultType(child)); //$NON-NLS-1$ - int childSectionDepth = child instanceof SectionSpec ? sectionDepth + 1 : sectionDepth; - boolean childTruncated = writeJson(writer, child, options, childSectionDepth); - writer.name("truncated").value(childTruncated); //$NON-NLS-1$ - writer.endObject(); - truncated |= childTruncated; + writer.name("sections").beginArray(); //$NON-NLS-1$ + for (int ii = 0; ii < limit; ii++) + { + Spec child = children.get(ii); + writer.beginObject(); + writer.name("kind").value(rootResultType(child)); //$NON-NLS-1$ + int childSectionDepth = child instanceof SectionSpec ? sectionDepth + 1 : sectionDepth; + boolean childTruncated = writeJson(writer, child, options, childSectionDepth); + writer.name("truncated").value(childTruncated); //$NON-NLS-1$ + writer.endObject(); + truncated |= childTruncated; + } + writer.endArray(); } - writer.endArray(); return truncated; } @@ -174,7 +173,7 @@ private boolean writeQuery(JsonWriter writer, QuerySpec spec, SerializationOptio throws CliException { writeSpecMetadata(writer, spec); - writer.name("queryCommand").value(spec.getCommand()); //$NON-NLS-1$ + writeStringField(writer, "queryCommand", spec.getCommand()); //$NON-NLS-1$ return writeResult(writer, spec.getResult(), options, spec.getName(), sectionDepth); } @@ -248,29 +247,36 @@ private boolean writeResult(JsonWriter writer, IResult result, SerializationOpti private void writeSpecMetadata(JsonWriter writer, Spec spec) { - writer.name("name").value(spec.getName()); //$NON-NLS-1$ - writer.name("template").value(spec.getTemplate()); //$NON-NLS-1$ - writer.name("params").beginObject(); //$NON-NLS-1$ + writeStringField(writer, "name", spec.getName()); //$NON-NLS-1$ + writeStringField(writer, "template", spec.getTemplate()); //$NON-NLS-1$ List keys = new ArrayList(spec.getParams().keySet()); Collections.sort(keys); - for (String key : keys) + if (!keys.isEmpty()) { - writer.name(key).value(spec.getParams().get(key)); + writer.name("params").beginObject(); //$NON-NLS-1$ + for (String key : keys) + { + writer.name(key).value(spec.getParams().get(key)); + } + writer.endObject(); } - writer.endObject(); } private boolean writeNestedSection(JsonWriter writer, SectionSpec spec, SerializationOptions options, int sectionDepth) throws CliException { if (sectionDepth + 1 >= options.getTreeDepthLimit()) - { - writer.name("sections").beginArray().endArray(); //$NON-NLS-1$ return !spec.getChildren().isEmpty(); - } return writeSectionContents(writer, spec, options, sectionDepth + 1); } + private void writeStringField(JsonWriter writer, String name, String value) + { + if (value == null || value.length() == 0) + return; + writer.name(name).value(value); + } + private void appendText(StringBuilder builder, Spec spec, SerializationOptions options, int depth, int sectionDepth) { indent(builder, depth); diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/StructuredResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/StructuredResultSerializer.java index 488d91f0..8153f163 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/StructuredResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/StructuredResultSerializer.java @@ -117,8 +117,7 @@ protected void writeAgentRow(JsonWriter writer, IStructuredResult result, Column CellValue[] cells = readCells(result, columns, row); for (int ii = 0; ii < columns.length; ii++) { - writer.name(columns[ii].id); - writer.rawValue(agentRawValue(columns[ii].column, cells[ii].value)); + writeField(writer, columns[ii].id, agentRawValue(columns[ii].column, cells[ii].value)); } writeAgentAddress(writer, result, columns, row, options); writeAgentCellMetadata(writer, columns, cells); @@ -252,6 +251,25 @@ protected void writeAgentAddress(JsonWriter writer, IStructuredResult result, Co writer.name("_address").value(objectAddress); //$NON-NLS-1$ } + protected boolean writeField(JsonWriter writer, String name, Object value) + { + if (value == null) + return false; + + writer.name(name); + writer.rawValue(value); + return true; + } + + protected boolean writeStringField(JsonWriter writer, String name, String value) + { + if (value == null || value.length() == 0) + return false; + + writer.name(name).value(value); //$NON-NLS-1$ + return true; + } + protected void writeRowValues(JsonWriter writer, IStructuredResult result, Column[] columns, Object row) { CellValue[] cells = readCells(result, columns, row); diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TableResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TableResultSerializer.java index a0822921..30dabb4f 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TableResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TableResultSerializer.java @@ -19,25 +19,7 @@ public class TableResultSerializer extends StructuredResultSerializer { public boolean writeJson(JsonWriter writer, IResultTable table, SerializationOptions options) { - Column[] columns = table.getColumns(); - writeColumns(writer, columns); - - int rowCount = table.getRowCount(); - int limit = Math.min(rowCount, options.getLimit()); - boolean truncated = rowCount > limit; - - writer.name("rows").beginArray(); //$NON-NLS-1$ - for (int ii = 0; ii < limit; ii++) - { - Object row = table.getRow(ii); - writer.beginObject(); - writeRowValues(writer, table, columns, row); - writeContext(writer, table, row, options); - writer.endObject(); - } - writer.endArray(); - - return truncated; + return writeAgentJson(writer, table, options); } public boolean writeAgentJson(JsonWriter writer, IResultTable table, SerializationOptions options) diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TextResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TextResultSerializer.java index 357082d2..3a54f237 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TextResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TextResultSerializer.java @@ -15,7 +15,7 @@ public class TextResultSerializer { public boolean writeJson(JsonWriter writer, TextResult result) { - writer.name("text").value(toText(result)); //$NON-NLS-1$ + writer.name("content").value(toText(result)); //$NON-NLS-1$ return false; } diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/ThreadsResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/ThreadsResultSerializer.java index e74cbc33..ba299739 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/ThreadsResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/ThreadsResultSerializer.java @@ -24,25 +24,31 @@ public class ThreadsResultSerializer public boolean writeJson(JsonWriter writer, ThreadsResult result) { - writer.name("notice").value(result.getNotice()); //$NON-NLS-1$ + writeStringField(writer, "notice", result.getNotice()); //$NON-NLS-1$ writeSummary(writer, result.getSummary()); writer.name("threads").beginArray(); //$NON-NLS-1$ for (ThreadsResult.ThreadEntry entry : result.getThreads()) { writer.beginObject(); writer.name("name").value(entry.getName()); //$NON-NLS-1$ - writer.name("technicalName").value(entry.getTechnicalName()); //$NON-NLS-1$ - writer.name("objectAddress").value(entry.getObjectAddress()); //$NON-NLS-1$ - writer.name("state").value(entry.getState()); //$NON-NLS-1$ + writeStringField(writer, "technicalName", entry.getTechnicalName()); //$NON-NLS-1$ + writeStringField(writer, "objectAddress", entry.getObjectAddress()); //$NON-NLS-1$ + writeStringField(writer, "state", entry.getState()); //$NON-NLS-1$ writer.name("retainedBytes").value(entry.getRetainedBytes()); //$NON-NLS-1$ writer.name("stackAvailable").value(entry.isStackAvailable()); //$NON-NLS-1$ - writer.name("stackUnavailableReason").value(entry.getStackUnavailableReason()); //$NON-NLS-1$ - writer.name("stackFrames").beginArray(); //$NON-NLS-1$ - for (String frame : entry.getStackFrames()) + if (!entry.isStackAvailable()) { - writer.value(frame); + writeStringField(writer, "stackUnavailableReason", entry.getStackUnavailableReason()); //$NON-NLS-1$ + } + if (!entry.getStackFrames().isEmpty()) + { + writer.name("stackFrames").beginArray(); //$NON-NLS-1$ + for (String frame : entry.getStackFrames()) + { + writer.value(frame); + } + writer.endArray(); } - writer.endArray(); writer.endObject(); } writer.endArray(); @@ -88,6 +94,13 @@ private void writeSummary(JsonWriter writer, ThreadsResult.Summary summary) writer.endObject(); } + private void writeStringField(JsonWriter writer, String name, String value) + { + if (value == null || value.length() == 0) + return; + writer.name(name).value(value); + } + private void appendOverview(StringBuilder builder, List threads) { if (threads.isEmpty()) diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TopConsumersResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TopConsumersResultSerializer.java index 113436b6..58d14a1a 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TopConsumersResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TopConsumersResultSerializer.java @@ -31,17 +31,7 @@ public TopConsumersResultSerializer() public boolean writeJson(JsonWriter writer, TopConsumersResult result, SerializationOptions options) { - writer.name("totalRetainedHeap").value(result.getTotalRetainedHeap()); //$NON-NLS-1$ - - boolean truncated = false; - truncated |= writeObjectSection(writer, "biggestObjects", "Biggest Objects", result.getBiggestObjects(), //$NON-NLS-1$ //$NON-NLS-2$ - options); - truncated |= writeDominatorSection(writer, "classes", "Biggest Top-Level Dominator Classes", //$NON-NLS-1$ //$NON-NLS-2$ - result.getClasses(), options); - truncated |= writeDominatorSection(writer, "classLoaders", "Biggest Top-Level Dominator Class Loaders", //$NON-NLS-1$ //$NON-NLS-2$ - result.getClassLoaders(), options); - truncated |= writePackageSection(writer, result.getPackages(), options); - return truncated; + return writeAgentJson(writer, result, options); } public boolean writeAgentJson(JsonWriter writer, TopConsumersResult result, SerializationOptions options) @@ -61,7 +51,8 @@ public boolean writeAgentJson(JsonWriter writer, TopConsumersResult result, Seri writeAgentObjectRow(writer, result.getBiggestObjects().get(ii), options); } writer.endArray(); - writer.name("biggestObjectsTruncated").value(biggestObjectsTruncated); //$NON-NLS-1$ + if (biggestObjectsTruncated) + writer.name("biggestObjectsTruncated").value(true); //$NON-NLS-1$ writer.name("classes").beginArray(); //$NON-NLS-1$ for (int ii = 0; ii < classLimit; ii++) @@ -69,7 +60,8 @@ public boolean writeAgentJson(JsonWriter writer, TopConsumersResult result, Seri writeAgentDominatorRow(writer, result.getClasses().get(ii), options); } writer.endArray(); - writer.name("classesTruncated").value(classesTruncated); //$NON-NLS-1$ + if (classesTruncated) + writer.name("classesTruncated").value(true); //$NON-NLS-1$ writer.name("classLoaders").beginArray(); //$NON-NLS-1$ for (int ii = 0; ii < classLoaderLimit; ii++) @@ -77,19 +69,17 @@ public boolean writeAgentJson(JsonWriter writer, TopConsumersResult result, Seri writeAgentDominatorRow(writer, result.getClassLoaders().get(ii), options); } writer.endArray(); - writer.name("classLoadersTruncated").value(classLoadersTruncated); //$NON-NLS-1$ + if (classLoadersTruncated) + writer.name("classLoadersTruncated").value(true); //$NON-NLS-1$ PackageTruncationState packageState = new PackageTruncationState(options.getTreeNodeLimit()); - writer.name("packages"); //$NON-NLS-1$ - if (result.getPackages() == null) - { - writer.nullValue(); - } - else + if (result.getPackages() != null) { + writer.name("packages"); //$NON-NLS-1$ writeAgentPackageNode(writer, result.getPackages(), options, packageState, 0); } - writer.name("packagesTruncated").value(packageState.truncated); //$NON-NLS-1$ + if (packageState.truncated) + writer.name("packagesTruncated").value(true); //$NON-NLS-1$ return biggestObjectsTruncated || classesTruncated || classLoadersTruncated || packageState.truncated; } diff --git a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TreeResultSerializer.java b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TreeResultSerializer.java index e92d5c99..cf5243ba 100644 --- a/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TreeResultSerializer.java +++ b/plugins/org.eclipse.mat.cli/src/org/eclipse/mat/cli/internal/serialization/TreeResultSerializer.java @@ -25,14 +25,7 @@ public class TreeResultSerializer extends StructuredResultSerializer public boolean writeJson(JsonWriter writer, IResultTree tree, SerializationOptions options) { - Column[] columns = tree.getColumns(); - ColumnSchema[] schema = buildColumnSchemas(columns); - writeColumns(writer, columns); - writer.name("rows").beginArray(); //$NON-NLS-1$ - TruncationState state = new TruncationState(options.getTreeNodeLimit()); - writeNodes(writer, tree, columns, schema, tree.getElements(), 0, options, state, new PathState(), null); - writer.endArray(); - return state.truncated; + return writeAgentJson(writer, tree, options); } public boolean writeAgentJson(JsonWriter writer, IResultTree tree, SerializationOptions options) @@ -41,7 +34,7 @@ public boolean writeAgentJson(JsonWriter writer, IResultTree tree, Serialization ColumnSchema[] schema = buildColumnSchemas(columns); writer.name("items").beginArray(); //$NON-NLS-1$ TruncationState state = new TruncationState(options.getTreeNodeLimit()); - writeAgentNodes(writer, tree, columns, schema, tree.getElements(), 0, options, state, new PathState(), null); + writeAgentNodes(writer, tree, columns, schema, tree.getElements(), 0, options, state, new PathState(), false); writer.endArray(); return state.truncated; } @@ -62,130 +55,57 @@ public String toText(IResultTree tree, SerializationOptions options, boolean sho return builder.toString(); } - private void writeNodes(JsonWriter writer, IResultTree tree, Column[] columns, ColumnSchema[] schema, List rows, - int depth, SerializationOptions options, TruncationState state, PathState path, String parentPath) + private void writeAgentNodes(JsonWriter writer, IResultTree tree, Column[] columns, ColumnSchema[] schema, + List rows, int depth, SerializationOptions options, TruncationState state, PathState path, + boolean dropNullChildren) { - int limit = Math.min(rows.size(), options.getLimit()); - if (rows.size() > limit) + VisibleNodeBatch batch = collectVisibleNodes(tree, columns, schema, rows, options.getLimit(), dropNullChildren); + if (batch.truncated) state.truncated = true; - for (int ii = 0; ii < limit; ii++) + for (AgentNode node : batch.nodes) { if (state.remainingNodes <= 0) { state.truncated = true; break; } - - Object row = rows.get(ii); - CellValue[] cells = readCells(tree, columns, row); - boolean hasChildren = tree.hasChildren(row); - Integer objectId = rowObjectId(tree, row); - String nodePath = pathFor(parentPath, rows.size(), ii, columns, schema, row, cells); - state.remainingNodes--; - - writer.beginObject(); - writer.name("path").value(nodePath); //$NON-NLS-1$ - writer.name("valueKind").value(valueKind(columns, schema, cells, objectId)); //$NON-NLS-1$ - writer.name("hasChildren").value(hasChildren); //$NON-NLS-1$ - writeRowValues(writer, tree, columns, row); - writeContext(writer, tree, row, options); - - boolean cycle = path.isCycle(objectId); - writer.name("_cycle").value(cycle); //$NON-NLS-1$ - if (cycle) - { - writer.name("children").beginArray().endArray(); //$NON-NLS-1$ - } - else if (depth + 1 >= options.getTreeDepthLimit()) - { - if (hasChildren) - state.truncated = true; - writer.name("children").beginArray().endArray(); //$NON-NLS-1$ - } - else - { - writer.name("children").beginArray(); //$NON-NLS-1$ - List children = hasChildren ? tree.getChildren(row) : null; - path.push(objectId); - if (children != null) - { - if (state.remainingNodes > 0) - writeNodes(writer, tree, columns, schema, children, depth + 1, options, state, path, nodePath); - else - state.truncated = true; - } - path.pop(objectId); - writer.endArray(); - } - writer.endObject(); + writeAgentNode(writer, tree, columns, schema, node, depth, options, state, path); } } - private void writeAgentNodes(JsonWriter writer, IResultTree tree, Column[] columns, ColumnSchema[] schema, - List rows, int depth, SerializationOptions options, TruncationState state, PathState path, - String parentPath) + private void writeAgentNode(JsonWriter writer, IResultTree tree, Column[] columns, ColumnSchema[] schema, + AgentNode node, int depth, SerializationOptions options, TruncationState state, PathState path) { - int limit = Math.min(rows.size(), options.getLimit()); - if (rows.size() > limit) - state.truncated = true; + state.remainingNodes--; - for (int ii = 0; ii < limit; ii++) + writer.beginObject(); + if (node.slot != null) + writer.name("slot").value(node.slot.intValue()); //$NON-NLS-1$ + writeAgentRow(writer, tree, schema, node.row, options); + + boolean cycle = path.isCycle(node.objectId); + if (cycle) + writer.name("_cycle").value(true); //$NON-NLS-1$ + + boolean childrenTruncated = false; + if (node.hasChildren && !cycle) { - if (state.remainingNodes <= 0) + List children = tree.getChildren(node.row); + VisibleNodeBatch childBatch = collectVisibleNodes(tree, columns, schema, children, options.getLimit(), true); + childrenTruncated = childBatch.truncated; + if (depth + 1 >= options.getTreeDepthLimit()) { - state.truncated = true; - break; + if (!childBatch.nodes.isEmpty()) + childrenTruncated = true; } - - Object row = rows.get(ii); - CellValue[] cells = readCells(tree, columns, row); - boolean hasChildren = tree.hasChildren(row); - Integer objectId = rowObjectId(tree, row); - String nodePath = pathFor(parentPath, rows.size(), ii, columns, schema, row, cells); - state.remainingNodes--; - - writer.beginObject(); - writer.name("path").value(nodePath); //$NON-NLS-1$ - writer.name("valueKind").value(valueKind(columns, schema, cells, objectId)); //$NON-NLS-1$ - writer.name("hasChildren").value(hasChildren); //$NON-NLS-1$ - writeAgentRow(writer, tree, schema, row, options); - - boolean cycle = path.isCycle(objectId); - if (cycle) - writer.name("_cycle").value(true); //$NON-NLS-1$ - - boolean childrenTruncated = false; - boolean wroteChildren = false; - if (hasChildren && !cycle) + else if (!childBatch.nodes.isEmpty()) { - if (depth + 1 >= options.getTreeDepthLimit()) - { - childrenTruncated = true; - state.truncated = true; - } - else + if (state.remainingNodes > 0) { - List children = tree.getChildren(row); - int childLimit = Math.min(children.size(), options.getLimit()); - if (children.size() > childLimit) - { - childrenTruncated = true; - state.truncated = true; - } - - if (childLimit > 0 && state.remainingNodes > 0) - { - writer.name("_children").beginArray(); //$NON-NLS-1$ - wroteChildren = true; - } - else if (childLimit > 0) - { - childrenTruncated = true; - state.truncated = true; - } - - for (int childIndex = 0; childIndex < childLimit; childIndex++) + writer.name("_children").beginArray(); //$NON-NLS-1$ + path.push(node.objectId); + for (AgentNode child : childBatch.nodes) { if (state.remainingNodes <= 0) { @@ -193,19 +113,24 @@ else if (childLimit > 0) state.truncated = true; break; } - path.push(objectId); - writeAgentNodes(writer, tree, columns, schema, children.subList(childIndex, childIndex + 1), - depth + 1, options, state, path, nodePath); - path.pop(objectId); + writeAgentNode(writer, tree, columns, schema, child, depth + 1, options, state, path); } + path.pop(node.objectId); + writer.endArray(); + } + else + { + childrenTruncated = true; + state.truncated = true; } } - if (wroteChildren) - writer.endArray(); - if (childrenTruncated) - writer.name("_childrenTruncated").value(true); //$NON-NLS-1$ - writer.endObject(); } + if (childrenTruncated) + { + writer.name("_childrenTruncated").value(true); //$NON-NLS-1$ + state.truncated = true; + } + writer.endObject(); } private void appendNodes(StringBuilder builder, IResultTree tree, Column[] columns, ColumnSchema[] schema, @@ -657,11 +582,95 @@ private TextNode(Object row, CellValue[] cells, boolean hasChildren, Integer obj } } + private static final class AgentNode + { + private final Object row; + private final CellValue[] cells; + private final boolean hasChildren; + private final Integer objectId; + private final Integer slot; + + private AgentNode(Object row, CellValue[] cells, boolean hasChildren, Integer objectId, Integer slot) + { + this.row = row; + this.cells = cells; + this.hasChildren = hasChildren; + this.objectId = objectId; + this.slot = slot; + } + } + + private static final class VisibleNodeBatch + { + private final List nodes; + private final boolean truncated; + + private VisibleNodeBatch(List nodes, boolean truncated) + { + this.nodes = nodes; + this.truncated = truncated; + } + } + private Integer rowObjectId(IResultTree tree, Object row) { return contextObjectId(safeContext(tree, row)); } + private VisibleNodeBatch collectVisibleNodes(IResultTree tree, Column[] columns, ColumnSchema[] schema, List rows, + int limit, boolean dropNullChildren) + { + List visible = new ArrayList(Math.min(rows.size(), limit)); + boolean truncated = false; + + for (int ii = 0; ii < rows.size(); ii++) + { + Object row = rows.get(ii); + CellValue[] cells = readCells(tree, columns, row); + boolean hasChildren = tree.hasChildren(row); + Integer objectId = rowObjectId(tree, row); + if (dropNullChildren && shouldDropNullNode(columns, schema, cells, objectId, hasChildren)) + continue; + + if (visible.size() >= limit) + { + truncated = true; + break; + } + + Integer slot = shouldWriteSlot(columns, schema, row, cells, ii, visible.size()) ? Integer.valueOf(ii) : null; + visible.add(new AgentNode(row, cells, hasChildren, objectId, slot)); + } + + return new VisibleNodeBatch(visible, truncated); + } + + private boolean shouldDropNullNode(Column[] columns, ColumnSchema[] schema, CellValue[] cells, Integer objectId, + boolean hasChildren) + { + return !hasChildren && "null".equals(valueKind(columns, schema, cells, objectId)); //$NON-NLS-1$ + } + + private boolean shouldWriteSlot(Column[] columns, ColumnSchema[] schema, Object row, CellValue[] cells, int rawIndex, + int visibleIndex) + { + if (rawIndex == visibleIndex) + return false; + + String name = pathColumnDisplay(columns, schema, row, cells, "name", false); //$NON-NLS-1$ + if (name == null) + return false; + if (!name.startsWith("[") || !name.endsWith("]")) //$NON-NLS-1$ //$NON-NLS-2$ + return false; + + for (int ii = 1; ii < name.length() - 1; ii++) + { + if (!Character.isDigit(name.charAt(ii))) + return false; + } + return name.length() > 2; + } + private static final class PathState { private final Set ancestors = new HashSet(); diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/cli/CliCommandExecutorTest.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/cli/CliCommandExecutorTest.java index 7a0b9e85..094a8dd3 100644 --- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/cli/CliCommandExecutorTest.java +++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/cli/CliCommandExecutorTest.java @@ -48,7 +48,7 @@ public void executesSummaryCommandAgainstHprofSnapshot() throws Exception File heap = copyHeap(TestSnapshots.SUN_JDK5_13_32BIT); String json = executeJson(new String[] { "summary", heap.getAbsolutePath(), "--format", "json" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v2\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v1\"")); //$NON-NLS-1$ assertTrue(json.contains("\"resultKind\":\"summary\"")); //$NON-NLS-1$ assertTrue(json.contains("\"summary\":")); //$NON-NLS-1$ assertTrue(json.contains("\"path\":\"" + heap.getAbsolutePath().replace("\\", "\\\\") + "\"")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ @@ -73,7 +73,7 @@ public void executesThreadsCommandAgainstHprofSnapshotAsJson() throws Exception File heap = copyHeap(TestSnapshots.SUN_JDK5_13_32BIT); String json = executeJson(new String[] { "threads", heap.getAbsolutePath(), "--format", "json", "--limit", "2" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ - assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v2\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v1\"")); //$NON-NLS-1$ assertTrue(json.contains("\"resultKind\":\"threads\"")); //$NON-NLS-1$ assertTrue(json.contains("\"notice\":\"best-effort from heap dump, not a full jstack equivalent\"")); //$NON-NLS-1$ assertTrue(json.contains("\"summary\":{\"totalThreads\":")); //$NON-NLS-1$ @@ -81,7 +81,8 @@ public void executesThreadsCommandAgainstHprofSnapshotAsJson() throws Exception assertTrue(json.contains("\"threads\":[{")); //$NON-NLS-1$ assertTrue(json.contains("\"objectAddress\":\"0x")); //$NON-NLS-1$ assertTrue(json.contains("\"stackAvailable\":")); //$NON-NLS-1$ - assertTrue(json.contains("\"stackFrames\":[")); //$NON-NLS-1$ + assertTrue(json.contains("\"stackUnavailableReason\":\"") || json.contains("\"stackFrames\":[")); //$NON-NLS-1$ //$NON-NLS-2$ + assertFalse(json.contains("\"stackFrames\":[]")); //$NON-NLS-1$ } @Test @@ -90,7 +91,7 @@ public void executesHistogramCommandAgainstHprofSnapshot() throws Exception File heap = copyHeap(TestSnapshots.SUN_JDK5_13_32BIT); String json = executeJson(new String[] { "histogram", heap.getAbsolutePath(), "--format", "json", "--limit", "5" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ - assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v2\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v1\"")); //$NON-NLS-1$ assertTrue(json.contains("\"resultKind\":\"table\"")); //$NON-NLS-1$ assertTrue(json.contains("\"items\":[{")); //$NON-NLS-1$ assertTrue(json.contains("\"class_name\":")); //$NON-NLS-1$ @@ -195,17 +196,15 @@ public void executesInspectObjectCommandAgainstHprofSnapshotAsJson() throws Exce address, "--limit", "5", "--depth", "3" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ assertTrue(json.contains("\"resultKind\":\"tree\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"path\":\"\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"valueKind\":\"preview\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"hasChildren\":false")); //$NON-NLS-1$ assertTrue(json.contains("\"kind\":\"object\"")); //$NON-NLS-1$ assertTrue(json.contains("\"name\":\"\"")); //$NON-NLS-1$ assertTrue(json.contains("\"type\":\"java.lang.String\"")); //$NON-NLS-1$ assertTrue(json.contains("\"value\":\"\\\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"object_address\":null")); //$NON-NLS-1$ assertTrue(json.contains("\"_meta\":{\"value\":{\"kind\":\"text_preview\"")); //$NON-NLS-1$ assertFalse(json.contains("\"_children\":")); //$NON-NLS-1$ - assertFalse(json.contains("\"path\":\".value\"")); //$NON-NLS-1$ + assertFalse(json.contains("\"path\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"valueKind\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"hasChildren\":")); //$NON-NLS-1$ assertFalse(json.contains("\"schema\":")); //$NON-NLS-1$ } @@ -260,12 +259,10 @@ public void executesInspectObjectCommandAgainstEnumAsTextAndJson() throws Except String json = executeJson(new String[] { "inspect-object", heap.getAbsolutePath(), "--format", "json", "--object", objectAddress }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - assertTrue(json.contains("\"valueKind\":\"primitive\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"hasChildren\":false")); //$NON-NLS-1$ - assertTrue(json.contains("\"object_address\":null")); //$NON-NLS-1$ assertTrue(json.contains("\"type\":\"" + className + "\"")); //$NON-NLS-1$ //$NON-NLS-2$ assertTrue(json.matches("(?s).*\"value\":\"[A-Z_]+\".*")); //$NON-NLS-1$ assertFalse(json.contains("\"_children\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"valueKind\":")); //$NON-NLS-1$ } @Test @@ -277,13 +274,11 @@ public void executesInspectObjectSelectFieldsAgainstHprofSnapshotAsJson() throws address, "--select-fields", "value", "--limit", "5" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ assertTrue(json.contains("\"kind\":\"field\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"path\":\"\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"valueKind\":\"reference\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"hasChildren\":true")); //$NON-NLS-1$ assertTrue(json.contains("\"name\":\"value\"")); //$NON-NLS-1$ assertTrue(json.contains("\"type\":\"char[]\"")); //$NON-NLS-1$ assertTrue(json.contains("\"_children\":[{")); //$NON-NLS-1$ assertTrue(json.contains("\"_meta\":{\"value\":{\"kind\":\"text_preview\"")); //$NON-NLS-1$ + assertFalse(json.contains("\"path\":")); //$NON-NLS-1$ } @Test @@ -295,13 +290,11 @@ public void executesInspectObjectFieldPathsToPrimitiveAsJson() throws Exception address, "--field-paths", "count" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ assertTrue(json.contains("\"kind\":\"field\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"path\":\"\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"valueKind\":\"primitive\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"hasChildren\":false")); //$NON-NLS-1$ assertTrue(json.contains("\"name\":\"count\"")); //$NON-NLS-1$ assertTrue(json.contains("\"type\":\"int\"")); //$NON-NLS-1$ assertFalse(json.contains("\"_context\":")); //$NON-NLS-1$ assertFalse(json.contains("\"_children\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"path\":")); //$NON-NLS-1$ } @Test @@ -324,11 +317,9 @@ public void executesInspectObjectRepeatedSelectFieldsAsJson() throws Exception String json = executeJson(new String[] { "inspect-object", heap.getAbsolutePath(), "--format", "json", "--object", address, "--select-fields", "count", "--select-fields", "offset" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ - assertTrue(json.contains("\"path\":\"[0]\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"path\":\"[1]\"")); //$NON-NLS-1$ assertTrue(json.contains("\"name\":\"count\"")); //$NON-NLS-1$ assertTrue(json.contains("\"name\":\"offset\"")); //$NON-NLS-1$ - assertFalse(json.contains("\"path\":\".count\"")); //$NON-NLS-1$ + assertFalse(json.contains("\"path\":")); //$NON-NLS-1$ } @Test @@ -373,7 +364,7 @@ public void executesTopConsumersCommandAgainstHprofSnapshotAsStructuredJson() th String json = executeJson(new String[] { "top-consumers", heap.getAbsolutePath(), "--format", "json", "--limit", "5" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ - assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v2\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v1\"")); //$NON-NLS-1$ assertTrue(json.contains("\"resultKind\":\"top-consumers\"")); //$NON-NLS-1$ assertTrue(json.contains("\"biggestObjects\":")); //$NON-NLS-1$ assertTrue(json.contains("\"classes\":")); //$NON-NLS-1$ @@ -386,7 +377,7 @@ public void executesDescribeCommandInJsonMode() throws Exception { String json = executeJson(new String[] { "describe", "histogram", "--format", "json" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v2\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v1\"")); //$NON-NLS-1$ assertTrue(json.contains("\"resultKind\":\"describe\"")); //$NON-NLS-1$ assertTrue(json.contains("\"name\":\"histogram\"")); //$NON-NLS-1$ assertTrue(json.contains("\"usage\":\"mat-cli histogram [--limit N] [--format text|json]\"")); //$NON-NLS-1$ @@ -425,7 +416,7 @@ public void executesSchemaCommandInJsonMode() throws Exception assertTrue(json.contains("\"resultKind\":\"schema\"")); //$NON-NLS-1$ assertTrue(json.contains("\"name\":\"top-consumers\"")); //$NON-NLS-1$ assertTrue(json.contains("\"payloadKind\":\"top-consumers\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"payloadFields\":[\"totalRetainedHeap\",\"biggestObjects[]\",\"biggestObjects[].objectAddress\",\"biggestObjectsTruncated\",\"classes[]\",\"classes[].objectAddress\",\"classesTruncated\",\"classLoaders[]\",\"classLoaders[].objectAddress\",\"classLoadersTruncated\",\"packages\",\"packagesTruncated\"]")); //$NON-NLS-1$ + assertTrue(json.contains("\"payloadFields\":[\"totalRetainedHeap\",\"biggestObjects[]\",\"biggestObjects[].objectAddress\",\"biggestObjectsTruncated when true\",\"classes[]\",\"classes[].objectAddress\",\"classesTruncated when true\",\"classLoaders[]\",\"classLoaders[].objectAddress\",\"classLoadersTruncated when true\",\"packages when available\",\"packagesTruncated when true\"]")); //$NON-NLS-1$ assertTrue(json.contains("\"jsonEnvelope\":[\"schemaVersion\",\"resultKind\",\"truncated\"")); //$NON-NLS-1$ } @@ -436,7 +427,7 @@ public void executesThreadsSchemaCommandInJsonMode() throws Exception assertTrue(json.contains("\"resultKind\":\"schema\"")); //$NON-NLS-1$ assertTrue(json.contains("\"payloadKind\":\"threads\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"threads[].stackFrames[]\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"threads[].stackFrames[] when non-empty\"")); //$NON-NLS-1$ } @Test @@ -470,7 +461,7 @@ public void executesHistogramCommandWithJsonTableContract() throws Exception File heap = copyHeap(TestSnapshots.SUN_JDK5_13_32BIT); String json = executeJson(new String[] { "histogram", heap.getAbsolutePath(), "--format", "json", "--limit", "2" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ - assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v2\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v1\"")); //$NON-NLS-1$ assertTrue(json.contains("\"resultKind\":\"table\"")); //$NON-NLS-1$ assertTrue(json.contains("\"items\":[{")); //$NON-NLS-1$ assertTrue(json.contains("\"retained_heap\":")); //$NON-NLS-1$ @@ -489,7 +480,7 @@ public void executesTopConsumersCommandWithJsonContract() throws Exception assertTrue(json.contains("\"classLoaders\":[{")); //$NON-NLS-1$ assertTrue(json.contains("\"objectAddress\":\"0x")); //$NON-NLS-1$ assertTrue(json.contains("\"packages\":{\"name\":\"\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"packagesTruncated\":")); //$NON-NLS-1$ + assertTrue(json.contains("\"packagesTruncated\":true")); //$NON-NLS-1$ assertFalse(json.contains("\"objectId\":")); //$NON-NLS-1$ assertFalse(json.contains("\"_context\":")); //$NON-NLS-1$ assertFalse(json.contains("\"suggestedNextCommands\":")); //$NON-NLS-1$ diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/cli/ResultSerializerTest.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/cli/ResultSerializerTest.java index f4ebbb23..d9981905 100644 --- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/cli/ResultSerializerTest.java +++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/cli/ResultSerializerTest.java @@ -69,9 +69,9 @@ public void serializesTableResultToJson() String json = writer.toString(); assertFalse(truncated); - assertTrue(json.contains("\"columns\":[{\"label\":\"Name\",\"type\":\"java.lang.String\"},{\"label\":\"Count\",\"type\":\"int\"}]")); //$NON-NLS-1$ - assertTrue(json.contains("\"values\":[\"Alpha\",7]")); //$NON-NLS-1$ - assertTrue(json.contains("\"context\":{\"objectId\":42}")); //$NON-NLS-1$ + assertTrue(json.contains("\"items\":[{\"name\":\"Alpha\",\"count\":7}]")); //$NON-NLS-1$ + assertFalse(json.contains("\"columns\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"context\":")); //$NON-NLS-1$ } @Test @@ -121,9 +121,8 @@ public void serializesStructuredCellErrorsWithoutInliningFakeStrings() throws Ex String json = writer.toString(); assertFalse(truncated); - assertTrue(json.contains("\"values\":[\"Alpha\",null]")); //$NON-NLS-1$ - assertTrue(json.contains("\"displayValues\":[\"Alpha\",null]")); //$NON-NLS-1$ - assertTrue(json.contains("\"cellErrors\":[{\"columnIndex\":1,\"columnLabel\":\"Count\",\"class\":\"java.lang.IllegalStateException\",\"message\":\"boom\"}]")); //$NON-NLS-1$ + assertTrue(json.contains("\"items\":[{\"name\":\"Alpha\",\"_errors\":{\"count\":{\"class\":\"java.lang.IllegalStateException\",\"message\":\"boom\"}}}]")); //$NON-NLS-1$ + assertFalse(json.contains("\"count\":null")); //$NON-NLS-1$ assertFalse(json.contains("\",\"valueKind\":\"reference\",\"hasChildren\":true,\"values\":[\"Root\",1]")); //$NON-NLS-1$ - assertTrue(json.contains("\"children\":[{\"path\":\".Leaf\",\"valueKind\":\"reference\",\"hasChildren\":false,\"values\":[\"Leaf\",2]")); //$NON-NLS-1$ - assertTrue(json.contains("\"context\":{\"objectId\":77}")); //$NON-NLS-1$ + assertTrue(json.contains("\"items\":[{\"name\":\"Root\",\"depth\":1,\"_children\":[{\"name\":\"Leaf\",\"depth\":2}]}]")); //$NON-NLS-1$ + assertFalse(json.contains("\"path\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"context\":")); //$NON-NLS-1$ } @Test @@ -172,8 +171,7 @@ public void serializesTreeResultToAgentJson() String json = writer.toString(); assertFalse(truncated); - assertTrue(json.contains("\"items\":[{\"path\":\"\",\"valueKind\":\"reference\",\"hasChildren\":true,\"name\":\"Root\",\"depth\":1")); //$NON-NLS-1$ - assertTrue(json.contains("\"_children\":[{\"path\":\".Leaf\",\"valueKind\":\"reference\",\"hasChildren\":false,\"name\":\"Leaf\",\"depth\":2")); //$NON-NLS-1$ + assertTrue(json.contains("\"items\":[{\"name\":\"Root\",\"depth\":1,\"_children\":[{\"name\":\"Leaf\",\"depth\":2}]}]")); //$NON-NLS-1$ assertFalse(json.contains("\"schema\":")); //$NON-NLS-1$ assertFalse(json.contains("\"_childrenTruncated\":false")); //$NON-NLS-1$ } @@ -236,11 +234,11 @@ public void serializesDisplayValueMetadataInAgentTreeJson() String json = writer.toString(); assertFalse(truncated); - assertTrue(json.contains("\"path\":\"\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"valueKind\":\"preview\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"hasChildren\":false")); //$NON-NLS-1$ assertTrue(json.contains("\"value\":\"61 62 63 ...\"")); //$NON-NLS-1$ assertTrue(json.contains("\"_meta\":{\"value\":{\"kind\":\"hex_preview\",\"length\":64,\"truncated\":true,\"encoding\":\"hex\"}}")); //$NON-NLS-1$ + assertFalse(json.contains("\"path\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"valueKind\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"hasChildren\":")); //$NON-NLS-1$ } @Test @@ -256,7 +254,7 @@ public void rendersTreeTextAsCompactTree() } @Test - public void rendersDecoratorSpacingInTableTextAndJsonDisplayValues() + public void rendersDecoratorSpacingInTableTextAndOmitsDisplayOnlyJsonFormatting() { TableResultSerializer serializer = new TableResultSerializer(); @@ -273,13 +271,13 @@ public void rendersDecoratorSpacingInTableTextAndJsonDisplayValues() String json = writer.toString(); assertFalse(truncated); - assertTrue(json.contains("\"values\":[\"Value\",7]")); //$NON-NLS-1$ - assertTrue(json.contains("\"displayValues\":[\"pre Value post\",\"7\"]")); //$NON-NLS-1$ - assertFalse(json.contains("preValuepost")); //$NON-NLS-1$ + assertTrue(json.contains("\"items\":[{\"name\":\"Value\",\"count\":7}]")); //$NON-NLS-1$ + assertFalse(json.contains("pre Value post")); //$NON-NLS-1$ + assertFalse(json.contains("displayValues")); //$NON-NLS-1$ } @Test - public void rendersDecoratorSpacingInTreeTextAndPreservesPathValues() + public void rendersDecoratorSpacingInTreeTextAndCompactsJsonValues() { TreeResultSerializer serializer = new TreeResultSerializer(); @@ -297,9 +295,27 @@ public void rendersDecoratorSpacingInTreeTextAndPreservesPathValues() String json = writer.toString(); assertFalse(truncated); - assertTrue(json.contains("\"path\":\".preLeafpost\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"values\":[\"Leaf\",2]")); //$NON-NLS-1$ - assertTrue(json.contains("\"displayValues\":[\"pre Leaf post\",\"2\"]")); //$NON-NLS-1$ + assertTrue(json.contains("\"items\":[{\"name\":\"Root\",\"depth\":1,\"_children\":[{\"name\":\"Leaf\",\"depth\":2}]}]")); //$NON-NLS-1$ + assertFalse(json.contains("pre Leaf post")); //$NON-NLS-1$ + assertFalse(json.contains("\"path\":")); //$NON-NLS-1$ + } + + @Test + public void preservesIndexedSlotsWhenNullChildrenAreCompacted() + { + TreeResultSerializer serializer = new TreeResultSerializer(); + JsonWriter writer = new JsonWriter(); + writer.beginObject(); + boolean truncated = serializer.writeAgentJson(writer, new SparseIndexedTree(), new SerializationOptions(10, 8)); + writer.name("truncated").value(truncated); //$NON-NLS-1$ + writer.endObject(); + + String json = writer.toString(); + assertFalse(truncated); + assertTrue(json.contains("\"slot\":1")); //$NON-NLS-1$ + assertTrue(json.contains("\"slot\":3")); //$NON-NLS-1$ + assertFalse(json.contains("\"name\":\"[0]\"")); //$NON-NLS-1$ + assertFalse(json.contains("\"name\":\"[2]\"")); //$NON-NLS-1$ } @Test @@ -365,7 +381,7 @@ public void rendersInlineInspectorRootValuesWithoutReferenceSyntax() } @Test - public void keepsHasChildrenWhenAgentTreeDepthTruncatesChildren() + public void marksTreeChildrenAsTruncatedWhenDepthLimitPreventsExpansion() { TreeResultSerializer serializer = new TreeResultSerializer(); JsonWriter writer = new JsonWriter(); @@ -376,13 +392,13 @@ public void keepsHasChildrenWhenAgentTreeDepthTruncatesChildren() String json = writer.toString(); assertTrue(truncated); - assertTrue(json.contains("\"path\":\"\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"hasChildren\":true")); //$NON-NLS-1$ + assertTrue(json.contains("\"name\":\"Root\"")); //$NON-NLS-1$ assertFalse(json.contains("\"_children\":")); //$NON-NLS-1$ + assertTrue(json.contains("\"_childrenTruncated\":true")); //$NON-NLS-1$ } @Test - public void serializesNullTreeValuesAsNullValueKind() + public void omitsNullTreeValuesFromCompactJson() { TreeResultSerializer serializer = new TreeResultSerializer(); JsonWriter writer = new JsonWriter(); @@ -393,9 +409,9 @@ public void serializesNullTreeValuesAsNullValueKind() String json = writer.toString(); assertFalse(truncated); - assertTrue(json.contains("\"path\":\"\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"valueKind\":\"null\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"value\":null")); //$NON-NLS-1$ + assertTrue(json.contains("\"items\":[{\"name\":\"Slot\"}]")); //$NON-NLS-1$ + assertFalse(json.contains("\"valueKind\":")); //$NON-NLS-1$ + assertFalse(json.contains("\"value\":null")); //$NON-NLS-1$ } @Test @@ -410,9 +426,9 @@ public void truncatesTreeJsonByTotalNodeBudget() String json = writer.toString(); assertTrue(truncated); - assertTrue(json.contains("\"values\":[\"Root\",0]")); //$NON-NLS-1$ - assertTrue(json.contains("\"values\":[\"Child A\",1]")); //$NON-NLS-1$ - assertFalse(json.contains("\"values\":[\"Child B\",1]")); //$NON-NLS-1$ + assertTrue(json.contains("\"name\":\"Root\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"name\":\"Child A\"")); //$NON-NLS-1$ + assertFalse(json.contains("\"name\":\"Child B\"")); //$NON-NLS-1$ } @Test @@ -474,7 +490,7 @@ public void truncatesNestedSectionsByDepth() throws Exception String json = writer.toString(); assertTrue(truncated); assertTrue(json.contains("\"name\":\"Nested\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"sections\":[]")); //$NON-NLS-1$ + assertFalse(json.contains("\"sections\":[]")); //$NON-NLS-1$ } @Test @@ -497,7 +513,7 @@ public void truncatesDirectSectionChildrenByDepth() throws Exception assertTrue(truncated); assertTrue(json.contains("\"name\":\"Inner\"")); //$NON-NLS-1$ assertFalse(json.contains("\"name\":\"Leaf\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"sections\":[]")); //$NON-NLS-1$ + assertFalse(json.contains("\"sections\":[]")); //$NON-NLS-1$ } @Test @@ -518,7 +534,7 @@ public void serializesPieResultInsideSection() throws Exception assertTrue(json.contains("\"resultType\":\"pie\"")); //$NON-NLS-1$ assertTrue(json.contains("\"label\":\"Suspect 1\"")); //$NON-NLS-1$ assertTrue(json.contains("\"color\":\"#ff0000\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"context\":{\"objectId\":101}")); //$NON-NLS-1$ + assertFalse(json.contains("\"context\":")); //$NON-NLS-1$ } @Test @@ -565,14 +581,12 @@ public void serializesTopConsumersSectionToCompactJson() throws Exception String json = writer.toString(); assertTrue(truncated); assertTrue(json.contains("\"totalRetainedHeap\":1000")); //$NON-NLS-1$ - assertTrue(json.contains("\"biggestObjects\":{")); //$NON-NLS-1$ - assertTrue(json.contains("\"biggestObjects\":{\"name\":\"Biggest Objects\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"biggestObjects\":[{")); //$NON-NLS-1$ assertTrue(json.contains("\"label\":\"Largest\"")); //$NON-NLS-1$ assertTrue(json.contains("\"retainedBytes\":500")); //$NON-NLS-1$ - assertTrue(json.contains("\"classes\":{\"name\":\"Biggest Top-Level Dominator Classes\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"classLoaders\":{\"name\":\"Biggest Top-Level Dominator Class Loaders\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"packages\":{\"name\":\"Biggest Top-Level Dominator Packages\"")); //$NON-NLS-1$ - assertTrue(json.contains("\"root\":{\"name\":\"\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"classes\":[{")); //$NON-NLS-1$ + assertTrue(json.contains("\"classLoaders\":[{")); //$NON-NLS-1$ + assertTrue(json.contains("\"packages\":{\"name\":\"\"")); //$NON-NLS-1$ } @Test @@ -682,7 +696,7 @@ public void serializesAgentErrorEnvelope() throws Exception } String json = output.toString(StandardCharsets.UTF_8.name()); - assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v2\"")); //$NON-NLS-1$ + assertTrue(json.contains("\"schemaVersion\":\"mat-cli/v1\"")); //$NON-NLS-1$ assertTrue(json.contains("\"resultKind\":\"error\"")); //$NON-NLS-1$ assertTrue(json.contains("\"exceptionClass\":\"java.lang.IllegalStateException\"")); //$NON-NLS-1$ assertTrue(json.contains("\"rootCauseClass\":\"java.lang.NullPointerException\"")); //$NON-NLS-1$ @@ -712,6 +726,8 @@ public void serializesThreadsJsonEnvelope() throws Exception assertTrue(json.contains("\"stackAvailable\":false")); //$NON-NLS-1$ assertTrue(json.contains("\"objectAddress\":\"0x2a\"")); //$NON-NLS-1$ assertFalse(json.contains("\"_context\":")); //$NON-NLS-1$ + assertTrue(json.contains("\"stackUnavailableReason\":\"" + ThreadsResult.STACK_UNAVAILABLE_REASON + "\"")); //$NON-NLS-1$ //$NON-NLS-2$ + assertFalse(json.contains("\"stackFrames\":[]")); //$NON-NLS-1$ } @Test @@ -1132,6 +1148,61 @@ public List getChildren(Object parent) } } + private static final class SparseIndexedTree implements IResultTree + { + private final IndexedNode root = new IndexedNode("Root", Integer.valueOf(0), 120, Arrays.asList( //$NON-NLS-1$ + new IndexedNode("[0]", null, -1, Collections.emptyList()), //$NON-NLS-1$ + new IndexedNode("[1]", Integer.valueOf(1), 121, Collections.emptyList()), //$NON-NLS-1$ + new IndexedNode("[2]", null, -1, Collections.emptyList()), //$NON-NLS-1$ + new IndexedNode("[3]", Integer.valueOf(1), 123, Collections.emptyList()))); //$NON-NLS-1$ + private final Column[] columns = new Column[] { new Column("Name", String.class), new Column("Value", int.class) }; //$NON-NLS-1$ //$NON-NLS-2$ + + public ResultMetaData getResultMetaData() + { + return null; + } + + public Column[] getColumns() + { + return columns; + } + + public Object getColumnValue(Object row, int columnIndex) + { + IndexedNode value = (IndexedNode) row; + return columnIndex == 0 ? value.name : value.value; + } + + public IContextObject getContext(Object row) + { + final IndexedNode value = (IndexedNode) row; + if (value.objectId < 0) + return null; + return new IContextObject() + { + public int getObjectId() + { + return value.objectId; + } + }; + } + + public List getElements() + { + return Collections.singletonList(root); + } + + public boolean hasChildren(Object element) + { + return !((IndexedNode) element).children.isEmpty(); + } + + public List getChildren(Object parent) + { + return ((IndexedNode) parent).children; + } + } + private static final class InspectorNullTree implements IResultTree, TreeTextStyleProvider { private final InspectorNode root = new InspectorNode("object", "", "sample.Type", "sample.Type", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ @@ -1765,6 +1836,22 @@ private Node(String name, Integer depth, int objectId, List children) } } + private static final class IndexedNode + { + private final String name; + private final Integer value; + private final int objectId; + private final List children; + + private IndexedNode(String name, Integer value, int objectId, List children) + { + this.name = name; + this.value = value; + this.objectId = objectId; + this.children = children; + } + } + private static final class PackageNode { private final String name;