1719 return tmpfile; |
1732 return tmpfile; |
1720 } |
1733 } |
1721 |
1734 |
1722 // Modular jar support |
1735 // Modular jar support |
1723 |
1736 |
1724 static <T> String toString(Collection<T> c, |
1737 /** |
1725 CharSequence prefix, |
1738 * Associates a module descriptor's zip entry name along with its |
1726 CharSequence suffix ) { |
1739 * bytes and an optional URI. Used when describing modules. |
1727 if (c.isEmpty()) |
1740 */ |
1728 return ""; |
1741 interface ModuleInfoEntry { |
1729 return c.stream().map(e -> e.toString()) |
1742 String name(); |
1730 .collect(joining(", ", prefix, suffix)); |
1743 Optional<String> uriString(); |
1731 } |
1744 InputStream bytes() throws IOException; |
1732 |
1745 } |
|
1746 |
|
1747 static class ZipFileModuleInfoEntry implements ModuleInfoEntry { |
|
1748 private final ZipFile zipFile; |
|
1749 private final ZipEntry entry; |
|
1750 ZipFileModuleInfoEntry(ZipFile zipFile, ZipEntry entry) { |
|
1751 this.zipFile = zipFile; |
|
1752 this.entry = entry; |
|
1753 } |
|
1754 @Override public String name() { return entry.getName(); } |
|
1755 @Override public InputStream bytes() throws IOException { |
|
1756 return zipFile.getInputStream(entry); |
|
1757 } |
|
1758 /** Returns an optional containing the effective URI. */ |
|
1759 @Override public Optional<String> uriString() { |
|
1760 String uri = (Paths.get(zipFile.getName())).toUri().toString(); |
|
1761 uri = "jar:" + uri + "/!" + entry.getName(); |
|
1762 return Optional.of(uri); |
|
1763 } |
|
1764 } |
|
1765 |
|
1766 static class StreamedModuleInfoEntry implements ModuleInfoEntry { |
|
1767 private final String name; |
|
1768 private final byte[] bytes; |
|
1769 StreamedModuleInfoEntry(String name, byte[] bytes) { |
|
1770 this.name = name; |
|
1771 this.bytes = bytes; |
|
1772 } |
|
1773 @Override public String name() { return name; } |
|
1774 @Override public InputStream bytes() throws IOException { |
|
1775 return new ByteArrayInputStream(bytes); |
|
1776 } |
|
1777 /** Returns an empty optional. */ |
|
1778 @Override public Optional<String> uriString() { |
|
1779 return Optional.empty(); // no URI can be derived |
|
1780 } |
|
1781 } |
|
1782 |
|
1783 /** Describes a module from a given zip file. */ |
1733 private boolean describeModule(ZipFile zipFile) throws IOException { |
1784 private boolean describeModule(ZipFile zipFile) throws IOException { |
1734 ZipEntry[] zes = zipFile.stream() |
1785 ZipFileModuleInfoEntry[] infos = zipFile.stream() |
1735 .filter(e -> isModuleInfoEntry(e.getName())) |
1786 .filter(e -> isModuleInfoEntry(e.getName())) |
1736 .sorted(Validator.ENTRY_COMPARATOR) |
1787 .sorted(Validator.ENTRY_COMPARATOR) |
1737 .toArray(ZipEntry[]::new); |
1788 .map(e -> new ZipFileModuleInfoEntry(zipFile, e)) |
1738 |
1789 .toArray(ZipFileModuleInfoEntry[]::new); |
1739 if (zes.length == 0) { |
1790 |
1740 // No module descriptor found, derive the automatic module name |
1791 if (infos.length == 0) { |
|
1792 // No module descriptor found, derive and describe the automatic module |
1741 String fn = zipFile.getName(); |
1793 String fn = zipFile.getName(); |
1742 ModuleFinder mf = ModuleFinder.of(Paths.get(fn)); |
1794 ModuleFinder mf = ModuleFinder.of(Paths.get(fn)); |
1743 try { |
1795 try { |
1744 Set<ModuleReference> mref = mf.findAll(); |
1796 Set<ModuleReference> mref = mf.findAll(); |
1745 if (mref.isEmpty()) { |
1797 if (mref.isEmpty()) { |
1746 output(formatMsg("error.unable.derive.automodule", fn)); |
1798 output(formatMsg("error.unable.derive.automodule", fn)); |
1747 return true; |
1799 return true; |
1748 } |
1800 } |
1749 ModuleDescriptor md = mref.iterator().next().descriptor(); |
1801 ModuleDescriptor md = mref.iterator().next().descriptor(); |
1750 output(getMsg("out.automodule")); |
1802 output(getMsg("out.automodule") + "\n"); |
1751 describeModule(md, null, null, "automatic"); |
1803 describeModule(md, null, null, ""); |
1752 } catch (FindException e) { |
1804 } catch (FindException e) { |
1753 String msg = formatMsg("error.unable.derive.automodule", fn); |
1805 String msg = formatMsg("error.unable.derive.automodule", fn); |
1754 Throwable t = e.getCause(); |
1806 Throwable t = e.getCause(); |
1755 if (t != null) |
1807 if (t != null) |
1756 msg = msg + "\n" + t.getMessage(); |
1808 msg = msg + "\n" + t.getMessage(); |
1757 output(msg); |
1809 output(msg); |
1758 } |
1810 } |
1759 } else { |
1811 } else { |
1760 for (ZipEntry ze : zes) { |
1812 return describeModuleFromEntries(infos); |
1761 try (InputStream is = zipFile.getInputStream(ze)) { |
|
1762 describeModule(is, ze.getName()); |
|
1763 } |
|
1764 } |
|
1765 } |
1813 } |
1766 return true; |
1814 return true; |
1767 } |
1815 } |
1768 |
1816 |
1769 private boolean describeModule(FileInputStream fis) |
1817 private boolean describeModuleFromStream(FileInputStream fis) |
1770 throws IOException |
1818 throws IOException |
1771 { |
1819 { |
|
1820 List<ModuleInfoEntry> infos = new LinkedList<>(); |
|
1821 |
1772 try (BufferedInputStream bis = new BufferedInputStream(fis); |
1822 try (BufferedInputStream bis = new BufferedInputStream(fis); |
1773 ZipInputStream zis = new ZipInputStream(bis)) { |
1823 ZipInputStream zis = new ZipInputStream(bis)) { |
1774 ZipEntry e; |
1824 ZipEntry e; |
1775 while ((e = zis.getNextEntry()) != null) { |
1825 while ((e = zis.getNextEntry()) != null) { |
1776 String ename = e.getName(); |
1826 String ename = e.getName(); |
1777 if (isModuleInfoEntry(ename)){ |
1827 if (isModuleInfoEntry(ename)) { |
1778 moduleInfos.put(ename, zis.readAllBytes()); |
1828 infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes())); |
1779 } |
1829 } |
1780 } |
1830 } |
1781 } |
1831 } |
1782 if (moduleInfos.size() == 0) |
1832 |
|
1833 if (infos.size() == 0) |
1783 return false; |
1834 return false; |
1784 String[] names = moduleInfos.keySet().stream() |
1835 |
1785 .sorted(Validator.ENTRYNAME_COMPARATOR) |
1836 ModuleInfoEntry[] sorted = infos.stream() |
1786 .toArray(String[]::new); |
1837 .sorted(Comparator.comparing(ModuleInfoEntry::name, ENTRYNAME_COMPARATOR)) |
1787 for (String name : names) { |
1838 .toArray(ModuleInfoEntry[]::new); |
1788 describeModule(new ByteArrayInputStream(moduleInfos.get(name)), name); |
1839 |
|
1840 return describeModuleFromEntries(sorted); |
|
1841 } |
|
1842 |
|
1843 private boolean lessThanEqualReleaseValue(ModuleInfoEntry entry) { |
|
1844 return intVersionFromEntry(entry) <= releaseValue ? true : false; |
|
1845 } |
|
1846 |
|
1847 private static String versionFromEntryName(String name) { |
|
1848 String s = name.substring(VERSIONS_DIR_LENGTH); |
|
1849 return s.substring(0, s.indexOf("/")); |
|
1850 } |
|
1851 |
|
1852 private static int intVersionFromEntry(ModuleInfoEntry entry) { |
|
1853 String name = entry.name(); |
|
1854 if (!name.startsWith(VERSIONS_DIR)) |
|
1855 return BASE_VERSION; |
|
1856 |
|
1857 String s = name.substring(VERSIONS_DIR_LENGTH); |
|
1858 s = s.substring(0, s.indexOf('/')); |
|
1859 return Integer.valueOf(s); |
|
1860 } |
|
1861 |
|
1862 /** |
|
1863 * Describes a single module descriptor, determined by the specified |
|
1864 * --release, if any, from the given ordered entries. |
|
1865 * The given infos must be ordered as per ENTRY_COMPARATOR. |
|
1866 */ |
|
1867 private boolean describeModuleFromEntries(ModuleInfoEntry[] infos) |
|
1868 throws IOException |
|
1869 { |
|
1870 assert infos.length > 0; |
|
1871 |
|
1872 // Informative: output all non-root descriptors, if any |
|
1873 String releases = Arrays.stream(infos) |
|
1874 .filter(e -> !e.name().equals(MODULE_INFO)) |
|
1875 .map(ModuleInfoEntry::name) |
|
1876 .map(Main::versionFromEntryName) |
|
1877 .collect(joining(" ")); |
|
1878 if (!releases.equals("")) |
|
1879 output("releases: " + releases + "\n"); |
|
1880 |
|
1881 // Describe the operative descriptor for the specified --release, if any |
|
1882 if (releaseValue != -1) { |
|
1883 ModuleInfoEntry entry = null; |
|
1884 int i = 0; |
|
1885 while (i < infos.length && lessThanEqualReleaseValue(infos[i])) { |
|
1886 entry = infos[i]; |
|
1887 i++; |
|
1888 } |
|
1889 |
|
1890 if (entry == null) { |
|
1891 output(formatMsg("error.no.operative.descriptor", |
|
1892 String.valueOf(releaseValue))); |
|
1893 return false; |
|
1894 } |
|
1895 |
|
1896 String uriString = entry.uriString().orElse(""); |
|
1897 try (InputStream is = entry.bytes()) { |
|
1898 describeModule(is, uriString); |
|
1899 } |
|
1900 } else { |
|
1901 // no specific --release specified, output the root, if any |
|
1902 if (infos[0].name().equals(MODULE_INFO)) { |
|
1903 String uriString = infos[0].uriString().orElse(""); |
|
1904 try (InputStream is = infos[0].bytes()) { |
|
1905 describeModule(is, uriString); |
|
1906 } |
|
1907 } else { |
|
1908 // no root, output message to specify --release |
|
1909 output(getMsg("error.no.root.descriptor")); |
|
1910 } |
1789 } |
1911 } |
1790 return true; |
1912 return true; |
1791 } |
1913 } |
1792 |
1914 |
1793 static <T> String toString(Collection<T> set) { |
1915 static <T> String toString(Collection<T> set) { |
1794 if (set.isEmpty()) { return ""; } |
1916 if (set.isEmpty()) { return ""; } |
1795 return set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT)) |
1917 return " " + set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT)) |
1796 .collect(joining(" ")); |
1918 .sorted().collect(joining(" ")); |
1797 } |
1919 } |
1798 |
1920 |
1799 private void describeModule(InputStream entryInputStream, String ename) |
1921 |
|
1922 private void describeModule(InputStream entryInputStream, String uriString) |
1800 throws IOException |
1923 throws IOException |
1801 { |
1924 { |
1802 ModuleInfo.Attributes attrs = ModuleInfo.read(entryInputStream, null); |
1925 ModuleInfo.Attributes attrs = ModuleInfo.read(entryInputStream, null); |
1803 ModuleDescriptor md = attrs.descriptor(); |
1926 ModuleDescriptor md = attrs.descriptor(); |
1804 ModuleTarget target = attrs.target(); |
1927 ModuleTarget target = attrs.target(); |
1805 ModuleHashes hashes = attrs.recordedHashes(); |
1928 ModuleHashes hashes = attrs.recordedHashes(); |
1806 |
1929 |
1807 describeModule(md, target, hashes, ename); |
1930 describeModule(md, target, hashes, uriString); |
1808 } |
1931 } |
1809 |
1932 |
1810 private void describeModule(ModuleDescriptor md, |
1933 private void describeModule(ModuleDescriptor md, |
1811 ModuleTarget target, |
1934 ModuleTarget target, |
1812 ModuleHashes hashes, |
1935 ModuleHashes hashes, |
1813 String ename) |
1936 String uriString) |
1814 throws IOException |
1937 throws IOException |
1815 { |
1938 { |
1816 StringBuilder sb = new StringBuilder(); |
1939 StringBuilder sb = new StringBuilder(); |
1817 sb.append("\nmodule ") |
1940 |
1818 .append(md.toNameAndVersion()) |
1941 sb.append(md.toNameAndVersion()); |
1819 .append(" (").append(ename).append(")"); |
1942 |
1820 |
1943 if (!uriString.equals("")) |
|
1944 sb.append(" ").append(uriString); |
1821 if (md.isOpen()) |
1945 if (md.isOpen()) |
1822 sb.append("\n open "); |
1946 sb.append(" open"); |
1823 |
1947 if (md.isAutomatic()) |
1824 md.requires().stream() |
1948 sb.append(" automatic"); |
1825 .sorted(Comparator.comparing(Requires::name)) |
1949 sb.append("\n"); |
1826 .forEach(r -> { |
1950 |
1827 sb.append("\n requires "); |
1951 // unqualified exports (sorted by package) |
1828 if (!r.modifiers().isEmpty()) |
1952 md.exports().stream() |
1829 sb.append(toString(r.modifiers())).append(" "); |
1953 .sorted(Comparator.comparing(Exports::source)) |
1830 sb.append(r.name()); |
1954 .filter(e -> !e.isQualified()) |
1831 }); |
1955 .forEach(e -> sb.append("exports ").append(e.source()) |
1832 |
1956 .append(toString(e.modifiers())).append("\n")); |
|
1957 |
|
1958 // dependences |
|
1959 md.requires().stream().sorted() |
|
1960 .forEach(r -> sb.append("requires ").append(r.name()) |
|
1961 .append(toString(r.modifiers())).append("\n")); |
|
1962 |
|
1963 // service use and provides |
1833 md.uses().stream().sorted() |
1964 md.uses().stream().sorted() |
1834 .forEach(p -> sb.append("\n uses ").append(p)); |
1965 .forEach(s -> sb.append("uses ").append(s).append("\n")); |
1835 |
1966 |
|
1967 md.provides().stream() |
|
1968 .sorted(Comparator.comparing(Provides::service)) |
|
1969 .forEach(p -> sb.append("provides ").append(p.service()) |
|
1970 .append(" with") |
|
1971 .append(toString(p.providers())) |
|
1972 .append("\n")); |
|
1973 |
|
1974 // qualified exports |
1836 md.exports().stream() |
1975 md.exports().stream() |
1837 .sorted(Comparator.comparing(Exports::source)) |
1976 .sorted(Comparator.comparing(Exports::source)) |
1838 .forEach(p -> sb.append("\n exports ").append(p)); |
1977 .filter(Exports::isQualified) |
1839 |
1978 .forEach(e -> sb.append("qualified exports ").append(e.source()) |
|
1979 .append(" to").append(toString(e.targets())) |
|
1980 .append("\n")); |
|
1981 |
|
1982 // open packages |
1840 md.opens().stream() |
1983 md.opens().stream() |
1841 .sorted(Comparator.comparing(Opens::source)) |
1984 .sorted(Comparator.comparing(Opens::source)) |
1842 .forEach(p -> sb.append("\n opens ").append(p)); |
1985 .filter(o -> !o.isQualified()) |
1843 |
1986 .forEach(o -> sb.append("opens ").append(o.source()) |
1844 Set<String> concealed = new HashSet<>(md.packages()); |
1987 .append(toString(o.modifiers())) |
|
1988 .append("\n")); |
|
1989 |
|
1990 md.opens().stream() |
|
1991 .sorted(Comparator.comparing(Opens::source)) |
|
1992 .filter(Opens::isQualified) |
|
1993 .forEach(o -> sb.append("qualified opens ").append(o.source()) |
|
1994 .append(toString(o.modifiers())) |
|
1995 .append(" to").append(toString(o.targets())) |
|
1996 .append("\n")); |
|
1997 |
|
1998 // non-exported/non-open packages |
|
1999 Set<String> concealed = new TreeSet<>(md.packages()); |
1845 md.exports().stream().map(Exports::source).forEach(concealed::remove); |
2000 md.exports().stream().map(Exports::source).forEach(concealed::remove); |
1846 md.opens().stream().map(Opens::source).forEach(concealed::remove); |
2001 md.opens().stream().map(Opens::source).forEach(concealed::remove); |
1847 concealed.stream().sorted() |
2002 concealed.forEach(p -> sb.append("contains ").append(p).append("\n")); |
1848 .forEach(p -> sb.append("\n contains ").append(p)); |
2003 |
1849 |
2004 md.mainClass().ifPresent(v -> sb.append("main-class ").append(v).append("\n")); |
1850 md.provides().stream() |
|
1851 .sorted(Comparator.comparing(Provides::service)) |
|
1852 .forEach(p -> sb.append("\n provides ").append(p.service()) |
|
1853 .append(" with ") |
|
1854 .append(toString(p.providers()))); |
|
1855 |
|
1856 md.mainClass().ifPresent(v -> sb.append("\n main-class " + v)); |
|
1857 |
2005 |
1858 if (target != null) { |
2006 if (target != null) { |
1859 String osName = target.osName(); |
2007 String targetPlatform = target.targetPlatform(); |
1860 if (osName != null) |
2008 if (!targetPlatform.isEmpty()) |
1861 sb.append("\n operating-system-name " + osName); |
2009 sb.append("platform ").append(targetPlatform).append("\n"); |
1862 String osArch = target.osArch(); |
|
1863 if (osArch != null) |
|
1864 sb.append("\n operating-system-architecture " + osArch); |
|
1865 } |
2010 } |
1866 |
2011 |
1867 if (hashes != null) { |
2012 if (hashes != null) { |
1868 hashes.names().stream().sorted().forEach( |
2013 hashes.names().stream().sorted().forEach( |
1869 mod -> sb.append("\n hashes ").append(mod).append(" ") |
2014 mod -> sb.append("hashes ").append(mod).append(" ") |
1870 .append(hashes.algorithm()).append(" ") |
2015 .append(hashes.algorithm()).append(" ") |
1871 .append(toHex(hashes.hashFor(mod)))); |
2016 .append(toHex(hashes.hashFor(mod))) |
|
2017 .append("\n")); |
1872 } |
2018 } |
1873 |
2019 |
1874 output(sb.toString()); |
2020 output(sb.toString()); |
1875 } |
2021 } |
1876 |
2022 |