chore: include nb-javac-android as subproject

This commit is contained in:
Akash Yadav
2023-02-14 18:35:37 +05:30
parent 415cf4302c
commit 29f9868f54
761 changed files with 245669 additions and 8 deletions
@@ -0,0 +1,119 @@
package com.itsaky.androidide.config;
import jdkx.lang.model.SourceVersion;
import java.lang.reflect.Field;
public class JavacConfigProvider {
/**
* Property that can be set using <code>System.setProperty</code> to override
* the default <code>java.home</code> property in compiler.
*/
public static String PROP_ANDROIDIDE_JAVA_HOME = "androidide.java.home";
/**
* The latest source version that can be modeled.
*/
private static SourceVersion latestSourceVersion;
/**
* The latest source version supported by the compiler.
*/
private static SourceVersion latestSupportedSourceVersion;
/**
* Whether Java 9+ modules are enabled.
*/
private static boolean modulesEnabled = true;
static {
try {
Field latest = tryGetRelease("RELEASE_17");
if (latest == null) {
latest = tryGetRelease("RELEASE_11");
}
if (latest == null) {
latest = tryGetRelease("RELEASE_8");
}
if (latest == null) {
throw new IllegalStateException();
}
latestSourceVersion = (SourceVersion)latest.get(null);
latestSupportedSourceVersion = latestSourceVersion;
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
static Field tryGetRelease(String release) {
try {
return SourceVersion.class.getDeclaredField(release);
} catch (NoSuchFieldException e) {
return null;
}
}
/**
* Gets the <code>androidide.java.home</code> property if it has been specified
* else fallbacks to the <code>java.home</code> property.
*/
public static String getJavaHome() {
String javaHome = System.getProperty(PROP_ANDROIDIDE_JAVA_HOME);
if (javaHome == null || javaHome.trim().length() == 0) {
javaHome = System.getProperty("java.home");
}
return javaHome;
}
/**
* Whether Java 9+ modules are enabled.
*/
public static boolean isModulesEnabled() {
return modulesEnabled;
}
/**
* Disable Java 9+ modules.
*/
public static void disableModules() {
modulesEnabled = false;
}
/**
* Enable Java 9+ modules.
*/
public static void enableModules() {
modulesEnabled = true;
}
/**
* Get the latest source version that can be modeled.
*/
public static SourceVersion getLatestSourceVersion() {
return latestSourceVersion;
}
/**
* Set the latest source version that can be modeled.
*/
public static void setLatestSourceVersion(SourceVersion newVersion) {
latestSourceVersion = newVersion;
}
/**
* Set the latest source version supported by the compiler.
*/
public static SourceVersion getLatestSupportedSourceVersion() {
return latestSupportedSourceVersion;
}
/**
* Set the latest source version supported by the compiler.
*/
public static void setLatestSupportedSourceVersion(SourceVersion newVersion) {
latestSupportedSourceVersion = newVersion;
}
}
@@ -0,0 +1,27 @@
package com.itsaky.androidide.zipfs2;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.spi.FileSystemProvider;
import java.util.HashMap;
import javac.internal.jrtfs.JrtFileSystemProvider;
public class AndroidFsProvider {
// Can be overridden to provide custom implementation
public static AndroidFsProvider INSTANCE = new AndroidFsProvider();
public FileSystemProvider zipFsProvider() {
return new ZipFileSystemProvider();
}
public FileSystem jrtFileSystem () {
try {
return new JrtFileSystemProvider().newFileSystem(URI.create("jrt:/"), new HashMap<>(0));
} catch (IOException e) {
throw new RuntimeException ("Unable to create instance of JRTFileSystem", e);
}
}
}
@@ -0,0 +1,252 @@
/*
* Copyright (c) 2018, 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 com.itsaky.androidide.zipfs2;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.SeekableByteChannel;
import java.util.Arrays;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ByteArrayChannel implements SeekableByteChannel {
private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
private byte buf[];
/*
* The current position of this channel.
*/
private int pos;
/*
* The index that is one greater than the last valid byte in the channel.
*/
private int last;
private boolean closed;
private boolean readonly;
/*
* Creates a {@code ByteArrayChannel} with size {@code sz}.
*/
ByteArrayChannel(int sz, boolean readonly) {
this.buf = new byte[sz];
this.pos = this.last = 0;
this.readonly = readonly;
}
/*
* Creates a ByteArrayChannel with its 'pos' at 0 and its 'last' at buf's end.
* Note: no defensive copy of the 'buf', used directly.
*/
ByteArrayChannel(byte[] buf, boolean readonly) {
this.buf = buf;
this.pos = 0;
this.last = buf.length;
this.readonly = readonly;
}
@Override
public boolean isOpen() {
return !closed;
}
@Override
public long position() throws IOException {
beginRead();
try {
ensureOpen();
return pos;
} finally {
endRead();
}
}
@Override
public SeekableByteChannel position(long pos) throws IOException {
beginWrite();
try {
ensureOpen();
if (pos < 0 || pos >= Integer.MAX_VALUE)
throw new IllegalArgumentException("Illegal position " + pos);
this.pos = Math.min((int)pos, last);
return this;
} finally {
endWrite();
}
}
@Override
public int read(ByteBuffer dst) throws IOException {
beginWrite();
try {
ensureOpen();
if (pos == last)
return -1;
int n = Math.min(dst.remaining(), last - pos);
dst.put(buf, pos, n);
pos += n;
return n;
} finally {
endWrite();
}
}
@Override
public SeekableByteChannel truncate(long size) throws IOException {
if (readonly)
throw new NonWritableChannelException();
ensureOpen();
throw new UnsupportedOperationException();
}
@Override
public int write(ByteBuffer src) throws IOException {
if (readonly)
throw new NonWritableChannelException();
beginWrite();
try {
ensureOpen();
int n = src.remaining();
ensureCapacity(pos + n);
src.get(buf, pos, n);
pos += n;
if (pos > last) {
last = pos;
}
return n;
} finally {
endWrite();
}
}
@Override
public long size() throws IOException {
beginRead();
try {
ensureOpen();
return last;
} finally {
endRead();
}
}
@Override
public void close() throws IOException {
if (closed)
return;
beginWrite();
try {
closed = true;
buf = null;
pos = 0;
last = 0;
} finally {
endWrite();
}
}
/**
* Creates a newly allocated byte array. Its size is the current
* size of this channel and the valid contents of the buffer
* have been copied into it.
*
* @return the current contents of this channel, as a byte array.
*/
public byte[] toByteArray() {
beginRead();
try {
// avoid copy if last == bytes.length?
return Arrays.copyOf(buf, last);
} finally {
endRead();
}
}
private void ensureOpen() throws IOException {
if (closed)
throw new ClosedChannelException();
}
private final void beginWrite() {
rwlock.writeLock().lock();
}
private final void endWrite() {
rwlock.writeLock().unlock();
}
private final void beginRead() {
rwlock.readLock().lock();
}
private final void endRead() {
rwlock.readLock().unlock();
}
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
if (minCapacity - buf.length > 0) {
grow(minCapacity);
}
}
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = buf.length;
int newCapacity = oldCapacity << 1;
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
buf = Arrays.copyOf(buf, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
}
@@ -0,0 +1,36 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.itsaky.androidide.zipfs2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* @author itsaky
*/
public class Collections2 {
public static <T> List<T> listOf(T... values) {
final List<T> result = new ArrayList<>();
if (values == null) {
return result;
}
result.addAll(Arrays.asList(values));
return result;
}
public static <T> Set<T> setOf(T... values) {
final Set<T> result = new HashSet<>();
if (values == null) {
return result;
}
result.addAll(Arrays.asList(values));
return result;
}
}
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2007, 2018, 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 com.itsaky.androidide.zipfs2;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.Paths;
public class JarFileSystemProvider extends ZipFileSystemProvider {
@Override
public String getScheme() {
return "jar";
}
@Override
protected Path uriToPath(URI uri) {
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) {
throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
}
try {
String uristr = uri.toString();
int end = uristr.indexOf("!/");
uristr = uristr.substring(4, (end == -1) ? uristr.length() : end);
uri = new URI(uristr);
return Paths.get(new URI("file", uri.getHost(), uri.getPath(), null))
.toAbsolutePath();
} catch (URISyntaxException e) {
throw new AssertionError(e); //never thrown
}
}
@Override
public Path getPath(URI uri) {
FileSystem fs = getFileSystem(uri);
String path = uri.getFragment();
if (path == null) {
String uristr = uri.toString();
int off = uristr.indexOf("!/");
if (off != -1)
path = uristr.substring(off + 2);
}
if (path != null)
return fs.getPath(path);
throw new IllegalArgumentException("URI: "
+ uri
+ " does not contain path fragment ex. jar:///c:/foo.zip!/BAR");
}
}
@@ -0,0 +1,21 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Interface.java to edit this template
*/
package com.itsaky.androidide.zipfs2;
import java.nio.file.Path;
import java.util.Map;
/**
* @author itsaky
*/
public interface JarPackageProvider {
/**
* Get the package map from the given archive path.
* @return The cached package entries. Should return null or empty map to walk the archive file tree instead.
* The keys must be <code>openjdk.tools.javac.file.RelativePath.RelativeDirectory</code>
*/
public Map<? extends Object, Path> getPackages(Path archivePath);
}
@@ -0,0 +1,151 @@
/*
* Copyright (c) 2009, 2018, 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 com.itsaky.androidide.zipfs2;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.Arrays;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Utility class for zipfile name and comment decoding and encoding
*
* @author Xueming Shen
*/
public class ZipCoder {
static class UTF8 extends ZipCoder {
UTF8() {
super(UTF_8);
}
@Override
byte[] getBytes(String s) { // fast pass for ascii
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) > 0x7f) return super.getBytes(s);
}
return s.getBytes(ISO_8859_1);
}
@Override
String toString(byte[] ba) {
for (byte b : ba) {
if (b < 0) return super.toString(ba);
}
return new String(ba, ISO_8859_1);
}
}
private static final ZipCoder utf8 = new UTF8();
public static ZipCoder get(String csn) {
Charset cs = Charset.forName(csn);
if (cs.name().equals("UTF-8")) {
return utf8;
}
return new ZipCoder(cs);
}
String toString(byte[] ba) {
CharsetDecoder cd = decoder().reset();
int clen = (int)(ba.length * cd.maxCharsPerByte());
char[] ca = new char[clen];
if (clen == 0)
return new String(ca);
ByteBuffer bb = ByteBuffer.wrap(ba, 0, ba.length);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = cd.flush(cb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
return new String(ca, 0, cb.position());
}
byte[] getBytes(String s) {
CharsetEncoder ce = encoder().reset();
char[] ca = s.toCharArray();
int len = (int)(ca.length * ce.maxBytesPerChar());
byte[] ba = new byte[len];
if (len == 0)
return ba;
ByteBuffer bb = ByteBuffer.wrap(ba);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = ce.encode(cb, bb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = ce.flush(bb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
if (bb.position() == ba.length) // defensive copy?
return ba;
else
return Arrays.copyOf(ba, bb.position());
}
boolean isUTF8() {
return cs == UTF_8;
}
private Charset cs;
private ZipCoder(Charset cs) {
this.cs = cs;
}
private final ThreadLocal<CharsetDecoder> decTL = new ThreadLocal<>();
private final ThreadLocal<CharsetEncoder> encTL = new ThreadLocal<>();
private CharsetDecoder decoder() {
CharsetDecoder dec = decTL.get();
if (dec == null) {
dec = cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
decTL.set(dec);
}
return dec;
}
private CharsetEncoder encoder() {
CharsetEncoder enc = encTL.get();
if (enc == null) {
enc = cs.newEncoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
encTL.set(enc);
}
return enc;
}
}
@@ -0,0 +1,251 @@
/*
* Copyright (c) 2009, 2018, 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 com.itsaky.androidide.zipfs2;
/**
* @author Xueming Shen
*/
public class ZipConstants {
/*
* Compression methods
*/
static final int METHOD_STORED = 0;
static final int METHOD_DEFLATED = 8;
static final int METHOD_DEFLATED64 = 9;
static final int METHOD_BZIP2 = 12;
static final int METHOD_LZMA = 14;
static final int METHOD_LZ77 = 19;
static final int METHOD_AES = 99;
/*
* General purpose bit flag
*/
static final int FLAG_ENCRYPTED = 0x01;
static final int FLAG_DATADESCR = 0x08; // crc, size and csize in dd
static final int FLAG_USE_UTF8 = 0x800; // If this bit is set the filename and
// comment fields for this file must be
// encoded using UTF-8.
/*
* Header signatures
*/
static long LOCSIG = 0x04034b50L; // "PK\003\004"
static long EXTSIG = 0x08074b50L; // "PK\007\008"
static long CENSIG = 0x02014b50L; // "PK\001\002"
static long ENDSIG = 0x06054b50L; // "PK\005\006"
/*
* Header sizes in bytes (including signatures)
*/
static final int LOCHDR = 30; // LOC header size
static final int EXTHDR = 16; // EXT header size
static final int CENHDR = 46; // CEN header size
static final int ENDHDR = 22; // END header size
/*
* Local file (LOC) header field offsets
*/
static final int LOCVER = 4; // version needed to extract
static final int LOCFLG = 6; // general purpose bit flag
static final int LOCHOW = 8; // compression method
static final int LOCTIM = 10; // modification time
static final int LOCCRC = 14; // uncompressed file crc-32 value
static final int LOCSIZ = 18; // compressed size
static final int LOCLEN = 22; // uncompressed size
static final int LOCNAM = 26; // filename length
static final int LOCEXT = 28; // extra field length
/*
* Extra local (EXT) header field offsets
*/
static final int EXTCRC = 4; // uncompressed file crc-32 value
static final int EXTSIZ = 8; // compressed size
static final int EXTLEN = 12; // uncompressed size
/*
* Central directory (CEN) header field offsets
*/
static final int CENVEM = 4; // version made by
static final int CENVER = 6; // version needed to extract
static final int CENFLG = 8; // encrypt, decrypt flags
static final int CENHOW = 10; // compression method
static final int CENTIM = 12; // modification time
static final int CENCRC = 16; // uncompressed file crc-32 value
static final int CENSIZ = 20; // compressed size
static final int CENLEN = 24; // uncompressed size
static final int CENNAM = 28; // filename length
static final int CENEXT = 30; // extra field length
static final int CENCOM = 32; // comment length
static final int CENDSK = 34; // disk number start
static final int CENATT = 36; // internal file attributes
static final int CENATX = 38; // external file attributes
static final int CENOFF = 42; // LOC header offset
/*
* End of central directory (END) header field offsets
*/
static final int ENDSUB = 8; // number of entries on this disk
static final int ENDTOT = 10; // total number of entries
static final int ENDSIZ = 12; // central directory size in bytes
static final int ENDOFF = 16; // offset of first CEN header
static final int ENDCOM = 20; // zip file comment length
/*
* ZIP64 constants
*/
static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006"
static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007"
static final int ZIP64_ENDHDR = 56; // ZIP64 end header size
static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size
static final int ZIP64_EXTHDR = 24; // EXT header size
static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID
static final int ZIP64_MINVAL32 = 0xFFFF;
static final long ZIP64_MINVAL = 0xFFFFFFFFL;
/*
* Zip64 End of central directory (END) header field offsets
*/
static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir
static final int ZIP64_ENDVEM = 12; // version made by
static final int ZIP64_ENDVER = 14; // version needed to extract
static final int ZIP64_ENDNMD = 16; // number of this disk
static final int ZIP64_ENDDSK = 20; // disk number of start
static final int ZIP64_ENDTOD = 24; // total number of entries on this disk
static final int ZIP64_ENDTOT = 32; // total number of entries
static final int ZIP64_ENDSIZ = 40; // central directory size in bytes
static final int ZIP64_ENDOFF = 48; // offset of first CEN header
static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector
/*
* Zip64 End of central directory locator field offsets
*/
static final int ZIP64_LOCDSK = 4; // disk number start
static final int ZIP64_LOCOFF = 8; // offset of zip64 end
static final int ZIP64_LOCTOT = 16; // total number of disks
/*
* Zip64 Extra local (EXT) header field offsets
*/
static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value
static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte
static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte
/*
* Extra field header ID
*/
static final int EXTID_ZIP64 = 0x0001; // ZIP64
static final int EXTID_NTFS = 0x000a; // NTFS
static final int EXTID_UNIX = 0x000d; // UNIX
static final int EXTID_EFS = 0x0017; // Strong Encryption
static final int EXTID_EXTT = 0x5455; // Info-ZIP Extended Timestamp
/*
* fields access methods
*/
///////////////////////////////////////////////////////
static final int CH(byte[] b, int n) {
return Byte.toUnsignedInt(b[n]);
}
static final int SH(byte[] b, int n) {
return Byte.toUnsignedInt(b[n]) | (Byte.toUnsignedInt(b[n + 1]) << 8);
}
static final long LG(byte[] b, int n) {
return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL;
}
static final long LL(byte[] b, int n) {
return (LG(b, n)) | (LG(b, n + 4) << 32);
}
static long getSig(byte[] b, int n) { return LG(b, n); }
private static boolean pkSigAt(byte[] b, int n, int b1, int b2) {
return b[n] == 'P' & b[n + 1] == 'K' & b[n + 2] == b1 & b[n + 3] == b2;
}
static boolean cenSigAt(byte[] b, int n) { return pkSigAt(b, n, 1, 2); }
static boolean locSigAt(byte[] b, int n) { return pkSigAt(b, n, 3, 4); }
static boolean endSigAt(byte[] b, int n) { return pkSigAt(b, n, 5, 6); }
static boolean extSigAt(byte[] b, int n) { return pkSigAt(b, n, 7, 8); }
static boolean end64SigAt(byte[] b, int n) { return pkSigAt(b, n, 6, 6); }
static boolean locator64SigAt(byte[] b, int n) { return pkSigAt(b, n, 6, 7); }
// local file (LOC) header fields
static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature
static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract
static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags
static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method
static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time
static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data
static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size
static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size
static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length
static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length
// extra local (EXT) header fields
static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data
static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size
static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size
// end of central directory header (END) fields
static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk
static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries
static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size
static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset
static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment
static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);}
// zip64 end of central directory recoder fields
static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk
static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries
static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size
static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset
static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset
// central directory header (CEN) fields
static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); }
static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); }
static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); }
static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); }
static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);}
static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);}
static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);}
static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);}
static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);}
static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);}
static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);}
static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);}
static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);}
static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);}
static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);}
static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);}
/* The END header is followed by a variable length comment of size < 64k. */
static final long END_MAXLEN = 0xFFFF + ENDHDR;
static final int READBLOCKSZ = 128;
}
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2009, 2019, 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 com.itsaky.androidide.zipfs2;
import java.io.IOException;
import java.nio.file.ClosedDirectoryStreamException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
*
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
*/
public class ZipDirectoryStream implements DirectoryStream<Path> {
private final ZipFileSystem zipfs;
private final ZipPath dir;
private final DirectoryStream.Filter<? super Path> filter;
private volatile boolean isClosed;
private volatile Iterator<Path> itr;
ZipDirectoryStream(ZipPath dir,
DirectoryStream.Filter<? super java.nio.file.Path> filter)
throws IOException
{
this.zipfs = dir.getFileSystem();
this.dir = dir;
this.filter = filter;
// sanity check
if (!zipfs.isDirectory(dir.getResolvedPath()))
throw new NotDirectoryException(dir.toString());
}
@Override
public synchronized Iterator<Path> iterator() {
if (isClosed)
throw new ClosedDirectoryStreamException();
if (itr != null)
throw new IllegalStateException("Iterator has already been returned");
try {
itr = zipfs.iteratorOf(dir, filter);
} catch (IOException e) {
throw new DirectoryIteratorException(e);
}
return new Iterator<Path>() {
@Override
public boolean hasNext() {
if (isClosed)
return false;
return itr.hasNext();
}
@Override
public synchronized Path next() {
if (isClosed)
throw new NoSuchElementException();
return itr.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public synchronized void close() throws IOException {
isClosed = true;
}
}
@@ -0,0 +1,174 @@
/*
* Copyright (c) 2009, 2018, 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 com.itsaky.androidide.zipfs2;
import java.io.IOException;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileTime;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
*/
public class ZipFileAttributeView implements BasicFileAttributeView {
private static enum AttrID {
size,
creationTime,
lastAccessTime,
lastModifiedTime,
isDirectory,
isRegularFile,
isSymbolicLink,
isOther,
fileKey,
compressedSize,
crc,
method
};
private final ZipPath path;
private final boolean isZipView;
private ZipFileAttributeView(ZipPath path, boolean isZipView) {
this.path = path;
this.isZipView = isZipView;
}
@SuppressWarnings("unchecked") // Cast to V
static <V extends FileAttributeView> V get(ZipPath path, Class<V> type) {
if (type == null)
throw new NullPointerException();
if (type == BasicFileAttributeView.class)
return (V)new ZipFileAttributeView(path, false);
if (type == ZipFileAttributeView.class)
return (V)new ZipFileAttributeView(path, true);
return null;
}
static ZipFileAttributeView get(ZipPath path, String type) {
if (type == null)
throw new NullPointerException();
if (type.equals("basic"))
return new ZipFileAttributeView(path, false);
if (type.equals("zip"))
return new ZipFileAttributeView(path, true);
return null;
}
@Override
public String name() {
return isZipView ? "zip" : "basic";
}
public ZipFileAttributes readAttributes() throws IOException {
return path.getAttributes();
}
@Override
public void setTimes(FileTime lastModifiedTime,
FileTime lastAccessTime,
FileTime createTime)
throws IOException
{
path.setTimes(lastModifiedTime, lastAccessTime, createTime);
}
void setAttribute(String attribute, Object value)
throws IOException
{
try {
if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime)
setTimes((FileTime)value, null, null);
if (AttrID.valueOf(attribute) == AttrID.lastAccessTime)
setTimes(null, (FileTime)value, null);
if (AttrID.valueOf(attribute) == AttrID.creationTime)
setTimes(null, null, (FileTime)value);
return;
} catch (IllegalArgumentException x) {}
throw new UnsupportedOperationException("'" + attribute +
"' is unknown or read-only attribute");
}
Map<String, Object> readAttributes(String attributes)
throws IOException
{
ZipFileAttributes zfas = readAttributes();
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
if ("*".equals(attributes)) {
for (AttrID id : AttrID.values()) {
try {
map.put(id.name(), attribute(id, zfas));
} catch (IllegalArgumentException x) {}
}
} else {
String[] as = attributes.split(",");
for (String a : as) {
try {
map.put(a, attribute(AttrID.valueOf(a), zfas));
} catch (IllegalArgumentException x) {}
}
}
return map;
}
Object attribute(AttrID id, ZipFileAttributes zfas) {
switch (id) {
case size:
return zfas.size();
case creationTime:
return zfas.creationTime();
case lastAccessTime:
return zfas.lastAccessTime();
case lastModifiedTime:
return zfas.lastModifiedTime();
case isDirectory:
return zfas.isDirectory();
case isRegularFile:
return zfas.isRegularFile();
case isSymbolicLink:
return zfas.isSymbolicLink();
case isOther:
return zfas.isOther();
case fileKey:
return zfas.fileKey();
case compressedSize:
if (isZipView)
return zfas.compressedSize();
break;
case crc:
if (isZipView)
return zfas.crc();
break;
case method:
if (isZipView)
return zfas.method();
break;
}
return null;
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2009, 2018, 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 com.itsaky.androidide.zipfs2;
import java.nio.file.attribute.BasicFileAttributes;
/**
* The attributes of a file stored in a zip file.
*
* @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal
*/
public interface ZipFileAttributes extends BasicFileAttributes {
public long compressedSize();
public long crc();
public int method();
public byte[] extra();
public byte[] comment();
public String toString();
}
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2009, 2018, 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 com.itsaky.androidide.zipfs2;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileStoreAttributeView;
/**
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
*/
public class ZipFileStore extends FileStore {
private final ZipFileSystem zfs;
ZipFileStore(ZipPath zpath) {
this.zfs = zpath.getFileSystem();
}
@Override
public String name() {
return zfs.toString() + "/";
}
@Override
public String type() {
return "zipfs";
}
@Override
public boolean isReadOnly() {
return zfs.isReadOnly();
}
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
return (type == BasicFileAttributeView.class ||
type == ZipFileAttributeView.class);
}
@Override
public boolean supportsFileAttributeView(String name) {
return name.equals("basic") || name.equals("zip");
}
@Override
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
if (type == null)
throw new NullPointerException();
return (V)null;
}
@Override
public long getTotalSpace() throws IOException {
return new ZipFileStoreAttributes(this).totalSpace();
}
@Override
public long getUsableSpace() throws IOException {
return new ZipFileStoreAttributes(this).usableSpace();
}
@Override
public long getUnallocatedSpace() throws IOException {
return new ZipFileStoreAttributes(this).unallocatedSpace();
}
@Override
public Object getAttribute(String attribute) throws IOException {
if (attribute.equals("totalSpace"))
return getTotalSpace();
if (attribute.equals("usableSpace"))
return getUsableSpace();
if (attribute.equals("unallocatedSpace"))
return getUnallocatedSpace();
throw new UnsupportedOperationException("does not support the given attribute");
}
private static class ZipFileStoreAttributes {
final FileStore fstore;
final long size;
public ZipFileStoreAttributes(ZipFileStore fileStore)
throws IOException
{
Path path = FileSystems.getDefault().getPath(fileStore.name());
this.size = Files.size(path);
this.fstore = Files.getFileStore(path);
}
public long totalSpace() {
return size;
}
public long usableSpace() throws IOException {
if (!fstore.isReadOnly())
return fstore.getUsableSpace();
return 0;
}
public long unallocatedSpace() throws IOException {
if (!fstore.isReadOnly())
return fstore.getUnallocatedSpace();
return 0;
}
}
}
@@ -0,0 +1,319 @@
/*
* Copyright (c) 2009, 2019, 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 com.itsaky.androidide.zipfs2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.*;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.spi.FileSystemProvider;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.zip.ZipException;
/**
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
*/
public class ZipFileSystemProvider extends FileSystemProvider {
private final Map<Path, ZipFileSystem> filesystems = new HashMap<>();
public ZipFileSystemProvider() {}
@Override
public String getScheme() {
return "jar";
}
protected Path uriToPath(URI uri) {
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) {
throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
}
try {
// only support legacy JAR URL syntax jar:{uri}!/{entry} for now
String spec = uri.getRawSchemeSpecificPart();
int sep = spec.indexOf("!/");
if (sep != -1) {
spec = spec.substring(0, sep);
}
return Paths.get(new URI(spec)).toAbsolutePath();
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
private boolean ensureFile(Path path) {
try {
BasicFileAttributes attrs =
Files.readAttributes(path, BasicFileAttributes.class);
if (!attrs.isRegularFile())
throw new UnsupportedOperationException();
return true;
} catch (IOException ioe) {
return false;
}
}
protected ZipFileSystem createFs(Path path, Map<String, ?> env) throws IOException {
return new ZipFileSystem(this, path, env);
}
@Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env)
throws IOException
{
Path path = uriToPath(uri);
synchronized(filesystems) {
Path realPath = null;
if (ensureFile(path)) {
realPath = path.toRealPath();
if (filesystems.containsKey(realPath))
throw new FileSystemAlreadyExistsException();
}
ZipFileSystem zipfs;
try {
zipfs = createFs(path, env);
} catch (ZipException ze) {
String pname = path.toString();
if (pname.endsWith(".zip") || pname.endsWith(".jar"))
throw ze;
// assume NOT a zip/jar file
throw new UnsupportedOperationException();
}
if (realPath == null) { // newly created
realPath = path.toRealPath();
}
filesystems.put(realPath, zipfs);
return zipfs;
}
}
@Override
public FileSystem newFileSystem(Path path, Map<String, ?> env)
throws IOException
{
ensureFile(path);
try {
return createFs(path, env);
} catch (ZipException ze) {
String pname = path.toString();
if (pname.endsWith(".zip") || pname.endsWith(".jar"))
throw ze;
throw new UnsupportedOperationException();
}
}
@Override
public Path getPath(URI uri) {
String spec = uri.getSchemeSpecificPart();
int sep = spec.indexOf("!/");
if (sep == -1)
throw new IllegalArgumentException("URI: "
+ uri
+ " does not contain path info ex. jar:file:/c:/foo.zip!/BAR");
return getFileSystem(uri).getPath(spec.substring(sep + 1));
}
@Override
public FileSystem getFileSystem(URI uri) {
synchronized (filesystems) {
ZipFileSystem zipfs = null;
try {
zipfs = filesystems.get(uriToPath(uri).toRealPath());
} catch (IOException x) {
// ignore the ioe from toRealPath(), return FSNFE
}
if (zipfs == null)
throw new FileSystemNotFoundException();
return zipfs;
}
}
// Checks that the given file is a UnixPath
static final ZipPath toZipPath(Path path) {
if (path == null)
throw new NullPointerException();
if (!(path instanceof ZipPath))
throw new ProviderMismatchException();
return (ZipPath)path;
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
toZipPath(path).checkAccess(modes);
}
@Override
public void copy(Path src, Path target, CopyOption... options)
throws IOException
{
toZipPath(src).copy(toZipPath(target), options);
}
@Override
public void createDirectory(Path path, FileAttribute<?>... attrs)
throws IOException
{
toZipPath(path).createDirectory(attrs);
}
@Override
public final void delete(Path path) throws IOException {
toZipPath(path).delete();
}
@Override
public <V extends FileAttributeView> V
getFileAttributeView(Path path, Class<V> type, LinkOption... options)
{
return ZipFileAttributeView.get(toZipPath(path), type);
}
@Override
public FileStore getFileStore(Path path) throws IOException {
return toZipPath(path).getFileStore();
}
@Override
public boolean isHidden(Path path) {
return toZipPath(path).isHidden();
}
@Override
public boolean isSameFile(Path path, Path other) throws IOException {
return toZipPath(path).isSameFile(other);
}
@Override
public void move(Path src, Path target, CopyOption... options)
throws IOException
{
toZipPath(src).move(toZipPath(target), options);
}
@Override
public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
Set<? extends OpenOption> options,
ExecutorService exec,
FileAttribute<?>... attrs)
throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public SeekableByteChannel newByteChannel(Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return toZipPath(path).newByteChannel(options, attrs);
}
@Override
public DirectoryStream<Path> newDirectoryStream(
Path path, Filter<? super Path> filter) throws IOException
{
return toZipPath(path).newDirectoryStream(filter);
}
@Override
public FileChannel newFileChannel(Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return toZipPath(path).newFileChannel(options, attrs);
}
@Override
public InputStream newInputStream(Path path, OpenOption... options)
throws IOException
{
return toZipPath(path).newInputStream(options);
}
@Override
public OutputStream newOutputStream(Path path, OpenOption... options)
throws IOException
{
return toZipPath(path).newOutputStream(options);
}
@Override
@SuppressWarnings("unchecked") // Cast to A
public <A extends BasicFileAttributes> A
readAttributes(Path path, Class<A> type, LinkOption... options)
throws IOException
{
if (type == BasicFileAttributes.class || type == ZipFileAttributes.class)
return (A)toZipPath(path).getAttributes();
return null;
}
@Override
public Map<String, Object>
readAttributes(Path path, String attribute, LinkOption... options)
throws IOException
{
return toZipPath(path).readAttributes(attribute, options);
}
@Override
public Path readSymbolicLink(Path link) throws IOException {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public void setAttribute(Path path, String attribute,
Object value, LinkOption... options)
throws IOException
{
toZipPath(path).setAttribute(attribute, value, options);
}
//////////////////////////////////////////////////////////////
void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException {
synchronized (filesystems) {
zfpath = zfpath.toRealPath();
if (filesystems.get(zfpath) == zfs)
filesystems.remove(zfpath);
}
}
}
@@ -0,0 +1,218 @@
/*
* Copyright (c) 2009, 2018, 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 com.itsaky.androidide.zipfs2;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Map;
import static com.itsaky.androidide.zipfs2.ZipConstants.*;
import static com.itsaky.androidide.zipfs2.ZipUtils.dosToJavaTime;
import static com.itsaky.androidide.zipfs2.ZipUtils.unixToJavaTime;
import static com.itsaky.androidide.zipfs2.ZipUtils.winToJavaTime;
/**
* Print all loc and cen headers of the ZIP file
*
* @author Xueming Shen
*/
public class ZipInfo {
public static void main(String[] args) throws Throwable {
if (args.length < 1) {
print("Usage: java ZipInfo zfname");
} else {
Map<String, ?> env = Collections.emptyMap();
ZipFileSystem zfs = (ZipFileSystem)(new ZipFileSystemProvider()
.newFileSystem(Paths.get(args[0]), env));
byte[] cen = zfs.cen;
if (cen == null) {
print("zip file is empty%n");
return;
}
int pos = 0;
byte[] buf = new byte[1024];
int no = 1;
while (pos + CENHDR < cen.length) {
print("----------------#%d--------------------%n", no++);
printCEN(cen, pos);
// use size CENHDR as the extra bytes to read, just in case the
// loc.extra is bigger than the cen.extra, try to avoid to read
// twice
long len = LOCHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENHDR;
if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
ZipFileSystem.zerror("read loc header failed");
if (LOCEXT(buf) > CENEXT(cen, pos) + CENHDR) {
// have to read the second time;
len = LOCHDR + LOCNAM(buf) + LOCEXT(buf);
if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
ZipFileSystem.zerror("read loc header failed");
}
printLOC(buf);
pos += CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos);
}
zfs.close();
}
}
static void print(String fmt, Object... objs) {
System.out.printf(fmt, objs);
}
static void printLOC(byte[] loc) {
print("%n");
print("[Local File Header]%n");
print(" Signature : %#010x%n", LOCSIG(loc));
if (LOCSIG(loc) != LOCSIG) {
print(" Wrong signature!");
return;
}
print(" Version : %#6x [%d.%d]%n",
LOCVER(loc), LOCVER(loc) / 10, LOCVER(loc) % 10);
print(" Flag : %#6x%n", LOCFLG(loc));
print(" Method : %#6x%n", LOCHOW(loc));
print(" LastMTime : %#10x [%tc]%n",
LOCTIM(loc), dosToJavaTime(LOCTIM(loc)));
print(" CRC : %#10x%n", LOCCRC(loc));
print(" CSize : %#10x%n", LOCSIZ(loc));
print(" Size : %#10x%n", LOCLEN(loc));
print(" NameLength : %#6x [%s]%n",
LOCNAM(loc), new String(loc, LOCHDR, LOCNAM(loc)));
print(" ExtraLength : %#6x%n", LOCEXT(loc));
if (LOCEXT(loc) != 0)
printExtra(loc, LOCHDR + LOCNAM(loc), LOCEXT(loc));
}
static void printCEN(byte[] cen, int off) {
print("[Central Directory Header]%n");
print(" Signature : %#010x%n", CENSIG(cen, off));
if (CENSIG(cen, off) != CENSIG) {
print(" Wrong signature!");
return;
}
print(" VerMadeby : %#6x [%d, %d.%d]%n",
CENVEM(cen, off), (CENVEM(cen, off) >> 8),
(CENVEM(cen, off) & 0xff) / 10,
(CENVEM(cen, off) & 0xff) % 10);
print(" VerExtract : %#6x [%d.%d]%n",
CENVER(cen, off), CENVER(cen, off) / 10, CENVER(cen, off) % 10);
print(" Flag : %#6x%n", CENFLG(cen, off));
print(" Method : %#6x%n", CENHOW(cen, off));
print(" LastMTime : %#10x [%tc]%n",
CENTIM(cen, off), dosToJavaTime(CENTIM(cen, off)));
print(" CRC : %#10x%n", CENCRC(cen, off));
print(" CSize : %#10x%n", CENSIZ(cen, off));
print(" Size : %#10x%n", CENLEN(cen, off));
print(" NameLen : %#6x [%s]%n",
CENNAM(cen, off), new String(cen, off + CENHDR, CENNAM(cen, off)));
print(" ExtraLen : %#6x%n", CENEXT(cen, off));
if (CENEXT(cen, off) != 0)
printExtra(cen, off + CENHDR + CENNAM(cen, off), CENEXT(cen, off));
print(" CommentLen : %#6x%n", CENCOM(cen, off));
print(" DiskStart : %#6x%n", CENDSK(cen, off));
print(" Attrs : %#6x%n", CENATT(cen, off));
print(" AttrsEx : %#10x%n", CENATX(cen, off));
print(" LocOff : %#10x%n", CENOFF(cen, off));
}
static long locoff(byte[] cen, int pos) {
long locoff = CENOFF(cen, pos);
if (locoff == ZIP64_MINVAL) { //ZIP64
int off = pos + CENHDR + CENNAM(cen, pos);
int end = off + CENEXT(cen, pos);
while (off + 4 < end) {
int tag = SH(cen, off);
int sz = SH(cen, off + 2);
if (tag != EXTID_ZIP64) {
off += 4 + sz;
continue;
}
off += 4;
if (CENLEN(cen, pos) == ZIP64_MINVAL)
off += 8;
if (CENSIZ(cen, pos) == ZIP64_MINVAL)
off += 8;
return LL(cen, off);
}
// should never be here
}
return locoff;
}
static void printExtra(byte[] extra, int off, int len) {
int end = off + len;
while (off + 4 <= end) {
int tag = SH(extra, off);
int sz = SH(extra, off + 2);
print(" [tag=0x%04x, sz=%d, data= ", tag, sz);
if (off + sz > end) {
print(" Error: Invalid extra data, beyond extra length");
break;
}
off += 4;
for (int i = 0; i < sz; i++)
print("%02x ", extra[off + i]);
print("]%n");
switch (tag) {
case EXTID_ZIP64 :
print(" ->ZIP64: ");
int pos = off;
while (pos + 8 <= off + sz) {
print(" *0x%x ", LL(extra, pos));
pos += 8;
}
print("%n");
break;
case EXTID_NTFS:
print(" ->PKWare NTFS%n");
// 4 bytes reserved
if (SH(extra, off + 4) != 0x0001 || SH(extra, off + 6) != 24)
print(" Error: Invalid NTFS sub-tag or subsz");
print(" mtime:%tc%n",
winToJavaTime(LL(extra, off + 8)));
print(" atime:%tc%n",
winToJavaTime(LL(extra, off + 16)));
print(" ctime:%tc%n",
winToJavaTime(LL(extra, off + 24)));
break;
case EXTID_EXTT:
print(" ->Info-ZIP Extended Timestamp: flag=%x%n",extra[off]);
pos = off + 1 ;
while (pos + 4 <= off + sz) {
print(" *%tc%n",
unixToJavaTime(LG(extra, pos)));
pos += 4;
}
break;
default:
print(" ->[tag=%x, size=%d]%n", tag, sz);
}
off += sz;
}
}
}
@@ -0,0 +1,330 @@
/*
* Copyright (c) 2009, 2018, 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 com.itsaky.androidide.zipfs2;
import java.io.IOException;
import java.io.OutputStream;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.regex.PatternSyntaxException;
/**
* @author Xueming Shen
*/
class ZipUtils {
/*
* Writes a 16-bit short to the output stream in little-endian byte order.
*/
public static void writeShort(OutputStream os, int v) throws IOException {
os.write(v & 0xff);
os.write((v >>> 8) & 0xff);
}
/*
* Writes a 32-bit int to the output stream in little-endian byte order.
*/
public static void writeInt(OutputStream os, long v) throws IOException {
os.write((int)(v & 0xff));
os.write((int)((v >>> 8) & 0xff));
os.write((int)((v >>> 16) & 0xff));
os.write((int)((v >>> 24) & 0xff));
}
/*
* Writes a 64-bit int to the output stream in little-endian byte order.
*/
public static void writeLong(OutputStream os, long v) throws IOException {
os.write((int)(v & 0xff));
os.write((int)((v >>> 8) & 0xff));
os.write((int)((v >>> 16) & 0xff));
os.write((int)((v >>> 24) & 0xff));
os.write((int)((v >>> 32) & 0xff));
os.write((int)((v >>> 40) & 0xff));
os.write((int)((v >>> 48) & 0xff));
os.write((int)((v >>> 56) & 0xff));
}
/*
* Writes an array of bytes to the output stream.
*/
public static void writeBytes(OutputStream os, byte[] b)
throws IOException
{
os.write(b, 0, b.length);
}
/*
* Writes an array of bytes to the output stream.
*/
public static void writeBytes(OutputStream os, byte[] b, int off, int len)
throws IOException
{
os.write(b, off, len);
}
/*
* Append a slash at the end, if it does not have one yet
*/
public static byte[] toDirectoryPath(byte[] dir) {
if (dir.length != 0 && dir[dir.length - 1] != '/') {
dir = Arrays.copyOf(dir, dir.length + 1);
dir[dir.length - 1] = '/';
}
return dir;
}
/*
* Converts DOS time to Java time (number of milliseconds since epoch).
*/
public static long dosToJavaTime(long dtime) {
int year = (int) (((dtime >> 25) & 0x7f) + 1980);
int month = (int) ((dtime >> 21) & 0x0f);
int day = (int) ((dtime >> 16) & 0x1f);
int hour = (int) ((dtime >> 11) & 0x1f);
int minute = (int) ((dtime >> 5) & 0x3f);
int second = (int) ((dtime << 1) & 0x3e);
if (month > 0 && month < 13 && day > 0 && hour < 24 && minute < 60 && second < 60) {
try {
LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second);
return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond(
ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS);
} catch (DateTimeException dte) {
// ignore
}
}
return overflowDosToJavaTime(year, month, day, hour, minute, second);
}
/*
* Deal with corner cases where an arguably mal-formed DOS time is used
*/
@SuppressWarnings("deprecation") // Use of Date constructor
private static long overflowDosToJavaTime(int year, int month, int day,
int hour, int minute, int second) {
return new Date(year - 1900, month - 1, day, hour, minute, second).getTime();
}
/*
* Converts Java time to DOS time.
*/
public static long javaToDosTime(long time) {
Instant instant = Instant.ofEpochMilli(time);
LocalDateTime ldt = LocalDateTime.ofInstant(
instant, ZoneId.systemDefault());
int year = ldt.getYear() - 1980;
if (year < 0) {
return (1 << 21) | (1 << 16);
}
return (year << 25 |
ldt.getMonthValue() << 21 |
ldt.getDayOfMonth() << 16 |
ldt.getHour() << 11 |
ldt.getMinute() << 5 |
ldt.getSecond() >> 1) & 0xffffffffL;
}
// used to adjust values between Windows and java epoch
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
public static final long winToJavaTime(long wtime) {
return TimeUnit.MILLISECONDS.convert(
wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS);
}
public static final long javaToWinTime(long time) {
return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS)
- WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
}
public static final long unixToJavaTime(long utime) {
return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS);
}
public static final long javaToUnixTime(long time) {
return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS);
}
private static final String regexMetaChars = ".^$+{[]|()";
private static final String globMetaChars = "\\*?[{";
private static boolean isRegexMeta(char c) {
return regexMetaChars.indexOf(c) != -1;
}
private static boolean isGlobMeta(char c) {
return globMetaChars.indexOf(c) != -1;
}
private static char EOL = 0; //TBD
private static char next(String glob, int i) {
if (i < glob.length()) {
return glob.charAt(i);
}
return EOL;
}
/*
* Creates a regex pattern from the given glob expression.
*
* @throws PatternSyntaxException
*/
public static String toRegexPattern(String globPattern) {
boolean inGroup = false;
StringBuilder regex = new StringBuilder("^");
int i = 0;
while (i < globPattern.length()) {
char c = globPattern.charAt(i++);
switch (c) {
case '\\':
// escape special characters
if (i == globPattern.length()) {
throw new PatternSyntaxException("No character to escape",
globPattern, i - 1);
}
char next = globPattern.charAt(i++);
if (isGlobMeta(next) || isRegexMeta(next)) {
regex.append('\\');
}
regex.append(next);
break;
case '/':
regex.append(c);
break;
case '[':
// don't match name separator in class
regex.append("[[^/]&&[");
if (next(globPattern, i) == '^') {
// escape the regex negation char if it appears
regex.append("\\^");
i++;
} else {
// negation
if (next(globPattern, i) == '!') {
regex.append('^');
i++;
}
// hyphen allowed at start
if (next(globPattern, i) == '-') {
regex.append('-');
i++;
}
}
boolean hasRangeStart = false;
char last = 0;
while (i < globPattern.length()) {
c = globPattern.charAt(i++);
if (c == ']') {
break;
}
if (c == '/') {
throw new PatternSyntaxException("Explicit 'name separator' in class",
globPattern, i - 1);
}
// TBD: how to specify ']' in a class?
if (c == '\\' || c == '[' ||
c == '&' && next(globPattern, i) == '&') {
// escape '\', '[' or "&&" for regex class
regex.append('\\');
}
regex.append(c);
if (c == '-') {
if (!hasRangeStart) {
throw new PatternSyntaxException("Invalid range",
globPattern, i - 1);
}
if ((c = next(globPattern, i++)) == EOL || c == ']') {
break;
}
if (c < last) {
throw new PatternSyntaxException("Invalid range",
globPattern, i - 3);
}
regex.append(c);
hasRangeStart = false;
} else {
hasRangeStart = true;
last = c;
}
}
if (c != ']') {
throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
}
regex.append("]]");
break;
case '{':
if (inGroup) {
throw new PatternSyntaxException("Cannot nest groups",
globPattern, i - 1);
}
regex.append("(?:(?:");
inGroup = true;
break;
case '}':
if (inGroup) {
regex.append("))");
inGroup = false;
} else {
regex.append('}');
}
break;
case ',':
if (inGroup) {
regex.append(")|(?:");
} else {
regex.append(',');
}
break;
case '*':
if (next(globPattern, i) == '*') {
// crosses directory boundaries
regex.append(".*");
i++;
} else {
// within directory boundary
regex.append("[^/]*");
}
break;
case '?':
regex.append("[^/]");
break;
default:
if (isRegexMeta(c)) {
regex.append('\\');
}
regex.append(c);
}
}
if (inGroup) {
throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
}
return regex.append('$').toString();
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2019, 2020, 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 javac.internal;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Indicates the API declaration in question is associated with a
* <em>preview feature</em>. See JEP 12: "Preview Language and VM
* Features" (http://openjdk.java.net/jeps/12).
*
* Note this internal annotation is handled specially by the javac compiler.
* To work properly with {@code --release older-release}, it requires special
* handling in {@code make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java}
* and {@code src/jdk.compiler/share/classes/openjdk/tools/javac/jvm/ClassReader.java}.
*
* @since 14
*/
// Match the meaningful targets of java.lang.Deprecated, omit local
// variables and parameter declarations
@Target({java.lang.annotation.ElementType.METHOD,
java.lang.annotation.ElementType.CONSTRUCTOR,
java.lang.annotation.ElementType.FIELD,
java.lang.annotation.ElementType.PACKAGE,
java.lang.annotation.ElementType.TYPE})
// CLASS retention will hopefully be sufficient for the purposes at hand
@Retention(RetentionPolicy.CLASS)
// *Not* @Documented
public @interface PreviewFeature {
/**
* Name of the preview feature the annotated API is associated
* with.
*/
public Feature feature();
public boolean reflective() default false;
public enum Feature {
SWITCH_PATTERN_MATCHING,
/**
* A key for testing.
*/
TEST,
;
}
}
@@ -0,0 +1,468 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Objects;
import java.util.stream.IntStream;
import javac.internal.jimage.decompressor.Decompressor;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class BasicImageReader implements AutoCloseable {
@SuppressWarnings("removal")
private static boolean isSystemProperty(String key, String value, String def) {
// No lambdas during bootstrap
return AccessController.doPrivileged(
new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return value.equals(System.getProperty(key, def));
}
});
}
static private final boolean IS_64_BIT =
isSystemProperty("sun.arch.data.model", "64", "32");
static private final boolean USE_JVM_MAP =
isSystemProperty("jdk.image.use.jvm.map", "true", "true");
static private final boolean MAP_ALL =
isSystemProperty("jdk.image.map.all", "true", IS_64_BIT ? "true" : "false");
private final Path imagePath;
private final ByteOrder byteOrder;
private final String name;
private final ByteBuffer memoryMap;
private final FileChannel channel;
private final ImageHeader header;
private final long indexSize;
private final IntBuffer redirect;
private final IntBuffer offsets;
private final ByteBuffer locations;
private final ByteBuffer strings;
private final ImageStringsReader stringsReader;
private final Decompressor decompressor;
@SuppressWarnings("removal")
protected BasicImageReader(Path path, ByteOrder byteOrder)
throws IOException {
this.imagePath = Objects.requireNonNull(path);
this.byteOrder = Objects.requireNonNull(byteOrder);
this.name = this.imagePath.toString();
ByteBuffer map;
if (USE_JVM_MAP && BasicImageReader.class.getClassLoader() == null) {
// Check to see if the jvm has opened the file using libjimage
// native entry when loading the image for this runtime
map = NativeImageBuffer.getNativeMap(name);
} else {
map = null;
}
// Open the file only if no memory map yet or is 32 bit jvm
if (map != null && MAP_ALL) {
channel = null;
} else {
channel = FileChannel.open(imagePath, StandardOpenOption.READ);
// No lambdas during bootstrap
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
if (BasicImageReader.class.getClassLoader() == null) {
try {
Class<?> fileChannelImpl =
Class.forName("sun.nio.ch.FileChannelImpl");
Method setUninterruptible =
fileChannelImpl.getMethod("setUninterruptible");
setUninterruptible.invoke(channel);
} catch (ClassNotFoundException |
NoSuchMethodException |
IllegalAccessException |
InvocationTargetException ex) {
// fall thru - will only happen on JDK-8 systems where this code
// is only used by tools using jrt-fs (non-critical.)
}
}
return null;
}
});
}
// If no memory map yet and 64 bit jvm then memory map entire file
if (MAP_ALL && map == null) {
map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
}
// Assume we have a memory map to read image file header
ByteBuffer headerBuffer = map;
int headerSize = ImageHeader.getHeaderSize();
// If no memory map then read header from image file
if (headerBuffer == null) {
headerBuffer = ByteBuffer.allocateDirect(headerSize);
if (channel.read(headerBuffer, 0L) == headerSize) {
headerBuffer.rewind();
} else {
throw new IOException("\"" + name + "\" is not an image file");
}
} else if (headerBuffer.capacity() < headerSize) {
throw new IOException("\"" + name + "\" is not an image file");
}
// Interpret the image file header
header = readHeader(intBuffer(headerBuffer, 0, headerSize));
indexSize = header.getIndexSize();
// If no memory map yet then must be 32 bit jvm not previously mapped
if (map == null) {
// Just map the image index
map = channel.map(FileChannel.MapMode.READ_ONLY, 0, indexSize);
}
memoryMap = map.asReadOnlyBuffer();
// Interpret the image index
if (memoryMap.capacity() < indexSize) {
throw new IOException("The image file \"" + name + "\" is corrupted");
}
redirect = intBuffer(memoryMap, header.getRedirectOffset(), header.getRedirectSize());
offsets = intBuffer(memoryMap, header.getOffsetsOffset(), header.getOffsetsSize());
locations = slice(memoryMap, header.getLocationsOffset(), header.getLocationsSize());
strings = slice(memoryMap, header.getStringsOffset(), header.getStringsSize());
stringsReader = new ImageStringsReader(this);
decompressor = new Decompressor();
}
protected BasicImageReader(Path imagePath) throws IOException {
this(imagePath, ByteOrder.nativeOrder());
}
public static BasicImageReader open(Path imagePath) throws IOException {
return new BasicImageReader(imagePath, ByteOrder.nativeOrder());
}
public ImageHeader getHeader() {
return header;
}
private ImageHeader readHeader(IntBuffer buffer) throws IOException {
ImageHeader result = ImageHeader.readFrom(buffer);
if (result.getMagic() != ImageHeader.MAGIC) {
throw new IOException("\"" + name + "\" is not an image file");
}
if (result.getMajorVersion() != ImageHeader.MAJOR_VERSION ||
result.getMinorVersion() != ImageHeader.MINOR_VERSION) {
throw new IOException("The image file \"" + name + "\" is not " +
"the correct version. Major: " + result.getMajorVersion() +
". Minor: " + result.getMinorVersion());
}
return result;
}
private static ByteBuffer slice(ByteBuffer buffer, int position, int capacity) {
// Note that this is the only limit and position manipulation of
// BasicImageReader private ByteBuffers. The synchronize could be avoided
// by cloning the buffer to make a local copy, but at the cost of creating
// a new object.
synchronized(buffer) {
buffer.limit(position + capacity);
buffer.position(position);
return buffer.slice();
}
}
private IntBuffer intBuffer(ByteBuffer buffer, int offset, int size) {
return slice(buffer, offset, size).order(byteOrder).asIntBuffer();
}
public static void releaseByteBuffer(ByteBuffer buffer) {
Objects.requireNonNull(buffer);
if (!MAP_ALL) {
ImageBufferCache.releaseBuffer(buffer);
}
}
public String getName() {
return name;
}
public ByteOrder getByteOrder() {
return byteOrder;
}
public Path getImagePath() {
return imagePath;
}
@Override
public void close() throws IOException {
if (channel != null) {
channel.close();
}
}
public ImageStringsReader getStrings() {
return stringsReader;
}
public ImageLocation findLocation(String module, String name) {
int index = getLocationIndex(module, name);
if (index < 0) {
return null;
}
long[] attributes = getAttributes(offsets.get(index));
if (!ImageLocation.verify(module, name, attributes, stringsReader)) {
return null;
}
return new ImageLocation(attributes, stringsReader);
}
public ImageLocation findLocation(String name) {
int index = getLocationIndex(name);
if (index < 0) {
return null;
}
long[] attributes = getAttributes(offsets.get(index));
if (!ImageLocation.verify(name, attributes, stringsReader)) {
return null;
}
return new ImageLocation(attributes, stringsReader);
}
public boolean verifyLocation(String module, String name) {
int index = getLocationIndex(module, name);
if (index < 0) {
return false;
}
int locationOffset = offsets.get(index);
return ImageLocation.verify(module, name, locations, locationOffset, stringsReader);
}
// Details of the algorithm used here can be found in
// jdk.tools.jlink.internal.PerfectHashBuilder.
public int getLocationIndex(String name) {
int count = header.getTableLength();
int index = redirect.get(ImageStringsReader.hashCode(name) % count);
if (index < 0) {
// index is twos complement of location attributes index.
return -index - 1;
} else if (index > 0) {
// index is hash seed needed to compute location attributes index.
return ImageStringsReader.hashCode(name, index) % count;
} else {
// No entry.
return -1;
}
}
private int getLocationIndex(String module, String name) {
int count = header.getTableLength();
int index = redirect.get(ImageStringsReader.hashCode(module, name) % count);
if (index < 0) {
// index is twos complement of location attributes index.
return -index - 1;
} else if (index > 0) {
// index is hash seed needed to compute location attributes index.
return ImageStringsReader.hashCode(module, name, index) % count;
} else {
// No entry.
return -1;
}
}
public String[] getEntryNames() {
int[] attributeOffsets = new int[offsets.capacity()];
offsets.get(attributeOffsets);
return IntStream.of(attributeOffsets)
.filter(o -> o != 0)
.mapToObj(o -> ImageLocation.readFrom(this, o).getFullName())
.sorted()
.toArray(String[]::new);
}
ImageLocation getLocation(int offset) {
return ImageLocation.readFrom(this, offset);
}
public long[] getAttributes(int offset) {
if (offset < 0 || offset >= locations.limit()) {
throw new IndexOutOfBoundsException("offset");
}
return ImageLocation.decompress(locations, offset);
}
public String getString(int offset) {
if (offset < 0 || offset >= strings.limit()) {
throw new IndexOutOfBoundsException("offset");
}
return ImageStringsReader.stringFromByteBuffer(strings, offset);
}
public int match(int offset, String string, int stringOffset) {
if (offset < 0 || offset >= strings.limit()) {
throw new IndexOutOfBoundsException("offset");
}
return ImageStringsReader.stringFromByteBufferMatches(strings, offset, string, stringOffset);
}
private byte[] getBufferBytes(ByteBuffer buffer) {
Objects.requireNonNull(buffer);
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes);
return bytes;
}
private ByteBuffer readBuffer(long offset, long size) {
if (offset < 0 || Integer.MAX_VALUE <= offset) {
throw new IndexOutOfBoundsException("Bad offset: " + offset);
}
if (size < 0 || Integer.MAX_VALUE <= size) {
throw new IndexOutOfBoundsException("Bad size: " + size);
}
if (MAP_ALL) {
ByteBuffer buffer = slice(memoryMap, (int)offset, (int)size);
buffer.order(ByteOrder.BIG_ENDIAN);
return buffer;
} else {
if (channel == null) {
throw new InternalError("Image file channel not open");
}
ByteBuffer buffer = ImageBufferCache.getBuffer(size);
int read;
try {
read = channel.read(buffer, offset);
buffer.rewind();
} catch (IOException ex) {
ImageBufferCache.releaseBuffer(buffer);
throw new RuntimeException(ex);
}
if (read != size) {
ImageBufferCache.releaseBuffer(buffer);
throw new RuntimeException("Short read: " + read +
" instead of " + size + " bytes");
}
return buffer;
}
}
public byte[] getResource(String name) {
Objects.requireNonNull(name);
ImageLocation location = findLocation(name);
return location != null ? getResource(location) : null;
}
public byte[] getResource(ImageLocation loc) {
ByteBuffer buffer = getResourceBuffer(loc);
if (buffer != null) {
byte[] bytes = getBufferBytes(buffer);
ImageBufferCache.releaseBuffer(buffer);
return bytes;
}
return null;
}
public ByteBuffer getResourceBuffer(ImageLocation loc) {
Objects.requireNonNull(loc);
long offset = loc.getContentOffset() + indexSize;
long compressedSize = loc.getCompressedSize();
long uncompressedSize = loc.getUncompressedSize();
if (compressedSize < 0 || Integer.MAX_VALUE < compressedSize) {
throw new IndexOutOfBoundsException(
"Bad compressed size: " + compressedSize);
}
if (uncompressedSize < 0 || Integer.MAX_VALUE < uncompressedSize) {
throw new IndexOutOfBoundsException(
"Bad uncompressed size: " + uncompressedSize);
}
if (compressedSize == 0) {
return readBuffer(offset, uncompressedSize);
} else {
ByteBuffer buffer = readBuffer(offset, compressedSize);
if (buffer != null) {
byte[] bytesIn = getBufferBytes(buffer);
ImageBufferCache.releaseBuffer(buffer);
byte[] bytesOut;
try {
bytesOut = decompressor.decompressResource(byteOrder,
(int strOffset) -> getString(strOffset), bytesIn);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return ByteBuffer.wrap(bytesOut);
}
}
return null;
}
public InputStream getResourceStream(ImageLocation loc) {
Objects.requireNonNull(loc);
byte[] bytes = getResource(loc);
return new ByteArrayInputStream(bytes);
}
}
@@ -0,0 +1,158 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrt-fs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
class ImageBufferCache {
private static final int MAX_CACHED_BUFFERS = 3;
private static final int LARGE_BUFFER = 0x10000;
/*
* We used to have a class BufferReference extending from WeakReference<ByteBuffer>.
* BufferReference class had an instance field called "capacity". This field was
* used to make DECREASING_CAPACITY_NULLS_LAST comparator stable in the presence
* of GC clearing the WeakReference concurrently.
*
* But this scheme results in metaspace leak. The thread local is alive till the
* the thread is alive. And so ImageBufferCache$BufferReference class was kept alive.
* Because this class and ImageBufferCache$BufferReference are all loaded by a URL
* class loader from jrt-fs.jar, the class loader and so all the classes loaded by it
* were alive!
*
* Solution is to avoid using a URL loader loaded class type with thread local. All we
* need is a pair of WeakReference<ByteBuffer>, Integer (saved capacity for stability
* of comparator). We use Map.Entry as pair implementation. With this, all types used
* with thread local are bootstrap types and so no metaspace leak.
*/
@SuppressWarnings("unchecked")
private static final ThreadLocal<Map.Entry<WeakReference<ByteBuffer>, Integer>[]> CACHE =
new ThreadLocal<Map.Entry<WeakReference<ByteBuffer>, Integer>[]>() {
@Override
protected Map.Entry<WeakReference<ByteBuffer>, Integer>[] initialValue() {
// 1 extra slot to simplify logic of releaseBuffer()
return (Map.Entry<WeakReference<ByteBuffer>, Integer>[])new Map.Entry<?,?>[MAX_CACHED_BUFFERS + 1];
}
};
private static ByteBuffer allocateBuffer(long size) {
return ByteBuffer.allocateDirect((int)((size + 0xFFF) & ~0xFFF));
}
static ByteBuffer getBuffer(long size) {
if (size < 0 || Integer.MAX_VALUE < size) {
throw new IndexOutOfBoundsException("size");
}
ByteBuffer result = null;
if (size > LARGE_BUFFER) {
result = allocateBuffer(size);
} else {
Map.Entry<WeakReference<ByteBuffer>, Integer>[] cache = CACHE.get();
// buffers are ordered by decreasing capacity
// cache[MAX_CACHED_BUFFERS] is always null
for (int i = MAX_CACHED_BUFFERS - 1; i >= 0; i--) {
Map.Entry<WeakReference<ByteBuffer>, Integer> reference = cache[i];
if (reference != null) {
ByteBuffer buffer = getByteBuffer(reference);
if (buffer != null && size <= buffer.capacity()) {
cache[i] = null;
result = buffer;
result.rewind();
break;
}
}
}
if (result == null) {
result = allocateBuffer(size);
}
}
result.limit((int)size);
return result;
}
static void releaseBuffer(ByteBuffer buffer) {
if (buffer.capacity() > LARGE_BUFFER) {
return;
}
Map.Entry<WeakReference<ByteBuffer>, Integer>[] cache = CACHE.get();
// expunge cleared BufferRef(s)
for (int i = 0; i < MAX_CACHED_BUFFERS; i++) {
Map.Entry<WeakReference<ByteBuffer>, Integer> reference = cache[i];
if (reference != null && getByteBuffer(reference) == null) {
cache[i] = null;
}
}
// insert buffer back with new BufferRef wrapping it
cache[MAX_CACHED_BUFFERS] = newCacheEntry(buffer);
Arrays.sort(cache, DECREASING_CAPACITY_NULLS_LAST);
// squeeze the smallest one out
cache[MAX_CACHED_BUFFERS] = null;
}
private static Map.Entry<WeakReference<ByteBuffer>, Integer> newCacheEntry(ByteBuffer bb) {
return new AbstractMap.SimpleEntry<WeakReference<ByteBuffer>, Integer>(
new WeakReference<ByteBuffer>(bb), bb.capacity());
}
private static int getCapacity(Map.Entry<WeakReference<ByteBuffer>, Integer> e) {
return e == null? 0 : e.getValue();
}
private static ByteBuffer getByteBuffer(Map.Entry<WeakReference<ByteBuffer>, Integer> e) {
return e == null? null : e.getKey().get();
}
private static Comparator<Map.Entry<WeakReference<ByteBuffer>, Integer>> DECREASING_CAPACITY_NULLS_LAST =
new Comparator<Map.Entry<WeakReference<ByteBuffer>, Integer>>() {
@Override
public int compare(Map.Entry<WeakReference<ByteBuffer>, Integer> br1,
Map.Entry<WeakReference<ByteBuffer>, Integer> br2) {
return Integer.compare(getCapacity(br1), getCapacity(br2));
}
};
}
@@ -0,0 +1,183 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Objects;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public final class ImageHeader {
public static final int MAGIC = 0xCAFEDADA;
public static final int MAJOR_VERSION = 1;
public static final int MINOR_VERSION = 0;
private static final int HEADER_SLOTS = 7;
private final int magic;
private final int majorVersion;
private final int minorVersion;
private final int flags;
private final int resourceCount;
private final int tableLength;
private final int locationsSize;
private final int stringsSize;
public ImageHeader(int resourceCount, int tableCount,
int locationsSize, int stringsSize) {
this(MAGIC, MAJOR_VERSION, MINOR_VERSION, 0, resourceCount,
tableCount, locationsSize, stringsSize);
}
public ImageHeader(int magic, int majorVersion, int minorVersion,
int flags, int resourceCount,
int tableLength, int locationsSize, int stringsSize)
{
this.magic = magic;
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.flags = flags;
this.resourceCount = resourceCount;
this.tableLength = tableLength;
this.locationsSize = locationsSize;
this.stringsSize = stringsSize;
}
public static int getHeaderSize() {
return HEADER_SLOTS * 4;
}
static ImageHeader readFrom(IntBuffer buffer) {
Objects.requireNonNull(buffer);
if (buffer.capacity() != HEADER_SLOTS) {
throw new InternalError(
"jimage header not the correct size: " + buffer.capacity());
}
int magic = buffer.get(0);
int version = buffer.get(1);
int majorVersion = version >>> 16;
int minorVersion = version & 0xFFFF;
int flags = buffer.get(2);
int resourceCount = buffer.get(3);
int tableLength = buffer.get(4);
int locationsSize = buffer.get(5);
int stringsSize = buffer.get(6);
return new ImageHeader(magic, majorVersion, minorVersion, flags,
resourceCount, tableLength, locationsSize, stringsSize);
}
public void writeTo(ImageStream stream) {
Objects.requireNonNull(stream);
stream.ensure(getHeaderSize());
writeTo(stream.getBuffer());
}
public void writeTo(ByteBuffer buffer) {
Objects.requireNonNull(buffer);
buffer.putInt(magic);
buffer.putInt(majorVersion << 16 | minorVersion);
buffer.putInt(flags);
buffer.putInt(resourceCount);
buffer.putInt(tableLength);
buffer.putInt(locationsSize);
buffer.putInt(stringsSize);
}
public int getMagic() {
return magic;
}
public int getMajorVersion() {
return majorVersion;
}
public int getMinorVersion() {
return minorVersion;
}
public int getFlags() {
return flags;
}
public int getResourceCount() {
return resourceCount;
}
public int getTableLength() {
return tableLength;
}
public int getRedirectSize() {
return tableLength * 4;
}
public int getOffsetsSize() {
return tableLength * 4;
}
public int getLocationsSize() {
return locationsSize;
}
public int getStringsSize() {
return stringsSize;
}
public int getIndexSize() {
return getHeaderSize() +
getRedirectSize() +
getOffsetsSize() +
getLocationsSize() +
getStringsSize();
}
int getRedirectOffset() {
return getHeaderSize();
}
int getOffsetsOffset() {
return getRedirectOffset() +
getRedirectSize();
}
int getLocationsOffset() {
return getOffsetsOffset() +
getOffsetsSize();
}
int getStringsOffset() {
return getLocationsOffset() +
getLocationsSize();
}
}
@@ -0,0 +1,369 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.nio.ByteBuffer;
import java.util.Objects;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class ImageLocation {
public static final int ATTRIBUTE_END = 0;
public static final int ATTRIBUTE_MODULE = 1;
public static final int ATTRIBUTE_PARENT = 2;
public static final int ATTRIBUTE_BASE = 3;
public static final int ATTRIBUTE_EXTENSION = 4;
public static final int ATTRIBUTE_OFFSET = 5;
public static final int ATTRIBUTE_COMPRESSED = 6;
public static final int ATTRIBUTE_UNCOMPRESSED = 7;
public static final int ATTRIBUTE_COUNT = 8;
protected final long[] attributes;
protected final ImageStrings strings;
public ImageLocation(long[] attributes, ImageStrings strings) {
this.attributes = Objects.requireNonNull(attributes);
this.strings = Objects.requireNonNull(strings);
}
ImageStrings getStrings() {
return strings;
}
static long[] decompress(ByteBuffer bytes, int offset) {
Objects.requireNonNull(bytes);
long[] attributes = new long[ATTRIBUTE_COUNT];
int limit = bytes.limit();
while (offset < limit) {
int data = bytes.get(offset++) & 0xFF;
if (data <= 0x7) { // ATTRIBUTE_END
break;
}
int kind = data >>> 3;
if (ATTRIBUTE_COUNT <= kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}
int length = (data & 0x7) + 1;
attributes[kind] = readValue(length, bytes, offset, limit);
offset += length;
}
return attributes;
}
public static byte[] compress(long[] attributes) {
Objects.requireNonNull(attributes);
ImageStream stream = new ImageStream(16);
for (int kind = ATTRIBUTE_END + 1; kind < ATTRIBUTE_COUNT; kind++) {
long value = attributes[kind];
if (value != 0) {
int n = (63 - Long.numberOfLeadingZeros(value)) >> 3;
stream.put((kind << 3) | n);
for (int i = n; i >= 0; i--) {
stream.put((int)(value >> (i << 3)));
}
}
}
stream.put(ATTRIBUTE_END << 3);
return stream.toArray();
}
public boolean verify(String name) {
return verify(name, attributes, strings);
}
/**
* A simpler verification would be {@code name.equals(getFullName())}, but
* by not creating the full name and enabling early returns we allocate
* fewer objects.
*/
static boolean verify(String name, long[] attributes, ImageStrings strings) {
Objects.requireNonNull(name);
final int length = name.length();
int index = 0;
int moduleOffset = (int)attributes[ATTRIBUTE_MODULE];
if (moduleOffset != 0 && length >= 1) {
int moduleLen = strings.match(moduleOffset, name, 1);
index = moduleLen + 1;
if (moduleLen < 0
|| length <= index
|| name.charAt(0) != '/'
|| name.charAt(index++) != '/') {
return false;
}
}
return verifyName(null, name, index, length, 0,
(int) attributes[ATTRIBUTE_PARENT],
(int) attributes[ATTRIBUTE_BASE],
(int) attributes[ATTRIBUTE_EXTENSION],
strings);
}
static boolean verify(String module, String name, ByteBuffer locations,
int locationOffset, ImageStrings strings) {
int moduleOffset = 0;
int parentOffset = 0;
int baseOffset = 0;
int extOffset = 0;
int limit = locations.limit();
while (locationOffset < limit) {
int data = locations.get(locationOffset++) & 0xFF;
if (data <= 0x7) { // ATTRIBUTE_END
break;
}
int kind = data >>> 3;
if (ATTRIBUTE_COUNT <= kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}
int length = (data & 0x7) + 1;
switch (kind) {
case ATTRIBUTE_MODULE:
moduleOffset = (int) readValue(length, locations, locationOffset, limit);
break;
case ATTRIBUTE_BASE:
baseOffset = (int) readValue(length, locations, locationOffset, limit);
break;
case ATTRIBUTE_PARENT:
parentOffset = (int) readValue(length, locations, locationOffset, limit);
break;
case ATTRIBUTE_EXTENSION:
extOffset = (int) readValue(length, locations, locationOffset, limit);
break;
}
locationOffset += length;
}
return verifyName(module, name, 0, name.length(),
moduleOffset, parentOffset, baseOffset, extOffset, strings);
}
private static long readValue(int length, ByteBuffer buffer, int offset, int limit) {
long value = 0;
for (int j = 0; j < length; j++) {
value <<= 8;
if (offset >= limit) {
throw new InternalError("Missing jimage attribute data");
}
value |= buffer.get(offset++) & 0xFF;
}
return value;
}
static boolean verify(String module, String name, long[] attributes,
ImageStrings strings) {
Objects.requireNonNull(module);
Objects.requireNonNull(name);
return verifyName(module, name, 0, name.length(),
(int) attributes[ATTRIBUTE_MODULE],
(int) attributes[ATTRIBUTE_PARENT],
(int) attributes[ATTRIBUTE_BASE],
(int) attributes[ATTRIBUTE_EXTENSION],
strings);
}
private static boolean verifyName(String module, String name, int index, int length,
int moduleOffset, int parentOffset, int baseOffset, int extOffset, ImageStrings strings) {
if (moduleOffset != 0) {
if (strings.match(moduleOffset, module, 0) != module.length()) {
return false;
}
}
if (parentOffset != 0) {
int parentLen = strings.match(parentOffset, name, index);
if (parentLen < 0) {
return false;
}
index += parentLen;
if (length <= index || name.charAt(index++) != '/') {
return false;
}
}
int baseLen = strings.match(baseOffset, name, index);
if (baseLen < 0) {
return false;
}
index += baseLen;
if (extOffset != 0) {
if (length <= index
|| name.charAt(index++) != '.') {
return false;
}
int extLen = strings.match(extOffset, name, index);
if (extLen < 0) {
return false;
}
index += extLen;
}
return length == index;
}
long getAttribute(int kind) {
if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}
return attributes[kind];
}
String getAttributeString(int kind) {
if (kind < ATTRIBUTE_END || ATTRIBUTE_COUNT <= kind) {
throw new InternalError(
"Invalid jimage attribute kind: " + kind);
}
return getStrings().get((int)attributes[kind]);
}
public String getModule() {
return getAttributeString(ATTRIBUTE_MODULE);
}
public int getModuleOffset() {
return (int)getAttribute(ATTRIBUTE_MODULE);
}
public String getBase() {
return getAttributeString(ATTRIBUTE_BASE);
}
public int getBaseOffset() {
return (int)getAttribute(ATTRIBUTE_BASE);
}
public String getParent() {
return getAttributeString(ATTRIBUTE_PARENT);
}
public int getParentOffset() {
return (int)getAttribute(ATTRIBUTE_PARENT);
}
public String getExtension() {
return getAttributeString(ATTRIBUTE_EXTENSION);
}
public int getExtensionOffset() {
return (int)getAttribute(ATTRIBUTE_EXTENSION);
}
public String getFullName() {
return getFullName(false);
}
public String getFullName(boolean modulesPrefix) {
StringBuilder builder = new StringBuilder();
if (getModuleOffset() != 0) {
if (modulesPrefix) {
builder.append("/modules");
}
builder.append('/');
builder.append(getModule());
builder.append('/');
}
if (getParentOffset() != 0) {
builder.append(getParent());
builder.append('/');
}
builder.append(getBase());
if (getExtensionOffset() != 0) {
builder.append('.');
builder.append(getExtension());
}
return builder.toString();
}
String buildName(boolean includeModule, boolean includeParent,
boolean includeName) {
StringBuilder builder = new StringBuilder();
if (includeModule && getModuleOffset() != 0) {
builder.append("/modules/");
builder.append(getModule());
}
if (includeParent && getParentOffset() != 0) {
builder.append('/');
builder.append(getParent());
}
if (includeName) {
if (includeModule || includeParent) {
builder.append('/');
}
builder.append(getBase());
if (getExtensionOffset() != 0) {
builder.append('.');
builder.append(getExtension());
}
}
return builder.toString();
}
public long getContentOffset() {
return getAttribute(ATTRIBUTE_OFFSET);
}
public long getCompressedSize() {
return getAttribute(ATTRIBUTE_COMPRESSED);
}
public long getUncompressedSize() {
return getAttribute(ATTRIBUTE_UNCOMPRESSED);
}
static ImageLocation readFrom(BasicImageReader reader, int offset) {
Objects.requireNonNull(reader);
long[] attributes = reader.getAttributes(offset);
ImageStringsReader strings = reader.getStrings();
return new ImageLocation(attributes, strings);
}
}
@@ -0,0 +1,853 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public final class ImageReader implements AutoCloseable {
private final SharedImageReader reader;
private volatile boolean closed;
private ImageReader(SharedImageReader reader) {
this.reader = reader;
}
public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException {
Objects.requireNonNull(imagePath);
Objects.requireNonNull(byteOrder);
return SharedImageReader.open(imagePath, byteOrder);
}
public static ImageReader open(Path imagePath) throws IOException {
return open(imagePath, ByteOrder.nativeOrder());
}
@Override
public void close() throws IOException {
if (closed) {
throw new IOException("image file already closed");
}
reader.close(this);
closed = true;
}
private void ensureOpen() throws IOException {
if (closed) {
throw new IOException("image file closed");
}
}
private void requireOpen() {
if (closed) {
throw new IllegalStateException("image file closed");
}
}
// directory management interface
public Directory getRootDirectory() throws IOException {
ensureOpen();
return reader.getRootDirectory();
}
public Node findNode(String name) throws IOException {
ensureOpen();
return reader.findNode(name);
}
public byte[] getResource(Node node) throws IOException {
ensureOpen();
return reader.getResource(node);
}
public byte[] getResource(Resource rs) throws IOException {
ensureOpen();
return reader.getResource(rs);
}
public ImageHeader getHeader() {
requireOpen();
return reader.getHeader();
}
public static void releaseByteBuffer(ByteBuffer buffer) {
BasicImageReader.releaseByteBuffer(buffer);
}
public String getName() {
requireOpen();
return reader.getName();
}
public ByteOrder getByteOrder() {
requireOpen();
return reader.getByteOrder();
}
public Path getImagePath() {
requireOpen();
return reader.getImagePath();
}
public ImageStringsReader getStrings() {
requireOpen();
return reader.getStrings();
}
public ImageLocation findLocation(String mn, String rn) {
requireOpen();
return reader.findLocation(mn, rn);
}
public boolean verifyLocation(String mn, String rn) {
requireOpen();
return reader.verifyLocation(mn, rn);
}
public ImageLocation findLocation(String name) {
requireOpen();
return reader.findLocation(name);
}
public String[] getEntryNames() {
requireOpen();
return reader.getEntryNames();
}
public String[] getModuleNames() {
requireOpen();
int off = "/modules/".length();
return reader.findNode("/modules")
.getChildren()
.stream()
.map(Node::getNameString)
.map(s -> s.substring(off, s.length()))
.toArray(String[]::new);
}
public long[] getAttributes(int offset) {
requireOpen();
return reader.getAttributes(offset);
}
public String getString(int offset) {
requireOpen();
return reader.getString(offset);
}
public byte[] getResource(String name) {
requireOpen();
return reader.getResource(name);
}
public byte[] getResource(ImageLocation loc) {
requireOpen();
return reader.getResource(loc);
}
public ByteBuffer getResourceBuffer(ImageLocation loc) {
requireOpen();
return reader.getResourceBuffer(loc);
}
public InputStream getResourceStream(ImageLocation loc) {
requireOpen();
return reader.getResourceStream(loc);
}
private static final class SharedImageReader extends BasicImageReader {
static final int SIZE_OF_OFFSET = Integer.BYTES;
static final Map<Path, SharedImageReader> OPEN_FILES = new HashMap<>();
// List of openers for this shared image.
final Set<ImageReader> openers;
// attributes of the .jimage file. jimage file does not contain
// attributes for the individual resources (yet). We use attributes
// of the jimage file itself (creation, modification, access times).
// Iniitalized lazily, see {@link #imageFileAttributes()}.
BasicFileAttributes imageFileAttributes;
// directory management implementation
final HashMap<String, Node> nodes;
volatile Directory rootDir;
Directory packagesDir;
Directory modulesDir;
private SharedImageReader(Path imagePath, ByteOrder byteOrder) throws IOException {
super(imagePath, byteOrder);
this.openers = new HashSet<>();
this.nodes = new HashMap<>();
}
public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException {
Objects.requireNonNull(imagePath);
Objects.requireNonNull(byteOrder);
synchronized (OPEN_FILES) {
SharedImageReader reader = OPEN_FILES.get(imagePath);
if (reader == null) {
// Will fail with an IOException if wrong byteOrder.
reader = new SharedImageReader(imagePath, byteOrder);
OPEN_FILES.put(imagePath, reader);
} else if (reader.getByteOrder() != byteOrder) {
throw new IOException("\"" + reader.getName() + "\" is not an image file");
}
ImageReader image = new ImageReader(reader);
reader.openers.add(image);
return image;
}
}
public void close(ImageReader image) throws IOException {
Objects.requireNonNull(image);
synchronized (OPEN_FILES) {
if (!openers.remove(image)) {
throw new IOException("image file already closed");
}
if (openers.isEmpty()) {
close();
nodes.clear();
rootDir = null;
if (!OPEN_FILES.remove(this.getImagePath(), this)) {
throw new IOException("image file not found in open list");
}
}
}
}
void addOpener(ImageReader reader) {
synchronized (OPEN_FILES) {
openers.add(reader);
}
}
boolean removeOpener(ImageReader reader) {
synchronized (OPEN_FILES) {
return openers.remove(reader);
}
}
// directory management interface
Directory getRootDirectory() {
return buildRootDirectory();
}
/**
* Lazily build a node from a name.
*/
synchronized Node buildNode(String name) {
Node n;
boolean isPackages = name.startsWith("/packages");
boolean isModules = !isPackages && name.startsWith("/modules");
if (!(isModules || isPackages)) {
return null;
}
ImageLocation loc = findLocation(name);
if (loc != null) { // A sub tree node
if (isPackages) {
n = handlePackages(name, loc);
} else { // modules sub tree
n = handleModulesSubTree(name, loc);
}
} else { // Asking for a resource? /modules/java.base/java/lang/Object.class
if (isModules) {
n = handleResource(name);
} else {
// Possibly ask for /packages/java.lang/java.base
// although /packages/java.base not created
n = handleModuleLink(name);
}
}
return n;
}
synchronized Directory buildRootDirectory() {
Directory root = rootDir; // volatile read
if (root != null) {
return root;
}
root = newDirectory(null, "/");
root.setIsRootDir();
// /packages dir
packagesDir = newDirectory(root, "/packages");
packagesDir.setIsPackagesDir();
// /modules dir
modulesDir = newDirectory(root, "/modules");
modulesDir.setIsModulesDir();
root.setCompleted(true);
return rootDir = root;
}
/**
* To visit sub tree resources.
*/
interface LocationVisitor {
void visit(ImageLocation loc);
}
void visitLocation(ImageLocation loc, LocationVisitor visitor) {
byte[] offsets = getResource(loc);
ByteBuffer buffer = ByteBuffer.wrap(offsets);
buffer.order(getByteOrder());
IntBuffer intBuffer = buffer.asIntBuffer();
for (int i = 0; i < offsets.length / SIZE_OF_OFFSET; i++) {
int offset = intBuffer.get(i);
ImageLocation pkgLoc = getLocation(offset);
visitor.visit(pkgLoc);
}
}
void visitPackageLocation(ImageLocation loc) {
// Retrieve package name
String pkgName = getBaseExt(loc);
// Content is array of offsets in Strings table
byte[] stringsOffsets = getResource(loc);
ByteBuffer buffer = ByteBuffer.wrap(stringsOffsets);
buffer.order(getByteOrder());
IntBuffer intBuffer = buffer.asIntBuffer();
// For each module, create a link node.
for (int i = 0; i < stringsOffsets.length / SIZE_OF_OFFSET; i++) {
// skip empty state, useless.
intBuffer.get(i);
i++;
int offset = intBuffer.get(i);
String moduleName = getString(offset);
Node targetNode = findNode("/modules/" + moduleName);
if (targetNode != null) {
String pkgDirName = packagesDir.getName() + "/" + pkgName;
Directory pkgDir = (Directory) nodes.get(pkgDirName);
newLinkNode(pkgDir, pkgDir.getName() + "/" + moduleName, targetNode);
}
}
}
Node handlePackages(String name, ImageLocation loc) {
long size = loc.getUncompressedSize();
Node n = null;
// Only possiblities are /packages, /packages/package/module
if (name.equals("/packages")) {
visitLocation(loc, (childloc) -> {
findNode(childloc.getFullName());
});
packagesDir.setCompleted(true);
n = packagesDir;
} else {
if (size != 0) { // children are offsets to module in StringsTable
String pkgName = getBaseExt(loc);
Directory pkgDir = newDirectory(packagesDir, packagesDir.getName() + "/" + pkgName);
visitPackageLocation(loc);
pkgDir.setCompleted(true);
n = pkgDir;
} else { // Link to module
String pkgName = loc.getParent();
String modName = getBaseExt(loc);
Node targetNode = findNode("/modules/" + modName);
if (targetNode != null) {
String pkgDirName = packagesDir.getName() + "/" + pkgName;
Directory pkgDir = (Directory) nodes.get(pkgDirName);
Node linkNode = newLinkNode(pkgDir, pkgDir.getName() + "/" + modName, targetNode);
n = linkNode;
}
}
}
return n;
}
// Asking for /packages/package/module although
// /packages/<pkg>/ not yet created, need to create it
// prior to return the link to module node.
Node handleModuleLink(String name) {
// eg: unresolved /packages/package/module
// Build /packages/package node
Node ret = null;
String radical = "/packages/";
String path = name;
if (path.startsWith(radical)) {
int start = radical.length();
int pkgEnd = path.indexOf('/', start);
if (pkgEnd != -1) {
String pkg = path.substring(start, pkgEnd);
String pkgPath = radical + pkg;
Node n = findNode(pkgPath);
// If not found means that this is a symbolic link such as:
// /packages/java.util/java.base/java/util/Vector.class
// and will be done by a retry of the filesystem
for (Node child : n.getChildren()) {
if (child.name.equals(name)) {
ret = child;
break;
}
}
}
}
return ret;
}
Node handleModulesSubTree(String name, ImageLocation loc) {
Node n;
assert (name.equals(loc.getFullName()));
Directory dir = makeDirectories(name);
visitLocation(loc, (childloc) -> {
String path = childloc.getFullName();
if (path.startsWith("/modules")) { // a package
makeDirectories(path);
} else { // a resource
makeDirectories(childloc.buildName(true, true, false));
newResource(dir, childloc);
}
});
dir.setCompleted(true);
n = dir;
return n;
}
Node handleResource(String name) {
Node n = null;
if (!name.startsWith("/modules/")) {
return null;
}
// Make sure that the thing that follows "/modules/" is a module name.
int moduleEndIndex = name.indexOf('/', "/modules/".length());
if (moduleEndIndex == -1) {
return null;
}
ImageLocation moduleLoc = findLocation(name.substring(0, moduleEndIndex));
if (moduleLoc == null || moduleLoc.getModuleOffset() == 0) {
return null;
}
String locationPath = name.substring("/modules".length());
ImageLocation resourceLoc = findLocation(locationPath);
if (resourceLoc != null) {
Directory dir = makeDirectories(resourceLoc.buildName(true, true, false));
Resource res = newResource(dir, resourceLoc);
n = res;
}
return n;
}
String getBaseExt(ImageLocation loc) {
String base = loc.getBase();
String ext = loc.getExtension();
if (ext != null && !ext.isEmpty()) {
base = base + "." + ext;
}
return base;
}
synchronized Node findNode(String name) {
buildRootDirectory();
Node n = nodes.get(name);
if (n == null || !n.isCompleted()) {
n = buildNode(name);
}
return n;
}
/**
* Returns the file attributes of the image file.
*/
BasicFileAttributes imageFileAttributes() {
BasicFileAttributes attrs = imageFileAttributes;
if (attrs == null) {
try {
Path file = getImagePath();
attrs = Files.readAttributes(file, BasicFileAttributes.class);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
imageFileAttributes = attrs;
}
return attrs;
}
Directory newDirectory(Directory parent, String name) {
Directory dir = Directory.create(parent, name, imageFileAttributes());
nodes.put(dir.getName(), dir);
return dir;
}
Resource newResource(Directory parent, ImageLocation loc) {
Resource res = Resource.create(parent, loc, imageFileAttributes());
nodes.put(res.getName(), res);
return res;
}
LinkNode newLinkNode(Directory dir, String name, Node link) {
LinkNode linkNode = LinkNode.create(dir, name, link);
nodes.put(linkNode.getName(), linkNode);
return linkNode;
}
Directory makeDirectories(String parent) {
Directory last = rootDir;
for (int offset = parent.indexOf('/', 1);
offset != -1;
offset = parent.indexOf('/', offset + 1)) {
String dir = parent.substring(0, offset);
last = makeDirectory(dir, last);
}
return makeDirectory(parent, last);
}
Directory makeDirectory(String dir, Directory last) {
Directory nextDir = (Directory) nodes.get(dir);
if (nextDir == null) {
nextDir = newDirectory(last, dir);
}
return nextDir;
}
byte[] getResource(Node node) throws IOException {
if (node.isResource()) {
return super.getResource(node.getLocation());
}
throw new IOException("Not a resource: " + node);
}
byte[] getResource(Resource rs) throws IOException {
return super.getResource(rs.getLocation());
}
}
// jimage file does not store directory structure. We build nodes
// using the "path" strings found in the jimage file.
// Node can be a directory or a resource
public abstract static class Node {
private static final int ROOT_DIR = 0b0000_0000_0000_0001;
private static final int PACKAGES_DIR = 0b0000_0000_0000_0010;
private static final int MODULES_DIR = 0b0000_0000_0000_0100;
private int flags;
private final String name;
private final BasicFileAttributes fileAttrs;
private boolean completed;
protected Node(String name, BasicFileAttributes fileAttrs) {
this.name = Objects.requireNonNull(name);
this.fileAttrs = Objects.requireNonNull(fileAttrs);
}
/**
* A node is completed when all its direct children have been built.
*
* @return
*/
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
public final void setIsRootDir() {
flags |= ROOT_DIR;
}
public final boolean isRootDir() {
return (flags & ROOT_DIR) != 0;
}
public final void setIsPackagesDir() {
flags |= PACKAGES_DIR;
}
public final boolean isPackagesDir() {
return (flags & PACKAGES_DIR) != 0;
}
public final void setIsModulesDir() {
flags |= MODULES_DIR;
}
public final boolean isModulesDir() {
return (flags & MODULES_DIR) != 0;
}
public final String getName() {
return name;
}
public final BasicFileAttributes getFileAttributes() {
return fileAttrs;
}
// resolve this Node (if this is a soft link, get underlying Node)
public final Node resolveLink() {
return resolveLink(false);
}
public Node resolveLink(boolean recursive) {
return this;
}
// is this a soft link Node?
public boolean isLink() {
return false;
}
public boolean isDirectory() {
return false;
}
public List<Node> getChildren() {
throw new IllegalArgumentException("not a directory: " + getNameString());
}
public boolean isResource() {
return false;
}
public ImageLocation getLocation() {
throw new IllegalArgumentException("not a resource: " + getNameString());
}
public long size() {
return 0L;
}
public long compressedSize() {
return 0L;
}
public String extension() {
return null;
}
public long contentOffset() {
return 0L;
}
public final FileTime creationTime() {
return fileAttrs.creationTime();
}
public final FileTime lastAccessTime() {
return fileAttrs.lastAccessTime();
}
public final FileTime lastModifiedTime() {
return fileAttrs.lastModifiedTime();
}
public final String getNameString() {
return name;
}
@Override
public final String toString() {
return getNameString();
}
@Override
public final int hashCode() {
return name.hashCode();
}
@Override
public final boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof Node) {
return name.equals(((Node) other).name);
}
return false;
}
}
// directory node - directory has full path name without '/' at end.
static final class Directory extends Node {
private final List<Node> children;
private Directory(String name, BasicFileAttributes fileAttrs) {
super(name, fileAttrs);
children = new ArrayList<>();
}
static Directory create(Directory parent, String name, BasicFileAttributes fileAttrs) {
Directory d = new Directory(name, fileAttrs);
if (parent != null) {
parent.addChild(d);
}
return d;
}
@Override
public boolean isDirectory() {
return true;
}
@Override
public List<Node> getChildren() {
return Collections.unmodifiableList(children);
}
void addChild(Node node) {
children.add(node);
}
public void walk(Consumer<? super Node> consumer) {
consumer.accept(this);
for (Node child : children) {
if (child.isDirectory()) {
((Directory)child).walk(consumer);
} else {
consumer.accept(child);
}
}
}
}
// "resource" is .class or any other resource (compressed/uncompressed) in a jimage.
// full path of the resource is the "name" of the resource.
static class Resource extends Node {
private final ImageLocation loc;
private Resource(ImageLocation loc, BasicFileAttributes fileAttrs) {
super(loc.getFullName(true), fileAttrs);
this.loc = loc;
}
static Resource create(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) {
Resource rs = new Resource(loc, fileAttrs);
parent.addChild(rs);
return rs;
}
@Override
public boolean isCompleted() {
return true;
}
@Override
public boolean isResource() {
return true;
}
@Override
public ImageLocation getLocation() {
return loc;
}
@Override
public long size() {
return loc.getUncompressedSize();
}
@Override
public long compressedSize() {
return loc.getCompressedSize();
}
@Override
public String extension() {
return loc.getExtension();
}
@Override
public long contentOffset() {
return loc.getContentOffset();
}
}
// represents a soft link to another Node
static class LinkNode extends Node {
private final Node link;
private LinkNode(String name, Node link) {
super(name, link.getFileAttributes());
this.link = link;
}
static LinkNode create(Directory parent, String name, Node link) {
LinkNode ln = new LinkNode(name, link);
parent.addChild(ln);
return ln;
}
@Override
public boolean isCompleted() {
return true;
}
@Override
public Node resolveLink(boolean recursive) {
return (recursive && link instanceof LinkNode) ? ((LinkNode)link).resolveLink(true) : link;
}
@Override
public boolean isLink() {
return true;
}
}
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
/**
* Factory to get ImageReader
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class ImageReaderFactory {
private ImageReaderFactory() {}
// AndroidIDE changed: Allow overriding java home.
private static final String JAVA_HOME = com.itsaky.androidide.config.JavacConfigProvider.getJavaHome();
private static final Path BOOT_MODULES_JIMAGE =
Paths.get(JAVA_HOME, "lib", "modules");
private static final Map<Path, ImageReader> readers = new ConcurrentHashMap<>();
/**
* Returns an {@code ImageReader} to read from the given image file
*/
public static ImageReader get(Path jimage) throws IOException {
Objects.requireNonNull(jimage);
try {
return readers.computeIfAbsent(jimage, OPENER);
} catch (UncheckedIOException io) {
throw io.getCause();
}
}
private static Function<Path, ImageReader> OPENER = new Function<Path, ImageReader>() {
public ImageReader apply(Path path) {
try {
return ImageReader.open(path);
} catch (IOException io) {
throw new UncheckedIOException(io);
}
}
};
/**
* Returns the {@code ImageReader} to read the image file in this
* run-time image.
*
* @throws UncheckedIOException if an I/O error occurs
*/
public static ImageReader getImageReader() {
try {
return get(BOOT_MODULES_JIMAGE);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
}
@@ -0,0 +1,207 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Objects;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class ImageStream {
private ByteBuffer buffer;
public ImageStream() {
this(1024, ByteOrder.nativeOrder());
}
public ImageStream(int size) {
this(size, ByteOrder.nativeOrder());
}
public ImageStream(byte[] bytes) {
this(bytes, ByteOrder.nativeOrder());
}
public ImageStream(ByteOrder byteOrder) {
this(1024, byteOrder);
}
public ImageStream(int size, ByteOrder byteOrder) {
buffer = ByteBuffer.allocate(size);
buffer.order(Objects.requireNonNull(byteOrder));
}
public ImageStream(byte[] bytes, ByteOrder byteOrder) {
buffer = ByteBuffer.wrap(Objects.requireNonNull(bytes));
buffer.order(Objects.requireNonNull(byteOrder));
}
public ImageStream(ByteBuffer buffer) {
this.buffer = Objects.requireNonNull(buffer);
}
public ImageStream align(int alignment) {
int padding = (getSize() - 1) & ((1 << alignment) - 1);
for (int i = 0; i < padding; i++) {
put((byte)0);
}
return this;
}
public void ensure(int needs) {
if (needs < 0) {
throw new IndexOutOfBoundsException("Bad value: " + needs);
}
if (needs > buffer.remaining()) {
byte[] bytes = buffer.array();
ByteOrder byteOrder = buffer.order();
int position = buffer.position();
int newSize = needs <= bytes.length ? bytes.length << 1 : position + needs;
buffer = ByteBuffer.allocate(newSize);
buffer.order(byteOrder);
buffer.put(bytes, 0, position);
}
}
public boolean hasByte() {
return buffer.remaining() != 0;
}
public boolean hasBytes(int needs) {
return needs <= buffer.remaining();
}
public void skip(int n) {
if (n < 0) {
throw new IndexOutOfBoundsException("skip value = " + n);
}
buffer.position(buffer.position() + n);
}
public int get() {
return buffer.get() & 0xFF;
}
public void get(byte bytes[], int offset, int size) {
buffer.get(bytes, offset, size);
}
public int getShort() {
return buffer.getShort();
}
public int getInt() {
return buffer.getInt();
}
public long getLong() {
return buffer.getLong();
}
public ImageStream put(byte byt) {
ensure(1);
buffer.put(byt);
return this;
}
public ImageStream put(int byt) {
return put((byte)byt);
}
public ImageStream put(byte bytes[], int offset, int size) {
ensure(size);
buffer.put(bytes, offset, size);
return this;
}
public ImageStream put(ImageStream stream) {
put(stream.buffer.array(), 0, stream.buffer.position());
return this;
}
public ImageStream putShort(short value) {
ensure(2);
buffer.putShort(value);
return this;
}
public ImageStream putShort(int value) {
return putShort((short)value);
}
public ImageStream putInt(int value) {
ensure(4);
buffer.putInt(value);
return this;
}
public ImageStream putLong(long value) {
ensure(8);
buffer.putLong(value);
return this;
}
public ByteBuffer getBuffer() {
return buffer;
}
public int getPosition() {
return buffer.position();
}
public int getSize() {
return buffer.position();
}
public byte[] getBytes() {
return buffer.array();
}
public void setPosition(int offset) {
buffer.position(offset);
}
public byte[] toArray() {
return Arrays.copyOf(buffer.array(), buffer.position());
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
/**
* @implNote This interface needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public interface ImageStrings {
String get(int offset);
int add(final String string);
/**
* If there's a string at {@code offset} matching in full a substring of
* {@code string} starting at {@code stringOffset}, return the length
* of that string. Otherwise returns -1. Optional operation.
*/
default int match(int offset, String string, int stringOffset) {
throw new UnsupportedOperationException();
}
}
@@ -0,0 +1,387 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.io.UTFDataFormatException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class ImageStringsReader implements ImageStrings {
public static final int HASH_MULTIPLIER = 0x01000193;
public static final int POSITIVE_MASK = 0x7FFFFFFF;
private final BasicImageReader reader;
ImageStringsReader(BasicImageReader reader) {
this.reader = Objects.requireNonNull(reader);
}
@Override
public String get(int offset) {
return reader.getString(offset);
}
@Override
public int match(int offset, String string, int stringOffset) {
return reader.match(offset, string, stringOffset);
}
@Override
public int add(final String string) {
throw new InternalError("Can not add strings at runtime");
}
public static int hashCode(String s) {
return hashCode(s, HASH_MULTIPLIER);
}
public static int hashCode(String s, int seed) {
return unmaskedHashCode(s, seed) & POSITIVE_MASK;
}
public static int hashCode(String module, String name) {
return hashCode(module, name, HASH_MULTIPLIER);
}
public static int hashCode(String module, String name, int seed) {
seed = (seed * HASH_MULTIPLIER) ^ ('/');
seed = unmaskedHashCode(module, seed);
seed = (seed * HASH_MULTIPLIER) ^ ('/');
seed = unmaskedHashCode(name, seed);
return seed & POSITIVE_MASK;
}
public static int unmaskedHashCode(String s, int seed) {
int slen = s.length();
byte[] buffer = null;
for (int i = 0; i < slen; i++) {
int uch = s.charAt(i);
if ((uch & ~0x7F) != 0) {
if (buffer == null) {
buffer = new byte[8];
}
int mask = ~0x3F;
int n = 0;
do {
buffer[n++] = (byte)(0x80 | (uch & 0x3F));
uch >>= 6;
mask >>= 1;
} while ((uch & mask) != 0);
buffer[n] = (byte)((mask << 1) | uch);
do {
seed = (seed * HASH_MULTIPLIER) ^ (buffer[n--] & 0xFF);
} while (0 <= n);
} else if (uch == 0) {
seed = (seed * HASH_MULTIPLIER) ^ (0xC0);
seed = (seed * HASH_MULTIPLIER) ^ (0x80);
} else {
seed = (seed * HASH_MULTIPLIER) ^ (uch);
}
}
return seed;
}
static int charsFromMUTF8Length(byte[] bytes, int offset, int count) {
int length = 0;
for (int i = offset; i < offset + count; i++) {
byte ch = bytes[i];
if (ch == 0) {
break;
}
if ((ch & 0xC0) != 0x80) {
length++;
}
}
return length;
}
static void charsFromMUTF8(char[] chars, byte[] bytes, int offset, int count) throws UTFDataFormatException {
int j = 0;
for (int i = offset; i < offset + count; i++) {
byte ch = bytes[i];
if (ch == 0) {
break;
}
boolean is_unicode = (ch & 0x80) != 0;
int uch = ch & 0x7F;
if (is_unicode) {
int mask = 0x40;
while ((uch & mask) != 0) {
ch = bytes[++i];
if ((ch & 0xC0) != 0x80) {
throw new UTFDataFormatException("bad continuation 0x" + Integer.toHexString(ch));
}
uch = ((uch & ~mask) << 6) | (ch & 0x3F);
mask <<= 6 - 1;
}
if ((uch & 0xFFFF) != uch) {
throw new UTFDataFormatException("character out of range \\u" + Integer.toHexString(uch));
}
}
chars[j++] = (char)uch;
}
}
public static String stringFromMUTF8(byte[] bytes, int offset, int count) {
int length = charsFromMUTF8Length(bytes, offset, count);
char[] chars = new char[length];
try {
charsFromMUTF8(chars, bytes, offset, count);
} catch (UTFDataFormatException ex) {
throw new InternalError("Attempt to convert non modified UTF-8 byte sequence", ex);
}
return new String(chars);
}
public static String stringFromMUTF8(byte[] bytes) {
return stringFromMUTF8(bytes, 0, bytes.length);
}
/**
* Calculates the number of characters in the String present at the
* specified offset. As an optimization, the length returned will
* be positive if the characters are all ASCII, and negative otherwise.
*/
private static int charsFromByteBufferLength(ByteBuffer buffer, int offset) {
int length = 0;
int limit = buffer.limit();
boolean asciiOnly = true;
while (offset < limit) {
byte ch = buffer.get(offset++);
if (ch < 0) {
asciiOnly = false;
} else if (ch == 0) {
return asciiOnly ? length : -length;
}
if ((ch & 0xC0) != 0x80) {
length++;
}
}
throw new InternalError("No terminating zero byte for modified UTF-8 byte sequence");
}
private static void charsFromByteBuffer(char[] chars, ByteBuffer buffer, int offset) {
int j = 0;
int limit = buffer.limit();
while (offset < limit) {
byte ch = buffer.get(offset++);
if (ch == 0) {
return;
}
boolean is_unicode = (ch & 0x80) != 0;
int uch = ch & 0x7F;
if (is_unicode) {
int mask = 0x40;
while ((uch & mask) != 0) {
ch = buffer.get(offset++);
if ((ch & 0xC0) != 0x80) {
throw new InternalError("Bad continuation in " +
"modified UTF-8 byte sequence: " + ch);
}
uch = ((uch & ~mask) << 6) | (ch & 0x3F);
mask <<= 6 - 1;
}
}
if ((uch & 0xFFFF) != uch) {
throw new InternalError("UTF-32 char in modified UTF-8 " +
"byte sequence: " + uch);
}
chars[j++] = (char)uch;
}
throw new InternalError("No terminating zero byte for modified UTF-8 byte sequence");
}
public static String stringFromByteBuffer(ByteBuffer buffer) {
return stringFromByteBuffer(buffer, 0);
}
/* package-private */
static String stringFromByteBuffer(ByteBuffer buffer, int offset) {
int length = charsFromByteBufferLength(buffer, offset);
if (length > 0) {
byte[] asciiBytes = new byte[length];
// Ideally we could use buffer.get(offset, asciiBytes, 0, length)
// here, but that was introduced in JDK 13
for (int i = 0; i < length; i++) {
asciiBytes[i] = buffer.get(offset++);
}
return new String(asciiBytes, StandardCharsets.US_ASCII);
}
char[] chars = new char[-length];
charsFromByteBuffer(chars, buffer, offset);
return new String(chars);
}
/* package-private */
static int stringFromByteBufferMatches(ByteBuffer buffer, int offset, String string, int stringOffset) {
// ASCII fast-path
int limit = buffer.limit();
int current = offset;
int slen = string.length();
while (current < limit) {
byte ch = buffer.get(current);
if (ch <= 0) {
if (ch == 0) {
// Match
return current - offset;
}
// non-ASCII byte, run slow-path from current offset
break;
}
if (slen <= stringOffset || string.charAt(stringOffset) != (char)ch) {
// No match
return -1;
}
stringOffset++;
current++;
}
// invariant: remainder of the string starting at current is non-ASCII,
// so return value from charsFromByteBufferLength will be negative
int length = -charsFromByteBufferLength(buffer, current);
char[] chars = new char[length];
charsFromByteBuffer(chars, buffer, current);
for (int i = 0; i < length; i++) {
if (string.charAt(stringOffset++) != chars[i]) {
return -1;
}
}
return length;
}
static int mutf8FromStringLength(String s) {
int length = 0;
int slen = s.length();
for (int i = 0; i < slen; i++) {
char ch = s.charAt(i);
int uch = ch & 0xFFFF;
if ((uch & ~0x7F) != 0) {
int mask = ~0x3F;
int n = 0;
do {
n++;
uch >>= 6;
mask >>= 1;
} while ((uch & mask) != 0);
length += n + 1;
} else if (uch == 0) {
length += 2;
} else {
length++;
}
}
return length;
}
static void mutf8FromString(byte[] bytes, int offset, String s) {
int j = offset;
byte[] buffer = null;
int slen = s.length();
for (int i = 0; i < slen; i++) {
char ch = s.charAt(i);
int uch = ch & 0xFFFF;
if ((uch & ~0x7F) != 0) {
if (buffer == null) {
buffer = new byte[8];
}
int mask = ~0x3F;
int n = 0;
do {
buffer[n++] = (byte)(0x80 | (uch & 0x3F));
uch >>= 6;
mask >>= 1;
} while ((uch & mask) != 0);
buffer[n] = (byte)((mask << 1) | uch);
do {
bytes[j++] = buffer[n--];
} while (0 <= n);
} else if (uch == 0) {
bytes[j++] = (byte)0xC0;
bytes[j++] = (byte)0x80;
} else {
bytes[j++] = (byte)uch;
}
}
}
public static byte[] mutf8FromString(String string) {
int length = mutf8FromStringLength(string);
byte[] bytes = new byte[length];
mutf8FromString(bytes, 0, string);
return bytes;
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage;
import java.nio.ByteBuffer;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
@SuppressWarnings("removal")
class NativeImageBuffer {
static {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("jimage");
return null;
}
});
}
native static ByteBuffer getNativeMap(String imagePath);
}
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
*
* Index compressor. Use the minimal amount of bytes required to store
* an integer.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class CompressIndexes {
private static final int COMPRESSED_FLAG = 1 << (Byte.SIZE - 1);
private static final int HEADER_WIDTH = 3;
private static final int HEADER_SHIFT = Byte.SIZE - HEADER_WIDTH;
public static List<Integer> decompressFlow(byte[] values) {
List<Integer> lst = new ArrayList<>();
for (int i = 0; i < values.length; i += getHeaderLength(values[i])) {
int decompressed = decompress(values, i);
lst.add(decompressed);
}
return lst;
}
public static int readInt(DataInputStream cr) throws IOException {
// Get header byte.
byte header = cr.readByte();
// Determine size.
int size = getHeaderLength(header);
// Prepare result.
int result = getHeaderValue(header);
// For each value byte
for (int i = 1; i < size; i++) {
// Merge byte value.
result <<= Byte.SIZE;
result |= cr.readByte() & 0xFF;
}
return result;
}
private static boolean isCompressed(byte b) {
return (b & COMPRESSED_FLAG) != 0;
}
private static int getHeaderLength(byte b) {
return isCompressed(b) ? (b >> HEADER_SHIFT) & 3 : Integer.BYTES;
}
private static int getHeaderValue(byte b) {
return isCompressed(b) ? b & (1 << HEADER_SHIFT) - 1 : b;
}
public static int decompress(byte[] value, int offset) {
// Get header byte.
byte header = value[offset];
// Determine size.
int size = getHeaderLength(header);
// Prepare result.
int result = getHeaderValue(header);
// For each value byte
for (int i = 1; i < size; i++) {
// Merge byte value.
result <<= Byte.SIZE;
result |= value[offset + i] & 0xFF;
}
return result;
}
public static byte[] compress(int value) {
// Only positive values are supported.
if (value < 0) {
throw new IllegalArgumentException("value < 0");
}
// Determine number of significant digits.
int width = 32 - Integer.numberOfLeadingZeros(value);
// Determine number of byte to represent. Allow for header if
// compressed.
int size = Math.min(((width + HEADER_WIDTH - 1) >> 3) + 1, Integer.BYTES);
// Allocate result buffer.
byte[] result = new byte[size];
// Insert significant bytes in result.
for (int i = 0; i < size; i++) {
result[i] = (byte)(value >> ((size - i - 1) * Byte.SIZE));
}
// If compressed, mark and insert size.
if (size < Integer.BYTES) {
result[0] |= (byte)(COMPRESSED_FLAG | (size << HEADER_SHIFT));
}
return result;
}
}
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Objects;
import javac.internal.jimage.decompressor.ResourceDecompressor.StringsProvider;
/**
*
* A resource header for compressed resource. This class is handled internally,
* you don't have to add header to the resource, headers are added automatically
* for compressed resources.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public final class CompressedResourceHeader {
private static final int SIZE = 29;
public static final int MAGIC = 0xCAFEFAFA;
private final long uncompressedSize;
private final long compressedSize;
private final int decompressorNameOffset;
private final int contentOffset;
private final boolean isTerminal;
public CompressedResourceHeader(long compressedSize,
long uncompressedSize, int decompressorNameOffset, int contentOffset,
boolean isTerminal) {
this.compressedSize = compressedSize;
this.uncompressedSize = uncompressedSize;
this.decompressorNameOffset = decompressorNameOffset;
this.contentOffset = contentOffset;
this.isTerminal = isTerminal;
}
public boolean isTerminal() {
return isTerminal;
}
public int getDecompressorNameOffset() {
return decompressorNameOffset;
}
public int getContentOffset() {
return contentOffset;
}
public String getStoredContent(StringsProvider provider) {
Objects.requireNonNull(provider);
if(contentOffset == -1) {
return null;
}
return provider.getString(contentOffset);
}
public long getUncompressedSize() {
return uncompressedSize;
}
public long getResourceSize() {
return compressedSize;
}
public byte[] getBytes(ByteOrder order) {
Objects.requireNonNull(order);
ByteBuffer buffer = ByteBuffer.allocate(SIZE);
buffer.order(order);
buffer.putInt(MAGIC);
buffer.putLong(compressedSize);
buffer.putLong(uncompressedSize);
buffer.putInt(decompressorNameOffset);
buffer.putInt(contentOffset);
buffer.put(isTerminal ? (byte)1 : (byte)0);
return buffer.array();
}
public static int getSize() {
return SIZE;
}
public static CompressedResourceHeader readFromResource(ByteOrder order,
byte[] resource) {
Objects.requireNonNull(order);
Objects.requireNonNull(resource);
if (resource.length < getSize()) {
return null;
}
ByteBuffer buffer = ByteBuffer.wrap(resource, 0, SIZE);
buffer.order(order);
int magic = buffer.getInt();
if(magic != MAGIC) {
return null;
}
long size = buffer.getLong();
long uncompressedSize = buffer.getLong();
int decompressorNameOffset = buffer.getInt();
int contentIndex = buffer.getInt();
byte isTerminal = buffer.get();
return new CompressedResourceHeader(size, uncompressedSize,
decompressorNameOffset, contentIndex, isTerminal == 1);
}
}
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import javac.internal.jimage.decompressor.ResourceDecompressor.StringsProvider;
/**
* Entry point to decompress resources.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public final class Decompressor {
private final Map<Integer, ResourceDecompressor> pluginsCache = new HashMap<>();
public Decompressor() {
}
/**
* Decompress a resource.
* @param order Byte order.
* @param provider Strings provider
* @param content The resource content to uncompress.
* @return A fully uncompressed resource.
* @throws IOException
*/
public byte[] decompressResource(ByteOrder order, StringsProvider provider,
byte[] content) throws IOException {
Objects.requireNonNull(order);
Objects.requireNonNull(provider);
Objects.requireNonNull(content);
CompressedResourceHeader header;
do {
header = CompressedResourceHeader.readFromResource(order, content);
if (header != null) {
ResourceDecompressor decompressor =
pluginsCache.get(header.getDecompressorNameOffset());
if (decompressor == null) {
String pluginName =
provider.getString(header.getDecompressorNameOffset());
if (pluginName == null) {
throw new IOException("Plugin name not found");
}
String storedContent = header.getStoredContent(provider);
Properties props = new Properties();
if (storedContent != null) {
try (ByteArrayInputStream stream
= new ByteArrayInputStream(storedContent.
getBytes(StandardCharsets.UTF_8));) {
props.loadFromXML(stream);
}
}
decompressor = ResourceDecompressorRepository.
newResourceDecompressor(props, pluginName);
if (decompressor == null) {
throw new IOException("Plugin not found: " + pluginName);
}
pluginsCache.put(header.getDecompressorNameOffset(), decompressor);
}
try {
content = decompressor.decompress(provider, content,
CompressedResourceHeader.getSize(), header.getUncompressedSize());
} catch (Exception ex) {
throw new IOException(ex);
}
}
} while (header != null);
return content;
}
}
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
/**
*
* JLink Image Decompressor.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public interface ResourceDecompressor {
public interface StringsProvider {
public String getString(int offset);
}
/**
* Decompressor unique name.
* @return The decompressor name.
*/
public String getName();
/**
* Decompress a resource.
* @param strings The String provider
* @param content The resource content
* @param offset Resource content offset
* @param originalSize Uncompressed size
* @return Uncompressed resource
* @throws Exception
*/
public byte[] decompress(StringsProvider strings, byte[] content, int offset,
long originalSize) throws Exception;
}
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.io.IOException;
import java.util.Properties;
/**
*
* JLink Resource Decompressor factory
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public abstract class ResourceDecompressorFactory {
private final String name;
protected ResourceDecompressorFactory(String name) {
this.name = name;
}
/**
* The Factory name.
* @return The name.
*/
public String getName() {
return name;
}
/**
* To build a new decompressor.
* @param properties Contains configuration.
* @return A new decompressor.
* @throws IOException
*/
public abstract ResourceDecompressor newDecompressor(Properties properties)
throws IOException;
}
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
*
* JLink Decompressors. All decompressors must be registered in the static
* initializer of this class.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public final class ResourceDecompressorRepository {
private ResourceDecompressorRepository() {
}
private static final Map<String, ResourceDecompressorFactory> factories = new HashMap<>();
static {
registerReaderProvider(new ZipDecompressorFactory());
registerReaderProvider(new StringSharingDecompressorFactory());
}
/**
* Build a new decompressor for the passed name.
* @param properties Contains plugin configuration.
* @param name The plugin name to build.
* @return A decompressor or null if not found
* @throws IOException
*/
public static ResourceDecompressor newResourceDecompressor(Properties properties,
String name) throws IOException {
ResourceDecompressorFactory fact = factories.get(name);
if (fact != null) {
return fact.newDecompressor(properties);
}
return null;
}
private static void registerReaderProvider(ResourceDecompressorFactory factory) {
factories.put(factory.getName(), factory);
}
}
@@ -0,0 +1,129 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.util.ArrayList;
import java.util.List;
/**
*
* A descriptor parser used to extract java type strings from
* UTF_8 descriptors.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class SignatureParser {
public static class ParseResult {
public final List<String> types = new ArrayList<>();
public String formatted;
private ParseResult() {}
}
private SignatureParser() {}
public static String reconstruct(String formatted, List<String> arguments) {
int arg_index = 0;
char[] chars = formatted.toCharArray();
StringBuilder out = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
out.append(c);
switch (c) {
case 'L': {
String pkg = arguments.get(arg_index);
if(!pkg.isEmpty()) {
out.append(pkg).append("/");
}
arg_index+=1;
out.append(arguments.get(arg_index));
arg_index+=1;
break;
}
default: {
break;
}
}
}
return out.toString();
}
public static ParseResult parseSignatureDescriptor(String str) {
ParseResult res = new ParseResult();
char[] chars = str.toCharArray();
StringBuilder type = null;
StringBuilder formatted = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
switch (c) {
case ';':
case ':':
case '<': {
if(type != null) {
String fullName = type.toString();
int endIndex = fullName.lastIndexOf("/");
String clazz = fullName;
String pkg = "";
if(endIndex != -1) {
pkg = fullName.substring(0, endIndex);
clazz = fullName.substring(endIndex+1);
}
res.types.add(pkg);
res.types.add(clazz);
}
formatted.append(c);
type = null;
break;
}
case 'L': {
if(type == null) {
type = new StringBuilder();
formatted.append(c);
} else {
type.append(c);
}
break;
}
default: {
if(type == null) {
formatted.append(c);
} else {
type.append(c);
}
break;
}
}
}
res.formatted = formatted.toString();
return res;
}
}
@@ -0,0 +1,240 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
/**
*
* A Decompressor that reconstructs the constant pool of classes.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class StringSharingDecompressor implements ResourceDecompressor {
public static final int EXTERNALIZED_STRING = 23;
public static final int EXTERNALIZED_STRING_DESCRIPTOR = 25;
private static final int CONSTANT_Utf8 = 1;
private static final int CONSTANT_Integer = 3;
private static final int CONSTANT_Float = 4;
private static final int CONSTANT_Long = 5;
private static final int CONSTANT_Double = 6;
private static final int CONSTANT_Class = 7;
private static final int CONSTANT_String = 8;
private static final int CONSTANT_Fieldref = 9;
private static final int CONSTANT_Methodref = 10;
private static final int CONSTANT_InterfaceMethodref = 11;
private static final int CONSTANT_NameAndType = 12;
private static final int CONSTANT_MethodHandle = 15;
private static final int CONSTANT_MethodType = 16;
private static final int CONSTANT_InvokeDynamic = 18;
private static final int CONSTANT_Module = 19;
private static final int CONSTANT_Package = 20;
private static final int[] SIZES = new int[21];
static {
//SIZES[CONSTANT_Utf8] = XXX;
SIZES[CONSTANT_Integer] = 4;
SIZES[CONSTANT_Float] = 4;
SIZES[CONSTANT_Long] = 8;
SIZES[CONSTANT_Double] = 8;
SIZES[CONSTANT_Class] = 2;
SIZES[CONSTANT_String] = 2;
SIZES[CONSTANT_Fieldref] = 4;
SIZES[CONSTANT_Methodref] = 4;
SIZES[CONSTANT_InterfaceMethodref] = 4;
SIZES[CONSTANT_NameAndType] = 4;
SIZES[CONSTANT_MethodHandle] = 3;
SIZES[CONSTANT_MethodType] = 2;
SIZES[CONSTANT_InvokeDynamic] = 4;
SIZES[CONSTANT_Module] = 2;
SIZES[CONSTANT_Package] = 2;
}
public static int[] getSizes() {
return SIZES.clone();
}
@SuppressWarnings("fallthrough")
public static byte[] normalize(StringsProvider provider, byte[] transformed,
int offset) throws IOException {
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(transformed,
offset, transformed.length - offset));
ByteArrayOutputStream outStream = new ByteArrayOutputStream(transformed.length);
DataOutputStream out = new DataOutputStream(outStream);
byte[] header = new byte[8]; //maginc/4, minor/2, major/2
stream.readFully(header);
out.write(header);
int count = stream.readUnsignedShort();
out.writeShort(count);
for (int i = 1; i < count; i++) {
int tag = stream.readUnsignedByte();
byte[] arr;
switch (tag) {
case CONSTANT_Utf8: {
out.write(tag);
String utf = stream.readUTF();
out.writeUTF(utf);
break;
}
case EXTERNALIZED_STRING: {
int index = CompressIndexes.readInt(stream);
String orig = provider.getString(index);
out.write(CONSTANT_Utf8);
out.writeUTF(orig);
break;
}
case EXTERNALIZED_STRING_DESCRIPTOR: {
String orig = reconstruct(provider, stream);
out.write(CONSTANT_Utf8);
out.writeUTF(orig);
break;
}
case CONSTANT_Long:
case CONSTANT_Double: {
i++;
}
default: {
out.write(tag);
int size = SIZES[tag];
arr = new byte[size];
stream.readFully(arr);
out.write(arr);
}
}
}
out.write(transformed, transformed.length - stream.available(),
stream.available());
out.flush();
return outStream.toByteArray();
}
private static String reconstruct(StringsProvider reader, DataInputStream cr)
throws IOException {
int descIndex = CompressIndexes.readInt(cr);
String desc = reader.getString(descIndex);
byte[] encodedDesc = getEncoded(desc);
int indexes_length = CompressIndexes.readInt(cr);
byte[] bytes = new byte[indexes_length];
cr.readFully(bytes);
List<Integer> indices = CompressIndexes.decompressFlow(bytes);
ByteBuffer buffer = ByteBuffer.allocate(encodedDesc.length * 2);
buffer.order(ByteOrder.BIG_ENDIAN);
int argIndex = 0;
for (byte c : encodedDesc) {
if (c == 'L') {
buffer = safeAdd(buffer, c);
int index = indices.get(argIndex);
argIndex += 1;
String pkg = reader.getString(index);
if (!pkg.isEmpty()) {
pkg = pkg + "/";
byte[] encoded = getEncoded(pkg);
buffer = safeAdd(buffer, encoded);
}
int classIndex = indices.get(argIndex);
argIndex += 1;
String clazz = reader.getString(classIndex);
byte[] encoded = getEncoded(clazz);
buffer = safeAdd(buffer, encoded);
} else {
buffer = safeAdd(buffer, c);
}
}
byte[] encoded = buffer.array();
ByteBuffer result = ByteBuffer.allocate(encoded.length + 2);
result.order(ByteOrder.BIG_ENDIAN);
result.putShort((short) buffer.position());
result.put(encoded, 0, buffer.position());
ByteArrayInputStream stream = new ByteArrayInputStream(result.array());
DataInputStream inStream = new DataInputStream(stream);
String str = inStream.readUTF();
return str;
}
public static byte[] getEncoded(String pre) throws IOException {
ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
DataOutputStream resultOut = new DataOutputStream(resultStream);
resultOut.writeUTF(pre);
byte[] content = resultStream.toByteArray();
// first 2 items are length;
if (content.length <= 2) {
return new byte[0];
}
return Arrays.copyOfRange(content, 2, content.length);
}
private static ByteBuffer safeAdd(ByteBuffer current, byte b) {
byte[] bytes = {b};
return safeAdd(current, bytes);
}
private static ByteBuffer safeAdd(ByteBuffer current, byte[] bytes) {
if (current.remaining() < bytes.length) {
ByteBuffer newBuffer = ByteBuffer.allocate((current.capacity()
+ bytes.length) * 2);
newBuffer.order(ByteOrder.BIG_ENDIAN);
newBuffer.put(current.array(), 0, current.position());
current = newBuffer;
}
current.put(bytes);
return current;
}
@Override
public String getName() {
return StringSharingDecompressorFactory.NAME;
}
public StringSharingDecompressor(Properties properties) {
}
@Override
public byte[] decompress(StringsProvider reader, byte[] content,
int offset, long originalSize) throws Exception {
return normalize(reader, content, offset);
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.io.IOException;
import java.util.Properties;
/**
*
* Constant Pool strings sharing Decompressor factory.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public class StringSharingDecompressorFactory extends ResourceDecompressorFactory {
public static final String NAME = "compact-cp";
public StringSharingDecompressorFactory() {
super(NAME);
}
@Override
public ResourceDecompressor newDecompressor(Properties properties)
throws IOException {
return new StringSharingDecompressor(properties);
}
}
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.io.ByteArrayOutputStream;
import java.util.zip.Inflater;
/**
*
* ZIP Decompressor
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
final class ZipDecompressor implements ResourceDecompressor {
@Override
public String getName() {
return ZipDecompressorFactory.NAME;
}
static byte[] decompress(byte[] bytesIn, int offset) throws Exception {
Inflater inflater = new Inflater();
inflater.setInput(bytesIn, offset, bytesIn.length - offset);
ByteArrayOutputStream stream = new ByteArrayOutputStream(bytesIn.length - offset);
byte[] buffer = new byte[1024];
while (!inflater.finished()) {
int count = inflater.inflate(buffer);
stream.write(buffer, 0, count);
}
stream.close();
byte[] bytesOut = stream.toByteArray();
inflater.end();
return bytesOut;
}
@Override
public byte[] decompress(StringsProvider reader, byte[] content, int offset,
long originalSize) throws Exception {
byte[] decompressed = decompress(content, offset);
return decompressed;
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2022, 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 javac.internal.jimage.decompressor;
import java.io.IOException;
import java.util.Properties;
/**
*
* ZIP decompressor factory
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public final class ZipDecompressorFactory extends ResourceDecompressorFactory {
public static final String NAME = "zip";
public ZipDecompressorFactory() {
super(NAME);
}
@Override
public ResourceDecompressor newDecompressor(Properties properties)
throws IOException {
return new ZipDecompressor();
}
}
@@ -0,0 +1,219 @@
/*
* Copyright (c) 2016, 2017, 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 javac.internal.jmod;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/** Helper class to read JMOD file */
public class JmodFile implements AutoCloseable {
// jmod magic number and version number
private static final int JMOD_MAJOR_VERSION = 0x01;
private static final int JMOD_MINOR_VERSION = 0x00;
private static final byte[] JMOD_MAGIC_NUMBER = {
0x4A, 0x4D, /* JM */ JMOD_MAJOR_VERSION, JMOD_MINOR_VERSION, /* version 1.0 */
};
public static void checkMagic(Path file) throws IOException {
try (InputStream in = Files.newInputStream(file);
BufferedInputStream bis = new BufferedInputStream(in)) {
// validate the header
byte[] magic = new byte[4];
bis.read(magic);
if (magic[0] != JMOD_MAGIC_NUMBER[0] || magic[1] != JMOD_MAGIC_NUMBER[1]) {
throw new IOException("Invalid JMOD file: " + file.toString());
}
if (magic[2] > JMOD_MAJOR_VERSION
|| (magic[2] == JMOD_MAJOR_VERSION && magic[3] > JMOD_MINOR_VERSION)) {
throw new IOException(
"Unsupported jmod version: "
+ magic[2]
+ "."
+ magic[3]
+ " in "
+ file.toString());
}
}
}
/** JMOD sections */
public static enum Section {
CLASSES("classes"),
CONFIG("conf"),
HEADER_FILES("include"),
LEGAL_NOTICES("legal"),
MAN_PAGES("man"),
NATIVE_LIBS("lib"),
NATIVE_CMDS("bin");
private final String jmodDir;
private Section(String jmodDir) {
this.jmodDir = jmodDir;
}
/** Returns the directory name in the JMOD file corresponding to this section */
public String jmodDir() {
return jmodDir;
}
}
/**
* JMOD file entry.
*
* <p>Each entry corresponds to a ZipEntry whose name is: Section::jmodDir + '/' + name
*/
public static class Entry {
private final ZipEntry zipEntry;
private final Section section;
private final String name;
private Entry(ZipEntry e) {
String name = e.getName();
int i = name.indexOf('/');
if (i <= 1) {
throw new RuntimeException("invalid jmod entry: " + name);
}
this.zipEntry = e;
this.section = section(name.substring(0, i));
this.name = name.substring(i + 1);
}
/** Returns the section of this entry. */
public Section section() {
return section;
}
/** Returns the name of this entry. */
public String name() {
return name;
}
/** Returns true if the entry is a directory in the JMOD file. */
public boolean isDirectory() {
return zipEntry.isDirectory();
}
/** Returns the size of this entry. */
public long size() {
return zipEntry.getSize();
}
public ZipEntry zipEntry() {
return zipEntry;
}
@Override
public String toString() {
return section.jmodDir() + "/" + name;
}
/*
* A map from the jmodDir name to Section
*/
static final Map<String, Section> NAME_TO_SECTION =
Arrays.stream(Section.values())
.collect(Collectors.toMap(Section::jmodDir, Function.identity()));
static Section section(String name) {
if (!NAME_TO_SECTION.containsKey(name)) {
throw new IllegalArgumentException("invalid section: " + name);
}
return NAME_TO_SECTION.get(name);
}
}
private final Path file;
private final ZipFile zipfile;
/** Constructs a {@code JmodFile} from a given path. */
public JmodFile(Path file) throws IOException {
checkMagic(file);
this.file = file;
this.zipfile = new ZipFile(file.toFile());
}
public static void writeMagicNumber(OutputStream os) throws IOException {
os.write(JMOD_MAGIC_NUMBER);
}
/**
* Returns the {@code Entry} for a resource in a JMOD file section or {@code null} if not found.
*/
public Entry getEntry(Section section, String name) {
String entry = section.jmodDir() + "/" + name;
ZipEntry ze = zipfile.getEntry(entry);
return (ze != null) ? new Entry(ze) : null;
}
/**
* Opens an {@code InputStream} for reading the named entry of the given section in this JMOD
* file.
*
* @throws IOException if the named entry is not found, or I/O error occurs when reading it
*/
public InputStream getInputStream(Section section, String name) throws IOException {
String entry = section.jmodDir() + "/" + name;
ZipEntry e = zipfile.getEntry(entry);
if (e == null) {
throw new IOException(name + " not found: " + file);
}
return zipfile.getInputStream(e);
}
/**
* Opens an {@code InputStream} for reading an entry in the JMOD file.
*
* @throws IOException if an I/O error occurs
*/
public InputStream getInputStream(Entry entry) throws IOException {
return zipfile.getInputStream(entry.zipEntry());
}
/** Returns a stream of entries in this JMOD file. */
public Stream<Entry> stream() {
return zipfile.stream().map(Entry::new);
}
@Override
public void close() throws IOException {
if (zipfile != null) {
zipfile.close();
}
}
}
@@ -0,0 +1,305 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javac.internal.jimage.ImageReader.Node;
/**
* A jrt file system built on $JAVA_HOME/modules directory ('exploded modules
* build')
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
class ExplodedImage extends SystemImage {
private static final String MODULES = "/modules/";
private static final String PACKAGES = "/packages/";
private static final int PACKAGES_LEN = PACKAGES.length();
private final FileSystem defaultFS;
private final String separator;
private final Map<String, PathNode> nodes = Collections.synchronizedMap(new HashMap<>());
private final BasicFileAttributes modulesDirAttrs;
ExplodedImage(Path modulesDir) throws IOException {
defaultFS = FileSystems.getDefault();
String str = defaultFS.getSeparator();
separator = str.equals("/") ? null : str;
modulesDirAttrs = Files.readAttributes(modulesDir, BasicFileAttributes.class);
initNodes();
}
// A Node that is backed by actual default file system Path
private final class PathNode extends Node {
// Path in underlying default file system
private Path path;
private PathNode link;
private List<Node> children;
PathNode(String name, Path path, BasicFileAttributes attrs) { // path
super(name, attrs);
this.path = path;
}
PathNode(String name, Node link) { // link
super(name, link.getFileAttributes());
this.link = (PathNode)link;
}
PathNode(String name, List<Node> children) { // dir
super(name, modulesDirAttrs);
this.children = children;
}
@Override
public boolean isDirectory() {
return children != null ||
(link == null && getFileAttributes().isDirectory());
}
@Override
public boolean isLink() {
return link != null;
}
@Override
public PathNode resolveLink(boolean recursive) {
if (link == null)
return this;
return recursive && link.isLink() ? link.resolveLink(true) : link;
}
byte[] getContent() throws IOException {
if (!getFileAttributes().isRegularFile())
throw new FileSystemException(getName() + " is not file");
return Files.readAllBytes(path);
}
@Override
public List<Node> getChildren() {
if (!isDirectory())
throw new IllegalArgumentException("not a directory: " + getNameString());
if (children == null) {
List<Node> list = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
for (Path p : stream) {
p = explodedModulesDir.relativize(p);
String pName = MODULES + nativeSlashToFrontSlash(p.toString());
Node node = findNode(pName);
if (node != null) { // findNode may choose to hide certain files!
list.add(node);
}
}
} catch (IOException x) {
return null;
}
children = list;
}
return children;
}
@Override
public long size() {
try {
return isDirectory() ? 0 : Files.size(path);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
}
@Override
public void close() throws IOException {
nodes.clear();
}
@Override
public byte[] getResource(Node node) throws IOException {
return ((PathNode)node).getContent();
}
// find Node for the given Path
@Override
public synchronized Node findNode(String str) {
Node node = findModulesNode(str);
if (node != null) {
return node;
}
// lazily created for paths like /packages/<package>/<module>/xyz
// For example /packages/java.lang/java.base/java/lang/
if (str.startsWith(PACKAGES)) {
// pkgEndIdx marks end of <package> part
int pkgEndIdx = str.indexOf('/', PACKAGES_LEN);
if (pkgEndIdx != -1) {
// modEndIdx marks end of <module> part
int modEndIdx = str.indexOf('/', pkgEndIdx + 1);
if (modEndIdx != -1) {
// make sure we have such module link!
// ie., /packages/<package>/<module> is valid
Node linkNode = nodes.get(str.substring(0, modEndIdx));
if (linkNode == null || !linkNode.isLink()) {
return null;
}
// map to "/modules/zyz" path and return that node
// For example, "/modules/java.base/java/lang" for
// "/packages/java.lang/java.base/java/lang".
String mod = MODULES + str.substring(pkgEndIdx + 1);
return findModulesNode(mod);
}
}
}
return null;
}
// find a Node for a path that starts like "/modules/..."
Node findModulesNode(String str) {
PathNode node = nodes.get(str);
if (node != null) {
return node;
}
// lazily created "/modules/xyz/abc/" Node
// This is mapped to default file system path "<JDK_MODULES_DIR>/xyz/abc"
Path p = underlyingPath(str);
if (p != null) {
try {
BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class);
if (attrs.isRegularFile()) {
Path f = p.getFileName();
if (f.toString().startsWith("_the."))
return null;
}
node = new PathNode(str, p, attrs);
nodes.put(str, node);
return node;
} catch (IOException x) {
// does not exists or unable to determine
}
}
return null;
}
Path underlyingPath(String str) {
if (str.startsWith(MODULES)) {
str = frontSlashToNativeSlash(str.substring("/modules".length()));
return defaultFS.getPath(explodedModulesDir.toString(), str);
}
return null;
}
// convert "/" to platform path separator
private String frontSlashToNativeSlash(String str) {
return separator == null ? str : str.replace("/", separator);
}
// convert platform path separator to "/"
private String nativeSlashToFrontSlash(String str) {
return separator == null ? str : str.replace(separator, "/");
}
// convert "/"s to "."s
private String slashesToDots(String str) {
return str.replace(separator != null ? separator : "/", ".");
}
// initialize file system Nodes
private void initNodes() throws IOException {
// same package prefix may exist in mutliple modules. This Map
// is filled by walking "jdk modules" directory recursively!
Map<String, List<String>> packageToModules = new HashMap<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(explodedModulesDir)) {
for (Path module : stream) {
if (Files.isDirectory(module)) {
String moduleName = module.getFileName().toString();
// make sure "/modules/<moduleName>" is created
findModulesNode(MODULES + moduleName);
Files.walk(module).filter(Files::isDirectory).forEach((p) -> {
p = module.relativize(p);
String pkgName = slashesToDots(p.toString());
// skip META-INFO and empty strings
if (!pkgName.isEmpty() && !pkgName.startsWith("META-INF")) {
List<String> moduleNames = packageToModules.get(pkgName);
if (moduleNames == null) {
moduleNames = new ArrayList<>();
packageToModules.put(pkgName, moduleNames);
}
moduleNames.add(moduleName);
}
});
}
}
}
// create "/modules" directory
// "nodes" map contains only /modules/<foo> nodes only so far and so add all as children of /modules
PathNode modulesDir = new PathNode("/modules", new ArrayList<>(nodes.values()));
nodes.put(modulesDir.getName(), modulesDir);
// create children under "/packages"
List<Node> packagesChildren = new ArrayList<>(packageToModules.size());
for (Map.Entry<String, List<String>> entry : packageToModules.entrySet()) {
String pkgName = entry.getKey();
List<String> moduleNameList = entry.getValue();
List<Node> moduleLinkNodes = new ArrayList<>(moduleNameList.size());
for (String moduleName : moduleNameList) {
Node moduleNode = findModulesNode(MODULES + moduleName);
PathNode linkNode = new PathNode(PACKAGES + pkgName + "/" + moduleName, moduleNode);
nodes.put(linkNode.getName(), linkNode);
moduleLinkNodes.add(linkNode);
}
PathNode pkgDir = new PathNode(PACKAGES + pkgName, moduleLinkNodes);
nodes.put(pkgDir.getName(), pkgDir);
packagesChildren.add(pkgDir);
}
// "/packages" dir
PathNode packagesDir = new PathNode("/packages", packagesChildren);
nodes.put(packagesDir.getName(), packagesDir);
// finally "/" dir!
List<Node> rootChildren = new ArrayList<>();
rootChildren.add(packagesDir);
rootChildren.add(modulesDir);
PathNode root = new PathNode("/", rootChildren);
nodes.put(root.getName(), root);
}
}
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.nio.file.DirectoryStream;
import java.nio.file.ClosedDirectoryStreamException;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.io.IOException;
/**
* DirectoryStream implementation for jrt file system implementations.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
final class JrtDirectoryStream implements DirectoryStream<Path> {
private final JrtPath dir;
private final DirectoryStream.Filter<? super Path> filter;
private boolean isClosed;
private Iterator<Path> itr;
JrtDirectoryStream(JrtPath dir,
DirectoryStream.Filter<? super java.nio.file.Path> filter)
throws IOException
{
this.dir = dir;
if (!dir.jrtfs.isDirectory(dir, true)) { // sanity check
throw new NotDirectoryException(dir.toString());
}
this.filter = filter;
}
@Override
public synchronized Iterator<Path> iterator() {
if (isClosed)
throw new ClosedDirectoryStreamException();
if (itr != null)
throw new IllegalStateException("Iterator has already been returned");
try {
itr = dir.jrtfs.iteratorOf(dir, filter);
} catch (IOException e) {
throw new IllegalStateException(e);
}
return new Iterator<Path>() {
@Override
public boolean hasNext() {
synchronized (JrtDirectoryStream.this) {
if (isClosed)
return false;
return itr.hasNext();
}
}
@Override
public Path next() {
synchronized (JrtDirectoryStream.this) {
if (isClosed)
throw new NoSuchElementException();
return itr.next();
}
}
};
}
@Override
public synchronized void close() throws IOException {
isClosed = true;
}
}
@@ -0,0 +1,198 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.nio.file.LinkOption;
import java.nio.file.attribute.*;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
/**
* File attribute view for jrt file system.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
final class JrtFileAttributeView implements BasicFileAttributeView {
private static enum AttrID {
size,
creationTime,
lastAccessTime,
lastModifiedTime,
isDirectory,
isRegularFile,
isSymbolicLink,
isOther,
fileKey,
compressedSize,
extension
};
private final JrtPath path;
private final boolean isJrtView;
private final LinkOption[] options;
private JrtFileAttributeView(JrtPath path, boolean isJrtView, LinkOption... options) {
this.path = path;
this.isJrtView = isJrtView;
this.options = options;
}
@SuppressWarnings("unchecked") // Cast to V
static <V extends FileAttributeView> V get(JrtPath path, Class<V> type, LinkOption... options) {
Objects.requireNonNull(type);
if (type == BasicFileAttributeView.class) {
return (V) new JrtFileAttributeView(path, false, options);
}
if (type == JrtFileAttributeView.class) {
return (V) new JrtFileAttributeView(path, true, options);
}
return null;
}
static JrtFileAttributeView get(JrtPath path, String type, LinkOption... options) {
Objects.requireNonNull(type);
if (type.equals("basic")) {
return new JrtFileAttributeView(path, false, options);
}
if (type.equals("jrt")) {
return new JrtFileAttributeView(path, true, options);
}
return null;
}
@Override
public String name() {
return isJrtView ? "jrt" : "basic";
}
@Override
public JrtFileAttributes readAttributes() throws IOException {
return path.getAttributes(options);
}
@Override
public void setTimes(FileTime lastModifiedTime,
FileTime lastAccessTime,
FileTime createTime) throws IOException {
path.setTimes(lastModifiedTime, lastAccessTime, createTime);
}
static void setAttribute(JrtPath path, String attribute, Object value)
throws IOException {
int colonPos = attribute.indexOf(':');
if (colonPos != -1) { // type = "basic", if no ":"
String type = attribute.substring(0, colonPos++);
if (!type.equals("basic") && !type.equals("jrt")) {
throw new UnsupportedOperationException(
"view <" + type + "> is not supported");
}
attribute = attribute.substring(colonPos);
}
try {
AttrID id = AttrID.valueOf(attribute);
if (id == AttrID.lastModifiedTime) {
path.setTimes((FileTime) value, null, null);
} else if (id == AttrID.lastAccessTime) {
path.setTimes(null, (FileTime) value, null);
} else if (id == AttrID.creationTime) {
path.setTimes(null, null, (FileTime) value);
}
return;
} catch (IllegalArgumentException x) {}
throw new UnsupportedOperationException("'" + attribute
+ "' is unknown or read-only attribute");
}
static Map<String, Object> readAttributes(JrtPath path, String attributes,
LinkOption... options)
throws IOException {
int colonPos = attributes.indexOf(':');
boolean isJrtView = false;
if (colonPos != -1) { // type = "basic", if no ":"
String type = attributes.substring(0, colonPos++);
if (!type.equals("basic") && !type.equals("jrt")) {
throw new UnsupportedOperationException("view <" + type +
"> is not supported");
}
isJrtView = true;
attributes = attributes.substring(colonPos);
}
JrtFileAttributes jrtfas = path.getAttributes();
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
if ("*".equals(attributes)) {
for (AttrID id : AttrID.values()) {
map.put(id.name(), attribute(id, jrtfas, isJrtView));
}
} else {
String[] as = attributes.split(",");
for (String a : as) {
//throw IllegalArgumentException
map.put(a, attribute(AttrID.valueOf(a), jrtfas, isJrtView));
}
}
return map;
}
static Object attribute(AttrID id, JrtFileAttributes jrtfas, boolean isJrtView) {
switch (id) {
case size:
return jrtfas.size();
case creationTime:
return jrtfas.creationTime();
case lastAccessTime:
return jrtfas.lastAccessTime();
case lastModifiedTime:
return jrtfas.lastModifiedTime();
case isDirectory:
return jrtfas.isDirectory();
case isRegularFile:
return jrtfas.isRegularFile();
case isSymbolicLink:
return jrtfas.isSymbolicLink();
case isOther:
return jrtfas.isOther();
case fileKey:
return jrtfas.fileKey();
case compressedSize:
if (isJrtView) {
return jrtfas.compressedSize();
}
break;
case extension:
if (isJrtView) {
return jrtfas.extension();
}
break;
}
return null;
}
}
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.Formatter;
import javac.internal.jimage.ImageReader.Node;
/**
* File attributes implementation for jrt image file system.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
final class JrtFileAttributes implements BasicFileAttributes {
private final Node node;
JrtFileAttributes(Node node) {
this.node = node;
}
///////// basic attributes ///////////
@Override
public FileTime creationTime() {
return node.creationTime();
}
@Override
public boolean isDirectory() {
return node.isDirectory();
}
@Override
public boolean isOther() {
return false;
}
@Override
public boolean isRegularFile() {
return !isDirectory();
}
@Override
public FileTime lastAccessTime() {
return node.lastAccessTime();
}
@Override
public FileTime lastModifiedTime() {
return node.lastModifiedTime();
}
@Override
public long size() {
return node.size();
}
@Override
public boolean isSymbolicLink() {
return node.isLink();
}
@Override
public Object fileKey() {
return node.resolveLink(true);
}
///////// jrtfs specific attributes ///////////
/**
* Compressed resource file. If not available or not applicable, 0L is
* returned.
*
* @return the compressed resource size for compressed resources.
*/
public long compressedSize() {
return node.compressedSize();
}
/**
* "file" extension of a file resource.
*
* @return extension string for the file resource
*/
public String extension() {
return node.extension();
}
@Override
public final String toString() {
StringBuilder sb = new StringBuilder(1024);
try (Formatter fm = new Formatter(sb)) {
if (creationTime() != null) {
fm.format(" creationTime : %tc%n", creationTime().toMillis());
} else {
fm.format(" creationTime : null%n");
}
if (lastAccessTime() != null) {
fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis());
} else {
fm.format(" lastAccessTime : null%n");
}
fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
fm.format(" isRegularFile : %b%n", isRegularFile());
fm.format(" isDirectory : %b%n", isDirectory());
fm.format(" isSymbolicLink : %b%n", isSymbolicLink());
fm.format(" isOther : %b%n", isOther());
fm.format(" fileKey : %s%n", fileKey());
fm.format(" size : %d%n", size());
fm.format(" compressedSize : %d%n", compressedSize());
fm.format(" extension : %s%n", extension());
}
return sb.toString();
}
}
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileStoreAttributeView;
import java.util.Objects;
/**
* File store implementation for jrt file systems.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
final class JrtFileStore extends FileStore {
protected final FileSystem jrtfs;
JrtFileStore(JrtPath jrtPath) {
this.jrtfs = jrtPath.getFileSystem();
}
@Override
public String name() {
return jrtfs.toString() + "/";
}
@Override
public String type() {
return "jrtfs";
}
@Override
public boolean isReadOnly() {
return jrtfs.isReadOnly();
}
@Override
public boolean supportsFileAttributeView(String name) {
return name.equals("basic") || name.equals("jrt");
}
@Override
@SuppressWarnings("unchecked")
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
Objects.requireNonNull(type, "type");
return (V) null;
}
@Override
public long getTotalSpace() throws IOException {
throw new UnsupportedOperationException("getTotalSpace");
}
@Override
public long getUsableSpace() throws IOException {
throw new UnsupportedOperationException("getUsableSpace");
}
@Override
public long getUnallocatedSpace() throws IOException {
throw new UnsupportedOperationException("getUnallocatedSpace");
}
@Override
public Object getAttribute(String attribute) throws IOException {
throw new UnsupportedOperationException("does not support " + attribute);
}
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
return type == BasicFileAttributeView.class ||
type == JrtFileAttributeView.class;
}
}
@@ -0,0 +1,490 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.ClosedFileSystemException;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemException;
import java.nio.file.InvalidPathException;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.ReadOnlyFileSystemException;
import java.nio.file.StandardOpenOption;
import java.nio.file.WatchService;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
import javac.internal.jimage.ImageReader.Node;
/**
* jrt file system implementation built on System jimage files.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
class JrtFileSystem extends FileSystem {
private final JrtFileSystemProvider provider;
private final JrtPath rootPath = new JrtPath(this, "/");
private volatile boolean isOpen;
private volatile boolean isClosable;
private SystemImage image;
JrtFileSystem(JrtFileSystemProvider provider, Map<String, ?> env)
throws IOException
{
this.provider = provider;
this.image = SystemImage.open(); // open image file
this.isOpen = true;
this.isClosable = env != null;
}
// FileSystem method implementations
@Override
public boolean isOpen() {
return isOpen;
}
@Override
public void close() throws IOException {
if (!isClosable)
throw new UnsupportedOperationException();
cleanup();
}
@Override
public FileSystemProvider provider() {
return provider;
}
@Override
public Iterable<Path> getRootDirectories() {
return Collections.singleton(getRootPath());
}
@Override
public JrtPath getPath(String first, String... more) {
if (more.length == 0) {
return new JrtPath(this, first);
}
StringBuilder sb = new StringBuilder();
sb.append(first);
for (String path : more) {
if (!path.isEmpty()) {
if (sb.length() > 0) {
sb.append('/');
}
sb.append(path);
}
}
return new JrtPath(this, sb.toString());
}
@Override
public final boolean isReadOnly() {
return true;
}
@Override
public final UserPrincipalLookupService getUserPrincipalLookupService() {
throw new UnsupportedOperationException();
}
@Override
public final WatchService newWatchService() {
throw new UnsupportedOperationException();
}
@Override
public final Iterable<FileStore> getFileStores() {
return Collections.singleton(getFileStore(getRootPath()));
}
private static final Set<String> supportedFileAttributeViews
= Collections.unmodifiableSet(
new HashSet<String>(Arrays.asList("basic", "jrt")));
@Override
public final Set<String> supportedFileAttributeViews() {
return supportedFileAttributeViews;
}
@Override
public final String toString() {
return "jrt:/";
}
@Override
public final String getSeparator() {
return "/";
}
@Override
public PathMatcher getPathMatcher(String syntaxAndInput) {
int pos = syntaxAndInput.indexOf(':');
if (pos <= 0 || pos == syntaxAndInput.length()) {
throw new IllegalArgumentException("pos is " + pos);
}
String syntax = syntaxAndInput.substring(0, pos);
String input = syntaxAndInput.substring(pos + 1);
String expr;
if (syntax.equalsIgnoreCase("glob")) {
expr = JrtUtils.toRegexPattern(input);
} else if (syntax.equalsIgnoreCase("regex")) {
expr = input;
} else {
throw new UnsupportedOperationException("Syntax '" + syntax
+ "' not recognized");
}
// return matcher
final Pattern pattern = Pattern.compile(expr);
return (Path path) -> pattern.matcher(path.toString()).matches();
}
JrtPath resolveLink(JrtPath path) throws IOException {
Node node = checkNode(path);
if (node.isLink()) {
node = node.resolveLink();
return new JrtPath(this, node.getName()); // TBD, normalized?
}
return path;
}
JrtFileAttributes getFileAttributes(JrtPath path, LinkOption... options)
throws IOException {
Node node = checkNode(path);
if (node.isLink() && followLinks(options)) {
return new JrtFileAttributes(node.resolveLink(true));
}
return new JrtFileAttributes(node);
}
/**
* returns the list of child paths of the given directory "path"
*
* @param path name of the directory whose content is listed
* @return iterator for child paths of the given directory path
*/
Iterator<Path> iteratorOf(JrtPath path, DirectoryStream.Filter<? super Path> filter)
throws IOException {
Node node = checkNode(path).resolveLink(true);
if (!node.isDirectory()) {
throw new NotDirectoryException(path.getName());
}
if (filter == null) {
return node.getChildren()
.stream()
.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
.iterator();
}
return node.getChildren()
.stream()
.map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName())))
.filter(p -> { try { return filter.accept(p);
} catch (IOException x) {}
return false;
})
.iterator();
}
// returns the content of the file resource specified by the path
byte[] getFileContent(JrtPath path) throws IOException {
Node node = checkNode(path);
if (node.isDirectory()) {
throw new FileSystemException(path + " is a directory");
}
//assert node.isResource() : "resource node expected here";
return image.getResource(node);
}
/////////////// Implementation details below this point //////////
// static utility methods
static ReadOnlyFileSystemException readOnly() {
return new ReadOnlyFileSystemException();
}
// do the supplied options imply that we have to chase symlinks?
static boolean followLinks(LinkOption... options) {
if (options != null) {
for (LinkOption lo : options) {
Objects.requireNonNull(lo);
if (lo == LinkOption.NOFOLLOW_LINKS) {
return false;
} else {
throw new AssertionError("should not reach here");
}
}
}
return true;
}
// check that the options passed are supported by (read-only) jrt file system
static void checkOptions(Set<? extends OpenOption> options) {
// check for options of null type and option is an intance of StandardOpenOption
for (OpenOption option : options) {
Objects.requireNonNull(option);
if (!(option instanceof StandardOpenOption)) {
throw new IllegalArgumentException(
"option class: " + option.getClass());
}
}
if (options.contains(StandardOpenOption.WRITE) ||
options.contains(StandardOpenOption.APPEND)) {
throw readOnly();
}
}
// clean up this file system - called from finalize and close
synchronized void cleanup() throws IOException {
if (isOpen) {
isOpen = false;
image.close();
image = null;
}
}
// These methods throw read only file system exception
final void setTimes(JrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime)
throws IOException {
throw readOnly();
}
// These methods throw read only file system exception
final void createDirectory(JrtPath jrtPath, FileAttribute<?>... attrs) throws IOException {
throw readOnly();
}
final void deleteFile(JrtPath jrtPath, boolean failIfNotExists)
throws IOException {
throw readOnly();
}
final OutputStream newOutputStream(JrtPath jrtPath, OpenOption... options)
throws IOException {
throw readOnly();
}
final void copyFile(boolean deletesrc, JrtPath srcPath, JrtPath dstPath, CopyOption... options)
throws IOException {
throw readOnly();
}
final FileChannel newFileChannel(JrtPath path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException {
throw new UnsupportedOperationException("newFileChannel");
}
final InputStream newInputStream(JrtPath path) throws IOException {
return new ByteArrayInputStream(getFileContent(path));
}
final SeekableByteChannel newByteChannel(JrtPath path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException {
checkOptions(options);
byte[] buf = getFileContent(path);
final ReadableByteChannel rbc
= Channels.newChannel(new ByteArrayInputStream(buf));
final long size = buf.length;
return new SeekableByteChannel() {
long read = 0;
@Override
public boolean isOpen() {
return rbc.isOpen();
}
@Override
public long position() throws IOException {
return read;
}
@Override
public SeekableByteChannel position(long pos)
throws IOException {
throw new UnsupportedOperationException();
}
@Override
public int read(ByteBuffer dst) throws IOException {
int n = rbc.read(dst);
if (n > 0) {
read += n;
}
return n;
}
@Override
public SeekableByteChannel truncate(long size)
throws IOException {
throw new NonWritableChannelException();
}
@Override
public int write(ByteBuffer src) throws IOException {
throw new NonWritableChannelException();
}
@Override
public long size() throws IOException {
return size;
}
@Override
public void close() throws IOException {
rbc.close();
}
};
}
final JrtFileStore getFileStore(JrtPath path) {
return new JrtFileStore(path);
}
final void ensureOpen() throws IOException {
if (!isOpen()) {
throw new ClosedFileSystemException();
}
}
final JrtPath getRootPath() {
return rootPath;
}
boolean isSameFile(JrtPath path1, JrtPath path2) throws IOException {
return checkNode(path1) == checkNode(path2);
}
boolean isLink(JrtPath path) throws IOException {
return checkNode(path).isLink();
}
boolean exists(JrtPath path) throws IOException {
try {
checkNode(path);
} catch (NoSuchFileException exp) {
return false;
}
return true;
}
boolean isDirectory(JrtPath path, boolean resolveLinks)
throws IOException {
Node node = checkNode(path);
return resolveLinks && node.isLink()
? node.resolveLink(true).isDirectory()
: node.isDirectory();
}
JrtPath toRealPath(JrtPath path, LinkOption... options)
throws IOException {
Node node = checkNode(path);
if (followLinks(options) && node.isLink()) {
node = node.resolveLink();
}
// image node holds the real/absolute path name
return new JrtPath(this, node.getName(), true);
}
private Node lookup(String path) {
try {
return image.findNode(path);
} catch (RuntimeException | IOException ex) {
throw new InvalidPathException(path, ex.toString());
}
}
private Node lookupSymbolic(String path) {
int i = 1;
while (i < path.length()) {
i = path.indexOf('/', i);
if (i == -1) {
break;
}
String prefix = path.substring(0, i);
Node node = lookup(prefix);
if (node == null) {
break;
}
if (node.isLink()) {
Node link = node.resolveLink(true);
// resolved symbolic path concatenated to the rest of the path
String resPath = link.getName() + path.substring(i);
node = lookup(resPath);
return node != null ? node : lookupSymbolic(resPath);
}
i++;
}
return null;
}
Node checkNode(JrtPath path) throws IOException {
ensureOpen();
String p = path.getResolvedPath();
Node node = lookup(p);
if (node == null) {
node = lookupSymbolic(p);
if (node == null) {
throw new NoSuchFileException(p);
}
}
return node;
}
}
@@ -0,0 +1,357 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.*;
import java.nio.file.*;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.attribute.*;
import java.nio.file.spi.FileSystemProvider;
import java.net.URI;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
/**
* File system provider for jrt file systems. Conditionally creates jrt fs on
* .jimage file or exploded modules directory of underlying JDK.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
public final class JrtFileSystemProvider extends FileSystemProvider {
private volatile FileSystem theFileSystem;
public JrtFileSystemProvider() {
}
@Override
public String getScheme() {
return "jrt";
}
/**
* Need RuntimePermission "accessSystemModules" to create or get jrt:/
*/
private void checkPermission() {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
RuntimePermission perm = new RuntimePermission("accessSystemModules");
sm.checkPermission(perm);
}
}
private void checkUri(URI uri) {
if (!uri.getScheme().equalsIgnoreCase(getScheme())) {
throw new IllegalArgumentException("URI does not match this provider");
}
if (uri.getAuthority() != null) {
throw new IllegalArgumentException("Authority component present");
}
if (uri.getPath() == null) {
throw new IllegalArgumentException("Path component is undefined");
}
if (!uri.getPath().equals("/")) {
throw new IllegalArgumentException("Path component should be '/'");
}
if (uri.getQuery() != null) {
throw new IllegalArgumentException("Query component present");
}
if (uri.getFragment() != null) {
throw new IllegalArgumentException("Fragment component present");
}
}
@Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env)
throws IOException {
Objects.requireNonNull(env);
checkPermission();
checkUri(uri);
// ANDROIDIDE-CHANGED: Do not check for java.home property and directly return new file system instance.
return new JrtFileSystem(this, env);
}
private static final String JRT_FS_JAR = "jrt-fs.jar";
private FileSystem newFileSystem(String targetHome, URI uri, Map<String, ?> env)
throws IOException {
Objects.requireNonNull(targetHome);
Path jrtfs = FileSystems.getDefault().getPath(targetHome, "lib", JRT_FS_JAR);
if (Files.notExists(jrtfs)) {
throw new IOException(jrtfs.toString() + " not exist");
}
Map<String,?> newEnv = new HashMap<>(env);
newEnv.remove("java.home");
ClassLoader cl = newJrtFsLoader(jrtfs);
try {
Class<?> c = Class.forName(JrtFileSystemProvider.class.getName(), false, cl);
@SuppressWarnings("deprecation")
Object tmp = c.newInstance();
return ((FileSystemProvider)tmp).newFileSystem(uri, newEnv);
} catch (ClassNotFoundException |
IllegalAccessException |
InstantiationException e) {
throw new IOException(e);
}
}
private static class JrtFsLoader extends URLClassLoader {
JrtFsLoader(URL[] urls) {
super(urls);
}
@Override
protected Class<?> loadClass(String cn, boolean resolve)
throws ClassNotFoundException
{
Class<?> c = findLoadedClass(cn);
if (c == null) {
URL u = findResource(cn.replace('.', '/') + ".class");
if (u != null) {
c = findClass(cn);
} else {
return super.loadClass(cn, resolve);
}
}
if (resolve)
resolveClass(c);
return c;
}
}
@SuppressWarnings("removal")
private static URLClassLoader newJrtFsLoader(Path jrtfs) {
final URL url;
try {
url = jrtfs.toUri().toURL();
} catch (MalformedURLException mue) {
throw new IllegalArgumentException(mue);
}
final URL[] urls = new URL[] { url };
return AccessController.doPrivileged(
new PrivilegedAction<URLClassLoader>() {
@Override
public URLClassLoader run() {
return new JrtFsLoader(urls);
}
}
);
}
@Override
public Path getPath(URI uri) {
checkPermission();
if (!uri.getScheme().equalsIgnoreCase(getScheme())) {
throw new IllegalArgumentException("URI does not match this provider");
}
if (uri.getAuthority() != null) {
throw new IllegalArgumentException("Authority component present");
}
if (uri.getQuery() != null) {
throw new IllegalArgumentException("Query component present");
}
if (uri.getFragment() != null) {
throw new IllegalArgumentException("Fragment component present");
}
String path = uri.getPath();
if (path == null || path.charAt(0) != '/' || path.contains("..")) {
throw new IllegalArgumentException("Invalid path component");
}
return getTheFileSystem().getPath("/modules" + path);
}
private FileSystem getTheFileSystem() {
checkPermission();
FileSystem fs = this.theFileSystem;
if (fs == null) {
synchronized (this) {
fs = this.theFileSystem;
if (fs == null) {
try {
this.theFileSystem = fs = new JrtFileSystem(this, null);
} catch (IOException ioe) {
throw new InternalError(ioe);
}
}
}
}
return fs;
}
@Override
public FileSystem getFileSystem(URI uri) {
checkPermission();
checkUri(uri);
return getTheFileSystem();
}
// Checks that the given file is a JrtPath
static final JrtPath toJrtPath(Path path) {
Objects.requireNonNull(path, "path");
if (!(path instanceof JrtPath)) {
throw new ProviderMismatchException();
}
return (JrtPath) path;
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
toJrtPath(path).checkAccess(modes);
}
@Override
public Path readSymbolicLink(Path link) throws IOException {
return toJrtPath(link).readSymbolicLink();
}
@Override
public void copy(Path src, Path target, CopyOption... options)
throws IOException {
toJrtPath(src).copy(toJrtPath(target), options);
}
@Override
public void createDirectory(Path path, FileAttribute<?>... attrs)
throws IOException {
toJrtPath(path).createDirectory(attrs);
}
@Override
public final void delete(Path path) throws IOException {
toJrtPath(path).delete();
}
@Override
@SuppressWarnings("unchecked")
public <V extends FileAttributeView> V
getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
return JrtFileAttributeView.get(toJrtPath(path), type, options);
}
@Override
public FileStore getFileStore(Path path) throws IOException {
return toJrtPath(path).getFileStore();
}
@Override
public boolean isHidden(Path path) {
return toJrtPath(path).isHidden();
}
@Override
public boolean isSameFile(Path path, Path other) throws IOException {
return toJrtPath(path).isSameFile(other);
}
@Override
public void move(Path src, Path target, CopyOption... options)
throws IOException {
toJrtPath(src).move(toJrtPath(target), options);
}
@Override
public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
Set<? extends OpenOption> options,
ExecutorService exec,
FileAttribute<?>... attrs)
throws IOException {
throw new UnsupportedOperationException();
}
@Override
public SeekableByteChannel newByteChannel(Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException {
return toJrtPath(path).newByteChannel(options, attrs);
}
@Override
public DirectoryStream<Path> newDirectoryStream(
Path path, Filter<? super Path> filter) throws IOException {
return toJrtPath(path).newDirectoryStream(filter);
}
@Override
public FileChannel newFileChannel(Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException {
return toJrtPath(path).newFileChannel(options, attrs);
}
@Override
public InputStream newInputStream(Path path, OpenOption... options)
throws IOException {
return toJrtPath(path).newInputStream(options);
}
@Override
public OutputStream newOutputStream(Path path, OpenOption... options)
throws IOException {
return toJrtPath(path).newOutputStream(options);
}
@Override
@SuppressWarnings("unchecked") // Cast to A
public <A extends BasicFileAttributes> A
readAttributes(Path path, Class<A> type, LinkOption... options)
throws IOException {
if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) {
return (A) toJrtPath(path).getAttributes(options);
}
return null;
}
@Override
public Map<String, Object>
readAttributes(Path path, String attribute, LinkOption... options)
throws IOException {
return toJrtPath(path).readAttributes(attribute, options);
}
@Override
public void setAttribute(Path path, String attribute,
Object value, LinkOption... options)
throws IOException {
toJrtPath(path).setAttribute(attribute, value, options);
}
}
@@ -0,0 +1,955 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.*;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileTime;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import static java.nio.file.StandardOpenOption.*;
import static java.nio.file.StandardCopyOption.*;
/**
* Base class for Path implementation of jrt file systems.
*
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
final class JrtPath implements Path {
final JrtFileSystem jrtfs;
private final String path;
private volatile int[] offsets;
JrtPath(JrtFileSystem jrtfs, String path) {
this.jrtfs = jrtfs;
this.path = normalize(path);
this.resolved = null;
}
JrtPath(JrtFileSystem jrtfs, String path, boolean normalized) {
this.jrtfs = jrtfs;
this.path = normalized ? path : normalize(path);
this.resolved = null;
}
final String getName() {
return path;
}
@Override
public final JrtPath getRoot() {
if (this.isAbsolute()) {
return jrtfs.getRootPath();
} else {
return null;
}
}
@Override
public final JrtPath getFileName() {
if (path.isEmpty())
return this;
if (path.length() == 1 && path.charAt(0) == '/')
return null;
int off = path.lastIndexOf('/');
if (off == -1)
return this;
return new JrtPath(jrtfs, path.substring(off + 1), true);
}
@Override
public final JrtPath getParent() {
initOffsets();
int count = offsets.length;
if (count == 0) { // no elements so no parent
return null;
}
int off = offsets[count - 1] - 1;
if (off <= 0) { // parent is root only (may be null)
return getRoot();
}
return new JrtPath(jrtfs, path.substring(0, off));
}
@Override
public final int getNameCount() {
initOffsets();
return offsets.length;
}
@Override
public final JrtPath getName(int index) {
initOffsets();
if (index < 0 || index >= offsets.length) {
throw new IllegalArgumentException("index: " +
index + ", offsets length: " + offsets.length);
}
int begin = offsets[index];
int end;
if (index == (offsets.length - 1)) {
end = path.length();
} else {
end = offsets[index + 1];
}
return new JrtPath(jrtfs, path.substring(begin, end));
}
@Override
public final JrtPath subpath(int beginIndex, int endIndex) {
initOffsets();
if (beginIndex < 0 || endIndex > offsets.length ||
beginIndex >= endIndex) {
throw new IllegalArgumentException(
"beginIndex: " + beginIndex + ", endIndex: " + endIndex +
", offsets length: " + offsets.length);
}
// starting/ending offsets
int begin = offsets[beginIndex];
int end;
if (endIndex == offsets.length) {
end = path.length();
} else {
end = offsets[endIndex];
}
return new JrtPath(jrtfs, path.substring(begin, end));
}
@Override
public final JrtPath toRealPath(LinkOption... options) throws IOException {
return jrtfs.toRealPath(this, options);
}
@Override
public final JrtPath toAbsolutePath() {
if (isAbsolute())
return this;
return new JrtPath(jrtfs, "/" + path, true);
}
@Override
public final URI toUri() {
String p = toAbsolutePath().path;
if (!p.startsWith("/modules") || p.contains("..")) {
throw new IOError(new RuntimeException(p + " cannot be represented as URI"));
}
p = p.substring("/modules".length());
if (p.isEmpty()) {
p = "/";
}
return toUri(p);
}
private boolean equalsNameAt(JrtPath other, int index) {
int mbegin = offsets[index];
int mlen;
if (index == (offsets.length - 1)) {
mlen = path.length() - mbegin;
} else {
mlen = offsets[index + 1] - mbegin - 1;
}
int obegin = other.offsets[index];
int olen;
if (index == (other.offsets.length - 1)) {
olen = other.path.length() - obegin;
} else {
olen = other.offsets[index + 1] - obegin - 1;
}
if (mlen != olen) {
return false;
}
int n = 0;
while (n < mlen) {
if (path.charAt(mbegin + n) != other.path.charAt(obegin + n)) {
return false;
}
n++;
}
return true;
}
@Override
public final JrtPath relativize(Path other) {
final JrtPath o = checkPath(other);
if (o.equals(this)) {
return new JrtPath(jrtfs, "", true);
}
if (path.isEmpty()) {
return o;
}
if (jrtfs != o.jrtfs || isAbsolute() != o.isAbsolute()) {
throw new IllegalArgumentException(
"Incorrect filesystem or path: " + other);
}
final String tp = this.path;
final String op = o.path;
if (op.startsWith(tp)) { // fast path
int off = tp.length();
if (op.charAt(off - 1) == '/')
return new JrtPath(jrtfs, op.substring(off), true);
if (op.charAt(off) == '/')
return new JrtPath(jrtfs, op.substring(off + 1), true);
}
int mc = this.getNameCount();
int oc = o.getNameCount();
int n = Math.min(mc, oc);
int i = 0;
while (i < n) {
if (!equalsNameAt(o, i)) {
break;
}
i++;
}
int dotdots = mc - i;
int len = dotdots * 3 - 1;
if (i < oc) {
len += (o.path.length() - o.offsets[i] + 1);
}
StringBuilder sb = new StringBuilder(len);
while (dotdots > 0) {
sb.append("..");
if (sb.length() < len) { // no tailing slash at the end
sb.append('/');
}
dotdots--;
}
if (i < oc) {
sb.append(o.path, o.offsets[i], o.path.length());
}
return new JrtPath(jrtfs, sb.toString(), true);
}
@Override
public JrtFileSystem getFileSystem() {
return jrtfs;
}
@Override
public final boolean isAbsolute() {
return !path.isEmpty() && path.charAt(0) == '/';
}
@Override
public final JrtPath resolve(Path other) {
final JrtPath o = checkPath(other);
if (this.path.isEmpty() || o.isAbsolute()) {
return o;
}
if (o.path.isEmpty()) {
return this;
}
StringBuilder sb = new StringBuilder(path.length() + o.path.length() + 1);
sb.append(path);
if (path.charAt(path.length() - 1) != '/')
sb.append('/');
sb.append(o.path);
return new JrtPath(jrtfs, sb.toString(), true);
}
@Override
public final Path resolveSibling(Path other) {
Objects.requireNonNull(other, "other");
Path parent = getParent();
return (parent == null) ? other : parent.resolve(other);
}
@Override
public final boolean startsWith(Path other) {
if (!(Objects.requireNonNull(other) instanceof JrtPath))
return false;
final JrtPath o = (JrtPath)other;
final String tp = this.path;
final String op = o.path;
if (isAbsolute() != o.isAbsolute() || !tp.startsWith(op)) {
return false;
}
int off = op.length();
if (off == 0) {
return tp.isEmpty();
}
// check match is on name boundary
return tp.length() == off || tp.charAt(off) == '/' ||
off == 0 || op.charAt(off - 1) == '/';
}
@Override
public final boolean endsWith(Path other) {
if (!(Objects.requireNonNull(other) instanceof JrtPath))
return false;
final JrtPath o = (JrtPath)other;
final JrtPath t = this;
int olast = o.path.length() - 1;
if (olast > 0 && o.path.charAt(olast) == '/') {
olast--;
}
int last = t.path.length() - 1;
if (last > 0 && t.path.charAt(last) == '/') {
last--;
}
if (olast == -1) { // o.path.length == 0
return last == -1;
}
if ((o.isAbsolute() && (!t.isAbsolute() || olast != last))
|| last < olast) {
return false;
}
for (; olast >= 0; olast--, last--) {
if (o.path.charAt(olast) != t.path.charAt(last)) {
return false;
}
}
return o.path.charAt(olast + 1) == '/' ||
last == -1 || t.path.charAt(last) == '/';
}
@Override
public final JrtPath resolve(String other) {
return resolve(getFileSystem().getPath(other));
}
@Override
public final Path resolveSibling(String other) {
return resolveSibling(getFileSystem().getPath(other));
}
@Override
public final boolean startsWith(String other) {
return startsWith(getFileSystem().getPath(other));
}
@Override
public final boolean endsWith(String other) {
return endsWith(getFileSystem().getPath(other));
}
@Override
public final JrtPath normalize() {
String res = getResolved();
if (res == path) { // no change
return this;
}
return new JrtPath(jrtfs, res, true);
}
private JrtPath checkPath(Path path) {
Objects.requireNonNull(path);
if (!(path instanceof JrtPath))
throw new ProviderMismatchException("path class: " +
path.getClass());
return (JrtPath) path;
}
// create offset list if not already created
private void initOffsets() {
if (this.offsets == null) {
int len = path.length();
// count names
int count = 0;
int off = 0;
while (off < len) {
char c = path.charAt(off++);
if (c != '/') {
count++;
off = path.indexOf('/', off);
if (off == -1)
break;
}
}
// populate offsets
int[] offsets = new int[count];
count = 0;
off = 0;
while (off < len) {
char c = path.charAt(off);
if (c == '/') {
off++;
} else {
offsets[count++] = off++;
off = path.indexOf('/', off);
if (off == -1)
break;
}
}
this.offsets = offsets;
}
}
private volatile String resolved;
final String getResolvedPath() {
String r = resolved;
if (r == null) {
if (isAbsolute()) {
r = getResolved();
} else {
r = toAbsolutePath().getResolvedPath();
}
resolved = r;
}
return r;
}
// removes redundant slashs, replace "\" to separator "/"
// and check for invalid characters
private static String normalize(String path) {
int len = path.length();
if (len == 0) {
return path;
}
char prevC = 0;
for (int i = 0; i < len; i++) {
char c = path.charAt(i);
if (c == '\\' || c == '\u0000') {
return normalize(path, i);
}
if (c == '/' && prevC == '/') {
return normalize(path, i - 1);
}
prevC = c;
}
if (prevC == '/' && len > 1) {
return path.substring(0, len - 1);
}
return path;
}
private static String normalize(String path, int off) {
int len = path.length();
StringBuilder to = new StringBuilder(len);
to.append(path, 0, off);
char prevC = 0;
while (off < len) {
char c = path.charAt(off++);
if (c == '\\') {
c = '/';
}
if (c == '/' && prevC == '/') {
continue;
}
if (c == '\u0000') {
throw new InvalidPathException(path,
"Path: NUL character not allowed");
}
to.append(c);
prevC = c;
}
len = to.length();
if (len > 1 && to.charAt(len - 1) == '/') {
to.deleteCharAt(len - 1);
}
return to.toString();
}
// Remove DotSlash(./) and resolve DotDot (..) components
private String getResolved() {
int length = path.length();
if (length == 0 || (path.indexOf("./") == -1 && path.charAt(length - 1) != '.')) {
return path;
} else {
return resolvePath();
}
}
private String resolvePath() {
int length = path.length();
char[] to = new char[length];
int nc = getNameCount();
int[] lastM = new int[nc];
int lastMOff = -1;
int m = 0;
for (int i = 0; i < nc; i++) {
int n = offsets[i];
int len = (i == offsets.length - 1) ? length - n
: offsets[i + 1] - n - 1;
if (len == 1 && path.charAt(n) == '.') {
if (m == 0 && path.charAt(0) == '/') // absolute path
to[m++] = '/';
continue;
}
if (len == 2 && path.charAt(n) == '.' && path.charAt(n + 1) == '.') {
if (lastMOff >= 0) {
m = lastM[lastMOff--]; // retreat
continue;
}
if (path.charAt(0) == '/') { // "/../xyz" skip
if (m == 0)
to[m++] = '/';
} else { // "../xyz" -> "../xyz"
if (m != 0 && to[m-1] != '/')
to[m++] = '/';
while (len-- > 0)
to[m++] = path.charAt(n++);
}
continue;
}
if (m == 0 && path.charAt(0) == '/' || // absolute path
m != 0 && to[m-1] != '/') { // not the first name
to[m++] = '/';
}
lastM[++lastMOff] = m;
while (len-- > 0)
to[m++] = path.charAt(n++);
}
if (m > 1 && to[m - 1] == '/')
m--;
return (m == to.length) ? new String(to) : new String(to, 0, m);
}
@Override
public final String toString() {
return path;
}
@Override
public final int hashCode() {
return path.hashCode();
}
@Override
public final boolean equals(Object obj) {
return obj instanceof JrtPath &&
this.path.equals(((JrtPath) obj).path);
}
@Override
public final int compareTo(Path other) {
final JrtPath o = checkPath(other);
return path.compareTo(o.path);
}
@Override
public final WatchKey register(
WatchService watcher,
WatchEvent.Kind<?>[] events,
WatchEvent.Modifier... modifiers) {
Objects.requireNonNull(watcher, "watcher");
Objects.requireNonNull(events, "events");
Objects.requireNonNull(modifiers, "modifiers");
throw new UnsupportedOperationException();
}
@Override
public final WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
return register(watcher, events, new WatchEvent.Modifier[0]);
}
@Override
public final File toFile() {
throw new UnsupportedOperationException();
}
@Override
public final Iterator<Path> iterator() {
return new Iterator<Path>() {
private int i = 0;
@Override
public boolean hasNext() {
return (i < getNameCount());
}
@Override
public Path next() {
if (i < getNameCount()) {
Path result = getName(i);
i++;
return result;
} else {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
throw new ReadOnlyFileSystemException();
}
};
}
// Helpers for JrtFileSystemProvider and JrtFileSystem
final JrtPath readSymbolicLink() throws IOException {
if (!jrtfs.isLink(this)) {
throw new IOException("not a symbolic link");
}
return jrtfs.resolveLink(this);
}
final boolean isHidden() {
return false;
}
final void createDirectory(FileAttribute<?>... attrs)
throws IOException {
jrtfs.createDirectory(this, attrs);
}
final InputStream newInputStream(OpenOption... options) throws IOException {
if (options.length > 0) {
for (OpenOption opt : options) {
if (opt != READ) {
throw new UnsupportedOperationException("'" + opt + "' not allowed");
}
}
}
return jrtfs.newInputStream(this);
}
final DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
throws IOException {
return new JrtDirectoryStream(this, filter);
}
final void delete() throws IOException {
jrtfs.deleteFile(this, true);
}
final void deleteIfExists() throws IOException {
jrtfs.deleteFile(this, false);
}
final JrtFileAttributes getAttributes(LinkOption... options) throws IOException {
JrtFileAttributes zfas = jrtfs.getFileAttributes(this, options);
if (zfas == null) {
throw new NoSuchFileException(toString());
}
return zfas;
}
final void setAttribute(String attribute, Object value, LinkOption... options)
throws IOException {
JrtFileAttributeView.setAttribute(this, attribute, value);
}
final Map<String, Object> readAttributes(String attributes, LinkOption... options)
throws IOException {
return JrtFileAttributeView.readAttributes(this, attributes, options);
}
final void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
throws IOException {
jrtfs.setTimes(this, mtime, atime, ctime);
}
final FileStore getFileStore() throws IOException {
// each JrtFileSystem only has one root (as requested for now)
if (exists()) {
return jrtfs.getFileStore(this);
}
throw new NoSuchFileException(path);
}
final boolean isSameFile(Path other) throws IOException {
if (this == other || this.equals(other)) {
return true;
}
if (other == null || this.getFileSystem() != other.getFileSystem()) {
return false;
}
this.checkAccess();
JrtPath o = (JrtPath) other;
o.checkAccess();
return this.getResolvedPath().equals(o.getResolvedPath()) ||
jrtfs.isSameFile(this, o);
}
final SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return jrtfs.newByteChannel(this, options, attrs);
}
final FileChannel newFileChannel(Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException {
return jrtfs.newFileChannel(this, options, attrs);
}
final void checkAccess(AccessMode... modes) throws IOException {
if (modes.length == 0) { // check if the path exists
jrtfs.checkNode(this); // no need to follow link. the "link" node
// is built from real node under "/module"
} else {
boolean w = false;
for (AccessMode mode : modes) {
switch (mode) {
case READ:
break;
case WRITE:
w = true;
break;
case EXECUTE:
throw new AccessDeniedException(toString());
default:
throw new UnsupportedOperationException();
}
}
jrtfs.checkNode(this);
if (w && jrtfs.isReadOnly()) {
throw new AccessDeniedException(toString());
}
}
}
final boolean exists() {
try {
return jrtfs.exists(this);
} catch (IOException x) {}
return false;
}
final OutputStream newOutputStream(OpenOption... options) throws IOException {
if (options.length == 0) {
return jrtfs.newOutputStream(this, CREATE_NEW, WRITE);
}
return jrtfs.newOutputStream(this, options);
}
final void move(JrtPath target, CopyOption... options) throws IOException {
if (this.jrtfs == target.jrtfs) {
jrtfs.copyFile(true, this, target, options);
} else {
copyToTarget(target, options);
delete();
}
}
final void copy(JrtPath target, CopyOption... options) throws IOException {
if (this.jrtfs == target.jrtfs) {
jrtfs.copyFile(false, this, target, options);
} else {
copyToTarget(target, options);
}
}
private void copyToTarget(JrtPath target, CopyOption... options)
throws IOException {
boolean replaceExisting = false;
boolean copyAttrs = false;
for (CopyOption opt : options) {
if (opt == REPLACE_EXISTING) {
replaceExisting = true;
} else if (opt == COPY_ATTRIBUTES) {
copyAttrs = true;
}
}
// attributes of source file
BasicFileAttributes jrtfas = getAttributes();
// check if target exists
boolean exists;
if (replaceExisting) {
try {
target.deleteIfExists();
exists = false;
} catch (DirectoryNotEmptyException x) {
exists = true;
}
} else {
exists = target.exists();
}
if (exists) {
throw new FileAlreadyExistsException(target.toString());
}
if (jrtfas.isDirectory()) {
// create directory or file
target.createDirectory();
} else {
try (InputStream is = jrtfs.newInputStream(this);
OutputStream os = target.newOutputStream()) {
byte[] buf = new byte[8192];
int n;
while ((n = is.read(buf)) != -1) {
os.write(buf, 0, n);
}
}
}
if (copyAttrs) {
BasicFileAttributeView view =
Files.getFileAttributeView(target, BasicFileAttributeView.class);
try {
view.setTimes(jrtfas.lastModifiedTime(),
jrtfas.lastAccessTime(),
jrtfas.creationTime());
} catch (IOException x) {
try {
target.delete(); // rollback?
} catch (IOException ignore) {}
throw x;
}
}
}
// adopted from sun.nio.fs.UnixUriUtils
private static URI toUri(String str) {
char[] path = str.toCharArray();
assert path[0] == '/';
StringBuilder sb = new StringBuilder();
sb.append(path[0]);
for (int i = 1; i < path.length; i++) {
char c = (char)(path[i] & 0xff);
if (match(c, L_PATH, H_PATH)) {
sb.append(c);
} else {
sb.append('%');
sb.append(hexDigits[(c >> 4) & 0x0f]);
sb.append(hexDigits[(c) & 0x0f]);
}
}
try {
return new URI("jrt:" + sb.toString());
} catch (URISyntaxException x) {
throw new AssertionError(x); // should not happen
}
}
// The following is copied from java.net.URI
// Compute the low-order mask for the characters in the given string
private static long lowMask(String chars) {
int n = chars.length();
long m = 0;
for (int i = 0; i < n; i++) {
char c = chars.charAt(i);
if (c < 64)
m |= (1L << c);
}
return m;
}
// Compute the high-order mask for the characters in the given string
private static long highMask(String chars) {
int n = chars.length();
long m = 0;
for (int i = 0; i < n; i++) {
char c = chars.charAt(i);
if ((c >= 64) && (c < 128))
m |= (1L << (c - 64));
}
return m;
}
// Compute a low-order mask for the characters
// between first and last, inclusive
private static long lowMask(char first, char last) {
long m = 0;
int f = Math.max(Math.min(first, 63), 0);
int l = Math.max(Math.min(last, 63), 0);
for (int i = f; i <= l; i++)
m |= 1L << i;
return m;
}
// Compute a high-order mask for the characters
// between first and last, inclusive
private static long highMask(char first, char last) {
long m = 0;
int f = Math.max(Math.min(first, 127), 64) - 64;
int l = Math.max(Math.min(last, 127), 64) - 64;
for (int i = f; i <= l; i++)
m |= 1L << i;
return m;
}
// Tell whether the given character is permitted by the given mask pair
private static boolean match(char c, long lowMask, long highMask) {
if (c < 64)
return ((1L << c) & lowMask) != 0;
if (c < 128)
return ((1L << (c - 64)) & highMask) != 0;
return false;
}
// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
// "8" | "9"
private static final long L_DIGIT = lowMask('0', '9');
private static final long H_DIGIT = 0L;
// upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
// "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
// "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
private static final long L_UPALPHA = 0L;
private static final long H_UPALPHA = highMask('A', 'Z');
// lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
// "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
// "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
private static final long L_LOWALPHA = 0L;
private static final long H_LOWALPHA = highMask('a', 'z');
// alpha = lowalpha | upalpha
private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA;
private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA;
// alphanum = alpha | digit
private static final long L_ALPHANUM = L_DIGIT | L_ALPHA;
private static final long H_ALPHANUM = H_DIGIT | H_ALPHA;
// mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
// "(" | ")"
private static final long L_MARK = lowMask("-_.!~*'()");
private static final long H_MARK = highMask("-_.!~*'()");
// unreserved = alphanum | mark
private static final long L_UNRESERVED = L_ALPHANUM | L_MARK;
private static final long H_UNRESERVED = H_ALPHANUM | H_MARK;
// pchar = unreserved | escaped |
// ":" | "@" | "&" | "=" | "+" | "$" | ","
private static final long L_PCHAR
= L_UNRESERVED | lowMask(":@&=+$,");
private static final long H_PCHAR
= H_UNRESERVED | highMask(":@&=+$,");
// All valid path characters
private static final long L_PATH = L_PCHAR | lowMask(";/");
private static final long H_PATH = H_PCHAR | highMask(";/");
private static final char[] hexDigits = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
}
@@ -0,0 +1,194 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.util.regex.PatternSyntaxException;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
final class JrtUtils {
private JrtUtils() {}
private static final String regexMetaChars = ".^$+{[]|()";
private static final String globMetaChars = "\\*?[{";
private static boolean isRegexMeta(char c) {
return regexMetaChars.indexOf(c) != -1;
}
private static boolean isGlobMeta(char c) {
return globMetaChars.indexOf(c) != -1;
}
private static final char EOL = 0;
private static char next(String glob, int i) {
if (i < glob.length()) {
return glob.charAt(i);
}
return EOL;
}
/*
* Creates a regex pattern from the given glob expression.
*
* @throws PatternSyntaxException
*/
public static String toRegexPattern(String globPattern) {
boolean inGroup = false;
StringBuilder regex = new StringBuilder("^");
int i = 0;
while (i < globPattern.length()) {
char c = globPattern.charAt(i++);
switch (c) {
case '\\':
// escape special characters
if (i == globPattern.length()) {
throw new PatternSyntaxException("No character to escape",
globPattern, i - 1);
}
char next = globPattern.charAt(i++);
if (isGlobMeta(next) || isRegexMeta(next)) {
regex.append('\\');
}
regex.append(next);
break;
case '/':
regex.append(c);
break;
case '[':
// don't match name separator in class
regex.append("[[^/]&&[");
if (next(globPattern, i) == '^') {
// escape the regex negation char if it appears
regex.append("\\^");
i++;
} else {
// negation
if (next(globPattern, i) == '!') {
regex.append('^');
i++;
}
// hyphen allowed at start
if (next(globPattern, i) == '-') {
regex.append('-');
i++;
}
}
boolean hasRangeStart = false;
char last = 0;
while (i < globPattern.length()) {
c = globPattern.charAt(i++);
if (c == ']') {
break;
}
if (c == '/') {
throw new PatternSyntaxException("Explicit 'name separator' in class",
globPattern, i - 1);
}
// TBD: how to specify ']' in a class?
if (c == '\\' || c == '[' ||
c == '&' && next(globPattern, i) == '&') {
// escape '\', '[' or "&&" for regex class
regex.append('\\');
}
regex.append(c);
if (c == '-') {
if (!hasRangeStart) {
throw new PatternSyntaxException("Invalid range",
globPattern, i - 1);
}
if ((c = next(globPattern, i++)) == EOL || c == ']') {
break;
}
if (c < last) {
throw new PatternSyntaxException("Invalid range",
globPattern, i - 3);
}
regex.append(c);
hasRangeStart = false;
} else {
hasRangeStart = true;
last = c;
}
}
if (c != ']') {
throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
}
regex.append("]]");
break;
case '{':
if (inGroup) {
throw new PatternSyntaxException("Cannot nest groups",
globPattern, i - 1);
}
regex.append("(?:(?:");
inGroup = true;
break;
case '}':
if (inGroup) {
regex.append("))");
inGroup = false;
} else {
regex.append('}');
}
break;
case ',':
if (inGroup) {
regex.append(")|(?:");
} else {
regex.append(',');
}
break;
case '*':
if (next(globPattern, i) == '*') {
// crosses directory boundaries
regex.append(".*");
i++;
} else {
// within directory boundary
regex.append("[^/]*");
}
break;
case '?':
regex.append("[^/]");
break;
default:
if (isRegexMeta(c)) {
regex.append('\\');
}
regex.append(c);
}
}
if (inGroup) {
throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
}
return regex.append('$').toString();
}
}
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2022, 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 javac.internal.jrtfs;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javac.internal.jimage.ImageReader;
import javac.internal.jimage.ImageReader.Node;
import com.itsaky.androidide.config.JavacConfigProvider;
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
@SuppressWarnings("removal")
abstract class SystemImage {
abstract Node findNode(String path) throws IOException;
abstract byte[] getResource(Node node) throws IOException;
abstract void close() throws IOException;
static SystemImage open() throws IOException {
if (modulesImageExists) {
// open a .jimage and build directory structure
final ImageReader image = ImageReader.open(moduleImageFile);
image.getRootDirectory();
return new SystemImage() {
@Override
Node findNode(String path) throws IOException {
return image.findNode(path);
}
@Override
byte[] getResource(Node node) throws IOException {
return image.getResource(node);
}
@Override
void close() throws IOException {
image.close();
}
};
}
if (Files.notExists(explodedModulesDir))
throw new FileSystemNotFoundException(explodedModulesDir.toString());
return new ExplodedImage(explodedModulesDir);
}
static final String RUNTIME_HOME;
// "modules" jimage file Path
static final Path moduleImageFile;
// "modules" jimage exists or not?
static final boolean modulesImageExists;
// <JAVA_HOME>/modules directory Path
static final Path explodedModulesDir;
static {
PrivilegedAction<String> pa = SystemImage::findHome;
RUNTIME_HOME = AccessController.doPrivileged(pa);
FileSystem fs = FileSystems.getDefault();
moduleImageFile = fs.getPath(RUNTIME_HOME, "lib", "modules");
explodedModulesDir = fs.getPath(RUNTIME_HOME, "modules");
modulesImageExists = AccessController.doPrivileged(
new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return Files.isRegularFile(moduleImageFile);
}
});
}
/**
* Returns the appropriate JDK home for this usage of the FileSystemProvider.
* When the CodeSource is null (null loader) then jrt:/ is the current runtime,
* otherwise the JDK home is located relative to jrt-fs.jar.
*/
private static String findHome() {
// AndroidIDE changed: Allow overriding java home.
return JavacConfigProvider.getJavaHome();
}
}
@@ -0,0 +1,226 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import java.util.Objects;
import jdkx.lang.model.element.*;
import jdkx.lang.model.SourceVersion;
import jdkx.tools.Diagnostic;
/**
* An abstract annotation processor designed to be a convenient
* superclass for most concrete annotation processors. This class
* examines annotation values to compute the {@linkplain
* #getSupportedOptions options}, {@linkplain
* #getSupportedAnnotationTypes annotation interfaces}, and
* {@linkplain #getSupportedSourceVersion source version} supported by
* its subtypes.
*
* <p>The getter methods may {@linkplain Messager#printMessage issue
* warnings} about noteworthy conditions using the facilities available
* after the processor has been {@linkplain #isInitialized
* initialized}.
*
* <p>Subclasses are free to override the implementation and
* specification of any of the methods in this class as long as the
* general {@link jdkx.annotation.processing.Processor Processor}
* contract for that method is obeyed.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public abstract class AbstractProcessor implements Processor {
/**
* Processing environment providing by the tool framework.
*/
protected ProcessingEnvironment processingEnv;
private boolean initialized = false;
/**
* Constructor for subclasses to call.
*/
protected AbstractProcessor() {}
/**
* If the processor class is annotated with {@link
* SupportedOptions}, return an unmodifiable set with the same set
* of strings as the annotation. If the class is not so
* annotated, an empty set is returned.
*
* @return the options recognized by this processor, or an empty
* set if none
*/
public Set<String> getSupportedOptions() {
SupportedOptions so = this.getClass().getAnnotation(SupportedOptions.class);
return (so == null) ?
Collections.emptySet() :
arrayToSet(so.value(), false, "option value", "@SupportedOptions");
}
/**
* If the processor class is annotated with {@link
* SupportedAnnotationTypes}, return an unmodifiable set with the
* same set of strings as the annotation. If the class is not so
* annotated, an empty set is returned.
*
* If the {@linkplain ProcessingEnvironment#getSourceVersion source
* version} does not support modules, in other words if it is less
* than or equal to {@link SourceVersion#RELEASE_8 RELEASE_8},
* then any leading {@linkplain Processor#getSupportedAnnotationTypes
* module prefixes} are stripped from the names.
*
* @return the names of the annotation interfaces supported by
* this processor, or an empty set if none
*/
public Set<String> getSupportedAnnotationTypes() {
SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class);
boolean initialized = isInitialized();
if (sat == null) {
if (initialized)
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
"No SupportedAnnotationTypes annotation " +
"found on " + this.getClass().getName() +
", returning an empty set.");
return Collections.emptySet();
} else {
boolean stripModulePrefixes =
initialized &&
processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_8) <= 0;
return arrayToSet(sat.value(), stripModulePrefixes,
"annotation type", "@SupportedAnnotationTypes");
}
}
/**
* If the processor class is annotated with {@link
* SupportedSourceVersion}, return the source version in the
* annotation. If the class is not so annotated, {@link
* SourceVersion#RELEASE_6} is returned.
*
* @return the latest source version supported by this processor
*/
public SourceVersion getSupportedSourceVersion() {
SupportedSourceVersion ssv = this.getClass().getAnnotation(SupportedSourceVersion.class);
SourceVersion sv = null;
if (ssv == null) {
sv = SourceVersion.RELEASE_6;
if (isInitialized())
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
"No SupportedSourceVersion annotation " +
"found on " + this.getClass().getName() +
", returning " + sv + ".");
} else
sv = ssv.value();
return sv;
}
/**
* Initializes the processor with the processing environment by
* setting the {@code processingEnv} field to the value of the
* {@code processingEnv} argument. An {@code
* IllegalStateException} will be thrown if this method is called
* more than once on the same object.
*
* @param processingEnv environment to access facilities the tool framework
* provides to the processor
* @throws IllegalStateException if this method is called more than once.
*/
public synchronized void init(ProcessingEnvironment processingEnv) {
if (initialized)
throw new IllegalStateException("Cannot call init more than once.");
Objects.requireNonNull(processingEnv, "Tool provided null ProcessingEnvironment");
this.processingEnv = processingEnv;
initialized = true;
}
/**
* {@inheritDoc}
*/
public abstract boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv);
/**
* {@return an empty iterable of completions}
*
* @param element {@inheritDoc}
* @param annotation {@inheritDoc}
* @param member {@inheritDoc}
* @param userText {@inheritDoc}
*/
public Iterable<? extends Completion> getCompletions(Element element,
AnnotationMirror annotation,
ExecutableElement member,
String userText) {
return Collections.emptyList();
}
/**
* {@return {@code true} if this object has been {@linkplain #init
* initialized}, {@code false} otherwise}
*/
protected synchronized boolean isInitialized() {
return initialized;
}
private Set<String> arrayToSet(String[] array,
boolean stripModulePrefixes,
String contentType,
String annotationName) {
assert array != null;
Set<String> set = new HashSet<>();
for (String s : array) {
boolean stripped = false;
if (stripModulePrefixes) {
int index = s.indexOf('/');
if (index != -1) {
s = s.substring(index + 1);
stripped = true;
}
}
boolean added = set.add(s);
// Don't issue a duplicate warning when the module name is
// stripped off to avoid spurious warnings in a case like
// "foo/a.B", "bar/a.B".
if (!added && !stripped && isInitialized() ) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
"Duplicate " + contentType +
" ``" + s + "'' for processor " +
this.getClass().getName() +
" in its " + annotationName +
"annotation.");
}
}
return Collections.unmodifiableSet(set);
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2006, 2020, 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 jdkx.annotation.processing;
/**
* A suggested {@linkplain Processor#getCompletions <em>completion</em>} for an
* annotation. A completion is text meant to be inserted into a
* program as part of an annotation.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface Completion {
/**
* {@return the text of the suggested completion}
*/
String getValue();
/**
* {@return an informative message about the completion}
*/
String getMessage();
}
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2006, 2020, 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 jdkx.annotation.processing;
/**
* Utility class for assembling {@link Completion} objects.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public class Completions {
// No instances for you.
private Completions() {}
private static class SimpleCompletion implements Completion {
private String value;
private String message;
SimpleCompletion(String value, String message) {
if (value == null || message == null)
throw new NullPointerException("Null completion strings not accepted.");
this.value = value;
this.message = message;
}
public String getValue() {
return value;
}
public String getMessage() {
return message;
}
@Override
public String toString() {
return "[\"" + value + "\", \"" + message + "\"]";
}
// Default equals and hashCode are fine.
}
/**
* {@return a completion of the value and message}
*
* @param value the text of the completion
* @param message a message about the completion
*/
public static Completion of(String value, String message) {
return new SimpleCompletion(value, message);
}
/**
* {@return a completion of the value and an empty message}
*
* @param value the text of the completion
*/
public static Completion of(String value) {
return new SimpleCompletion(value, "");
}
}
@@ -0,0 +1,411 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import jdkx.tools.JavaFileManager;
import jdkx.tools.*;
import jdkx.lang.model.element.Element;
import jdkx.lang.model.util.Elements;
import java.io.IOException;
/**
* This interface supports the creation of new files by an annotation
* processor. Files created in this way will be known to the
* annotation processing tool implementing this interface, better
* enabling the tool to manage them. Source and class files so
* created will be {@linkplain RoundEnvironment#getRootElements
* considered for processing} by the tool in a subsequent {@linkplain
* RoundEnvironment round of processing} after the {@code close}
* method has been called on the {@code Writer} or {@code
* OutputStream} used to write the contents of the file.
*
* Three kinds of files are distinguished: source files, class files,
* and auxiliary resource files.
*
* <p> There are two distinguished supported locations (subtrees
* within the logical file system) where newly created files are
* placed: one for {@linkplain
* jdkx.tools.StandardLocation#SOURCE_OUTPUT new source files}, and
* one for {@linkplain jdkx.tools.StandardLocation#CLASS_OUTPUT new
* class files}. (These might be specified on a tool's command line,
* for example, using flags such as {@code -s} and {@code -d}.) The
* actual locations for new source files and new class files may or
* may not be distinct on a particular run of the tool. Resource
* files may be created in either location. The methods for reading
* and writing resources take a relative name argument. A relative
* name is a non-null, non-empty sequence of path segments separated
* by {@code '/'}; {@code '.'} and {@code '..'} are invalid path
* segments. A valid relative name must match the
* &quot;path-rootless&quot; rule of <a
* href="http://www.ietf.org/rfc/rfc3986.txt">RFC&nbsp;3986</a>, section
* 3.3.
*
* <p>The file creation methods take a variable number of arguments to
* allow the <em>originating elements</em> to be provided as hints to
* the tool infrastructure to better manage dependencies. The
* originating elements are the classes or interfaces or packages
* (representing {@code package-info} files) or modules (representing
* {@code module-info} files) which caused an annotation processor to
* attempt to create a new file. For example, if an annotation
* processor tries to create a source file, {@code
* GeneratedFromUserSource}, in response to processing
*
* <blockquote><pre>
* &#64;Generate
* public class UserSource {}
* </pre></blockquote>
*
* the type element for {@code UserSource} should be passed as part of
* the creation method call as in:
*
* <blockquote><pre>
* filer.createSourceFile("GeneratedFromUserSource",
* eltUtils.getTypeElement("UserSource"));
* </pre></blockquote>
*
* If there are no originating elements, none need to be passed. This
* information may be used in an incremental environment to determine
* the need to rerun processors or remove generated files.
* Non-incremental environments may ignore the originating element
* information.
*
* <p> During each run of an annotation processing tool, a file with a
* given pathname may be created only once. If that file already
* exists before the first attempt to create it, the old contents will
* be deleted. Any subsequent attempt to create the same file during
* a run will throw a {@link FilerException}, as will attempting to
* create both a class file and source file for the same type name or
* same package name. The {@linkplain Processor initial inputs} to
* the tool are considered to be created by the zeroth round;
* therefore, attempting to create a source or class file
* corresponding to one of those inputs will result in a {@link
* FilerException}.
*
* <p> In general, processors must not knowingly attempt to overwrite
* existing files that were not generated by some processor. A {@code
* Filer} may reject attempts to open a file corresponding to an
* existing class or interface, like {@code java.lang.Object}. Likewise, the
* invoker of the annotation processing tool must not knowingly
* configure the tool such that the discovered processors will attempt
* to overwrite existing files that were not generated.
*
* <p> Processors can indicate a source or class file is generated by
* including a {@link jdkx.annotation.processing.Generated}
* annotation if the environment is configured so that that class or
* interface is accessible.
*
* @apiNote Some of the effect of overwriting a file can be
* achieved by using a <i>decorator</i>-style pattern. Instead of
* modifying a class directly, the class is designed so that either
* its superclass is generated by annotation processing or subclasses
* of the class are generated by annotation processing. If the
* subclasses are generated, the parent class may be designed to use
* factories instead of public constructors so that only subclass
* instances would be presented to clients of the parent class.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface Filer {
/**
* Creates a new source file and returns an object to allow
* writing to it. A source file for a class, interface, or a
* package can be created.
*
* The file's name and path (relative to the {@linkplain
* StandardLocation#SOURCE_OUTPUT root output location for source
* files}) are based on the name of the item to be declared in
* that file as well as the specified module for the item (if
* any).
*
* If more than one class or interface is being declared in a single file (that
* is, a single compilation unit), the name of the file should
* correspond to the name of the principal top-level class or interface (the
* public one, for example).
*
* <p>A source file can also be created to hold information about
* a package, including package annotations. To create a source
* file for a named package, have the {@code name} argument be the
* package's name followed by {@code ".package-info"}; to create a
* source file for an unnamed package, use {@code "package-info"}.
*
* <p>The optional module name is prefixed to the type name or
* package name and separated using a "{@code /}" character. For
* example, to create a source file for class {@code a.B} in module
* {@code foo}, use a {@code name} argument of {@code "foo/a.B"}.
*
* <p>If no explicit module prefix is given and modules are supported
* in the environment, a suitable module is inferred. If a suitable
* module cannot be inferred {@link FilerException} is thrown.
* An implementation may use information about the configuration of
* the annotation processing tool as part of the inference.
*
* <p>Creating a source file in or for an <em>unnamed</em> package in a <em>named</em>
* module is <em>not</em> supported.
*
* @apiNote To use a particular {@linkplain
* java.nio.charset.Charset charset} to encode the contents of the
* file, an {@code OutputStreamWriter} with the chosen charset can
* be created from the {@code OutputStream} from the returned
* object. If the {@code Writer} from the returned object is
* directly used for writing, its charset is determined by the
* implementation. An annotation processing tool may have an
* {@code -encoding} flag or analogous option for specifying this;
* otherwise, it will typically be the platform's default
* encoding.
*
* <p>To avoid subsequent errors, the contents of the source file
* should be compatible with the {@linkplain
* ProcessingEnvironment#getSourceVersion source version} being used
* for this run.
*
* @implNote In the reference implementation, if the annotation
* processing tool is processing a single module <i>M</i>,
* then <i>M</i> is used as the module for files created without
* an explicit module prefix. If the tool is processing multiple
* modules, and {@link
* Elements#getPackageElement(java.lang.CharSequence)
* Elements.getPackageElement(package-of(name))}
* returns a package, the module that owns the returned package is used
* as the target module. A separate option may be used to provide the target
* module if it cannot be determined using the above rules.
*
* @param name canonical (fully qualified) name of the principal class or interface
* being declared in this file or a package name followed by
* {@code ".package-info"} for a package information file
* @param originatingElements class, interface, package, or module
* elements causally associated with the creation of this file,
* may be elided or {@code null}
* @return a {@code JavaFileObject} to write the new source file
* @throws FilerException if the same pathname has already been
* created, the same class or interface has already been created, the name is
* otherwise not valid for the entity requested to being created,
* if the target module cannot be determined, if the target
* module is not writable, or a module is specified when the environment
* doesn't support modules.
* @throws IOException if the file cannot be created
* @jls 7.3 Compilation Units
*/
JavaFileObject createSourceFile(CharSequence name,
Element... originatingElements) throws IOException;
/**
* Creates a new class file, and returns an object to allow
* writing to it. A class file for a class, interface, or a package can
* be created.
*
* The file's name and path (relative to the {@linkplain
* StandardLocation#CLASS_OUTPUT root output location for class
* files}) are based on the name of the item to be declared as
* well as the specified module for the item (if any).
*
* <p>A class file can also be created to hold information about a
* package, including package annotations. To create a class file
* for a named package, have the {@code name} argument be the
* package's name followed by {@code ".package-info"}; creating a
* class file for an unnamed package is not supported.
*
* <p>The optional module name is prefixed to the type name or
* package name and separated using a "{@code /}" character. For
* example, to create a class file for class {@code a.B} in module
* {@code foo}, use a {@code name} argument of {@code "foo/a.B"}.
*
* <p>If no explicit module prefix is given and modules are supported
* in the environment, a suitable module is inferred. If a suitable
* module cannot be inferred {@link FilerException} is thrown.
* An implementation may use information about the configuration of
* the annotation processing tool as part of the inference.
*
* <p>Creating a class file in or for an <em>unnamed</em> package in a <em>named</em>
* module is <em>not</em> supported.
*
* @apiNote To avoid subsequent errors, the contents of the class
* file should be compatible with the {@linkplain
* ProcessingEnvironment#getSourceVersion source version} being
* used for this run.
*
* @implNote In the reference implementation, if the annotation
* processing tool is processing a single module <i>M</i>,
* then <i>M</i> is used as the module for files created without
* an explicit module prefix. If the tool is processing multiple
* modules, and {@link
* Elements#getPackageElement(java.lang.CharSequence)
* Elements.getPackageElement(package-of(name))}
* returns a package, the module that owns the returned package is used
* as the target module. A separate option may be used to provide the target
* module if it cannot be determined using the above rules.
*
* @param name binary name of the class or interface being written
* or a package name followed by {@code ".package-info"} for a
* package information file
* @param originatingElements class or interface or package or
* module elements causally associated with the creation of this
* file, may be elided or {@code null}
* @return a {@code JavaFileObject} to write the new class file
* @throws FilerException if the same pathname has already been
* created, the same class or interface has already been created, the name is
* not valid for a class or interface, if the target module cannot be determined,
* if the target module is not writable, or a module is specified when
* the environment doesn't support modules.
* @throws IOException if the file cannot be created
*/
JavaFileObject createClassFile(CharSequence name,
Element... originatingElements) throws IOException;
/**
* Creates a new auxiliary resource file for writing and returns a
* file object for it. The file may be located along with the
* newly created source files, newly created binary files, or
* other supported location. The locations {@link
* StandardLocation#CLASS_OUTPUT CLASS_OUTPUT} and {@link
* StandardLocation#SOURCE_OUTPUT SOURCE_OUTPUT} must be
* supported. The resource may be named relative to some module
* and/or package (as are source and class files), and from there
* by a relative pathname. In a loose sense, the full pathname of
* the new file will be the concatenation of {@code location},
* {@code moduleAndPkg}, and {@code relativeName}.
*
* If {@code moduleAndPkg} contains a "{@code /}" character, the
* prefix before the "{@code /}" character is the module name and
* the suffix after the "{@code /}" character is the package
* name. The package suffix may be empty. If {@code moduleAndPkg}
* does not contain a "{@code /}" character, the entire argument
* is interpreted as a package name.
*
* <p>If the given location is neither a {@linkplain
* JavaFileManager.Location#isModuleOrientedLocation()
* module oriented location}, nor an {@linkplain
* JavaFileManager.Location#isOutputLocation()
* output location containing multiple modules}, and the explicit
* module prefix is given, {@link FilerException} is thrown.
*
* <p>If the given location is either a module oriented location,
* or an output location containing multiple modules, and no explicit
* modules prefix is given, a suitable module is
* inferred. If a suitable module cannot be inferred {@link
* FilerException} is thrown. An implementation may use information
* about the configuration of the annotation processing tool
* as part of the inference.
*
* <p>Files created via this method are <em>not</em> registered for
* annotation processing, even if the full pathname of the file
* would correspond to the full pathname of a new source file
* or new class file.
*
* @implNote In the reference implementation, if the annotation
* processing tool is processing a single module <i>M</i>,
* then <i>M</i> is used as the module for files created without
* an explicit module prefix. If the tool is processing multiple
* modules, and {@link
* Elements#getPackageElement(java.lang.CharSequence)
* Elements.getPackageElement(package-of(name))}
* returns a package, the module that owns the returned package is used
* as the target module. A separate option may be used to provide the target
* module if it cannot be determined using the above rules.
*
* @param location location of the new file
* @param moduleAndPkg module and/or package relative to which the file
* should be named, or the empty string if none
* @param relativeName final pathname components of the file
* @param originatingElements class or interface or package or
* module elements causally associated with the creation of this
* file, may be elided or
* {@code null}
* @return a {@code FileObject} to write the new resource
* @throws IOException if the file cannot be created
* @throws FilerException if the same pathname has already been
* created, if the target module cannot be determined,
* or if the target module is not writable, or if an explicit
* target module is specified and the location does not support it.
* @throws IllegalArgumentException for an unsupported location
* @throws IllegalArgumentException if {@code moduleAndPkg} is ill-formed
* @throws IllegalArgumentException if {@code relativeName} is not relative
*/
FileObject createResource(JavaFileManager.Location location,
CharSequence moduleAndPkg,
CharSequence relativeName,
Element... originatingElements) throws IOException;
/**
* Returns an object for reading an existing resource. The
* locations {@link StandardLocation#CLASS_OUTPUT CLASS_OUTPUT}
* and {@link StandardLocation#SOURCE_OUTPUT SOURCE_OUTPUT} must
* be supported.
*
* <p>If {@code moduleAndPkg} contains a "{@code /}" character, the
* prefix before the "{@code /}" character is the module name and
* the suffix after the "{@code /}" character is the package
* name. The package suffix may be empty; however, if a module
* name is present, it must be nonempty. If {@code moduleAndPkg}
* does not contain a "{@code /}" character, the entire argument
* is interpreted as a package name.
*
* <p>If the given location is neither a {@linkplain
* JavaFileManager.Location#isModuleOrientedLocation()
* module oriented location}, nor an {@linkplain
* JavaFileManager.Location#isOutputLocation()
* output location containing multiple modules}, and the explicit
* module prefix is given, {@link FilerException} is thrown.
*
* <p>If the given location is either a module oriented location,
* or an output location containing multiple modules, and no explicit
* modules prefix is given, a suitable module is
* inferred. If a suitable module cannot be inferred {@link
* FilerException} is thrown. An implementation may use information
* about the configuration of the annotation processing tool
* as part of the inference.
*
* @implNote In the reference implementation, if the annotation
* processing tool is processing a single module <i>M</i>,
* then <i>M</i> is used as the module for files read without
* an explicit module prefix. If the tool is processing multiple
* modules, and {@link
* Elements#getPackageElement(java.lang.CharSequence)
* Elements.getPackageElement(package-of(name))}
* returns a package, the module that owns the returned package is used
* as the source module. A separate option may be used to provide the target
* module if it cannot be determined using the above rules.
*
* @param location location of the file
* @param moduleAndPkg module and/or package relative to which the file
* should be searched for, or the empty string if none
* @param relativeName final pathname components of the file
* @return an object to read the file
* @throws FilerException if the same pathname has already been
* opened for writing, if the source module cannot be determined,
* or if the target module is not writable, or if an explicit target
* module is specified and the location does not support it.
* @throws IOException if the file cannot be opened
* @throws IllegalArgumentException for an unsupported location
* @throws IllegalArgumentException if {@code moduleAndPkg} is ill-formed
* @throws IllegalArgumentException if {@code relativeName} is not relative
*/
FileObject getResource(JavaFileManager.Location location,
CharSequence moduleAndPkg,
CharSequence relativeName) throws IOException;
}
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import java.io.IOException;
/**
* Indicates a {@link Filer} detected an attempt to open a file that
* would violate the guarantees provided by the {@code Filer}. Those
* guarantees include not creating the same file more than once, not
* creating multiple files corresponding to the same class or
* interface or package, and not creating files for classes or
* interfaces with invalid names.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public class FilerException extends IOException {
static final long serialVersionUID = 8426423106453163293L;
/**
* Constructs an exception with the specified detail message.
* @param s the detail message, which should include the name of
* the file attempting to be opened; may be {@code null}
*/
public FilerException(String s) {
super(s);
}
}
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2005, 2019, 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 jdkx.annotation.processing;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* The Generated annotation is used to mark source code that has been generated.
* It can also be used to differentiate user written code from generated code in
* a single file.
*
* <h2>Examples:</h2>
* <pre>
* &#064;Generated("com.example.Generator")
* </pre>
* <pre>
* &#064;Generated(value="com.example.Generator", date= "2017-07-04T12:08:56.235-0700")
* </pre>
* <pre>
* &#064;Generated(value="com.example.Generator", date= "2017-07-04T12:08:56.235-0700",
* comments= "comment 1")
* </pre>
*
* @since 9
*/
@Documented
@Retention(SOURCE)
@Target({PACKAGE, TYPE, METHOD, CONSTRUCTOR, FIELD,
LOCAL_VARIABLE, PARAMETER})
public @interface Generated {
/**
* The value element MUST have the name of the code generator. The
* name is the fully qualified name of the code generator.
*
* @return The name of the code generator
*/
String[] value();
/**
* Date when the source was generated. The date element must follow the ISO
* 8601 standard. For example the date element would have the following
* value 2017-07-04T12:08:56.235-0700 which represents 2017-07-04 12:08:56
* local time in the U.S. Pacific Time time zone.
*
* @return The date the source was generated
*/
String date() default "";
/**
* A place holder for any comments that the code generator may want to
* include in the generated code.
*
* @return Comments that the code generated included
*/
String comments() default "";
}
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2005, 2006, 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 jdkx.annotation.processing;
import jdkx.tools.Diagnostic;
import jdkx.lang.model.element.*;
/**
* A {@code Messager} provides the way for an annotation processor to
* report error messages, warnings, and other notices. Elements,
* annotations, and annotation values can be passed to provide a
* location hint for the message. However, such location hints may be
* unavailable or only approximate.
*
* <p>Printing a message with an {@linkplain
* jdkx.tools.Diagnostic.Kind#ERROR error kind} will {@linkplain
* RoundEnvironment#errorRaised raise an error}.
*
* <p>Note that the messages &quot;printed&quot; by methods in this
* interface may or may not appear as textual output to a location
* like {@link System#out} or {@link System#err}. Implementations may
* choose to present this information in a different fashion, such as
* messages in a window.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see ProcessingEnvironment#getLocale
* @since 1.6
*/
public interface Messager {
/**
* Prints a message of the specified kind.
*
* @param kind the kind of message
* @param msg the message, or an empty string if none
*/
void printMessage(Diagnostic.Kind kind, CharSequence msg);
/**
* Prints a message of the specified kind at the location of the
* element.
*
* @param kind the kind of message
* @param msg the message, or an empty string if none
* @param e the element to use as a position hint
*/
void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e);
/**
* Prints a message of the specified kind at the location of the
* annotation mirror of the annotated element.
*
* @param kind the kind of message
* @param msg the message, or an empty string if none
* @param e the annotated element
* @param a the annotation to use as a position hint
*/
void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a);
/**
* Prints a message of the specified kind at the location of the
* annotation value inside the annotation mirror of the annotated
* element.
*
* @param kind the kind of message
* @param msg the message, or an empty string if none
* @param e the annotated element
* @param a the annotation containing the annotation value
* @param v the annotation value to use as a position hint
*/
void printMessage(Diagnostic.Kind kind,
CharSequence msg,
Element e,
AnnotationMirror a,
AnnotationValue v);
}
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import java.util.Map;
import java.util.Locale;
import jdkx.lang.model.SourceVersion;
import jdkx.lang.model.util.Elements;
import jdkx.lang.model.util.Types;
/**
* An annotation processing tool framework will {@linkplain
* Processor#init provide an annotation processor with an object
* implementing this interface} so the processor can use facilities
* provided by the framework to write new files, report error
* messages, and find other utilities.
*
* <p>Third parties may wish to provide value-add wrappers around the
* facility objects from this interface, for example a {@code Filer}
* extension that allows multiple processors to coordinate writing out
* a single source file. To enable this, for processors running in a
* context where their side effects via the API could be visible to
* each other, the tool infrastructure must provide corresponding
* facility objects that are {@code .equals}, {@code Filer}s that are
* {@code .equals}, and so on. In addition, the tool invocation must
* be able to be configured such that from the perspective of the
* running annotation processors, at least the chosen subset of helper
* classes are viewed as being loaded by the same class loader.
* (Since the facility objects manage shared state, the implementation
* of a wrapper class must know whether or not the same base facility
* object has been wrapped before.)
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface ProcessingEnvironment {
/**
* {@return the processor-specific options passed to the annotation
* processing tool} Options are returned in the form of a map from
* option name to option value. For an option with no value, the
* corresponding value in the map is {@code null}.
*
* <p>See documentation of the particular tool infrastructure
* being used for details on how to pass in processor-specific
* options. For example, a command-line implementation may
* distinguish processor-specific options by prefixing them with a
* known string like {@code "-A"}; other tool implementations may
* follow different conventions or provide alternative mechanisms.
* A given implementation may also provide implementation-specific
* ways of finding options passed to the tool in addition to the
* processor-specific options.
*/
Map<String,String> getOptions();
/**
* {@return the messager used to report errors, warnings, and other
* notices}
*/
Messager getMessager();
/**
* {@return the filer used to create new source, class, or auxiliary
* files}
*/
Filer getFiler();
/**
* {@return an implementation of some utility methods for
* operating on elements}
*/
Elements getElementUtils();
/**
* {@return an implementation of some utility methods for
* operating on types}
*/
Types getTypeUtils();
/**
* {@return the source version that any generated {@linkplain
* Filer#createSourceFile source} and {@linkplain
* Filer#createClassFile class} files should conform to}
*
* @see Processor#getSupportedSourceVersion
*/
SourceVersion getSourceVersion();
/**
* {@return the current locale or {@code null} if no locale is in
* effect} The locale can be be used to provide localized
* {@linkplain Messager messages}.
*/
Locale getLocale();
/**
* Returns {@code true} if <em>preview features</em> are enabled
* and {@code false} otherwise.
* @return whether or not preview features are enabled
*
* @implSpec The default implementation of this method returns
* {@code false}.
*
* @since 13
*/
default boolean isPreviewEnabled() {
return false;
}
}
@@ -0,0 +1,477 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import java.util.Set;
import jdkx.lang.model.util.Elements;
import jdkx.lang.model.AnnotatedConstruct;
import jdkx.lang.model.element.*;
import jdkx.lang.model.SourceVersion;
/**
* The interface for an annotation processor.
*
* <p>Annotation processing happens in a sequence of {@linkplain
* jdkx.annotation.processing.RoundEnvironment rounds}. On each
* round, a processor may be asked to {@linkplain #process process} a
* subset of the annotations found on the source and class files
* produced by a prior round. The inputs to the first round of
* processing are the initial inputs to a run of the tool; these
* initial inputs can be regarded as the output of a virtual zeroth
* round of processing. If a processor was asked to process on a
* given round, it will be asked to process on subsequent rounds,
* including the last round, even if there are no annotations for it
* to process. The tool infrastructure may also ask a processor to
* process files generated implicitly by the tool's operation.
*
* <p> Each implementation of a {@code Processor} must provide a
* public no-argument constructor to be used by tools to instantiate
* the processor. The tool infrastructure will interact with classes
* implementing this interface as follows:
*
* <ol>
*
* <li>If an existing {@code Processor} object is not being used, to
* create an instance of a processor the tool calls the no-arg
* constructor of the processor class.
*
* <li>Next, the tool calls the {@link #init init} method with
* an appropriate {@link ProcessingEnvironment}.
*
* <li>Afterwards, the tool calls {@link #getSupportedAnnotationTypes
* getSupportedAnnotationTypes}, {@link #getSupportedOptions
* getSupportedOptions}, and {@link #getSupportedSourceVersion
* getSupportedSourceVersion}. These methods are only called once per
* run, not on each round.
*
* <li>As appropriate, the tool calls the {@link #process process}
* method on the {@code Processor} object; a new {@code Processor}
* object is <em>not</em> created for each round.
*
* </ol>
*
* If a processor object is created and used without the above
* protocol being followed, then the processor's behavior is not
* defined by this interface specification.
*
* <p> The tool uses a <i>discovery process</i> to find annotation
* processors and decide whether or not they should be run. By
* configuring the tool, the set of potential processors can be
* controlled. For example, for a {@link jdkx.tools.JavaCompiler
* JavaCompiler} the list of candidate processors to run can be
* {@linkplain jdkx.tools.JavaCompiler.CompilationTask#setProcessors
* set directly} or controlled by a {@linkplain
* jdkx.tools.StandardLocation#ANNOTATION_PROCESSOR_PATH search path}
* used for a {@linkplain java.util.ServiceLoader service-style}
* lookup. Other tool implementations may have different
* configuration mechanisms, such as command line options; for
* details, refer to the particular tool's documentation. Which
* processors the tool asks to {@linkplain #process run} is a function
* of the interfaces of the annotations <em>{@linkplain
* AnnotatedConstruct present}</em> on the {@linkplain
* RoundEnvironment#getRootElements root elements}, what {@linkplain
* #getSupportedAnnotationTypes annotation interfaces a processor
* supports}, and whether or not a processor {@linkplain #process
* claims the annotation interfaces it processes}. A processor will
* be asked to process a subset of the annotation interfaces it
* supports, possibly an empty set.
*
* For a given round, the tool computes the set of annotation
* interfaces that are present on the elements enclosed within the
* root elements. If there is at least one annotation interface
* present, then as processors claim annotation interfaces, they are
* removed from the set of unmatched annotation interfaces. When the
* set is empty or no more processors are available, the round has run
* to completion. If there are no annotation interfaces present,
* annotation processing still occurs but only <i>universal
* processors</i> which support processing all annotation interfaces,
* {@code "*"}, can claim the (empty) set of annotation interfaces.
*
* <p>An annotation interface is considered present if there is at least
* one annotation of that interface present on an element enclosed within
* the root elements of a round. For this purpose, a type parameter is
* considered to be enclosed by its {@linkplain
* TypeParameterElement#getGenericElement generic
* element}.
* For this purpose, a package element is <em>not</em> considered to
* enclose the top-level classes and interfaces within that
* package. (A root element representing a package is created when a
* {@code package-info} file is processed.) Likewise, for this
* purpose, a module element is <em>not</em> considered to enclose the
* packages within that module. (A root element representing a module
* is created when a {@code module-info} file is processed.)
*
* Annotations on {@linkplain
* java.lang.annotation.ElementType#TYPE_USE type uses}, as opposed to
* annotations on elements, are ignored when computing whether or not
* an annotation interface is present.
*
* <p>An annotation is <em>present</em> if it meets the definition of being
* present given in {@link AnnotatedConstruct}. In brief, an
* annotation is considered present for the purposes of discovery if
* it is directly present or present via inheritance. An annotation is
* <em>not</em> considered present by virtue of being wrapped by a
* container annotation. Operationally, this is equivalent to an
* annotation being present on an element if and only if it would be
* included in the results of {@link
* Elements#getAllAnnotationMirrors(Element)} called on that element. Since
* annotations inside container annotations are not considered
* present, to properly process {@linkplain
* java.lang.annotation.Repeatable repeatable annotation interfaces},
* processors are advised to include both the repeatable annotation
* interface and its containing annotation interface in the set of {@linkplain
* #getSupportedAnnotationTypes() supported annotation interfaces} of a
* processor.
*
* <p>Note that if a processor supports {@code "*"} and returns {@code
* true}, all annotations are claimed. Therefore, a universal
* processor being used to, for example, implement additional validity
* checks should return {@code false} so as to not prevent other such
* checkers from being able to run.
*
* <p>If a processor throws an uncaught exception, the tool may cease
* other active annotation processors. If a processor raises an
* error, the current round will run to completion and the subsequent
* round will indicate an {@linkplain RoundEnvironment#errorRaised
* error was raised}. Since annotation processors are run in a
* cooperative environment, a processor should throw an uncaught
* exception only in situations where no error recovery or reporting
* is feasible.
*
* <p>The tool environment is not required to support annotation
* processors that access environmental resources, either {@linkplain
* RoundEnvironment per round} or {@linkplain ProcessingEnvironment
* cross-round}, in a multi-threaded fashion.
*
* <p>If the methods that return configuration information about the
* annotation processor return {@code null}, return other invalid
* input, or throw an exception, the tool infrastructure must treat
* this as an error condition.
*
* <p>To be robust when running in different tool implementations, an
* annotation processor should have the following properties:
*
* <ol>
*
* <li>The result of processing a given input is not a function of the presence or absence
* of other inputs (orthogonality).
*
* <li>Processing the same input produces the same output (consistency).
*
* <li>Processing input <i>A</i> followed by processing input <i>B</i>
* is equivalent to processing <i>B</i> then <i>A</i>
* (commutativity)
*
* <li>Processing an input does not rely on the presence of the output
* of other annotation processors (independence)
*
* </ol>
*
* <p>The {@link Filer} interface discusses restrictions on how
* processors can operate on files.
*
* @apiNote Implementors of this interface may find it convenient
* to extend {@link AbstractProcessor} rather than implementing this
* interface directly.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface Processor {
/**
* Returns the options recognized by this processor. An
* implementation of the processing tool must provide a way to
* pass processor-specific options distinctly from options passed
* to the tool itself, see {@link ProcessingEnvironment#getOptions
* getOptions}.
*
* <p>Each string returned in the set must be a period separated
* sequence of {@linkplain
* jdkx.lang.model.SourceVersion#isIdentifier identifiers}:
*
* <blockquote>
* <dl>
* <dt><i>SupportedOptionString:</i>
* <dd><i>Identifiers</i>
*
* <dt><i>Identifiers:</i>
* <dd> <i>Identifier</i>
* <dd> <i>Identifier</i> {@code .} <i>Identifiers</i>
*
* <dt><i>Identifier:</i>
* <dd>Syntactic identifier, including keywords and literals
* </dl>
* </blockquote>
*
* <p> A tool might use this information to determine if any
* options provided by a user are unrecognized by any processor,
* in which case it may wish to report a warning.
*
* @return the options recognized by this processor or an
* empty set if none
* @see jdkx.annotation.processing.SupportedOptions
*/
Set<String> getSupportedOptions();
/**
* Returns the names of the annotation interfaces supported by this
* processor. An element of the result may be the canonical
* (fully qualified) name of a supported annotation interface.
* Alternately it may be of the form &quot;<code><i>name</i>.*</code>&quot;
* representing the set of all annotation interfaces with canonical
* names beginning with &quot;<code><i>name.</i></code>&quot;.
*
* In either of those cases, the name of the annotation interface can
* be optionally preceded by a module name followed by a {@code
* "/"} character. For example, if a processor supports {@code
* "a.B"}, this can include multiple annotation interfaces named {@code
* a.B} which reside in different modules. To only support {@code
* a.B} in the {@code foo} module, instead use {@code "foo/a.B"}.
*
* If a module name is included, only an annotation in that module
* is matched. In particular, if a module name is given in an
* environment where modules are not supported, such as an
* annotation processing environment configured for a {@linkplain
* jdkx.annotation.processing.ProcessingEnvironment#getSourceVersion
* source version} without modules, then the annotation interfaces with
* a module name do <em>not</em> match.
*
* Finally, {@code "*"} by itself represents the set of all
* annotation interfaces, including the empty set. Note that a
* processor should not claim {@code "*"} unless it is actually
* processing all files; claiming unnecessary annotations may
* cause a performance slowdown in some environments.
*
* <p>Each string returned in the set must be accepted by the
* following grammar:
*
* <blockquote>
* <dl>
* <dt><i>SupportedAnnotationTypeString:</i>
* <dd><i>ModulePrefix</i><sub><i>opt</i></sub> <i>TypeName</i> <i>DotStar</i><sub><i>opt</i></sub>
* <dd><code>*</code>
*
* <dt><i>ModulePrefix:</i>
* <dd><i>ModuleName</i> <code>/</code>
*
* <dt><i>DotStar:</i>
* <dd><code>.</code> <code>*</code>
* </dl>
* </blockquote>
*
* where <i>TypeName</i> and <i>ModuleName</i> are as defined in
* <cite>The Java Language Specification</cite>
* ({@jls 6.5 Determining the Meaning of a Name}).
*
* @apiNote When running in an environment which supports modules,
* processors are encouraged to include the module prefix when
* describing their supported annotation interfaces. The method {@link
* AbstractProcessor#getSupportedAnnotationTypes
* AbstractProcessor.getSupportedAnnotationTypes} provides support
* for stripping off the module prefix when running in an
* environment without modules.
*
* @return the names of the annotation interfaces supported by this processor
* or an empty set if none
* @see jdkx.annotation.processing.SupportedAnnotationTypes
* @jls 3.8 Identifiers
*/
Set<String> getSupportedAnnotationTypes();
/**
* {@return the latest source version supported by this annotation
* processor}
*
* @see jdkx.annotation.processing.SupportedSourceVersion
* @see ProcessingEnvironment#getSourceVersion
*/
SourceVersion getSupportedSourceVersion();
/**
* Initializes the processor with the processing environment.
*
* @param processingEnv environment for facilities the tool framework
* provides to the processor
*/
void init(ProcessingEnvironment processingEnv);
/**
* Processes a set of annotation interfaces on type elements
* originating from the prior round and returns whether or not
* these annotation interfaces are claimed by this processor. If {@code
* true} is returned, the annotation interfaces are claimed and subsequent
* processors will not be asked to process them; if {@code false}
* is returned, the annotation interfaces are unclaimed and subsequent
* processors may be asked to process them. A processor may
* always return the same boolean value or may vary the result
* based on its own chosen criteria.
*
* <p>The input set will be empty if the processor supports {@code
* "*"} and the root elements have no annotations. A {@code
* Processor} must gracefully handle an empty set of annotations.
*
* @param annotations the annotation interfaces requested to be processed
* @param roundEnv environment for information about the current and prior round
* @return whether or not the set of annotation interfaces are claimed by this processor
*/
boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv);
/**
* Returns to the tool infrastructure an iterable of suggested
* completions to an annotation. Since completions are being asked
* for, the information provided about the annotation may be
* incomplete, as if for a source code fragment. A processor may
* return an empty iterable. Annotation processors should focus
* their efforts on providing completions for annotation members
* with additional validity constraints known to the processor, for
* example an {@code int} member whose value should lie between 1
* and 10 or a string member that should be recognized by a known
* grammar, such as a regular expression or a URL.
*
* <p>Since incomplete programs are being modeled, some of the
* parameters may only have partial information or may be {@code
* null}. At least one of {@code element} and {@code userText}
* must be non-{@code null}. If {@code element} is non-{@code null},
* {@code annotation} and {@code member} may be {@code
* null}. Processors may not throw a {@code NullPointerException}
* if some parameters are {@code null}; if a processor has no
* completions to offer based on the provided information, an
* empty iterable can be returned. The processor may also return
* a single completion with an empty value string and a message
* describing why there are no completions.
*
* <p>Completions are informative and may reflect additional
* validity checks performed by annotation processors. For
* example, consider the simple annotation:
*
* <blockquote>
* <pre>
* &#064;MersennePrime {
* int value();
* }
* </pre>
* </blockquote>
*
* (A Mersenne prime is prime number of the form
* 2<sup><i>n</i></sup> - 1.) Given an {@code AnnotationMirror}
* for this annotation interface, a list of all such primes in the
* {@code int} range could be returned without examining any other
* arguments to {@code getCompletions}:
*
* <blockquote>
* <pre>
* import static jdkx.annotation.processing.Completions.*;
* ...
* return List.of({@link Completions#of(String) of}(&quot;3&quot;),
* of(&quot;7&quot;),
* of(&quot;31&quot;),
* of(&quot;127&quot;),
* of(&quot;8191&quot;),
* of(&quot;131071&quot;),
* of(&quot;524287&quot;),
* of(&quot;2147483647&quot;));
* </pre>
* </blockquote>
*
* A more informative set of completions would include the number
* of each prime:
*
* <blockquote>
* <pre>
* return List.of({@link Completions#of(String, String) of}(&quot;3&quot;, &quot;M2&quot;),
* of(&quot;7&quot;, &quot;M3&quot;),
* of(&quot;31&quot;, &quot;M5&quot;),
* of(&quot;127&quot;, &quot;M7&quot;),
* of(&quot;8191&quot;, &quot;M13&quot;),
* of(&quot;131071&quot;, &quot;M17&quot;),
* of(&quot;524287&quot;, &quot;M19&quot;),
* of(&quot;2147483647&quot;, &quot;M31&quot;));
* </pre>
* </blockquote>
*
* However, if the {@code userText} is available, it can be checked
* to see if only a subset of the Mersenne primes are valid. For
* example, if the user has typed
*
* <blockquote>
* <code>
* &#064;MersennePrime(1
* </code>
* </blockquote>
*
* the value of {@code userText} will be {@code "1"}; and only
* two of the primes are possible completions:
*
* <blockquote>
* <pre>
* return Arrays.asList(of(&quot;127&quot;, &quot;M7&quot;),
* of(&quot;131071&quot;, &quot;M17&quot;));
* </pre>
* </blockquote>
*
* Sometimes no valid completion is possible. For example, there
* is no in-range Mersenne prime starting with 9:
*
* <blockquote>
* <code>
* &#064;MersennePrime(9
* </code>
* </blockquote>
*
* An appropriate response in this case is to either return an
* empty list of completions,
*
* <blockquote>
* <pre>
* return Collections.emptyList();
* </pre>
* </blockquote>
*
* or a single empty completion with a helpful message
*
* <blockquote>
* <pre>
* return Arrays.asList(of(&quot;&quot;, &quot;No in-range Mersenne primes start with 9&quot;));
* </pre>
* </blockquote>
*
* @param element the element being annotated
* @param annotation the (perhaps partial) annotation being
* applied to the element
* @param member the annotation member to return possible completions for
* @param userText source code text to be completed
*
* @return suggested completions to the annotation
*/
Iterable<? extends Completion> getCompletions(Element element,
AnnotationMirror annotation,
ExecutableElement member,
String userText);
}
@@ -0,0 +1,209 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import jdkx.lang.model.element.Element;
import jdkx.lang.model.element.TypeElement;
import java.util.LinkedHashSet;
import java.util.Collections;
import java.util.Set;
import java.lang.annotation.Annotation;
/**
* An annotation processing tool framework will {@linkplain
* Processor#process provide an annotation processor with an object
* implementing this interface} so that the processor can query for
* information about a round of annotation processing.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface RoundEnvironment {
/**
* {@return {@code true} if types generated by this round will not
* be subject to a subsequent round of annotation processing;
* returns {@code false} otherwise}
*/
boolean processingOver();
/**
* {@return {@code true} if an error was raised in the prior round
* of processing; returns {@code false} otherwise}
*/
boolean errorRaised();
/**
* Returns the {@linkplain Processor root elements} for annotation processing generated
* by the prior round.
*
* @return the root elements for annotation processing generated
* by the prior round, or an empty set if there were none
*/
Set<? extends Element> getRootElements();
/**
* Returns the elements annotated with the given annotation type.
* The annotation may appear directly or be inherited. Only
* package elements, module elements, and type elements <i>included</i> in this
* round of annotation processing, or declarations of members,
* constructors, parameters, type parameters, or record components
* declared within those, are returned. Included type elements are {@linkplain
* #getRootElements root types} and any member types nested within
* them. Elements of a package are not considered included simply
* because a {@code package-info} file for that package was
* created.
* Likewise, elements of a module are not considered included
* simply because a {@code module-info} file for that module was
* created.
*
* @param a annotation type being requested
* @return the elements annotated with the given annotation type,
* or an empty set if there are none
* @throws IllegalArgumentException if the argument does not
* represent an annotation type
*/
Set<? extends Element> getElementsAnnotatedWith(TypeElement a);
/**
* Returns the elements annotated with one or more of the given
* annotation types.
*
* @apiNote This method may be useful when processing repeating
* annotations by looking for an annotation type and its
* containing annotation type at the same time.
*
* @implSpec The default implementation of this method creates an
* empty result set, iterates over the annotations in the argument
* array calling {@link #getElementsAnnotatedWith(TypeElement)} on
* each annotation and adding those results to the result
* set. Finally, the contents of the result set are returned as an
* unmodifiable set.
*
* @param annotations annotation types being requested
* @return the elements annotated with one or more of the given
* annotation types, or an empty set if there are none
* @throws IllegalArgumentException if the any elements of the
* argument set do not represent an annotation type
* @jls 9.6.3 Repeatable Annotation Interfaces
* @since 9
*/
default Set<? extends Element> getElementsAnnotatedWithAny(TypeElement... annotations){
// Use LinkedHashSet rather than HashSet for predictability
Set<Element> result = new LinkedHashSet<>();
for (TypeElement annotation : annotations) {
result.addAll(getElementsAnnotatedWith(annotation));
}
return Collections.unmodifiableSet(result);
}
/**
* Returns the elements annotated with the given annotation type.
* The annotation may appear directly or be inherited. Only
* package elements, module elements, and type elements <i>included</i> in this
* round of annotation processing, or declarations of members,
* constructors, parameters, type parameters, or record components
* declared within those, are returned. Included type elements are {@linkplain
* #getRootElements root types} and any member types nested within
* them. Elements in a package are not considered included simply
* because a {@code package-info} file for that package was
* created.
* Likewise, elements of a module are not considered included
* simply because a {@code module-info} file for that module was
* created.
*
* <p> Note: An implementation of this method typically performs
* an internal conversion from the runtime reflective
* representation of an annotation type as a {@code Class} object
* to a different representation used for annotation
* processing. The set of annotation types present in the runtime
* context may differ from the set of annotation types present in
* the context of annotation processing in a particular
* environmental configuration. If an runtime annotation type is
* not present in the annotation processing context, the situation
* is not treated as an error and no elements are found for that
* annotation type.
*
* @param a annotation type being requested
* @return the elements annotated with the given annotation type,
* or an empty set if there are none
* @throws IllegalArgumentException if the argument does not
* represent an annotation type
*
* @see jdkx.lang.model.AnnotatedConstruct#getAnnotation(Class)
* @see jdkx.lang.model.AnnotatedConstruct#getAnnotationsByType(Class)
*/
Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a);
/**
* Returns the elements annotated with one or more of the given
* annotation types.
*
* <p> Note: An implementation of this method typically performs
* an internal conversion from the runtime reflective
* representation of an annotation type as a {@code Class} object
* to a different representation used for annotation
* processing. The set of annotation types present in the runtime
* context may differ from the set of annotation types present in
* the context of annotation processing in a particular
* environmental configuration. If an runtime annotation type is
* not present in the annotation processing context, the situation
* is not treated as an error and no elements are found for that
* annotation type.
*
* @apiNote This method may be useful when processing repeating
* annotations by looking for an annotation type and its
* containing annotation type at the same time.
*
* @implSpec The default implementation of this method creates an
* empty result set, iterates over the annotations in the argument
* set calling {@link #getElementsAnnotatedWith(Class)} on
* each annotation and adding those results to the result
* set. Finally, the contents of the result set are returned as an
* unmodifiable set.
*
* @param annotations annotation types being requested
* @return the elements annotated with one or more of the given
* annotation types, or an empty set if there are none
* @throws IllegalArgumentException if the any elements of the
* argument set do not represent an annotation type
* @jls 9.6.3 Repeatable Annotation Interfaces
*
* @see jdkx.lang.model.AnnotatedConstruct#getAnnotation(Class)
* @see jdkx.lang.model.AnnotatedConstruct#getAnnotationsByType(Class)
*
* @since 9
*/
default Set<? extends Element> getElementsAnnotatedWithAny(Set<Class<? extends Annotation>> annotations){
// Use LinkedHashSet rather than HashSet for predictability
Set<Element> result = new LinkedHashSet<>();
for (Class<? extends Annotation> annotation : annotations) {
result.addAll(getElementsAnnotatedWith(annotation));
}
return Collections.unmodifiableSet(result);
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;
/**
* An annotation used to indicate what annotation interfaces an
* annotation processor supports. The {@link
* Processor#getSupportedAnnotationTypes} method can construct its
* result from the value of this annotation, as done by {@link
* AbstractProcessor#getSupportedAnnotationTypes}. Only {@linkplain
* Processor#getSupportedAnnotationTypes strings conforming to the
* grammar} should be used as values.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface SupportedAnnotationTypes {
/**
* {@return the names of the supported annotation interfaces}
*/
String [] value();
}
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;
/**
* An annotation used to indicate what options an annotation processor
* supports. The {@link Processor#getSupportedOptions} method can
* construct its result from the value of this annotation, as done by
* {@link AbstractProcessor#getSupportedOptions}. Only {@linkplain
* Processor#getSupportedOptions strings conforming to the
* grammar} should be used as values.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface SupportedOptions {
/**
* {@return the supported options}
*/
String [] value();
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.annotation.processing;
import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;
import jdkx.lang.model.SourceVersion;
/**
* An annotation used to indicate the latest source version an
* annotation processor supports. The {@link
* Processor#getSupportedSourceVersion} method can construct its
* result from the value of this annotation, as done by {@link
* AbstractProcessor#getSupportedSourceVersion}.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface SupportedSourceVersion {
/**
* {@return the latest supported source version}
*/
SourceVersion value();
}
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2005, 2006, 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.
*/
/**
* Facilities for declaring annotation processors and for
* allowing annotation processors to communicate with an annotation processing
* tool environment.
*
* <p> Unless otherwise specified in a particular implementation, the
* collections returned by methods in this package should be expected
* to be unmodifiable by the caller and unsafe for concurrent access.
*
* <p> Unless otherwise specified, methods in this package will throw
* a {@code NullPointerException} if given a {@code null} argument.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
package jdkx.annotation.processing;
@@ -0,0 +1,251 @@
/*
* Copyright (c) 2013, 2020, 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 jdkx.lang.model;
import java.lang.annotation.*;
import java.util.List;
import jdkx.lang.model.element.*;
import jdkx.lang.model.type.*;
/**
* Represents a construct that can be annotated.
*
* A construct is either an {@linkplain
* jdkx.lang.model.element.Element element} or a {@linkplain
* jdkx.lang.model.type.TypeMirror type}. Annotations on an element
* are on a <em>declaration</em>, whereas annotations on a type are on
* a specific <em>use</em> of a type name.
*
* As defined by <cite>The Java Language Specification</cite>
* section {@jls 9.7.4}, an annotation on an element is a
* <em>declaration annotation</em> and an annotation on a type is a
* <em>type annotation</em>.
*
* The terms <em>directly present</em>, <em>present</em>,
* <em>indirectly present</em>, and <em>associated </em> are used
* throughout this interface to describe precisely which annotations,
* either declaration annotations or type annotations, are returned by
* the methods in this interface.
*
* <p>In the definitions below, an annotation <i>A</i> has an
* annotation type <i>AT</i>. If <i>AT</i> is a repeatable annotation
* type, the type of the containing annotation is <i>ATC</i>.
*
* <p>Annotation <i>A</i> is <em>directly present</em> on a construct
* <i>C</i> if either:
*
* <ul>
*
* <li><i>A</i> is {@linkplain
* jdkx.lang.model.util.Elements#getOrigin(AnnotatedConstruct,
* AnnotationMirror) explicitly or implicitly}
* declared as applying to
* the source code representation of <i>C</i>.
*
* <p>Typically, if exactly one annotation of type <i>AT</i> appears in
* the source code of representation of <i>C</i>, then <i>A</i> is
* explicitly declared as applying to <i>C</i>.
*
* An annotation of type <i>AT</i> on a {@linkplain
* RecordComponentElement record component} can be implicitly propagated
* down to affiliated mandated members. Type annotations modifying the
* type of a record component can be also propagated to mandated
* members. Propagation of the annotations to mandated members is
* governed by rules given in the <cite>The Java Language
* Specification</cite>.
*
* If there are multiple annotations of type <i>AT</i> present on
* <i>C</i>, then if <i>AT</i> is repeatable annotation type, an
* annotation of type <i>ATC</i> is {@linkplain jdkx.lang.model.util.Elements#getOrigin(AnnotatedConstruct, AnnotationMirror) implicitly declared} on <i>C</i>.
* <li> A representation of <i>A</i> appears in the executable output
* for <i>C</i>, such as the {@code RuntimeVisibleAnnotations} (JVMS {@jvms 4.7.16}) or
* {@code RuntimeVisibleParameterAnnotations} (JVMS {@jvms 4.7.17}) attributes of a class
* file.
*
* </ul>
*
* <p>An annotation <i>A</i> is <em>present</em> on a
* construct <i>C</i> if either:
* <ul>
*
* <li><i>A</i> is directly present on <i>C</i>.
*
* <li>No annotation of type <i>AT</i> is directly present on
* <i>C</i>, and <i>C</i> is a class and <i>AT</i> is inheritable
* and <i>A</i> is present on the superclass of <i>C</i>.
*
* </ul>
*
* An annotation <i>A</i> is <em>indirectly present</em> on a construct
* <i>C</i> if both:
*
* <ul>
*
* <li><i>AT</i> is a repeatable annotation type with a containing
* annotation type <i>ATC</i>.
*
* <li>An annotation of type <i>ATC</i> is directly present on
* <i>C</i> and <i>A</i> is an annotation included in the result of
* calling the {@code value} method of the directly present annotation
* of type <i>ATC</i>.
*
* </ul>
*
* An annotation <i>A</i> is <em>associated</em> with a construct
* <i>C</i> if either:
*
* <ul>
*
* <li> <i>A</i> is directly or indirectly present on <i>C</i>.
*
* <li> No annotation of type <i>AT</i> is directly or indirectly
* present on <i>C</i>, and <i>C</i> is a class, and <i>AT</i> is
* inheritable, and <i>A</i> is associated with the superclass of
* <i>C</i>.
*
* </ul>
*
* @since 1.8
* @jls 9.6 Annotation Interfaces
* @jls 9.6.4.3 {@code @Inherited}
* @jls 9.7.4 Where Annotations May Appear
* @jls 9.7.5 Multiple Annotations of the Same Interface
*/
public interface AnnotatedConstruct {
/**
* Returns the annotations that are <em>directly present</em> on
* this construct.
*
* @return the annotations <em>directly present</em> on this
* construct; an empty list if there are none
*/
List<? extends AnnotationMirror> getAnnotationMirrors();
/**
* {@return this construct's annotation of the specified type if
* such an annotation is <em>present</em>, else {@code null}}
*
* <p> The annotation returned by this method could contain an element
* whose value is of type {@code Class}.
* This value cannot be returned directly: information necessary to
* locate and load a class (such as the class loader to use) is
* not available, and the class might not be loadable at all.
* Attempting to read a {@code Class} object by invoking the relevant
* method on the returned annotation
* will result in a {@link MirroredTypeException},
* from which the corresponding {@link TypeMirror} may be extracted.
* Similarly, attempting to read a {@code Class[]}-valued element
* will result in a {@link MirroredTypesException}.
*
* <blockquote>
* <i>Note:</i> This method is unlike others in this and related
* interfaces. It operates on runtime reflective information &mdash;
* representations of annotation types currently loaded into the
* VM &mdash; rather than on the representations defined by and used
* throughout these interfaces. Consequently, calling methods on
* the returned annotation object can throw many of the exceptions
* that can be thrown when calling methods on an annotation object
* returned by core reflection. This method is intended for
* callers that are written to operate on a known, fixed set of
* annotation types.
* </blockquote>
*
* @param <A> the annotation type
* @param annotationType the {@code Class} object corresponding to
* the annotation type
*
* @see #getAnnotationMirrors()
* @see java.lang.reflect.AnnotatedElement#getAnnotation
* @see EnumConstantNotPresentException
* @see AnnotationTypeMismatchException
* @see IncompleteAnnotationException
* @see MirroredTypeException
* @see MirroredTypesException
* @jls 9.6.1 Annotation Interface Elements
*/
<A extends Annotation> A getAnnotation(Class<A> annotationType);
/**
* Returns annotations that are <em>associated</em> with this construct.
*
* If there are no annotations associated with this construct, the
* return value is an array of length 0.
*
* The order of annotations which are directly or indirectly
* present on a construct <i>C</i> is computed as if indirectly present
* annotations on <i>C</i> are directly present on <i>C</i> in place of their
* container annotation, in the order in which they appear in the
* value element of the container annotation.
*
* The difference between this method and {@link #getAnnotation(Class)}
* is that this method detects if its argument is a <em>repeatable
* annotation type</em>, and if so, attempts to find one or more
* annotations of that type by "looking through" a container annotation.
*
* <p> The annotations returned by this method could contain an element
* whose value is of type {@code Class}.
* This value cannot be returned directly: information necessary to
* locate and load a class (such as the class loader to use) is
* not available, and the class might not be loadable at all.
* Attempting to read a {@code Class} object by invoking the relevant
* method on the returned annotation
* will result in a {@link MirroredTypeException},
* from which the corresponding {@link TypeMirror} may be extracted.
* Similarly, attempting to read a {@code Class[]}-valued element
* will result in a {@link MirroredTypesException}.
*
* <blockquote>
* <i>Note:</i> This method is unlike others in this and related
* interfaces. It operates on runtime reflective information &mdash;
* representations of annotation types currently loaded into the
* VM &mdash; rather than on the representations defined by and used
* throughout these interfaces. Consequently, calling methods on
* the returned annotation object can throw many of the exceptions
* that can be thrown when calling methods on an annotation object
* returned by core reflection. This method is intended for
* callers that are written to operate on a known, fixed set of
* annotation types.
* </blockquote>
*
* @param <A> the annotation type
* @param annotationType the {@code Class} object corresponding to
* the annotation type
* @return this construct's annotations for the specified annotation
* type if present on this construct, else an empty array
*
* @see #getAnnotationMirrors()
* @see #getAnnotation(Class)
* @see java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)
* @see EnumConstantNotPresentException
* @see AnnotationTypeMismatchException
* @see IncompleteAnnotationException
* @see MirroredTypeException
* @see MirroredTypesException
* @jls 9.6 Annotation Interfaces
* @jls 9.6.1 Annotation Interface Elements
*/
<A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType);
}
@@ -0,0 +1,492 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model;
import java.util.Collections;
import java.util.Set;
import java.util.HashSet;
import com.itsaky.androidide.config.JavacConfigProvider;
/**
* Source versions of the Java programming language.
*
* See the appropriate edition of
* <cite>The Java Language Specification</cite>
* for information about a particular source version.
*
* <p>Note that additional source version constants will be added to
* model future releases of the language.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public enum SourceVersion {
/*
* Summary of language evolution
* 1.1: nested classes
* 1.2: strictfp
* 1.3: no changes
* 1.4: assert
* 1.5: annotations, generics, autoboxing, var-args...
* 1.6: no changes
* 1.7: diamond syntax, try-with-resources, etc.
* 1.8: lambda expressions and default methods
* 9: modules, small cleanups to 1.7 and 1.8 changes
* 10: local-variable type inference (var)
* 11: local-variable syntax for lambda parameters
* 12: no changes (switch expressions in preview)
* 13: no changes (switch expressions and text blocks in preview)
* 14: switch expressions (pattern matching and records in
* preview, text blocks in preview again)
* 15: text blocks (records and pattern matching in preview again)
*/
/**
* The original version.
*
* The language described in
* <cite>The Java Language Specification, First Edition</cite>.
*/
RELEASE_0,
/**
* The version recognized by the Java Platform 1.1.
*
* The language is {@code RELEASE_0} augmented with nested classes as described in the 1.1 update to
* <cite>The Java Language Specification, First Edition</cite>.
*/
RELEASE_1,
/**
* The version recognized by the Java 2 Platform, Standard Edition,
* v 1.2.
*
* The language described in
* <cite>The Java Language Specification,
* Second Edition</cite>, which includes the {@code
* strictfp} modifier.
*/
RELEASE_2,
/**
* The version recognized by the Java 2 Platform, Standard Edition,
* v 1.3.
*
* No major changes from {@code RELEASE_2}.
*/
RELEASE_3,
/**
* The version recognized by the Java 2 Platform, Standard Edition,
* v 1.4.
*
* Added a simple assertion facility.
*/
RELEASE_4,
/**
* The version recognized by the Java 2 Platform, Standard
* Edition 5.0.
*
* The language described in
* <cite>The Java Language Specification,
* Third Edition</cite>. First release to support
* generics, annotations, autoboxing, var-args, enhanced {@code
* for} loop, and hexadecimal floating-point literals.
*/
RELEASE_5,
/**
* The version recognized by the Java Platform, Standard Edition
* 6.
*
* No major changes from {@code RELEASE_5}.
*/
RELEASE_6,
/**
* The version recognized by the Java Platform, Standard Edition
* 7.
*
* Additions in this release include, diamond syntax for
* constructors, {@code try}-with-resources, strings in switch,
* binary literals, and multi-catch.
* @since 1.7
*/
RELEASE_7,
/**
* The version recognized by the Java Platform, Standard Edition
* 8.
*
* Additions in this release include lambda expressions and default methods.
* @since 1.8
*/
RELEASE_8,
/**
* The version recognized by the Java Platform, Standard Edition
* 9.
*
* Additions in this release include modules and removal of a
* single underscore from the set of legal identifier names.
*
* @since 9
*/
RELEASE_9,
/**
* The version recognized by the Java Platform, Standard Edition
* 10.
*
* Additions in this release include local-variable type inference
* ({@code var}).
*
* @since 10
*/
RELEASE_10,
/**
* The version recognized by the Java Platform, Standard Edition
* 11.
*
* Additions in this release include local-variable syntax for
* lambda parameters.
*
* @since 11
*/
RELEASE_11,
/**
* The version recognized by the Java Platform, Standard Edition
* 12.
*
* @since 12
*/
RELEASE_12,
/**
* The version recognized by the Java Platform, Standard Edition
* 13.
*
* @since 13
*/
RELEASE_13,
/**
* The version recognized by the Java Platform, Standard Edition
* 14.
*
* Additions in this release include switch expressions.
*
* @since 14
*/
RELEASE_14,
/**
* The version recognized by the Java Platform, Standard Edition
* 15.
*
* Additions in this release include text blocks.
*
* @since 15
*/
RELEASE_15,
/**
* The version recognized by the Java Platform, Standard Edition
* 16.
*
* Additions in this release include pattern matching for {@code
* instanceof} and records.
*
* @since 16
*/
RELEASE_16,
/**
* The version recognized by the Java Platform, Standard Edition
* 17.
*
* Additions in this release include sealed classes and
* restoration of always-strict floating-point semantics.
*
* @since 17
*/
RELEASE_17;
// Note that when adding constants for newer releases, the
// behavior of latest() and latestSupported() must be updated too.
/**
* {@return the latest source version that can be modeled}
*/
public static SourceVersion latest() {
// AndroidIDE changed: Allow overriding latest source version.
return JavacConfigProvider.getLatestSourceVersion();
}
private static final SourceVersion latestSupported = getLatestSupported();
/*
* The integer version to enum constant mapping implemented by
* this method assumes the JEP 322: "Time-Based Release
* Versioning" scheme is in effect. This scheme began in JDK
* 10. If the JDK versioning scheme is revised, this method may
* need to be updated accordingly.
*/
private static SourceVersion getLatestSupported() {
// AndroidIDE changed: Allow overriding latest supported source version.
return JavacConfigProvider.getLatestSupportedSourceVersion();
}
/**
* {@return the latest source version fully supported by the
* current execution environment} {@code RELEASE_9} or later must
* be returned.
*
* @apiNote This method is included alongside {@link latest} to
* allow identification of situations where the language model API
* is running on a platform version different than the latest
* version modeled by the API. One way that sort of situation can
* occur is if an IDE or similar tool is using the API to model
* source version <i>N</i> while running on platform version
* (<i>N</i>&nbsp;-&nbsp;1). Running in this configuration is
* supported by the API. Running an API on platform versions
* earlier than (<i>N</i>&nbsp;-&nbsp;1) or later than <i>N</i>
* may or may not work as an implementation detail. If an
* annotation processor was generating code to run under the
* current execution environment, the processor should only use
* platform features up to the {@code latestSupported} release,
* which may be earlier than the {@code latest} release.
*/
public static SourceVersion latestSupported() {
// AndroidIDE changed: Allow overriding latest supported source version.
return JavacConfigProvider.getLatestSupportedSourceVersion();
}
/**
* Returns whether or not {@code name} is a syntactically valid
* identifier (simple name) or keyword in the latest source
* version. The method returns {@code true} if the name consists
* of an initial character for which {@link
* Character#isJavaIdentifierStart(int)} returns {@code true},
* followed only by characters for which {@link
* Character#isJavaIdentifierPart(int)} returns {@code true}.
* This pattern matches regular identifiers, keywords, restricted
* keywords, restricted identifiers and the literals {@code "true"},
* {@code "false"}, {@code "null"}.
*
* The method returns {@code false} for all other strings.
*
* @param name the string to check
* @return {@code true} if this string is a
* syntactically valid identifier or keyword, {@code false}
* otherwise.
*
* @jls 3.8 Identifiers
*/
public static boolean isIdentifier(CharSequence name) {
String id = name.toString();
if (id.length() == 0) {
return false;
}
int cp = id.codePointAt(0);
if (!Character.isJavaIdentifierStart(cp)) {
return false;
}
for (int i = Character.charCount(cp);
i < id.length();
i += Character.charCount(cp)) {
cp = id.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp)) {
return false;
}
}
return true;
}
/**
* Returns whether or not {@code name} is a syntactically valid
* qualified name in the latest source version.
*
* Syntactically, a qualified name is a sequence of identifiers
* separated by period characters ("{@code .}"). This method
* splits the input string into period-separated segments and
* applies checks to each segment in turn.
*
* Unlike {@link #isIdentifier isIdentifier}, this method returns
* {@code false} for keywords, boolean literals, and the null
* literal in any segment.
*
* This method returns {@code true} for <i>restricted
* keywords</i> and <i>restricted identifiers</i>.
*
* @param name the string to check
* @return {@code true} if this string is a
* syntactically valid name, {@code false} otherwise.
* @jls 3.9 Keywords
* @jls 6.2 Names and Identifiers
*/
public static boolean isName(CharSequence name) {
return isName(name, latest());
}
/**
* Returns whether or not {@code name} is a syntactically valid
* qualified name in the given source version.
*
* Syntactically, a qualified name is a sequence of identifiers
* separated by period characters ("{@code .}"). This method
* splits the input string into period-separated segments and
* applies checks to each segment in turn.
*
* Unlike {@link #isIdentifier isIdentifier}, this method returns
* {@code false} for keywords, boolean literals, and the null
* literal in any segment.
*
* This method returns {@code true} for <i>restricted
* keywords</i> and <i>restricted identifiers</i>.
*
* @param name the string to check
* @param version the version to use
* @return {@code true} if this string is a
* syntactically valid name, {@code false} otherwise.
* @jls 3.9 Keywords
* @jls 6.2 Names and Identifiers
* @since 9
*/
public static boolean isName(CharSequence name, SourceVersion version) {
String id = name.toString();
for(String s : id.split("\\.", -1)) {
if (!isIdentifier(s) || isKeyword(s, version))
return false;
}
return true;
}
/**
* Returns whether or not {@code s} is a keyword, boolean literal,
* or null literal in the latest source version.
* This method returns {@code false} for <i>restricted
* keywords</i> and <i>restricted identifiers</i>.
*
* @param s the string to check
* @return {@code true} if {@code s} is a keyword, or boolean
* literal, or null literal, {@code false} otherwise.
* @jls 3.9 Keywords
* @jls 3.10.3 Boolean Literals
* @jls 3.10.8 The Null Literal
*/
public static boolean isKeyword(CharSequence s) {
return isKeyword(s, latest());
}
/**
* Returns whether or not {@code s} is a keyword, boolean literal,
* or null literal in the given source version.
* This method returns {@code false} for <i>restricted
* keywords</i> and <i>restricted identifiers</i>.
*
* @param s the string to check
* @param version the version to use
* @return {@code true} if {@code s} is a keyword, or boolean
* literal, or null literal, {@code false} otherwise.
* @jls 3.9 Keywords
* @jls 3.10.3 Boolean Literals
* @jls 3.10.8 The Null Literal
* @since 9
*/
public static boolean isKeyword(CharSequence s, SourceVersion version) {
String id = s.toString();
switch(id) {
// A trip through history
case "strictfp":
return version.compareTo(RELEASE_2) >= 0;
case "assert":
return version.compareTo(RELEASE_4) >= 0;
case "enum":
return version.compareTo(RELEASE_5) >= 0;
case "_":
return version.compareTo(RELEASE_9) >= 0;
// case "non-sealed": can be added once it is a keyword only
// dependent on release and not also preview features being
// enabled.
// Keywords common across versions
// Modifiers
case "public": case "protected": case "private":
case "abstract": case "static": case "final":
case "transient": case "volatile": case "synchronized":
case "native":
// Declarations
case "class": case "interface": case "extends":
case "package": case "throws": case "implements":
// Primitive types and void
case "boolean": case "byte": case "char":
case "short": case "int": case "long":
case "float": case "double":
case "void":
// Control flow
case "if": case "else":
case "try": case "catch": case "finally":
case "do": case "while":
case "for": case "continue":
case "switch": case "case": case "default":
case "break": case "throw": case "return":
// Other keywords
case "this": case "new": case "super":
case "import": case "instanceof":
// Forbidden!
case "goto": case "const":
// literals
case "null": case "true": case "false":
return true;
default:
return false;
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2009, 2020, 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 jdkx.lang.model;
/**
* Superclass of exceptions which indicate that an unknown kind of
* entity was encountered. This situation can occur if the language
* evolves and new kinds of constructs are introduced. Subclasses of
* this exception may be thrown by visitors to indicate that the
* visitor was created for a prior version of the language.
*
* @author Joseph D. Darcy
* @see jdkx.lang.model.element.UnknownElementException
* @see jdkx.lang.model.element.UnknownAnnotationValueException
* @see jdkx.lang.model.type.UnknownTypeException
* @since 1.7
*/
public class UnknownEntityException extends RuntimeException {
private static final long serialVersionUID = 269L;
/**
* Creates a new {@code UnknownEntityException} with the specified
* detail message.
*
* @param message the detail message
*/
protected UnknownEntityException(String message) {
super(message);
}
}
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import java.util.Map;
import jdkx.lang.model.type.DeclaredType;
/**
* Represents an annotation. An annotation associates a value with
* each element of an annotation interface.
*
* <p> Annotations should be compared using the {@code equals}
* method. There is no guarantee that any particular annotation will
* always be represented by the same object.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface AnnotationMirror {
/**
* {@return the type of this annotation}
*/
DeclaredType getAnnotationType();
/**
* Returns the values of this annotation's elements.
* This is returned in the form of a map that associates elements
* with their corresponding values.
* Only those elements with values explicitly present in the
* annotation are included, not those that are implicitly assuming
* their default values.
* The order of the map matches the order in which the
* values appear in the annotation's source.
*
* <p>Note that an annotation mirror of a marker annotation type
* will by definition have an empty map.
*
* <p>To fill in default values, use {@link
* jdkx.lang.model.util.Elements#getElementValuesWithDefaults
* getElementValuesWithDefaults}.
*
* @return the values of this annotation's elements,
* or an empty map if there are none
*/
Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues();
}
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
/**
* Represents a value of an annotation interface element.
* A value is of one of the following types:
* <ul><li> a wrapper class (such as {@link Integer}) for a primitive type
* <li> {@code String}
* <li> {@code TypeMirror}
* <li> {@code VariableElement} (representing an enum constant)
* <li> {@code AnnotationMirror}
* <li> {@code List<? extends AnnotationValue>}
* (representing the elements, in declared order, if the value is an array)
* </ul>
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface AnnotationValue {
/**
* {@return the value}
*/
Object getValue();
/**
* {@return a string representation of this value}
* This is returned in a form suitable for representing this value
* in the source code of an annotation.
*/
String toString();
/**
* Applies a visitor to this value.
*
* @param <R> the return type of the visitor's methods
* @param <P> the type of the additional parameter to the visitor's methods
* @param v the visitor operating on this value
* @param p additional parameter to the visitor
* @return a visitor-specified result
*/
<R, P> R accept(AnnotationValueVisitor<R, P> v, P p);
}
@@ -0,0 +1,259 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import java.util.List;
import jdkx.lang.model.type.TypeMirror;
import jdkx.lang.model.util.*;
/**
* A visitor of the values of annotation interface elements, using a
* variant of the visitor design pattern. Unlike a standard visitor
* which dispatches based on the concrete type of a member of a type
* hierarchy, this visitor dispatches based on the type of data
* stored; there are no distinct subclasses for storing, for example,
* {@code boolean} values versus {@code int} values. Classes
* implementing this interface are used to operate on a value when the
* type of that value is unknown at compile time. When a visitor is
* passed to a value's {@link AnnotationValue#accept accept} method,
* the <code>visit<i>Xyz</i></code> method applicable to that value is
* invoked.
*
* <p> Classes implementing this interface may or may not throw a
* {@code NullPointerException} if the additional parameter {@code p}
* is {@code null}; see documentation of the implementing class for
* details.
*
* @apiNote
* <strong>WARNING:</strong> It is possible that methods will be added
* to this interface to accommodate new, currently unknown, language
* structures added to future versions of the Java programming
* language.
*
* Such additions have already occurred in another visitor interface in
* this package to support language features added after this API was
* introduced.
*
* Visitor classes directly implementing this interface may be source
* incompatible with future versions of the platform. To avoid this
* source incompatibility, visitor implementations are encouraged to
* instead extend the appropriate abstract visitor class that
* implements this interface. However, an API should generally use
* this visitor interface as the type for parameters, return type,
* etc. rather than one of the abstract classes.
*
* <p>Methods to accommodate new language constructs are expected to
* be added as default methods to provide strong source compatibility,
* as done for {@link ElementVisitor#visitModule visitModule} in
* {@code ElementVisitor}. The implementations of the default methods
* in this interface will in turn call {@link visitUnknown
* visitUnknown}, behavior that will be overridden in concrete
* visitors supporting the source version with the new language
* construct.
*
* <p>There are several families of classes implementing this visitor
* interface in the {@linkplain jdkx.lang.model.util util
* package}. The families follow a naming pattern along the lines of
* {@code FooVisitor}<i>N</i> where <i>N</i> indicates the
* {@linkplain jdkx.lang.model.SourceVersion source version} the
* visitor is appropriate for.
*
* In particular, a {@code FooVisitor}<i>N</i> is expected to handle
* all language constructs present in source version <i>N</i>. If
* there are no new language constructs added in version
* <i>N</i>&nbsp;+&nbsp;1 (or subsequent releases), {@code
* FooVisitor}<i>N</i> may also handle that later source version; in
* that case, the {@link
* jdkx.annotation.processing.SupportedSourceVersion
* SupportedSourceVersion} annotation on the {@code
* FooVisitor}<i>N</i> class will indicate a later version.
*
* When visiting an annotation value representing a language construct
* introduced <strong>after</strong> source version <i>N</i>, a {@code
* FooVisitor}<i>N</i> will throw an {@link
* UnknownAnnotationValueException} unless that behavior is overridden.
*
* <p>When choosing which member of a visitor family to subclass,
* subclassing the most recent one increases the range of source
* versions covered. When choosing which visitor family to subclass,
* consider their built-in capabilities:
*
* <ul>
*
* <li>{@link AbstractAnnotationValueVisitor6
* AbstractAnnotationValueVisitor}s: Skeletal visitor implementations.
*
* <li>{@link SimpleAnnotationValueVisitor6
* SimpleAnnotationValueVisitor}s: Support default actions and a
* default return value.
*
* </ul>
*
* @param <R> the return type of this visitor's methods
* @param <P> the type of the additional parameter to this visitor's methods.
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface AnnotationValueVisitor<R, P> {
/**
* Visits an annotation value.
* @param av the value to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visit(AnnotationValue av, P p);
/**
* A convenience method equivalent to {@code visit(av, null)}.
*
* @implSpec The default implementation is {@code visit(av, null)}.
*
* @param av the value to visit
* @return a visitor-specified result
*/
default R visit(AnnotationValue av) {
return visit(av, null);
}
/**
* Visits a {@code boolean} value in an annotation.
* @param b the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitBoolean(boolean b, P p);
/**
* Visits a {@code byte} value in an annotation.
* @param b the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitByte(byte b, P p);
/**
* Visits a {@code char} value in an annotation.
* @param c the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitChar(char c, P p);
/**
* Visits a {@code double} value in an annotation.
* @param d the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitDouble(double d, P p);
/**
* Visits a {@code float} value in an annotation.
* @param f the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitFloat(float f, P p);
/**
* Visits an {@code int} value in an annotation.
* @param i the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitInt(int i, P p);
/**
* Visits a {@code long} value in an annotation.
* @param i the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitLong(long i, P p);
/**
* Visits a {@code short} value in an annotation.
* @param s the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitShort(short s, P p);
/**
* Visits a string value in an annotation.
* @param s the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitString(String s, P p);
/**
* Visits a type value in an annotation.
* @param t the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitType(TypeMirror t, P p);
/**
* Visits an {@code enum} value in an annotation.
* @param c the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitEnumConstant(VariableElement c, P p);
/**
* Visits an annotation value in an annotation.
* @param a the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitAnnotation(AnnotationMirror a, P p);
/**
* Visits an array value in an annotation.
* @param vals the value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
*/
R visitArray(List<? extends AnnotationValue> vals, P p);
/**
* Visits an unknown kind of annotation value.
* This can occur if the language evolves and new kinds
* of value can be stored in an annotation.
* @param av the unknown value being visited
* @param p a visitor-specified parameter
* @return the result of the visit
* @throws UnknownAnnotationValueException
* a visitor implementation may optionally throw this exception
*/
R visitUnknown(AnnotationValue av, P p);
}
@@ -0,0 +1,310 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import java.lang.annotation.Annotation;
import java.lang.annotation.AnnotationTypeMismatchException;
import java.lang.annotation.IncompleteAnnotationException;
import java.util.List;
import java.util.Set;
import jdkx.lang.model.type.*;
import jdkx.lang.model.util.*;
/**
* Represents a program element such as a module, package, class, or method.
* Each element represents a compile-time language-level construct
* (and not, for example, a runtime construct of the virtual machine).
*
* <p> Elements should be compared using the {@link #equals(Object)}
* method. There is no guarantee that any particular element will
* always be represented by the same object.
*
* <p> To implement operations based on the class of an {@code
* Element} object, either use a {@linkplain ElementVisitor visitor} or
* use the result of the {@link #getKind} method. Using {@code
* instanceof} is <em>not</em> necessarily a reliable idiom for
* determining the effective class of an object in this modeling
* hierarchy since an implementation may choose to have a single object
* implement multiple {@code Element} subinterfaces.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see Elements
* @see TypeMirror
* @since 1.6
*/
public interface Element extends jdkx.lang.model.AnnotatedConstruct {
/**
* {@return the type defined by this element}
*
* @see Types
* @see ExecutableElement#asType
* @see ModuleElement#asType
* @see PackageElement#asType
* @see TypeElement#asType
* @see TypeParameterElement#asType
* @see VariableElement#asType
*/
TypeMirror asType();
/**
* {@return the {@code kind} of this element}
*
* <ul>
*
* <li> The kind of a {@linkplain PackageElement package} is
* {@link ElementKind#PACKAGE PACKAGE}.
*
* <li> The kind of a {@linkplain ModuleElement module} is {@link
* ElementKind#MODULE MODULE}.
*
* <li> The kind of a {@linkplain TypeElement type element} is one
* of {@link ElementKind#ANNOTATION_TYPE ANNOTATION_TYPE}, {@link
* ElementKind#CLASS CLASS}, {@link ElementKind#ENUM ENUM}, {@link
* ElementKind#INTERFACE INTERFACE}, or {@link ElementKind#RECORD
* RECORD}.
*
* <li> The kind of a {@linkplain VariableElement variable} is one
* of {@link ElementKind#ENUM_CONSTANT ENUM_CONSTANT}, {@link
* ElementKind#EXCEPTION_PARAMETER EXCEPTION_PARAMETER}, {@link
* ElementKind#FIELD FIELD}, {@link ElementKind#LOCAL_VARIABLE
* LOCAL_VARIABLE}, {@link ElementKind#PARAMETER PARAMETER},
* {@link ElementKind#RESOURCE_VARIABLE RESOURCE_VARIABLE}, or
* {@link ElementKind#BINDING_VARIABLE BINDING_VARIABLE}.
*
* <li> The kind of an {@linkplain ExecutableElement executable}
* is one of {@link ElementKind#CONSTRUCTOR CONSTRUCTOR}, {@link
* ElementKind#INSTANCE_INIT INSTANCE_INIT}, {@link
* ElementKind#METHOD METHOD}, or {@link ElementKind#STATIC_INIT
* STATIC_INIT}.
*
* <li> The kind of a {@linkplain TypeParameterElement type parameter} is
* {@link ElementKind#TYPE_PARAMETER TYPE_PARAMETER}.
*
* <li> The kind of a {@linkplain RecordComponentElement record
* component} is {@link ElementKind#RECORD_COMPONENT
* RECORD_COMPONENT}.
*
* </ul>
*/
ElementKind getKind();
/**
* Returns the modifiers of this element, excluding annotations.
* Implicit modifiers, such as the {@code public} and {@code static}
* modifiers of interface members, are included.
*
* @return the modifiers of this element, or an empty set if there are none
*/
Set<Modifier> getModifiers();
/**
* {@return the simple (unqualified) name of this element} The
* name of a generic class or interface does not include any
* reference to its formal type parameters.
*
* For example, the simple name of the type element representing
* {@code java.util.Set<E>} is {@code "Set"}.
*
* If this element represents an unnamed {@linkplain
* PackageElement#getSimpleName package} or unnamed {@linkplain
* ModuleElement#getSimpleName module}, an <a
* href=Name.html#empty_name>empty name</a> is returned.
*
* If it represents a {@linkplain ExecutableElement#getSimpleName
* constructor}, the name "{@code <init>}" is returned. If it
* represents a {@linkplain ExecutableElement#getSimpleName static
* initializer}, the name "{@code <clinit>}" is returned.
*
* If it represents an {@linkplain TypeElement#getSimpleName
* anonymous class} or {@linkplain ExecutableElement#getSimpleName
* instance initializer}, an <a href=Name.html#empty_name>empty
* name</a> is returned.
*
* @see PackageElement#getSimpleName
* @see ExecutableElement#getSimpleName
* @see TypeElement#getSimpleName
* @see VariableElement#getSimpleName
* @see ModuleElement#getSimpleName
* @see RecordComponentElement#getSimpleName
* @revised 9
*/
Name getSimpleName();
/**
* Returns the innermost element
* within which this element is, loosely speaking, enclosed.
* <ul>
* <li> If this element is one whose declaration is lexically enclosed
* immediately within the declaration of another element, that other
* element is returned.
*
* <li> If this is a {@linkplain TypeElement#getEnclosingElement
* top-level class or interface}, its package is returned.
*
* <li> If this is a {@linkplain
* PackageElement#getEnclosingElement package}, its module is
* returned if such a module exists. Otherwise, {@code null} is returned.
*
* <li> If this is a {@linkplain
* TypeParameterElement#getEnclosingElement type parameter},
* {@linkplain TypeParameterElement#getGenericElement the
* generic element} of the type parameter is returned.
*
* <li> If this is a {@linkplain
* VariableElement#getEnclosingElement method or constructor
* parameter}, {@linkplain ExecutableElement the executable
* element} which declares the parameter is returned.
*
* <li> If this is a {@linkplain
* RecordComponentElement#getEnclosingElement record component},
* {@linkplain TypeElement the record class} which declares the
* record component is returned.
*
* <li> If this is a {@linkplain ModuleElement#getEnclosingElement
* module}, {@code null} is returned.
*
* </ul>
*
* @return the enclosing element, or {@code null} if there is none
* @see Elements#getPackageOf
* @revised 9
*/
Element getEnclosingElement();
/**
* Returns the elements that are, loosely speaking, directly
* enclosed by this element.
*
* A {@linkplain TypeElement#getEnclosedElements class or
* interface} is considered to enclose the fields, methods,
* constructors, record components, and member classes and interfaces that it directly declares.
*
* A {@linkplain PackageElement#getEnclosedElements package}
* encloses the top-level classes and interfaces within it, but is
* not considered to enclose subpackages.
*
* A {@linkplain ModuleElement#getEnclosedElements module}
* encloses packages within it.
*
* Enclosed elements may include implicitly declared {@linkplain
* Elements.Origin#MANDATED mandated} elements.
*
* Other kinds of elements are not currently considered to enclose
* any elements; however, that may change as this API or the
* programming language evolves.
*
* @apiNote Elements of certain kinds can be isolated using
* methods in {@link ElementFilter}.
*
* @return the enclosed elements, or an empty list if none
* @see TypeElement#getEnclosedElements
* @see PackageElement#getEnclosedElements
* @see ModuleElement#getEnclosedElements
* @see Elements#getAllMembers
* @jls 8.8.9 Default Constructor
* @jls 8.9 Enum Classes
* @revised 9
*/
List<? extends Element> getEnclosedElements();
/**
* {@return {@code true} if the argument represents the same
* element as {@code this}, or {@code false} otherwise}
*
* @apiNote The identity of an element involves implicit state
* not directly accessible from the element's methods, including
* state about the presence of unrelated types. Element objects
* created by different implementations of these interfaces should
* <i>not</i> be expected to be equal even if &quot;the same&quot;
* element is being modeled; this is analogous to the inequality
* of {@code Class} objects for the same class file loaded through
* different class loaders.
*
* @param obj the object to be compared with this element
*/
@Override
boolean equals(Object obj);
/**
* Obeys the general contract of {@link Object#hashCode Object.hashCode}.
*
* @see #equals
*/
@Override
int hashCode();
/**
* {@inheritDoc}
*
* <p>To get inherited annotations as well, use {@link
* Elements#getAllAnnotationMirrors(Element)
* getAllAnnotationMirrors}.
*
* <p>Note that any annotations returned by this method are
* declaration annotations.
*
* @since 1.6
*/
@Override
List<? extends AnnotationMirror> getAnnotationMirrors();
/**
* {@inheritDoc}
*
* <p>Note that any annotation returned by this method is a
* declaration annotation.
*
* @since 1.6
*/
@Override
<A extends Annotation> A getAnnotation(Class<A> annotationType);
/**
* {@inheritDoc}
*
* <p>Note that any annotations returned by this method are
* declaration annotations.
*
* @since 8
*/
@Override
<A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType);
/**
* Applies a visitor to this element.
*
* @param <R> the return type of the visitor's methods
* @param <P> the type of the additional parameter to the visitor's methods
* @param v the visitor operating on this element
* @param p additional parameter to the visitor
* @return a visitor-specified result
*/
<R, P> R accept(ElementVisitor<R, P> v, P p);
}
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
/**
* The {@code kind} of an element.
*
* <p>Note that it is possible additional element kinds will be added
* to accommodate new, currently unknown, language structures added to
* future versions of the Java programming language.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see Element
* @since 1.6
*/
public enum ElementKind {
/** A package. */
PACKAGE,
// Declared types
/** An enum class. */
ENUM,
/**
* A class not described by a more specific kind (like {@code
* ENUM} or {@code RECORD}).
*/
CLASS,
/** An annotation interface. (Formerly known as an annotation type.) */
ANNOTATION_TYPE,
/**
* An interface not described by a more specific kind (like
* {@code ANNOTATION_TYPE}).
*/
INTERFACE,
// Variables
/** An enum constant. */
ENUM_CONSTANT,
/**
* A field not described by a more specific kind (like
* {@code ENUM_CONSTANT}).
*/
FIELD,
/** A parameter of a method or constructor. */
PARAMETER,
/** A local variable. */
LOCAL_VARIABLE,
/** A parameter of an exception handler. */
EXCEPTION_PARAMETER,
// Executables
/** A method. */
METHOD,
/** A constructor. */
CONSTRUCTOR,
/** A static initializer. */
STATIC_INIT,
/** An instance initializer. */
INSTANCE_INIT,
/** A type parameter. */
TYPE_PARAMETER,
/**
* An implementation-reserved element. This is not the element
* you are looking for.
*/
OTHER,
// Constants added since initial release
/**
* A resource variable.
* @since 1.7
*/
RESOURCE_VARIABLE,
/**
* A module.
* @since 9
*/
MODULE,
/**
* A record class.
* @since 16
*/
RECORD,
/**
* A record component of a {@code record}.
* @since 16
*/
RECORD_COMPONENT,
/**
* A binding variable in a pattern.
* @since 16
*/
BINDING_VARIABLE;
/**
* Returns {@code true} if this is a kind of class:
* either {@code CLASS} or {@code ENUM} or {@code RECORD}.
*
* @return {@code true} if this is a kind of class
*/
@SuppressWarnings("preview")
public boolean isClass() {
return this == CLASS || this == ENUM || this == RECORD;
}
/**
* Returns {@code true} if this is a kind of interface:
* either {@code INTERFACE} or {@code ANNOTATION_TYPE}.
*
* @return {@code true} if this is a kind of interface
*/
public boolean isInterface() {
return this == INTERFACE || this == ANNOTATION_TYPE;
}
/**
* Returns {@code true} if this is a kind of field:
* either {@code FIELD} or {@code ENUM_CONSTANT}.
*
* @return {@code true} if this is a kind of field
*/
public boolean isField() {
return this == FIELD || this == ENUM_CONSTANT;
}
}
@@ -0,0 +1,227 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import jdkx.lang.model.util.*;
/**
* A visitor of program elements, in the style of the visitor design
* pattern. Classes implementing this interface are used to operate
* on an element when the kind of element is unknown at compile time.
* When a visitor is passed to an element's {@link Element#accept
* accept} method, the <code>visit<i>Xyz</i></code> method most applicable
* to that element is invoked.
*
* <p> Classes implementing this interface may or may not throw a
* {@code NullPointerException} if the additional parameter {@code p}
* is {@code null}; see documentation of the implementing class for
* details.
*
* @apiNote
* <strong>WARNING:</strong> It is possible that methods will be added
* to this interface to accommodate new, currently unknown, language
* structures added to future versions of the Java programming
* language.
*
* Such additions have already occurred to support language features
* added after this API was introduced.
*
* Visitor classes directly implementing this interface may be source
* incompatible with future versions of the platform. To avoid this
* source incompatibility, visitor implementations are encouraged to
* instead extend the appropriate abstract visitor class that
* implements this interface. However, an API should generally use
* this visitor interface as the type for parameters, return type,
* etc. rather than one of the abstract classes.
*
* <p>Methods to accommodate new language constructs are expected to
* be added as default methods to provide strong source compatibility,
* as done for {@link visitModule visitModule}. The implementations of
* the default methods will in turn call {@link visitUnknown
* visitUnknown}, behavior that will be overridden in concrete
* visitors supporting the source version with the new language
* construct.
*
* <p>There are several families of classes implementing this visitor
* interface in the {@linkplain jdkx.lang.model.util util
* package}. The families follow a naming pattern along the lines of
* {@code FooVisitor}<i>N</i> where <i>N</i> indicates the
* {@linkplain jdkx.lang.model.SourceVersion source version} the
* visitor is appropriate for.
*
* In particular, a {@code FooVisitor}<i>N</i> is expected to handle
* all language constructs present in source version <i>N</i>. If
* there are no new language constructs added in version
* <i>N</i>&nbsp;+&nbsp;1 (or subsequent releases), {@code
* FooVisitor}<i>N</i> may also handle that later source version; in
* that case, the {@link
* jdkx.annotation.processing.SupportedSourceVersion
* SupportedSourceVersion} annotation on the {@code
* FooVisitor}<i>N</i> class will indicate a later version.
*
* When visiting an element representing a language construct
* introduced <strong>after</strong> source version <i>N</i>, a {@code
* FooVisitor}<i>N</i> will throw an {@link UnknownElementException}
* unless that behavior is overridden.
*
* <p>When choosing which member of a visitor family to subclass,
* subclassing the most recent one increases the range of source
* versions covered. When choosing which visitor family to subclass,
* consider their built-in capabilities:
*
* <ul>
*
* <li>{@link AbstractElementVisitor6 AbstractElementVisitor}s:
* Skeletal visitor implementations.
*
* <li>{@link SimpleElementVisitor6 SimpleElementVisitor}s: Support
* default actions and a default return value.
*
* <li>{@link ElementKindVisitor6 ElementKindVisitor}s: Visit methods
* provided on a {@linkplain Element#getKind per-kind} granularity as
* some categories of elements can have more than one kind.
*
* <li>{@link ElementScanner6 ElementScanner}s: Scanners are visitors
* which traverse an element and the elements {@linkplain
* Element#getEnclosedElements enclosed} by it and associated with it.
*
* </ul>
*
* @param <R> the return type of this visitor's methods. Use {@link
* Void} for visitors that do not need to return results.
* @param <P> the type of the additional parameter to this visitor's
* methods. Use {@code Void} for visitors that do not need an
* additional parameter.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface ElementVisitor<R, P> {
/**
* Visits an element.
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visit(Element e, P p);
/**
* A convenience method equivalent to {@code visit(e, null)}.
*
* @implSpec The default implementation is {@code visit(e, null)}.
*
* @param e the element to visit
* @return a visitor-specified result
*/
default R visit(Element e) {
return visit(e, null);
}
/**
* Visits a package element.
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitPackage(PackageElement e, P p);
/**
* Visits a type element.
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitType(TypeElement e, P p);
/**
* Visits a variable element.
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitVariable(VariableElement e, P p);
/**
* Visits an executable element.
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitExecutable(ExecutableElement e, P p);
/**
* Visits a type parameter element.
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitTypeParameter(TypeParameterElement e, P p);
/**
* Visits an unknown kind of element.
* This can occur if the language evolves and new kinds
* of elements are added to the {@code Element} hierarchy.
*
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
* @throws UnknownElementException
* a visitor implementation may optionally throw this exception
*/
R visitUnknown(Element e, P p);
/**
* Visits a module element.
*
* @implSpec The default implementation visits a {@code
* ModuleElement} by calling {@code visitUnknown(e, p)}.
*
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
* @since 9
*/
default R visitModule(ModuleElement e, P p) {
return visitUnknown(e, p);
}
/**
* Visits a record component element.
*
* @implSpec The default implementation visits a {@code
* RecordComponentElement} by calling {@code visitUnknown(e, p)}.
*
* @param e the element to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
* @since 16
*/
default R visitRecordComponent(RecordComponentElement e, P p) {
return visitUnknown(e, p);
}
}
@@ -0,0 +1,144 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import java.util.List;
import jdkx.lang.model.type.*;
/**
* Represents a method, constructor, or initializer (static or
* instance) of a class or interface, including annotation type
* elements.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see ExecutableType
* @since 1.6
*/
public interface ExecutableElement extends Element, Parameterizable {
/**
* {@return the {@linkplain ExecutableType executable type} defined
* by this executable element}
*
* @see ExecutableType
*/
@Override
TypeMirror asType();
/**
* Returns the formal type parameters of this executable
* in declaration order.
*
* @return the formal type parameters, or an empty list
* if there are none
*/
List<? extends TypeParameterElement> getTypeParameters();
/**
* {@return the return type of this executable}
* Returns a {@link NoType} with kind {@link TypeKind#VOID VOID}
* if this executable is not a method, or is a method that does not
* return a value.
*/
TypeMirror getReturnType();
/**
* Returns the formal parameters of this executable.
* They are returned in declaration order.
*
* @return the formal parameters,
* or an empty list if there are none
*/
List<? extends VariableElement> getParameters();
/**
* Returns the receiver type of this executable,
* or {@link jdkx.lang.model.type.NoType NoType} with
* kind {@link jdkx.lang.model.type.TypeKind#NONE NONE}
* if the executable has no receiver type.
*
* An executable which is an instance method, or a constructor of an
* inner class, has a receiver type derived from the {@linkplain
* #getEnclosingElement declaring type}.
*
* An executable which is a static method, or a constructor of a
* non-inner class, or an initializer (static or instance), has no
* receiver type.
*
* @return the receiver type of this executable
* @since 1.8
*
* @jls 8.4 Method Declarations
* @jls 8.4.1 Formal Parameters
* @jls 8.8 Constructor Declarations
*/
TypeMirror getReceiverType();
/**
* {@return {@code true} if this method or constructor accepts a variable
* number of arguments and returns {@code false} otherwise}
*/
boolean isVarArgs();
/**
* {@return {@code true} if this method is a default method and
* returns {@code false} otherwise}
* @since 1.8
*/
boolean isDefault();
/**
* Returns the exceptions and other throwables listed in this
* method or constructor's {@code throws} clause in declaration
* order.
*
* @return the exceptions and other throwables listed in the
* {@code throws} clause, or an empty list if there are none
*/
List<? extends TypeMirror> getThrownTypes();
/**
* Returns the default value if this executable is an annotation
* interface element. Returns {@code null} if this method is not
* an annotation interface element, or if it is an annotation
* interface element with no default value.
*
* @return the default value, or {@code null} if none
*/
AnnotationValue getDefaultValue();
/**
* {@return the simple name of a constructor, method, or
* initializer} For a constructor, the name {@code "<init>"} is
* returned, for a static initializer, the name {@code "<clinit>"}
* is returned, and for an anonymous class or instance
* initializer, an <a href=Name.html#empty_name>empty name</a> is
* returned.
*/
@Override
Name getSimpleName();
}
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2005, 2021, 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 jdkx.lang.model.element;
/**
* Represents a modifier on a program element such
* as a class, method, or field.
*
* <p>Not all modifiers are applicable to all kinds of elements.
* When two or more modifiers appear in the source code of an element
* then it is customary, though not required, that they appear in the same
* order as the constants listed in the detail section below.
*
* <p>Note that it is possible additional modifiers will be added in
* future versions of the platform.
*
* @jls 8.1.1 Class Modifiers
* @jls 8.3.1 Field Modifiers
* @jls 8.4.3 Method Modifiers
* @jls 8.8.3 Constructor Modifiers
* @jls 9.1.1 Interface Modifiers
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public enum Modifier {
// Note java.lang.reflect.Modifier includes INTERFACE, but that's a VMism.
/** The modifier {@code public} */ PUBLIC,
/** The modifier {@code protected} */ PROTECTED,
/** The modifier {@code private} */ PRIVATE,
/** The modifier {@code abstract} */ ABSTRACT,
/**
* The modifier {@code default}
* @since 1.8
*/
DEFAULT,
/** The modifier {@code static} */ STATIC,
/**
* The modifier {@code sealed}
* @since 17
*/
SEALED,
/**
* The modifier {@code non-sealed}
* @since 17
*/
NON_SEALED {
public String toString() {
return "non-sealed";
}
},
/** The modifier {@code final} */ FINAL,
/** The modifier {@code transient} */ TRANSIENT,
/** The modifier {@code volatile} */ VOLATILE,
/** The modifier {@code synchronized} */ SYNCHRONIZED,
/** The modifier {@code native} */ NATIVE,
/** The modifier {@code strictfp} */ STRICTFP;
/**
* Returns this modifier's name as defined in <cite>The
* Java Language Specification</cite>.
* The modifier name is the {@linkplain #name() name of the enum
* constant} in lowercase and with any underscores ("{@code _}")
* replaced with hyphens ("{@code -}").
* @return the modifier's name
*/
@Override
public String toString() {
return name().toLowerCase(java.util.Locale.US);
}
}
@@ -0,0 +1,384 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import java.util.List;
import jdkx.lang.model.type.TypeMirror;
/**
* Represents a module program element. Provides access to
* information about the module, its directives, and its members.
*
* @see jdkx.lang.model.util.Elements#getModuleOf
* @since 9
* @jls 7.7 Module Declarations
*/
public interface ModuleElement extends Element, QualifiedNameable {
/**
* {@return a {@linkplain jdkx.lang.model.type.NoType pseudo-type}
* for this module}
*
* @see jdkx.lang.model.type.NoType
* @see jdkx.lang.model.type.TypeKind#MODULE
*/
@Override
TypeMirror asType();
/**
* Returns the fully qualified name of this module. For an
* {@linkplain #isUnnamed() unnamed module}, an <a
* href=Name.html#empty_name>empty name</a> is returned.
*
* @apiNote If the module name consists of one identifier, then
* this method returns that identifier, which is deemed to be
* module's fully qualified name despite not being in qualified
* form. If the module name consists of more than one identifier,
* then this method returns the entire name.
*
* @return the fully qualified name of this module, or an
* empty name if this is an unnamed module
*
* @jls 6.2 Names and Identifiers
*/
@Override
Name getQualifiedName();
/**
* Returns the simple name of this module. For an {@linkplain
* #isUnnamed() unnamed module}, an <a
* href=Name.html#empty_name>empty name</a> is returned.
*
* @apiNote If the module name consists of one identifier, then
* this method returns that identifier. If the module name
* consists of more than one identifier, then this method returns
* the rightmost such identifier, which is deemed to be the
* module's simple name.
*
* @return the simple name of this module or an empty name if
* this is an unnamed module
*
* @jls 6.2 Names and Identifiers
*/
@Override
Name getSimpleName();
/**
* {@return the packages within this module}
*/
@Override
List<? extends Element> getEnclosedElements();
/**
* {@return {@code true} if this is an open module and {@code
* false} otherwise}
*/
boolean isOpen();
/**
* {@return {@code true} if this is an unnamed module and {@code
* false} otherwise}
*
* @jls 7.7.5 Unnamed Modules
*/
boolean isUnnamed();
/**
* Returns {@code null} since a module is not enclosed by another
* element.
*
* @return {@code null}
*/
@Override
Element getEnclosingElement();
/**
* Returns the directives contained in the declaration of this module.
* @return the directives in the declaration of this module
*/
List<? extends Directive> getDirectives();
/**
* The {@code kind} of a directive.
*
* <p>Note that it is possible additional directive kinds will be added
* to accommodate new, currently unknown, language structures added to
* future versions of the Java programming language.
*
* @since 9
*/
enum DirectiveKind {
/** A "requires (static|transitive)* module-name" directive. */
REQUIRES,
/** An "exports package-name [to module-name-list]" directive. */
EXPORTS,
/** An "opens package-name [to module-name-list]" directive. */
OPENS,
/** A "uses service-name" directive. */
USES,
/** A "provides service-name with implementation-name" directive. */
PROVIDES
};
/**
* Represents a directive within the declaration of this
* module. The directives of a module declaration configure the
* module in the Java Platform Module System.
*
* @since 9
*/
interface Directive {
/**
* {@return the {@code kind} of this directive}
* <ul>
*
* <li> The kind of a {@linkplain RequiresDirective requires
* directive} is {@link DirectiveKind#REQUIRES REQUIRES}.
*
* <li> The kind of an {@linkplain ExportsDirective exports
* directive} is {@link DirectiveKind#EXPORTS EXPORTS}.
*
* <li> The kind of an {@linkplain OpensDirective opens
* directive} is {@link DirectiveKind#OPENS OPENS}.
*
* <li> The kind of a {@linkplain UsesDirective uses
* directive} is {@link DirectiveKind#USES USES}.
*
* <li> The kind of a {@linkplain ProvidesDirective provides
* directive} is {@link DirectiveKind#PROVIDES PROVIDES}.
*
* </ul>
*/
DirectiveKind getKind();
/**
* Applies a visitor to this directive.
*
* @param <R> the return type of the visitor's methods
* @param <P> the type of the additional parameter to the visitor's methods
* @param v the visitor operating on this directive
* @param p additional parameter to the visitor
* @return a visitor-specified result
*/
<R, P> R accept(DirectiveVisitor<R, P> v, P p);
}
/**
* A visitor of module directives, in the style of the visitor design
* pattern. Classes implementing this interface are used to operate
* on a directive when the kind of directive is unknown at compile time.
* When a visitor is passed to a directive's {@link Directive#accept
* accept} method, the <code>visit<i>Xyz</i></code> method applicable
* to that directive is invoked.
*
* <p> Classes implementing this interface may or may not throw a
* {@code NullPointerException} if the additional parameter {@code p}
* is {@code null}; see documentation of the implementing class for
* details.
*
* <p> <b>WARNING:</b> It is possible that methods will be added to
* this interface to accommodate new, currently unknown, language
* structures added to future versions of the Java programming
* language. Methods to accommodate new language constructs will
* be added in a source <em>compatible</em> way using
* <em>default methods</em>.
*
* @param <R> the return type of this visitor's methods. Use {@link
* Void} for visitors that do not need to return results.
* @param <P> the type of the additional parameter to this visitor's
* methods. Use {@code Void} for visitors that do not need an
* additional parameter.
*
* @since 9
*/
interface DirectiveVisitor<R, P> {
/**
* Visits any directive as if by passing itself to that
* directive's {@link Directive#accept accept} method and passing
* {@code null} for the additional parameter.
*
* @param d the directive to visit
* @return a visitor-specified result
* @implSpec The default implementation is {@code d.accept(v, null)}.
*/
default R visit(Directive d) {
return d.accept(this, null);
}
/**
* Visits any directive as if by passing itself to that
* directive's {@link Directive#accept accept} method.
*
* @param d the directive to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
* @implSpec The default implementation is {@code d.accept(v, p)}.
*/
default R visit(Directive d, P p) {
return d.accept(this, p);
}
/**
* Visits a {@code requires} directive.
* @param d the directive to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitRequires(RequiresDirective d, P p);
/**
* Visits an {@code exports} directive.
* @param d the directive to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitExports(ExportsDirective d, P p);
/**
* Visits an {@code opens} directive.
* @param d the directive to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitOpens(OpensDirective d, P p);
/**
* Visits a {@code uses} directive.
* @param d the directive to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitUses(UsesDirective d, P p);
/**
* Visits a {@code provides} directive.
* @param d the directive to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
*/
R visitProvides(ProvidesDirective d, P p);
/**
* Visits an unknown directive.
* This can occur if the language evolves and new kinds of directive are added.
* @param d the directive to visit
* @param p a visitor-specified parameter
* @return a visitor-specified result
* @throws UnknownDirectiveException a visitor implementation may optionally throw this exception
* @implSpec The default implementation throws {@code new UnknownDirectiveException(d, p)}.
*/
default R visitUnknown(Directive d, P p) {
throw new UnknownDirectiveException(d, p);
}
}
/**
* A dependency of a module.
* @since 9
*/
interface RequiresDirective extends Directive {
/**
* {@return whether or not this is a static dependency}
*/
boolean isStatic();
/**
* {@return whether or not this is a transitive dependency}
*/
boolean isTransitive();
/**
* {@return the module that is required}
*/
ModuleElement getDependency();
}
/**
* An exported package of a module.
* @since 9
*/
interface ExportsDirective extends Directive {
/**
* {@return the package being exported}
*/
PackageElement getPackage();
/**
* Returns the specific modules to which the package is being exported,
* or {@code null}, if the package is exported to all modules which
* have readability to this module.
* @return the specific modules to which the package is being exported
*/
List<? extends ModuleElement> getTargetModules();
}
/**
* An opened package of a module.
* @since 9
*/
interface OpensDirective extends Directive {
/**
* {@return the package being opened}
*/
PackageElement getPackage();
/**
* Returns the specific modules to which the package is being open
* or {@code null}, if the package is open all modules which
* have readability to this module.
* @return the specific modules to which the package is being opened
*/
List<? extends ModuleElement> getTargetModules();
}
/**
* An implementation of a service provided by a module.
* @since 9
*/
interface ProvidesDirective extends Directive {
/**
* {@return the service being provided}
*/
TypeElement getService();
/**
* {@return the implementations of the service being provided}
*/
List<? extends TypeElement> getImplementations();
}
/**
* A reference to a service used by a module.
* @since 9
*/
interface UsesDirective extends Directive {
/**
* {@return the service that is used}
*/
TypeElement getService();
}
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2006, 2020, 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 jdkx.lang.model.element;
/**
* An immutable sequence of characters. When created by the same
* implementation, objects implementing this interface must obey the
* general {@linkplain Object#equals equals contract} when compared
* with each other. Therefore, {@code Name} objects from the same
* implementation are usable in collections while {@code Name}s from
* different implementations may not work properly in collections.
*
* <p id="empty_name">An {@linkplain CharSequence#isEmpty() empty}
* {@code Name} has a {@linkplain CharSequence#length() length} of
* zero.
*
* <p>In the context of {@linkplain
* jdkx.annotation.processing.ProcessingEnvironment annotation
* processing}, the guarantees for "the same" implementation must
* include contexts where the {@linkplain jdkx.annotation.processing
* API mediated} side effects of {@linkplain
* jdkx.annotation.processing.Processor processors} could be visible
* to each other, including successive annotation processing
* {@linkplain jdkx.annotation.processing.RoundEnvironment rounds}.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see jdkx.lang.model.util.Elements#getName
* @since 1.6
*/
public interface Name extends CharSequence {
/**
* Returns {@code true} if the argument represents the same
* name as {@code this}, and {@code false} otherwise.
*
* <p>Note that the identity of a {@code Name} is a function both
* of its content in terms of a sequence of characters as well as
* the implementation which created it.
*
* @param obj the object to be compared with this element
* @return {@code true} if the specified object represents the same
* name as this
* @see Element#equals
*/
boolean equals(Object obj);
/**
* Obeys the general contract of {@link Object#hashCode Object.hashCode}.
*
* @see #equals
*/
int hashCode();
/**
* Compares this name to the specified {@code CharSequence}. The result
* is {@code true} if and only if this name represents the same sequence
* of {@code char} values as the specified sequence.
*
* @return {@code true} if this name represents the same sequence
* of {@code char} values as the specified sequence, {@code false}
* otherwise
*
* @param cs The sequence to compare this name against
* @see String#contentEquals(CharSequence)
*/
boolean contentEquals(CharSequence cs);
}
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
/**
* The <i>nesting kind</i> of a type element.
* Type elements come in four varieties:
* top-level, member, local, and anonymous.
* <i>Nesting kind</i> is a non-standard term used here to denote this
* classification.
*
* <p>Note that it is possible additional nesting kinds will be added
* in future versions of the platform.
*
* <p><b>Example:</b> The classes below are annotated with their nesting kind.
* <blockquote><pre>
*
* import java.lang.annotation.*;
* import static java.lang.annotation.RetentionPolicy.*;
* import jdkx.lang.model.element.*;
* import static jdkx.lang.model.element.NestingKind.*;
*
* &#64;Nesting(TOP_LEVEL)
* public class NestingExamples {
* &#64;Nesting(MEMBER)
* static class MemberClass1{}
*
* &#64;Nesting(MEMBER)
* class MemberClass2{}
*
* public static void main(String... argv) {
* &#64;Nesting(LOCAL)
* class LocalClass{};
*
* Class&lt;?&gt;[] classes = {
* NestingExamples.class,
* MemberClass1.class,
* MemberClass2.class,
* LocalClass.class
* };
*
* for(Class&lt;?&gt; clazz : classes) {
* System.out.format("%s is %s%n",
* clazz.getName(),
* clazz.getAnnotation(Nesting.class).value());
* }
* }
* }
*
* &#64;Retention(RUNTIME)
* &#64;interface Nesting {
* NestingKind value();
* }
* </pre></blockquote>
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public enum NestingKind {
/**
* A top-level class or interface, not contained within another
* class or interface.
*/
TOP_LEVEL,
/**
* A class or interface that is a named member of another class or
* interface.
* @jls 8.5 Member Class and Interface Declarations
*/
MEMBER,
/**
* A named class or interface declared within a construct other
* than a class or interface.
* @jls 14.3 Local Class Declarations
*/
LOCAL,
/**
* A class without a name.
* @jls 15.9.5 Anonymous Class Declarations
*/
ANONYMOUS;
/**
* Does this constant correspond to a nested type element?
* A <i>nested</i> type element is any that is not top-level.
* More specifically, an <i>inner</i> type element is any nested type element that
* is not {@linkplain Modifier#STATIC static}.
* @return whether or not the constant is nested
* @jls 14.3 Local Class Declarations
*/
public boolean isNested() {
return this != TOP_LEVEL;
}
}
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import java.util.List;
import jdkx.lang.model.type.TypeMirror;
/**
* Represents a package program element. Provides access to information
* about the package and its members.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see jdkx.lang.model.util.Elements#getPackageOf
* @since 1.6
*/
public interface PackageElement extends Element, QualifiedNameable {
/**
* {@return a {@linkplain jdkx.lang.model.type.NoType pseudo-type}
* for this package}
*
* @see jdkx.lang.model.type.NoType
* @see jdkx.lang.model.type.TypeKind#PACKAGE
*/
@Override
TypeMirror asType();
/**
* Returns the fully qualified name of this package. This is also
* known as the package's <i>canonical</i> name. For an
* {@linkplain #isUnnamed() unnamed package}, an <a
* href=Name.html#empty_name>empty name</a> is returned.
*
* @apiNote The fully qualified name of a named package that is
* not a subpackage of a named package is its simple name. The
* fully qualified name of a named package that is a subpackage of
* another named package consists of the fully qualified name of
* the containing package, followed by "{@code .}", followed by the simple
* (member) name of the subpackage.
*
* @return the fully qualified name of this package, or an
* empty name if this is an unnamed package
* @jls 6.7 Fully Qualified Names and Canonical Names
*/
Name getQualifiedName();
/**
* Returns the simple name of this package. For an {@linkplain
* #isUnnamed() unnamed package}, an <a
* href=Name.html#empty_name>empty name</a> is returned.
*
* @return the simple name of this package or an empty name if
* this is an unnamed package
*/
@Override
Name getSimpleName();
/**
* {@return the {@linkplain NestingKind#TOP_LEVEL top-level}
* classes and interfaces within this package} Note that
* subpackages are <em>not</em> considered to be enclosed by a
* package.
*/
@Override
List<? extends Element> getEnclosedElements();
/**
* {@return {@code true} if this is an unnamed package and {@code
* false} otherwise}
*
* @jls 7.4.2 Unnamed Packages
*/
boolean isUnnamed();
/**
* {@return the enclosing module if such a module exists; otherwise
* {@code null}}
*
* One situation where a module does not exist for a package is if
* the environment does not include modules, such as an annotation
* processing environment configured for a {@linkplain
* jdkx.annotation.processing.ProcessingEnvironment#getSourceVersion
* source version} without modules.
*
* @revised 9
*/
@Override
Element getEnclosingElement();
}
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2009, 2017, 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 jdkx.lang.model.element;
import java.util.List;
/**
* A mixin interface for an element that has type parameters.
*
* @author Joseph D. Darcy
* @since 1.7
*/
public interface Parameterizable extends Element {
/**
* Returns the formal type parameters of an element in
* declaration order.
*
* @return the formal type parameters, or an empty list
* if there are none
*/
List<? extends TypeParameterElement> getTypeParameters();
}
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2009, 2020, 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 jdkx.lang.model.element;
/**
* A mixin interface for an element that has a qualified name.
*
* @author Joseph D. Darcy
* @since 1.7
*/
public interface QualifiedNameable extends Element {
/**
* {@return the fully qualified name of an element}
*/
Name getQualifiedName();
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2019, 2020, 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 jdkx.lang.model.element;
/**
* Represents a record component.
*
* @since 16
*/
public interface RecordComponentElement extends Element {
/**
* {@return the enclosing element of this record component}
*
* The enclosing element of a record component is the record class
* declaring the record component.
*/
@Override
Element getEnclosingElement();
/**
* {@return the simple name of this record component}
*
* <p>The name of each record component must be distinct from the
* names of all other record components of the same record.
*
* @jls 6.2 Names and Identifiers
*/
@Override
Name getSimpleName();
/**
* Returns the executable element for the accessor associated with the
* given record component.
*
* @return the record component accessor.
*/
ExecutableElement getAccessor();
}
@@ -0,0 +1,228 @@
/*
* Copyright (c) 2005, 2021, 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 jdkx.lang.model.element;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jdkx.lang.model.type.*;
import jdkx.lang.model.util.*;
/**
* Represents a class or interface program element. Provides access
* to information about the class or interface and its members. Note
* that an enum class and a record class are specialized kinds of
* classes and an annotation interface is a specialized kind of
* interface.
*
* <p> While a {@code TypeElement} represents a class or interface
* <i>element</i>, a {@link DeclaredType} represents a class
* or interface <i>type</i>, the latter being a use
* (or <i>invocation</i>) of the former.
* The distinction is most apparent with generic types,
* for which a single element can define a whole
* family of types. For example, the element
* {@code java.util.Set} corresponds to the parameterized types
* {@code java.util.Set<String>} and {@code java.util.Set<Number>}
* (and many others), and to the raw type {@code java.util.Set}.
*
* <p> Each method of this interface that returns a list of elements
* will return them in the order that is natural for the underlying
* source of program information. For example, if the underlying
* source of information is Java source code, then the elements will be
* returned in source code order.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see DeclaredType
* @since 1.6
*/
public interface TypeElement extends Element, Parameterizable, QualifiedNameable {
/**
* Returns the type defined by this class or interface element,
* returning the <i>prototypical</i> type for an element
* representing a generic type.
*
* <p>A generic element defines a family of types, not just one.
* If this is a generic element, a prototypical type is
* returned which has the element's invocation on the
* type variables corresponding to its own formal type parameters.
* For example,
* for the generic class element {@code C<N extends Number>},
* the parameterized type {@code C<N>} is returned.
* The {@link Types} utility interface has more general methods
* for obtaining the full range of types defined by an element.
*
* @return the type defined by this type element
*
* @see Types#asMemberOf(DeclaredType, Element)
* @see Types#getDeclaredType(TypeElement, TypeMirror...)
*/
@Override
TypeMirror asType();
/**
* Returns the fields, methods, constructors, record components,
* and member classes and interfaces that are directly declared in
* this class or interface.
*
* This includes any {@linkplain Elements.Origin#MANDATED
* mandated} elements such as the (implicit) default constructor
* and the implicit {@code values} and {@code valueOf} methods of
* an enum class.
*
* @apiNote As a particular instance of the {@linkplain
* jdkx.lang.model.element general accuracy requirements} and the
* ordering behavior required of this interface, the list of
* enclosed elements will be returned in the natural order for the
* originating source of information about the class or interface.
* For example, if the information about the class or interface is
* originating from a source file, the elements will be returned
* in source code order. (However, in that case the ordering of
* {@linkplain Elements.Origin#MANDATED implicitly declared}
* elements, such as default constructors, is not specified.)
*
* @return the enclosed elements in proper order, or an empty list if none
*
* @jls 8.8.9 Default Constructor
* @jls 8.9.3 Enum Members
*/
@Override
List<? extends Element> getEnclosedElements();
/**
* Returns the <i>nesting kind</i> of this class or interface element.
*
* @return the nesting kind of this class or interface element
*/
NestingKind getNestingKind();
/**
* Returns the fully qualified name of this class or interface
* element. More precisely, it returns the <i>canonical</i> name.
* For local and anonymous classes, which do not have canonical
* names, an <a href=Name.html#empty_name>empty name</a> is
* returned.
*
* <p>The name of a generic class or interface does not include any reference
* to its formal type parameters.
* For example, the fully qualified name of the interface
* {@code java.util.Set<E>} is "{@code java.util.Set}".
* Nested classes and interfaces use "{@code .}" as a separator, as in
* "{@code java.util.Map.Entry}".
*
* @return the fully qualified name of this class or interface, or
* an empty name if none
*
* @see Elements#getBinaryName
* @jls 6.7 Fully Qualified Names and Canonical Names
*/
Name getQualifiedName();
/**
* Returns the simple name of this class or interface element.
*
* For an anonymous class, an <a href=Name.html#empty_name> empty
* name</a> is returned.
*
* @return the simple name of this class or interface,
* an empty name for an anonymous class
*
*/
@Override
Name getSimpleName();
/**
* Returns the direct superclass of this class or interface element.
* If this class or interface element represents an interface or the class
* {@code java.lang.Object}, then a {@link NoType}
* with kind {@link TypeKind#NONE NONE} is returned.
*
* @return the direct superclass, or a {@code NoType} if there is none
*/
TypeMirror getSuperclass();
/**
* Returns the interface types directly implemented by this class
* or extended by this interface.
*
* @return the interface types directly implemented by this class
* or extended by this interface, or an empty list if there are none
*/
List<? extends TypeMirror> getInterfaces();
/**
* Returns the formal type parameters of this class or interface element
* in declaration order.
*
* @return the formal type parameters, or an empty list
* if there are none
*/
List<? extends TypeParameterElement> getTypeParameters();
/**
* Returns the record components of this class or interface
* element in declaration order.
*
* @implSpec The default implementations of this method returns an
* empty and unmodifiable list.
*
* @return the record components, or an empty list if there are
* none
*
* @since 16
*/
default List<? extends RecordComponentElement> getRecordComponents() {
return Collections.unmodifiableList(Collections.emptyList());
}
/**
* Returns the permitted classes of this class or interface
* element in declaration order.
*
* @implSpec The default implementations of this method returns an
* empty and unmodifiable list.
*
* @return the permitted classes, or an empty list if there are none
*
* @since 17
*/
default List<? extends TypeMirror> getPermittedSubclasses() {
return Collections.unmodifiableList(new ArrayList());
}
/**
* Returns the package of a top-level class or interface and
* returns the immediately lexically enclosing element for a
* {@linkplain NestingKind#isNested nested} class or interface.
*
* @return the package of a top-level class or interface, the immediately
* lexically enclosing element for a nested class or interface
*/
@Override
Element getEnclosingElement();
}
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import java.util.List;
import jdkx.lang.model.type.TypeMirror;
import jdkx.lang.model.type.TypeVariable;
/**
* Represents a formal type parameter of a generic class, interface, method,
* or constructor element.
* A type parameter declares a {@link TypeVariable}.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see TypeVariable
* @since 1.6
*/
public interface TypeParameterElement extends Element {
/**
* {@return the {@linkplain TypeVariable type variable}
* corresponding to this type parameter element}
*
* @see TypeVariable
*/
@Override
TypeMirror asType();
/**
* {@return the generic class, interface, method, or constructor that is
* parameterized by this type parameter}
*/
Element getGenericElement();
/**
* Returns the bounds of this type parameter.
* These are the types given by the {@code extends} clause
* used to declare this type parameter.
* If no explicit {@code extends} clause was used,
* then {@code java.lang.Object} is considered to be the sole bound.
*
* @return the bounds of this type parameter, or an empty list if
* there are none
*/
List<? extends TypeMirror> getBounds();
/**
* {@return the {@linkplain TypeParameterElement#getGenericElement
* generic element} of this type parameter}
*/
@Override
Element getEnclosingElement();
}
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2005, 2019, 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 jdkx.lang.model.element;
import jdkx.lang.model.UnknownEntityException;
/**
* Indicates that an unknown kind of annotation value was encountered.
* This can occur if the language evolves and new kinds of annotation
* values can be stored in an annotation. May be thrown by an
* {@linkplain AnnotationValueVisitor annotation value visitor} to
* indicate that the visitor was created for a prior version of the
* language.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see AnnotationValueVisitor#visitUnknown
* @since 1.6
*/
public class UnknownAnnotationValueException extends UnknownEntityException {
private static final long serialVersionUID = 269L;
private transient AnnotationValue av;
private transient Object parameter;
/**
* Creates a new {@code UnknownAnnotationValueException}. The
* {@code p} parameter may be used to pass in an additional
* argument with information about the context in which the
* unknown annotation value was encountered; for example, the
* visit methods of {@link AnnotationValueVisitor} may pass in
* their additional parameter.
*
* @param av the unknown annotation value, may be {@code null}
* @param p an additional parameter, may be {@code null}
*/
public UnknownAnnotationValueException(AnnotationValue av, Object p) {
super("Unknown annotation value: \"" + av + "\"");
this.av = av;
this.parameter = p;
}
/**
* Returns the unknown annotation value.
* The value may be unavailable if this exception has been
* serialized and then read back in.
*
* @return the unknown element, or {@code null} if unavailable
*/
public AnnotationValue getUnknownAnnotationValue() {
return av;
}
/**
* Returns the additional argument.
*
* @return the additional argument, or {@code null} if unavailable
*/
public Object getArgument() {
return parameter;
}
}
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2005, 2017, 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 jdkx.lang.model.element;
import jdkx.lang.model.UnknownEntityException;
/**
* Indicates that an unknown kind of module directive was encountered.
* This can occur if the language evolves and new kinds of directives are
* added to the {@code Directive} hierarchy. May be thrown by a
* {@linkplain ModuleElement.DirectiveVisitor directive visitor} to
* indicate that the visitor was created for a prior version of the language.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see ModuleElement.DirectiveVisitor#visitUnknown
* @since 9
*/
public class UnknownDirectiveException extends UnknownEntityException {
private static final long serialVersionUID = 269L;
private final transient ModuleElement.Directive directive;
private final transient Object parameter;
/**
* Creates a new {@code UnknownElementException}. The {@code p}
* parameter may be used to pass in an additional argument with
* information about the context in which the unknown directive was
* encountered; for example, the visit methods of {@link
* ModuleElement.DirectiveVisitor DirectiveVisitor} may pass in
* their additional parameter.
*
* @param d the unknown directive, may be {@code null}
* @param p an additional parameter, may be {@code null}
*/
public UnknownDirectiveException(ModuleElement.Directive d, Object p) {
super("Unknown directive: " + d);
directive = d;
parameter = p;
}
/**
* Returns the unknown directive.
* The value may be unavailable if this exception has been
* serialized and then read back in.
*
* @return the unknown directive, or {@code null} if unavailable
*/
public ModuleElement.Directive getUnknownDirective() {
return directive;
}
/**
* Returns the additional argument.
*
* @return the additional argument, or {@code null} if unavailable
*/
public Object getArgument() {
return parameter;
}
}
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2005, 2019, 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 jdkx.lang.model.element;
import jdkx.lang.model.UnknownEntityException;
/**
* Indicates that an unknown kind of element was encountered. This
* can occur if the language evolves and new kinds of elements are
* added to the {@code Element} hierarchy. May be thrown by an
* {@linkplain ElementVisitor element visitor} to indicate that the
* visitor was created for a prior version of the language.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see ElementVisitor#visitUnknown
* @since 1.6
*/
public class UnknownElementException extends UnknownEntityException {
private static final long serialVersionUID = 269L;
private transient Element element;
private transient Object parameter;
/**
* Creates a new {@code UnknownElementException}. The {@code p}
* parameter may be used to pass in an additional argument with
* information about the context in which the unknown element was
* encountered; for example, the visit methods of {@link
* ElementVisitor} may pass in their additional parameter.
*
* @param e the unknown element, may be {@code null}
* @param p an additional parameter, may be {@code null}
*/
public UnknownElementException(Element e, Object p) {
super("Unknown element: \"" + e + "\"");
element = e;
this.parameter = p;
}
/**
* Returns the unknown element.
* The value may be unavailable if this exception has been
* serialized and then read back in.
*
* @return the unknown element, or {@code null} if unavailable
*/
public Element getUnknownElement() {
return element;
}
/**
* Returns the additional argument.
*
* @return the additional argument, or {@code null} if unavailable
*/
public Object getArgument() {
return parameter;
}
}
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.element;
import jdkx.lang.model.util.Elements;
import jdkx.lang.model.type.TypeMirror;
import jdkx.lang.model.type.TypeKind;
/**
* Represents a field, {@code enum} constant, method or constructor
* parameter, local variable, resource variable, or exception
* parameter.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface VariableElement extends Element {
/**
* {@return the type of this variable}
*
* Note that the types of variables range over {@linkplain
* TypeKind many kinds} of types, including primitive types,
* declared types, and array types, among others.
*
* @see TypeKind
*/
@Override
TypeMirror asType();
/**
* Returns the value of this variable if this is a {@code final}
* field initialized to a compile-time constant. Returns {@code
* null} otherwise. The value will be of a primitive type or a
* {@code String}. If the value is of a primitive type, it is
* wrapped in the appropriate wrapper class (such as {@link
* Integer}).
*
* <p>Note that not all {@code final} fields will have
* constant values. In particular, {@code enum} constants are
* <em>not</em> considered to be compile-time constants. To have a
* constant value, a field's type must be either a primitive type
* or {@code String}.
*
* @return the value of this variable if this is a {@code final}
* field initialized to a compile-time constant, or {@code null}
* otherwise
*
* @see Elements#getConstantExpression(Object)
* @jls 15.29 Constant Expressions
* @jls 4.12.4 final Variables
*/
Object getConstantValue();
/**
* {@return the simple name of this variable element}
*
* <p>For method and constructor parameters, the name of each
* parameter must be distinct from the names of all other
* parameters of the same executable. If the original source
* names are not available, an implementation may synthesize names
* subject to the distinctness requirement above.
*/
@Override
Name getSimpleName();
/**
* {@return the enclosing element of this variable}
*
* The enclosing element of a method or constructor parameter is
* the executable declaring the parameter.
*/
@Override
Element getEnclosingElement();
}
@@ -0,0 +1,124 @@
/*
* Copyright (c) 2005, 2020, 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.
*/
/**
* Interfaces used to model elements of the Java programming language.
*
* The term "element" in this package is used to refer to program
* elements, the declared entities that make up a program. Elements
* include classes, interfaces, methods, constructors, and fields.
* The interfaces in this package do not model the structure of a
* program inside a method body; for example there is no
* representation of a {@code for} loop or {@code try}-{@code finally}
* block. However, the interfaces can model some structures only
* appearing inside method bodies, such as local variables and
* anonymous classes.
*
* <p>When used in the context of annotation processing, an accurate
* model of the element being represented must be returned. As this
* is a language model, the source code provides the fiducial
* (reference) representation of the construct in question rather than
* a representation in an executable output like a class file.
* Executable output may serve as the basis for creating a modeling
* element. However, the process of translating source code to
* executable output may not permit recovering some aspects of the
* source code representation. For example, annotations with
* {@linkplain java.lang.annotation.RetentionPolicy#SOURCE source}
* {@linkplain java.lang.annotation.Retention retention} cannot be
* recovered from class files and class files might not be able to
* provide source position information.
*
* Names of {@linkplain
* jdkx.lang.model.element.ExecutableElement#getParameters()
* parameters} may not be recoverable from class files.
*
* The {@linkplain jdkx.lang.model.element.Modifier modifiers} on an
* element created from a class file may differ in some cases from an
* element for the same declaration created from a source file
* including:
*
* <ul>
* <li> {@code strictfp} on a class or interface
* <li> {@code final} on a parameter
* <li> {@code protected}, {@code private}, and {@code static} on classes and interfaces
* </ul>
*
* Some elements which are {@linkplain
* jdkx.lang.model.util.Elements.Origin#MANDATED mandated} may not be
* marked as such when created from class files.
*
* Additionally, {@linkplain
* jdkx.lang.model.util.Elements.Origin#SYNTHETIC synthetic}
* constructs in a class file, such as accessor methods used in
* implementing nested classes and {@linkplain
* jdkx.lang.model.util.Elements.Origin#isBridge(ExecutableElement)
* bridge methods} used in implementing covariant returns, are
* translation artifacts strictly outside of this model. However, when
* operating on class files, it is helpful be able to operate on such
* elements, screening them out when appropriate.
*
* <p>During annotation processing, operating on incomplete or
* erroneous programs is necessary; however, there are fewer
* guarantees about the nature of the resulting model. If the source
* code is not syntactically well-formed or has some other
* irrecoverable error that could not be removed by the generation of
* new classes or interfaces, a model may or may not be provided as a
* quality of implementation issue. If a program is syntactically
* valid but erroneous in some other fashion, any returned model must
* have no less information than if all the method bodies in the
* program were replaced by {@code "throw new RuntimeException();"}.
* If a program refers to a missing class or interface Xyz, the
* returned model must contain no less information than if the
* declaration of class or interface Xyz were assumed to be {@code
* "class Xyz {}"}, {@code "interface Xyz {}"}, {@code "enum Xyz {}"},
* {@code "@interface Xyz {}"}, or {@code "record Xyz {}"}. If a
* program refers to a missing class or interface {@code Xyz<K1,
* ... ,Kn>}, the returned model must contain no less information than
* if the declaration of Xyz were assumed to be {@code "class Xyz<T1,
* ... ,Tn> {}"} or {@code "interface Xyz<T1, ... ,Tn> {}"}
*
* <p> Unless otherwise specified in a particular implementation, the
* collections returned by methods in this package should be expected
* to be unmodifiable by the caller and unsafe for concurrent access.
*
* <p> Unless otherwise specified, methods in this package will throw
* a {@code NullPointerException} if given a {@code null} argument.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see jdkx.lang.model.util.Elements
* @jls 6.1 Declarations
* @jls 7.4 Package Declarations
* @jls 7.7 Module Declarations
* @jls 8.1 Class Declarations
* @jls 8.3 Field Declarations
* @jls 8.4 Method Declarations
* @jls 8.5 Member Class and Interface Declarations
* @jls 8.8 Constructor Declarations
* @jls 9.1 Interface Declarations
* @since 1.6
*/
package jdkx.lang.model.element;
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2005, 2019, 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.
*/
/**
* Types and hierarchies of packages comprising a {@index "Java language
* model"}, a model of the declarations and types of the Java
* programming language.
*
* The members of this package and its subpackages are for use in
* language modeling and language processing tasks and APIs including,
* but not limited to, the {@linkplain jdkx.annotation.processing
* annotation processing} framework.
*
* <p> This language model follows a <i>mirror</i>-based design; see
*
* <blockquote>
* Gilad Bracha and David Ungar. <i>Mirrors: Design Principles for
* Meta-level Facilities of Object-Oriented Programming Languages</i>.
* In Proc. of the ACM Conf. on Object-Oriented Programming, Systems,
* Languages and Applications, October 2004.
* </blockquote>
*
* In particular, the model makes a distinction between declared
* language constructs, like the {@linkplain jdkx.lang.model.element
* element} representing {@code java.util.Set}, and the family of
* {@linkplain jdkx.lang.model.type types} that may be associated
* with an element, like the raw type {@code java.util.Set}, {@code
* java.util.Set<String>}, and {@code java.util.Set<T>}.
*
* <p>Unless otherwise specified, methods in this package will throw
* a {@code NullPointerException} if given a {@code null} argument.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
package jdkx.lang.model;
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.type;
/**
* Represents an array type.
* A multidimensional array type is represented as an array type
* whose component type is also an array type.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface ArrayType extends ReferenceType {
/**
* {@return the component type of this array type}
*/
TypeMirror getComponentType();
}
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.type;
import java.util.List;
import jdkx.lang.model.element.Element;
import jdkx.lang.model.element.TypeElement;
import jdkx.lang.model.util.Types;
/**
* Represents a declared type, either a class type or an interface type.
* This includes parameterized types such as {@code java.util.Set<String>}
* as well as raw types.
*
* <p> While a {@code TypeElement} represents a class or interface
* <i>element</i>, a {@code DeclaredType} represents a class
* or interface <i>type</i>, the latter being a use
* (or <i>invocation</i>) of the former.
* See {@link TypeElement} for more on this distinction.
*
* <p> The supertypes (both class and interface types) of a declared
* type may be found using the {@link
* Types#directSupertypes(TypeMirror)} method. This returns the
* supertypes with any type arguments substituted in.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see TypeElement
* @since 1.6
*/
public interface DeclaredType extends ReferenceType {
/**
* {@return the element corresponding to this type}
*/
Element asElement();
/**
* Returns the type of the innermost enclosing instance or a
* {@code NoType} of kind {@code NONE} if there is no enclosing
* instance. Only types corresponding to inner classes have an
* enclosing instance.
*
* @return a type mirror for the enclosing type
* @jls 8.1.3 Inner Classes and Enclosing Instances
* @jls 15.9.2 Determining Enclosing Instances
*/
TypeMirror getEnclosingType();
/**
* Returns the actual type arguments of this type.
* For a type nested within a parameterized type
* (such as {@code Outer<String>.Inner<Number>}), only the type
* arguments of the innermost type are included.
*
* @return the actual type arguments of this type, or an empty list
* if none
*/
List<? extends TypeMirror> getTypeArguments();
}
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2005, 2008, 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 jdkx.lang.model.type;
/**
* Represents a class or interface type that cannot be properly modeled.
* This may be the result of a processing error,
* such as a missing class file or erroneous source code.
* Most queries for
* information derived from such a type (such as its members or its
* supertype) will not, in general, return meaningful results.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @since 1.6
*/
public interface ErrorType extends DeclaredType {
}
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.type;
import java.util.List;
import jdkx.lang.model.element.ExecutableElement;
/**
* Represents the type of an executable. An <i>executable</i>
* is a method, constructor, or initializer.
*
* <p> The executable is
* represented as when viewed as a method (or constructor or
* initializer) of some reference type.
* If that reference type is parameterized, then its actual
* type arguments are substituted into any types returned by the methods of
* this interface.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see ExecutableElement
* @since 1.6
*/
public interface ExecutableType extends TypeMirror {
/**
* Returns the type variables declared by the formal type parameters
* of this executable.
*
* @return the type variables declared by the formal type parameters,
* or an empty list if there are none
*/
List<? extends TypeVariable> getTypeVariables();
/**
* {@return the return type of this executable}
* Returns a {@link NoType} with kind {@link TypeKind#VOID VOID}
* if this executable is not a method, or is a method that does not
* return a value.
*/
TypeMirror getReturnType();
/**
* Returns the types of this executable's formal parameters.
*
* @return the types of this executable's formal parameters,
* or an empty list if there are none
*/
List<? extends TypeMirror> getParameterTypes();
/**
* Returns the receiver type of this executable,
* or {@link jdkx.lang.model.type.NoType NoType} with
* kind {@link jdkx.lang.model.type.TypeKind#NONE NONE}
* if the executable has no receiver type.
*
* An executable which is an instance method, or a constructor of an
* inner class, has a receiver type derived from the {@linkplain
* ExecutableElement#getEnclosingElement declaring type}.
*
* An executable which is a static method, or a constructor of a
* non-inner class, or an initializer (static or instance), has no
* receiver type.
*
* @return the receiver type of this executable
* @since 1.8
*
* @jls 8.4 Method Declarations
* @jls 8.4.1 Formal Parameters
* @jls 8.8 Constructor Declarations
*/
TypeMirror getReceiverType();
/**
* Returns the exceptions and other throwables listed in this
* executable's {@code throws} clause.
*
* @return the exceptions and other throwables listed in this
* executable's {@code throws} clause,
* or an empty list if there are none.
*/
List<? extends TypeMirror> getThrownTypes();
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2012, 2020, 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 jdkx.lang.model.type;
import java.util.List;
/**
* Represents an intersection type.
*
* <p>An intersection type can be either implicitly or explicitly
* declared in a program. For example, the bound of the type parameter
* {@code <T extends Number & Runnable>} is an (implicit) intersection
* type. This is represented by an {@code IntersectionType} with
* {@code Number} and {@code Runnable} as its bounds.
*
* @implNote In the reference implementation an {@code
* IntersectionType} is used to model the explicit target type of a
* cast expression.
*
* @since 1.8
*/
public interface IntersectionType extends TypeMirror {
/**
* {@return the bounds comprising this intersection type}
*/
List<? extends TypeMirror> getBounds();
}
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2005, 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 jdkx.lang.model.type;
import java.io.ObjectInputStream;
import java.io.IOException;
import jdkx.lang.model.element.Element;
/**
* Thrown when an application attempts to access the {@link Class} object
* corresponding to a {@link TypeMirror}.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see MirroredTypesException
* @see Element#getAnnotation(Class)
* @since 1.6
*/
public class MirroredTypeException extends MirroredTypesException {
private static final long serialVersionUID = 269;
private transient TypeMirror type; // cannot be serialized
/**
* Constructs a new MirroredTypeException for the specified type.
*
* @param type the type being accessed
*/
public MirroredTypeException(TypeMirror type) {
super("Attempt to access Class object for TypeMirror " + type.toString(), type);
this.type = type;
}
/**
* Returns the type mirror corresponding to the type being accessed.
* The type mirror may be unavailable if this exception has been
* serialized and then read back in.
*
* @return the type mirror, or {@code null} if unavailable
*/
public TypeMirror getTypeMirror() {
return type;
}
/**
* Explicitly set all transient fields.
* @param s the serial stream
* @throws ClassNotFoundException for a missing class during
* deserialization
* @throws IOException for an IO problem during deserialization
*/
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
type = null;
types = null;
}
}
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2005, 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 jdkx.lang.model.type;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.io.ObjectInputStream;
import java.io.IOException;
import jdkx.lang.model.element.Element;
/**
* Thrown when an application attempts to access a sequence of {@link
* Class} objects each corresponding to a {@link TypeMirror}.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see MirroredTypeException
* @see Element#getAnnotation(Class)
* @since 1.6
*/
public class MirroredTypesException extends RuntimeException {
private static final long serialVersionUID = 269;
transient List<? extends TypeMirror> types; // cannot be serialized
/*
* Trusted constructor to be called by MirroredTypeException.
*/
MirroredTypesException(String message, TypeMirror type) {
super(message);
List<TypeMirror> tmp = (new ArrayList<>());
tmp.add(type);
types = Collections.unmodifiableList(tmp);
}
/**
* Constructs a new MirroredTypesException for the specified types.
*
* @param types the types being accessed
*/
public MirroredTypesException(List<? extends TypeMirror> types) {
super("Attempt to access Class objects for TypeMirrors " +
(types = // defensive copy
new ArrayList<>(types)).toString() );
this.types = Collections.unmodifiableList(types);
}
/**
* Returns the type mirrors corresponding to the types being accessed.
* The type mirrors may be unavailable if this exception has been
* serialized and then read back in.
*
* @return the type mirrors in construction order, or {@code null} if unavailable
*/
public List<? extends TypeMirror> getTypeMirrors() {
return types;
}
/**
* Explicitly set all transient fields.
* @param s the serial stream
* @throws ClassNotFoundException for a missing class during
* deserialization
* @throws IOException for an IO problem during deserialization
*/
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
types = null;
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2005, 2017, 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 jdkx.lang.model.type;
import jdkx.lang.model.element.ExecutableElement;
/**
* A pseudo-type used where no actual type is appropriate.
* The kinds of {@code NoType} are:
* <ul>
* <li>{@link TypeKind#VOID VOID} - corresponds to the keyword {@code void}.
* <li>{@link TypeKind#PACKAGE PACKAGE} - the pseudo-type of a package element.
* <li>{@link TypeKind#MODULE MODULE} - the pseudo-type of a module element.
* <li>{@link TypeKind#NONE NONE} - used in other cases
* where no actual type is appropriate; for example, the superclass
* of {@code java.lang.Object}.
* </ul>
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @see ExecutableElement#getReturnType()
* @since 1.6
*/
public interface NoType extends TypeMirror {
}
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.type;
/**
* Represents the null type.
* This is the type of the expression {@code null},
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @jls 3.10.7 The Null Literal
* @jls 4.1 The Kinds of Types and Values
* @since 1.6
*/
public interface NullType extends ReferenceType {
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2005, 2020, 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 jdkx.lang.model.type;
/**
* Represents a primitive type. These include
* {@code boolean}, {@code byte}, {@code short}, {@code int},
* {@code long}, {@code char}, {@code float}, and {@code double}.
*
* @author Joseph D. Darcy
* @author Scott Seligman
* @author Peter von der Ah&eacute;
* @jls 4.2 Primitive Types and Values
* @since 1.6
*/
public interface PrimitiveType extends TypeMirror {
}

Some files were not shown because too many files have changed in this diff Show More