1 /******************************************************************************* 2 * Copyright (c) 2017 Till Brychcy and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Till Brychcy - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.compiler.env; 15 16 import java.io.File; 17 import java.io.IOException; 18 import java.util.jar.JarFile; 19 import java.util.jar.Manifest; 20 21 public class AutomaticModuleNaming { 22 23 private static final String AUTOMATIC_MODULE_NAME = "Automatic-Module-Name"; //$NON-NLS-1$ 24 25 /** 26 * Determine the automatic module name of a given jar as specified in {@link <a href= 27 * "http://download.java.net/java/jdk9/docs/api/java/lang/module/ModuleFinder.html#of-java.nio.file.Path...-"> 28 * ModuleFinder.of</a>} 29 */ determineAutomaticModuleName(final String jarFileName)30 public static char[] determineAutomaticModuleName(final String jarFileName) { 31 // "If the JAR file has the attribute "Automatic-Module-Name" in its main manifest then its value is the 32 // module name." 33 try (JarFile jar = new JarFile(jarFileName)) { 34 Manifest manifest = jar.getManifest(); 35 if (manifest != null) { 36 String automaticModuleName = manifest.getMainAttributes().getValue(AUTOMATIC_MODULE_NAME); 37 if (automaticModuleName != null) { 38 return automaticModuleName.toCharArray(); 39 } 40 } 41 } catch (IOException e) { 42 // ignore 43 } 44 // The module name is otherwise derived from the name of the JAR file. 45 return determineAutomaticModuleNameFromFileName(jarFileName, true, true); 46 } 47 48 /** 49 * Determine the automatic module name of a given jar or project as specified in {@link <a href= 50 * "http://download.java.net/java/jdk9/docs/api/java/lang/module/ModuleFinder.html#of-java.nio.file.Path...-"> 51 * ModuleFinder.of</a>} 52 * @param fileName names either a jar file or a java project in the workspace 53 * @param isFile <code>true</code> indicates that fileName denotes a file, <code>false</code> must be used for projects 54 * @param manifest representation of the META-INF/MANIFEST.MF entry within the given source (jar or project), or <code>null</code> 55 * @return the derived module name or <code>null</code> 56 */ determineAutomaticModuleName(final String fileName, boolean isFile, Manifest manifest)57 public static char[] determineAutomaticModuleName(final String fileName, boolean isFile, Manifest manifest) { 58 if (manifest != null) { 59 String automaticModuleName = manifest.getMainAttributes().getValue(AUTOMATIC_MODULE_NAME); 60 if (automaticModuleName != null) { 61 return automaticModuleName.toCharArray(); 62 } 63 } 64 return determineAutomaticModuleNameFromFileName(fileName, true, isFile); 65 } 66 67 /** 68 * Determine the automatic module name of a given jar or project as defined by an Automatic-Module-Name 69 * header in its manifest. 70 * @param manifest representation of the META-INF/MANIFEST.MF entry within the given source (jar or project), or <code>null</code> 71 * @return the derived module name or <code>null</code> 72 */ determineAutomaticModuleNameFromManifest(Manifest manifest)73 public static char[] determineAutomaticModuleNameFromManifest(Manifest manifest) { 74 if (manifest != null) { 75 String automaticModuleName = manifest.getMainAttributes().getValue(AUTOMATIC_MODULE_NAME); 76 if (automaticModuleName != null) { 77 return automaticModuleName.toCharArray(); 78 } 79 } 80 return null; 81 } 82 83 /** 84 * Determine the automatic module name if no "Automatic-Module-Name" was found in the Manifest, as specified in 85 * {@link <a href= 86 * "http://download.java.net/java/jdk9/docs/api/java/lang/module/ModuleFinder.html#of-java.nio.file.Path...-">ModuleFinder.of</a>} 87 * 88 * @param name 89 * the filename (or directory name) 90 * @param skipDirectory 91 * if true, parent directory names are skipped 92 * @param removeExtension 93 * if true, the ".jar" extension is removed. 94 */ determineAutomaticModuleNameFromFileName(String name, boolean skipDirectory, boolean removeExtension)95 public static char[] determineAutomaticModuleNameFromFileName(String name, boolean skipDirectory, 96 boolean removeExtension) { 97 int index; 98 int start = 0; 99 int end = name.length(); 100 if (skipDirectory) { 101 index = name.lastIndexOf(File.separatorChar); 102 start = index + 1; 103 } 104 105 // "The ".jar" suffix is removed" 106 if (removeExtension) { 107 if (name.endsWith(".jar") || name.endsWith(".JAR")) { //$NON-NLS-1$//$NON-NLS-2$ 108 end -= 4; 109 } 110 } 111 112 // "If the name matches the regular expression "-(\\d+(\\.|$))" then the module name will be derived from the 113 // subsequence preceding the hyphen of the first occurrence. [...]" 114 dashLoop: for (index = start; index < end - 1; index++) { 115 if (name.charAt(index) == '-' && name.charAt(index + 1) >= '0' && name.charAt(index + 1) <= '9') { 116 for (int index2 = index + 2; index2 < end; index2++) { 117 final char c = name.charAt(index2); 118 if (c == '.') { 119 break; 120 } 121 if (c < '0' || c > '9') { 122 continue dashLoop; 123 } 124 } 125 end = index; 126 break; 127 } 128 } 129 130 // "All non-alphanumeric characters ([^A-Za-z0-9]) in the module name are replaced with a dot ("."), all 131 // repeating dots are replaced with one dot, and all leading and trailing dots are removed." 132 StringBuilder sb = new StringBuilder(end - start); 133 boolean needDot = false; 134 for (int i = start; i < end; i++) { 135 char c = name.charAt(i); 136 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { 137 if (needDot) { 138 sb.append('.'); 139 needDot = false; 140 } 141 sb.append(c); 142 } else { 143 if (sb.length() > 0) { 144 needDot = true; 145 } 146 } 147 } 148 return sb.toString().toCharArray(); 149 } 150 151 } 152