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 */ 25 package jdk.tools.jlink.internal.plugins; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.nio.file.FileSystem; 30 import java.nio.file.Files; 31 import java.nio.file.PathMatcher; 32 import java.util.ArrayList; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.function.ToIntFunction; 37 import jdk.tools.jlink.plugin.PluginException; 38 import jdk.tools.jlink.plugin.ModuleEntry; 39 import jdk.tools.jlink.plugin.ModulePool; 40 import jdk.tools.jlink.plugin.TransformerPlugin; 41 import jdk.tools.jlink.internal.Utils; 42 43 /** 44 * 45 * Order Resources plugin 46 */ 47 public final class OrderResourcesPlugin implements TransformerPlugin { 48 public static final String NAME = "order-resources"; 49 private static final FileSystem JRT_FILE_SYSTEM = Utils.jrtFileSystem(); 50 51 private final List<ToIntFunction<String>> filters; 52 private final Map<String, Integer> orderedPaths; 53 54 public OrderResourcesPlugin() { 55 this.filters = new ArrayList<>(); 56 this.orderedPaths = new HashMap<>(); 57 } 58 59 @Override 60 public String getName() { 61 return NAME; 62 } 63 64 static class SortWrapper { 65 private final ModuleEntry resource; 66 private final int ordinal; 67 68 SortWrapper(ModuleEntry resource, int ordinal) { 69 this.resource = resource; 70 this.ordinal = ordinal; 71 } 72 73 ModuleEntry getResource() { 74 return resource; 75 } 76 77 String getPath() { 78 return resource.getPath(); 79 } 80 81 int getOrdinal() { 82 return ordinal; 83 } 84 } 85 86 private String stripModule(String path) { 87 if (path.startsWith("/")) { 88 int index = path.indexOf('/', 1); 89 90 if (index != -1) { 91 return path.substring(index + 1); 92 } 93 } 94 95 return path; 96 } 97 98 private int getOrdinal(ModuleEntry resource) { 99 String path = resource.getPath(); 100 101 Integer value = orderedPaths.get(stripModule(path)); 102 103 if (value != null) { 104 return value; 105 } 106 107 for (ToIntFunction<String> function : filters) { 108 int ordinal = function.applyAsInt(path); 109 110 if (ordinal != Integer.MAX_VALUE) { 111 return ordinal; 112 } 113 } 114 115 return Integer.MAX_VALUE; 116 } 117 118 private static int compare(SortWrapper wrapper1, SortWrapper wrapper2) { 119 int compare = wrapper1.getOrdinal() - wrapper2.getOrdinal(); 120 121 if (compare != 0) { 122 return compare; 123 } 124 125 return wrapper1.getPath().compareTo(wrapper2.getPath()); 126 } 127 128 @Override 129 public void visit(ModulePool in, ModulePool out) { 130 in.entries() 131 .filter(resource -> resource.getType() 132 .equals(ModuleEntry.Type.CLASS_OR_RESOURCE)) 133 .map((resource) -> new SortWrapper(resource, getOrdinal(resource))) 134 .sorted(OrderResourcesPlugin::compare) 135 .forEach((wrapper) -> out.add(wrapper.getResource())); 136 in.entries() 137 .filter(other -> !other.getType() 138 .equals(ModuleEntry.Type.CLASS_OR_RESOURCE)) 139 .forEach((other) -> out.add(other)); 140 } 141 142 @Override 143 public Category getType() { 144 return Category.SORTER; 145 } 146 147 @Override 148 public String getDescription() { 149 return PluginsResourceBundle.getDescription(NAME); 150 } 151 152 @Override 153 public boolean hasArguments() { 154 return true; 155 } 156 157 @Override 158 public String getArgumentsDescription() { 159 return PluginsResourceBundle.getArgument(NAME); 160 } 161 162 @Override 163 public void configure(Map<String, String> config) { 164 List<String> patterns = Utils.parseList(config.get(NAME)); 165 int ordinal = 0; 166 167 for (String pattern : patterns) { 168 if (pattern.startsWith("@")) { 169 File file = new File(pattern.substring(1)); 170 171 if (file.exists()) { 172 List<String> lines; 173 174 try { 175 lines = Files.readAllLines(file.toPath()); 176 } catch (IOException ex) { 177 throw new PluginException(ex); 178 } 179 180 for (String line : lines) { 181 if (!line.startsWith("#")) { 182 orderedPaths.put(line + ".class", ordinal++); 183 } 184 } 185 } 186 } else { 187 final int result = ordinal++; 188 final PathMatcher matcher = Utils.getPathMatcher(JRT_FILE_SYSTEM, pattern); 189 ToIntFunction<String> function = (path)-> matcher.matches(JRT_FILE_SYSTEM.getPath(path)) ? result : Integer.MAX_VALUE; 190 filters.add(function); 191 } 192 } 193 } 194 }