diff --git a/src/main/java/org/extendj/ragdoc/JsonBuilder.java b/src/main/java/org/extendj/ragdoc/JsonBuilder.java index 3b56ce98287ed8e15c4b456d917e67900743329b..b4db22c6eb38ca354c83edd7721e1e9b50348887 100644 --- a/src/main/java/org/extendj/ragdoc/JsonBuilder.java +++ b/src/main/java/org/extendj/ragdoc/JsonBuilder.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, Jesper Öqvist <jesper.oqvist@cs.lth.se> +/* Copyright (c) 2013-2018, Jesper Öqvist <jesper.oqvist@cs.lth.se> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,7 @@ import org.extendj.ast.ParameterDeclaration; import org.extendj.ast.TypeDecl; import org.extendj.ast.Variable; import org.extendj.util.RelativePath; +import org.extendj.util.Sorting; import se.llbit.json.Json; import se.llbit.json.JsonArray; import se.llbit.json.JsonObject; @@ -50,14 +51,12 @@ import se.llbit.json.JsonValue; import java.io.File; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; public class JsonBuilder { private Map<String, String> typeIndex = new HashMap<>(); @@ -463,19 +462,31 @@ public class JsonBuilder { if (!methodArray.isEmpty()) { JsonObject inherited = new JsonObject(); inherited.add("superclass", typeRef(superclass)); - inherited.add("members", methodArray); + JsonArray sorted = new JsonArray(); + for (JsonValue member : Sorting.sortBy(methodArray, Sorting.jsonStringFun)) { + sorted.add(member); + } + inherited.add("members", sorted); inheritedMethods.add(inherited); } if (!attributeArray.isEmpty()) { JsonObject inherited = new JsonObject(); inherited.add("superclass", typeRef(superclass)); - inherited.add("members", attributeArray); + JsonArray sorted = new JsonArray(); + for (JsonValue member : Sorting.sortBy(attributeArray, Sorting.jsonStringFun)) { + sorted.add(member); + } + inherited.add("members", sorted); inheritedAttributes.add(inherited); } if (!fieldArray.isEmpty()) { JsonObject inherited = new JsonObject(); inherited.add("superclass", typeRef(superclass)); - inherited.add("members", fieldArray); + JsonArray sorted = new JsonArray(); + for (JsonValue member : Sorting.sortBy(fieldArray, Sorting.jsonStringFun)) { + sorted.add(member); + } + inherited.add("members", sorted); inheritedFields.add(inherited); } klass = superclass; @@ -508,12 +519,7 @@ public class JsonBuilder { JsonObject group = new JsonObject(); group.add("kind", Json.of(kind)); JsonArray sortedMembers = new JsonArray(); - KeyFun<JsonValue, String> keyFun = new KeyFun<JsonValue, String>() { - @Override public String apply(JsonValue value) { - return value.object().get("name").asString(""); - } - }; - for (JsonValue v : sortBy(groupMap.get(kind), keyFun)) { + for (JsonValue v : Sorting.sortBy(groupMap.get(kind), Sorting.objectNameFun)) { sortedMembers.add(v); } group.add("members", sortedMembers); @@ -549,12 +555,7 @@ public class JsonBuilder { packageIndex.add(entry); } JsonArray sorted = new JsonArray(); - KeyFun<JsonObject, String> keyFun = new KeyFun<JsonObject, String>() { - @Override public String apply(JsonObject value) { - return value.object().get("name").asString(""); - } - }; - for (JsonValue v : sortBy(packageIndex, keyFun)) { + for (JsonValue v : Sorting.sortBy(packageIndex, Sorting.objectNameKeyFun)) { sorted.add(v); } return sorted; @@ -563,47 +564,11 @@ public class JsonBuilder { public JsonArray subtypesJson(TypeDecl type) { JsonArray subtypes = new JsonArray(); if (subtypeMap.containsKey(type)) { - KeyFun<TypeDecl, String> keyFun = new KeyFun<TypeDecl, String>() { - @Override public String apply(TypeDecl type) { - return type.name(); - } - }; - for (TypeDecl subtype : sortBy(subtypeMap.get(type), keyFun)) { + for (TypeDecl subtype : Sorting.sortBy(subtypeMap.get(type), Sorting.typeNameFun)) { subtypes.add(typeRef(subtype)); } } return subtypes; } - public interface KeyFun<T, R> { - R apply(T t); - } - - /** - * Returns a sorted JSON array based on the argument elements and a key function. - * - * <p>The result preserves duplicate elements and elements with identical keys.</p> - */ - static <T, K extends Comparable<? super K>> List<T> sortBy( - Iterable<T> iterable, - KeyFun<T, K> keyFun) { - Map<K, Collection<T>> members = new TreeMap<>(); - for (T value : iterable) { - K sortKey = keyFun.apply(value); - Collection<T> items = members.get(sortKey); - if (items == null) { - items = new ArrayList<>(); - members.put(sortKey, items); - } - items.add(value); - } - List<K> keys = new ArrayList<>(members.keySet()); - Collections.sort(keys); - ArrayList<T> result = new ArrayList<>(members.size()); - for (Collection<T> values : members.values()) { - result.addAll(values); - } - return result; - } - } diff --git a/src/main/java/org/extendj/util/Sorting.java b/src/main/java/org/extendj/util/Sorting.java new file mode 100644 index 0000000000000000000000000000000000000000..db10ddbfa7f5876b681b9d8a3a324d76f3d44a97 --- /dev/null +++ b/src/main/java/org/extendj/util/Sorting.java @@ -0,0 +1,103 @@ +/* Copyright (c) 2018, Jesper Öqvist <jesper.oqvist@cs.lth.se> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.extendj.util; + +import org.extendj.ast.TypeDecl; +import se.llbit.json.JsonObject; +import se.llbit.json.JsonValue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Helper functions for sorting collections. + */ +public class Sorting { + public interface KeyFun<T, R> { + R apply(T t); + } + + // These can be converted to Java 8 lambdas: + + public static final KeyFun<JsonValue, String> objectNameFun = new KeyFun<JsonValue, String>() { + @Override public String apply(JsonValue value) { + return value.object().get("name").asString(""); + } + }; + + public static final KeyFun<JsonObject, String> objectNameKeyFun = new KeyFun<JsonObject, String>() { + @Override public String apply(JsonObject value) { + return value.object().get("name").asString(""); + } + }; + + public static final KeyFun<TypeDecl, String> typeNameFun = new KeyFun<TypeDecl, String>() { + @Override public String apply(TypeDecl type) { + return type.name(); + } + }; + public static final KeyFun<JsonValue, String> jsonStringFun = new KeyFun<JsonValue, String>() { + @Override public String apply(JsonValue value) { + return value.asString(""); + } + }; + + /** + * Returns a sorted JSON array based on the argument elements and a key function. + * + * <p>The result preserves duplicate elements and elements with identical keys.</p> + */ + public static <T, K extends Comparable<? super K>> List<T> sortBy( + Iterable<T> iterable, + KeyFun<T, K> keyFun) { + Map<K, Collection<T>> members = new TreeMap<>(); + for (T value : iterable) { + K sortKey = keyFun.apply(value); + Collection<T> items = members.get(sortKey); + if (items == null) { + items = new ArrayList<>(); + members.put(sortKey, items); + } + items.add(value); + } + List<K> keys = new ArrayList<>(members.keySet()); + Collections.sort(keys); + ArrayList<T> result = new ArrayList<>(members.size()); + for (Collection<T> values : members.values()) { + result.addAll(values); + } + return result; + } + +}