--- /dev/null 2015-06-23 14:29:17.000000000 +0200 +++ new/src/java.base/share/classes/jdk/internal/jimage/ImageResourcesTree.java 2015-06-23 14:29:17.000000000 +0200 @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.jimage; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * A class to build a sorted tree of Resource paths as a tree of ImageLocation. + * + */ +// XXX Public only due to the JImageTask / JImageTask code duplication +public final class ImageResourcesTree { + + private static final String MODULES = "modules"; + private static final String PACKAGES = "packages"; + public static final String MODULES_STRING = UTF8String.MODULES_STRING.toString(); + public static final String PACKAGES_STRING = UTF8String.PACKAGES_STRING.toString(); + + public static boolean isTreeInfoResource(String path) { + return path.startsWith(PACKAGES_STRING) || path.startsWith(MODULES_STRING); + } + + /** + * Path item tree node. + */ + private static final class Node { + + private final String name; + private final Map children = new TreeMap<>(); + private final Node parent; + private ImageLocationWriter loc; + + private Node(String name, Node parent) { + this.name = name; + this.parent = parent; + + if (parent != null) { + parent.children.put(name, this); + } + } + + public String getPath() { + if (parent == null) { + return "/"; + } + return buildPath(this); + } + + public String getName() { + return name; + } + + public Node getChildren(String name) { + Node item = children.get(name); + return item; + } + + private static String buildPath(Node item) { + if (item == null) { + return null; + } + String path = buildPath(item.parent); + if (path == null) { + return item.getName(); + } else { + return path + "/" + item.getName(); + } + } + } + + /** + * Tree of nodes. + */ + private static final class Tree { + + private final Map directAccess = new HashMap<>(); + private final List paths; + private final Node root; + private Node modules; + private Node packages; + + private Tree(List paths) { + this.paths = paths; + root = new Node("", null); + buildTree(); + } + + private void buildTree() { + modules = new Node(MODULES, root); + directAccess.put(modules.getPath(), modules); + + Map> moduleToPackage = new TreeMap<>(); + Map> packageToModule = new TreeMap<>(); + + for (String p : paths) { + if (!p.startsWith("/")) { + continue; + } + String[] split = p.split("/"); + Node current = modules; + String module = null; + for (int i = 0; i < split.length; i++) { + String s = split[i]; + if (!s.isEmpty()) { + if (module == null) { + module = s; + } + Node n = current.children.get(s); + if (n == null) { + n = new Node(s, current); + if (i == split.length - 1) { // Leaf + String pkg = toPackageName(n.parent); + if (pkg != null && !pkg.startsWith("META-INF")) { + Set pkgs = moduleToPackage.get(module); + if (pkgs == null) { + pkgs = new TreeSet<>(); + moduleToPackage.put(module, pkgs); + } + pkgs.add(pkg); + } + } else { // put only sub trees, no leaf + directAccess.put(n.getPath(), n); + String pkg = toPackageName(n); + if (pkg != null && !pkg.startsWith("META-INF")) { + Set mods = packageToModule.get(pkg); + if (mods == null) { + mods = new TreeSet<>(); + packageToModule.put(pkg, mods); + } + mods.add(module); + + } + } + } + current = n; + } + } + } + packages = new Node(PACKAGES, root); + directAccess.put(packages.getPath(), packages); + for (Map.Entry> entry : moduleToPackage.entrySet()) { + for (String pkg : entry.getValue()) { + Node pkgNode = new Node(pkg, packages); + directAccess.put(pkgNode.getPath(), pkgNode); + + Node modNode = new Node(entry.getKey(), pkgNode); + directAccess.put(modNode.getPath(), modNode); + } + } + for (Map.Entry> entry : packageToModule.entrySet()) { + Node pkgNode = new Node(entry.getKey(), packages); + directAccess.put(pkgNode.getPath(), pkgNode); + for (String module : entry.getValue()) { + Node modNode = new Node(module, pkgNode); + directAccess.put(modNode.getPath(), modNode); + } + } + } + + public String toResourceName(Node node) { + if (!node.children.isEmpty()) { + throw new RuntimeException("Node is not a resource"); + } + return removeRadical(node); + } + + public String getModule(Node node) { + if (node.parent == null || node.getName().equals(MODULES) || + node.getName().startsWith(PACKAGES)) { + return null; + } + String path = removeRadical(node); + // "/xxx/..."; + path = path.substring(1); + int i = path.indexOf("/"); + if (i == -1) { + return path; + } else { + return path.substring(0, i); + } + } + + public String toPackageName(Node node) { + if (node.parent == null) { + return null; + } + String path = removeRadical(node.getPath(), "/" + MODULES + "/"); + String module = getModule(node); + if (path.equals(module)) { + return null; + } + String pkg = removeRadical(path, module + "/"); + return pkg.replaceAll("/", "."); + } + + public String removeRadical(Node node) { + String s = node.getPath(); + return removeRadical(node.getPath(), "/" + MODULES); + } + + private String removeRadical(String path, String str) { + return path.substring(str.length()); + } + + public Node getRoot() { + return root; + } + + public Map getMap() { + return directAccess; + } + + private boolean isPackageNode(Node node) { + if (!node.children.isEmpty()) { + throw new RuntimeException("Node is not a package"); + } + return node.getPath().startsWith("/" + PACKAGES); + } + } + + private static final class LocationsAdder { + + private long offset; + private final List content = new ArrayList<>(); + private final BasicImageWriter writer; + private final Tree tree; + + LocationsAdder(Tree tree, long offset, BasicImageWriter writer) { + this.tree = tree; + this.offset = offset; + this.writer = writer; + addLocations(tree.getRoot()); + } + + private int addLocations(Node current) { + int[] ret = new int[current.children.size()]; + int i = 0; + for (java.util.Map.Entry entry : current.children.entrySet()) { + ret[i] = addLocations(entry.getValue()); + i += 1; + } + if (current != tree.getRoot() && (ret.length > 0 || tree.isPackageNode(current))) { + int size = ret.length * 4; + writer.addLocation(current.getPath(), offset, 0, size); + offset += size; + } + return 0; + } + + private List computeContent() { + // Map used to associate Tree item with locations offset. + Map outLocations = new HashMap<>(); + for (ImageLocationWriter wr : writer.getLocations()) { + outLocations.put(wr.getFullNameString(), wr); + } + // Attach location to node + for (Map.Entry entry : outLocations.entrySet()) { + Node item = tree.getMap().get(entry.getKey()); + if (item != null) { + item.loc = entry.getValue(); + } + } + computeContent(tree.getRoot(), outLocations); + return content; + } + + private int computeContent(Node current, Map outLocations) { + int[] ret = new int[current.children.size()]; + int i = 0; + for (java.util.Map.Entry entry : current.children.entrySet()) { + ret[i] = computeContent(entry.getValue(), outLocations); + i += 1; + } + if (ret.length > 0) { + int size = ret.length * 4; + ByteBuffer buff = ByteBuffer.allocate(size); + buff.order(writer.getByteOrder()); + for (int val : ret) { + buff.putInt(val); + } + byte[] arr = buff.array(); + content.add(arr); + } else { + if (tree.isPackageNode(current)) { + current.loc = outLocations.get(current.getPath()); + } else { + String s = tree.toResourceName(current); + current.loc = outLocations.get(s); + } + } + return current == tree.getRoot() ? 0 : current.loc.getLocationOffset(); + } + } + + private final List paths; + private final LocationsAdder adder; + + public ImageResourcesTree(long offset, BasicImageWriter writer, List paths) { + this.paths = new ArrayList<>(); + this.paths.addAll(paths); + Collections.sort(this.paths); + Tree tree = new Tree(this.paths); + adder = new LocationsAdder(tree, offset, writer); + } + + public void addContent(DataOutputStream out) throws IOException { + List content = adder.computeContent(); + for (byte[] c : content) { + out.write(c, 0, c.length); + } + } +}