1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package jdk.tools.jlink.internal.plugins; 26 27import java.io.File; 28import java.io.IOException; 29import java.io.UncheckedIOException; 30import java.nio.file.FileSystem; 31import java.nio.file.Files; 32import java.nio.file.PathMatcher; 33import java.util.ArrayList; 34import java.util.HashMap; 35import java.util.List; 36import java.util.Map; 37import java.util.function.ToIntFunction; 38import jdk.tools.jlink.plugin.PluginException; 39import jdk.tools.jlink.plugin.ResourcePool; 40import jdk.tools.jlink.plugin.ResourcePoolBuilder; 41import jdk.tools.jlink.plugin.ResourcePoolEntry; 42import jdk.tools.jlink.plugin.Plugin; 43import jdk.tools.jlink.internal.Utils; 44 45/** 46 * 47 * Order Resources plugin 48 */ 49public final class OrderResourcesPlugin implements Plugin { 50 public static final String NAME = "order-resources"; 51 private static final FileSystem JRT_FILE_SYSTEM = Utils.jrtFileSystem(); 52 53 private final List<ToIntFunction<String>> filters; 54 private final Map<String, Integer> orderedPaths; 55 56 public OrderResourcesPlugin() { 57 this.filters = new ArrayList<>(); 58 this.orderedPaths = new HashMap<>(); 59 } 60 61 @Override 62 public String getName() { 63 return NAME; 64 } 65 66 static class SortWrapper { 67 private final ResourcePoolEntry resource; 68 private final int ordinal; 69 70 SortWrapper(ResourcePoolEntry resource, int ordinal) { 71 this.resource = resource; 72 this.ordinal = ordinal; 73 } 74 75 ResourcePoolEntry getResource() { 76 return resource; 77 } 78 79 String getPath() { 80 return resource.path(); 81 } 82 83 int getOrdinal() { 84 return ordinal; 85 } 86 } 87 88 private String stripModule(String path) { 89 if (path.startsWith("/")) { 90 int index = path.indexOf('/', 1); 91 92 if (index != -1) { 93 return path.substring(index + 1); 94 } 95 } 96 97 return path; 98 } 99 100 private int getOrdinal(ResourcePoolEntry resource) { 101 String path = resource.path(); 102 103 Integer value = orderedPaths.get(stripModule(path)); 104 105 if (value != null) { 106 return value; 107 } 108 109 for (ToIntFunction<String> function : filters) { 110 int ordinal = function.applyAsInt(path); 111 112 if (ordinal != Integer.MAX_VALUE) { 113 return ordinal; 114 } 115 } 116 117 return Integer.MAX_VALUE; 118 } 119 120 private static int compare(SortWrapper wrapper1, SortWrapper wrapper2) { 121 int compare = wrapper1.getOrdinal() - wrapper2.getOrdinal(); 122 123 if (compare != 0) { 124 return compare; 125 } 126 127 return wrapper1.getPath().compareTo(wrapper2.getPath()); 128 } 129 130 @Override 131 public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { 132 in.entries() 133 .filter(resource -> resource.type() 134 .equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) 135 .map((resource) -> new SortWrapper(resource, getOrdinal(resource))) 136 .sorted(OrderResourcesPlugin::compare) 137 .forEach((wrapper) -> out.add(wrapper.getResource())); 138 in.entries() 139 .filter(other -> !other.type() 140 .equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) 141 .forEach((other) -> out.add(other)); 142 143 return out.build(); 144 } 145 146 @Override 147 public Category getType() { 148 return Category.SORTER; 149 } 150 151 @Override 152 public String getDescription() { 153 return PluginsResourceBundle.getDescription(NAME); 154 } 155 156 @Override 157 public boolean hasArguments() { 158 return true; 159 } 160 161 @Override 162 public String getArgumentsDescription() { 163 return PluginsResourceBundle.getArgument(NAME); 164 } 165 166 @Override 167 public void configure(Map<String, String> config) { 168 List<String> patterns = Utils.parseList(config.get(NAME)); 169 int ordinal = 0; 170 171 for (String pattern : patterns) { 172 if (pattern.startsWith("@")) { 173 File file = new File(pattern.substring(1)); 174 175 if (file.exists()) { 176 List<String> lines; 177 178 try { 179 lines = Files.readAllLines(file.toPath()); 180 } catch (IOException ex) { 181 throw new UncheckedIOException(ex); 182 } 183 184 for (String line : lines) { 185 if (!line.startsWith("#")) { 186 orderedPaths.put(line + ".class", ordinal++); 187 } 188 } 189 } 190 } else { 191 final int result = ordinal++; 192 final PathMatcher matcher = Utils.getPathMatcher(JRT_FILE_SYSTEM, pattern); 193 ToIntFunction<String> function = (path)-> matcher.matches(JRT_FILE_SYSTEM.getPath(path)) ? result : Integer.MAX_VALUE; 194 filters.add(function); 195 } 196 } 197 } 198} 199