A plugin to show desugared and decompiled code produced by javac.
diff --git a/extra/java.debugjavac/.gitignore b/extra/java.debugjavac/.gitignore
new file mode 100644
index 0000000..a6f89c2
--- /dev/null
+++ b/extra/java.debugjavac/.gitignore
@@ -0,0 +1 @@
+/target/
\ No newline at end of file
diff --git a/extra/java.debugjavac/pom.xml b/extra/java.debugjavac/pom.xml
new file mode 100644
index 0000000..4a31783
--- /dev/null
+++ b/extra/java.debugjavac/pom.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.netbeans.modules</groupId>
+ <artifactId>java.debugjavac</artifactId>
+ <version>1.0</version>
+ <packaging>nbm</packaging>
+ <name>Show javac output</name>
+
+ <parent>
+ <groupId>org.apache.netbeans</groupId>
+ <artifactId>netbeans-parent</artifactId>
+ <version>2</version>
+ </parent>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.netbeans.utilities</groupId>
+ <artifactId>nbm-maven-plugin</artifactId>
+ <version>4.3</version>
+ <extensions>true</extensions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.1.2</version>
+ <configuration>
+ <archive>
+ <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.1.2</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>src/main/nbm/manifest.mf</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-api-annotations-common</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-awt</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-nodes</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-text</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-util</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-util-lookup</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-filesystems</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-modules</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-windows</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-api-java</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-api-java-classpath</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-java-platform</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-openide-loaders</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-lexer</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-editor-lib</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-editor-lib2</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-editor</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-editor-errorstripe-api</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-parsing-api</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-core-multiview</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-java-source-base</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-java-source</artifactId>
+ <version>${netbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.netbeans.api</groupId>
+ <artifactId>org-netbeans-modules-nbjunit</artifactId>
+ <version>${netbeans.version}</version>
+ <scope>test</scope>
+ </dependency>
+<!-- <dependency>
+ <groupId>com.sun</groupId>
+ <artifactId>tools-jar</artifactId>
+ <version>8</version>
+ <systemPath>${java.home}/../lib/tools.jar</systemPath>
+ <scope>system</scope>
+ </dependency>-->
+ </dependencies>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <netbeans.version>RELEASE112</netbeans.version>
+ </properties>
+</project>
\ No newline at end of file
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Bundle.properties b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Bundle.properties
new file mode 100644
index 0000000..92d3045
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Bundle.properties
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+OpenIDE-Module-Display-Category=Java
+OpenIDE-Module-Long-Description=\
+ Find out how javac desugars and compiles Java source code
+OpenIDE-Module-Name=Debugging for javac
+CTL_DecompiledTabCaption=Decompiled
+DecompileToolbar.jLabel1.text=Compiler:
+DecompileToolbar.jLabel2.text=Decompiler:
+OpenIDE-Module-Short-Description=Find out how javac desugars and compiles Java source code
+DecompileToolbar.jLabel3.text=Extra javac options:
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/CompilerDescription.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/CompilerDescription.java
new file mode 100644
index 0000000..0353d10
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/CompilerDescription.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.XMLDecoder;
+import java.beans.XMLEncoder;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import org.netbeans.api.java.platform.JavaPlatform;
+import org.netbeans.api.java.platform.JavaPlatformManager;
+import org.netbeans.api.java.platform.Specification;
+import org.netbeans.modules.java.debugjavac.Decompiler.Input;
+import org.netbeans.modules.java.debugjavac.Decompiler.Result;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.modules.InstalledFileLocator;
+import org.openide.modules.SpecificationVersion;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author lahvac
+ */
+public interface CompilerDescription {
+ public String getName();
+ public boolean isValid();
+ public Result decompile(DecompilerDescription decompiler, Input input);
+
+ public static class Factory {
+ private static Collection<? extends CompilerDescription> descriptions;
+ public static synchronized Collection<? extends CompilerDescription> descriptions() {
+ if (descriptions != null) return descriptions;
+
+ File moduleJar = InstalledFileLocator.getDefault().locate("modules/org-netbeans-modules-java-debugjavac.jar", null, false);
+
+ List<CompilerDescription> result = new ArrayList<>();
+
+ for (JavaPlatform platform : JavaPlatformManager.getDefault().getInstalledPlatforms()) {
+ if (!"j2se".equals(platform.getSpecification().getName())) continue;
+
+ for (FileObject installDir : platform.getInstallFolders()) {
+ FileObject toolsJar = installDir.getFileObject("lib/tools.jar");
+ FileObject jrtfs = installDir.getFileObject("lib/jrt-fs.jar");
+
+ if (toolsJar != null || jrtfs != null) {
+ result.add(new ExecCompilerDescription(platform, moduleJar, toolsJar != null ? FileUtil.toFile(toolsJar) : null));
+ }
+ }
+ }
+
+ return descriptions = Collections.unmodifiableList(result);
+ }
+
+ static {
+ JavaPlatformManager.getDefault().addPropertyChangeListener(new PropertyChangeListener() {
+ @Override public void propertyChange(PropertyChangeEvent evt) {
+ synchronized (CompilerDescription.class) {
+ descriptions = null;
+ //TODO: should refresh the existing combos
+ }
+ }
+ });
+ }
+
+ private static class LoaderBased implements CompilerDescription {
+ public final String displayName;
+ public final URL[] jars;
+
+ private LoaderBased(String displayName, URL[] jars) {
+ this.displayName = displayName;
+ this.jars = jars;
+ }
+
+ @Override
+ public String getName() {
+ return displayName;
+ }
+
+ private final AtomicReference<Boolean> valid = new AtomicReference<>();
+
+ public boolean isValid() {
+ Boolean val = valid.get();
+
+ if (val == null) {
+ valid.set(val = isValidImpl());
+ }
+
+ return val;
+ }
+
+ private boolean isValidImpl() {
+ ClassLoader loader = getClassLoader();
+
+ if (loader == null) return false;
+
+ try {
+ Class.forName("javax.tools.ToolProvider", true, loader);
+ return true;
+ } catch (Throwable ex) {
+ return false;
+ }
+ }
+
+ private ClassLoader classLoader;
+
+ private synchronized ClassLoader getClassLoader() {
+ if (classLoader == null) {
+ try {
+ List<URL> urls = new ArrayList<>();
+
+ urls.addAll(Arrays.asList(jars));
+ urls.add(InstalledFileLocator.getDefault().locate("modules/ext/decompile.jar", null, false).toURI().toURL());
+
+ classLoader = new URLClassLoader(urls.toArray(new URL[0]), DecompiledTab.class.getClassLoader());
+ } catch (MalformedURLException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ return classLoader;
+ }
+
+ @Override
+ public Result decompile(DecompilerDescription decompiler, Input input) {
+ ClassLoader loader = getClassLoader();
+
+ if (loader == null) return new Result("Internal error - cannot find ClassLoader.", null, null);
+
+ return decompiler.createDecompiler(loader).decompile(input);
+ }
+
+ }
+
+ private static final class ExecCompilerDescription implements CompilerDescription {
+
+ private static final SpecificationVersion HAS_ADD_EXPORTS = new SpecificationVersion("9");
+
+ private final JavaPlatform platform;
+ private final File moduleJar;
+ private final File toolsJar;
+
+ public ExecCompilerDescription(JavaPlatform platform, File moduleJar, File toolsJar) {
+ this.platform = platform;
+ this.moduleJar = moduleJar;
+ this.toolsJar = toolsJar;
+ }
+
+ @Override
+ public String getName() {
+ return platform.getDisplayName();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true; //TODO
+ }
+
+ @Override
+ public Result decompile(DecompilerDescription decompiler, Input input) {
+ try {
+ List<String> args = new ArrayList<>();
+ args.add(FileUtil.toFile(platform.findTool("java")).getAbsolutePath());
+ if (toolsJar != null) {
+ args.add("-Xbootclasspath/p:" + toolsJar.getAbsolutePath());
+ }
+ if (platform.getSpecification().getVersion().compareTo(HAS_ADD_EXPORTS) >= 0) {
+ args.add("--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED");
+ args.add("--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED");
+ args.add("--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED");
+ args.add("--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED");
+ args.add("--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED");
+ args.add("--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED");
+ args.add("--add-exports=jdk.jdeps/com.sun.tools.javap=ALL-UNNAMED");
+ }
+ args.add("-classpath");
+ args.add(moduleJar.getAbsolutePath());
+ args.add("org.netbeans.modules.java.debugjavac.impl.Main");
+ args.add(decompiler.id);
+ Process process = Runtime.getRuntime().exec(args.toArray(new String[0]));
+ try (XMLEncoder enc = new XMLEncoder(process.getOutputStream())) {
+ enc.writeObject(input);
+ }
+ try (XMLDecoder decl = new XMLDecoder(process.getInputStream())) {
+ return (Result) decl.readObject();
+ }
+ } catch (IOException ex) {
+ StringWriter exception = new StringWriter();
+ try (PrintWriter exceptionPW = new PrintWriter(exception)) {
+ ex.printStackTrace(exceptionPW);
+ }
+ return new Result(exception.toString(), null, null);
+ }
+
+ }
+
+ }
+
+ }
+
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DJavaDataObject.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DJavaDataObject.java
new file mode 100644
index 0000000..8d89340
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DJavaDataObject.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.io.IOException;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.MIMEResolver;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectExistsException;
+import org.openide.loaders.MultiDataObject;
+import org.openide.loaders.MultiFileLoader;
+import org.openide.util.NbBundle.Messages;
+
+@Messages({
+ "LBL_DJava_LOADER=Files of DJava"
+})
+@MIMEResolver.ExtensionRegistration(
+ displayName = "#LBL_DJava_LOADER",
+ mimeType = "text/x-java-decompiled",
+ extension = {"djava"},
+ position = 999207
+ )
+@DataObject.Registration(
+ mimeType = "text/x-java-decompiled",
+// iconBase = "SET/PATH/TO/ICON/HERE",
+ displayName = "#LBL_DJava_LOADER",
+ position = 300
+ )
+public class DJavaDataObject extends MultiDataObject {
+
+ public DJavaDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
+ super(pf, loader);
+ registerEditor("text/x-java-decompiled", false);
+ }
+
+ @Override
+ protected int associateLookup() {
+ return 1;
+ }
+
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.form b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.form
new file mode 100644
index 0000000..14a9135
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.form
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+ <AuxValues>
+ <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+ <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+ <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+ <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+ <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+ </AuxValues>
+
+ <Layout>
+ <DimensionLayout dim="0">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="102" alignment="0" attributes="0">
+ <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
+ <EmptySpace max="-2" attributes="0"/>
+ <Component id="compiler" min="-2" max="-2" attributes="0"/>
+ <EmptySpace type="unrelated" max="-2" attributes="0"/>
+ <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
+ <EmptySpace max="-2" attributes="0"/>
+ <Component id="decompiler" min="-2" max="-2" attributes="0"/>
+ <EmptySpace type="unrelated" max="-2" attributes="0"/>
+ <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
+ <EmptySpace max="-2" attributes="0"/>
+ <Component id="extraOptions" pref="338" max="32767" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ <DimensionLayout dim="1">
+ <Group type="103" groupAlignment="0" attributes="0">
+ <Group type="103" groupAlignment="3" attributes="0">
+ <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="compiler" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="decompiler" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
+ <Component id="extraOptions" alignment="3" min="-2" max="-2" attributes="0"/>
+ </Group>
+ </Group>
+ </DimensionLayout>
+ </Layout>
+ <SubComponents>
+ <Component class="javax.swing.JLabel" name="jLabel1">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/java/debugjavac/Bundle.properties" key="DecompileToolbar.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JComboBox" name="compiler">
+ <Properties>
+ <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+ <StringArray count="4">
+ <StringItem index="0" value="Item 1"/>
+ <StringItem index="1" value="Item 2"/>
+ <StringItem index="2" value="Item 3"/>
+ <StringItem index="3" value="Item 4"/>
+ </StringArray>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JLabel" name="jLabel2">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/java/debugjavac/Bundle.properties" key="DecompileToolbar.jLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JComboBox" name="decompiler">
+ <Properties>
+ <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+ <StringArray count="4">
+ <StringItem index="0" value="Item 1"/>
+ <StringItem index="1" value="Item 2"/>
+ <StringItem index="2" value="Item 3"/>
+ <StringItem index="3" value="Item 4"/>
+ </StringArray>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JLabel" name="jLabel3">
+ <Properties>
+ <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+ <ResourceString bundle="org/netbeans/modules/java/debugjavac/Bundle.properties" key="DecompileToolbar.jLabel3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
+ </Property>
+ </Properties>
+ </Component>
+ <Component class="javax.swing.JComboBox" name="extraOptions">
+ <Properties>
+ <Property name="editable" type="boolean" value="true"/>
+ <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+ <StringArray count="0"/>
+ </Property>
+ </Properties>
+ </Component>
+ </SubComponents>
+</Form>
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.java
new file mode 100644
index 0000000..3b29341
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.java
@@ -0,0 +1,334 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JList;
+import javax.swing.MutableComboBoxModel;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+import org.openide.util.NbPreferences;
+import org.openide.util.WeakListeners;
+
+/**
+ *
+ * @author lahvac
+ */
+public class DecompileToolbar extends javax.swing.JPanel {
+
+ private static final int HISTORY_LIMIT = 2;
+
+ public DecompileToolbar(final FileObject decompiled, final FileObject originalSource) {
+ initComponents();
+
+ DefaultComboBoxModel<CompilerDescription> compilerModel = new DefaultComboBoxModel<>();
+ Collection<? extends CompilerDescription> compilerDescriptions = CompilerDescription.Factory.descriptions();
+
+ for (CompilerDescription cd : compilerDescriptions) {
+ compilerModel.addElement(cd);
+ }
+
+ compiler.setModel(compilerModel);
+ compiler.setRenderer(new DefaultListCellRenderer() {
+ @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ if (value instanceof CompilerDescription) {
+ CompilerDescription compilerDescription = (CompilerDescription) value;
+
+ value = compilerDescription.getName() + (compilerDescription.isValid() ? "" : " - unusable");
+ }
+ return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ }
+ });
+ compiler.addActionListener(new ActionListener() {
+ @Override public void actionPerformed(ActionEvent e) {
+ try {
+ decompiled.setAttribute(CompilerDescription.class.getName(), compiler.getSelectedItem());
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ });
+
+ DefaultComboBoxModel<DecompilerDescription> decompilerModel = new DefaultComboBoxModel<>();
+ List<DecompilerDescription> decompilers = new ArrayList<>();
+
+ if (compilerDescriptions.size() > 0) {
+ compiler.setSelectedIndex(0);
+
+ for (DecompilerDescription decompiler : DecompilerDescription.getDecompilers()) {
+ decompilerModel.addElement(decompiler);
+ decompilers.add(decompiler);
+ }
+ }
+
+ decompiler.setModel(decompilerModel);
+ decompiler.setRenderer(new DefaultListCellRenderer() {
+ @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ if (value instanceof DecompilerDescription) value = ((DecompilerDescription) value).displayName;
+ return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ }
+ });
+ decompiler.addActionListener(new ActionListener() {
+ @Override public void actionPerformed(ActionEvent e) {
+ try {
+ decompiled.setAttribute(DecompilerDescription.class.getName(), ((DecompilerDescription) decompiler.getSelectedItem()).id);
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ });
+ decompiler.setSelectedIndex(0);
+ extraOptions.setModel(createModel());
+ Object extraParams = originalSource.getAttribute(DecompiledTab.PROP_EXTRA_PARAMS);
+ if (extraParams instanceof String) {
+ try {
+ extraOptions.setSelectedItem(extraParams);
+ decompiled.setAttribute(DecompiledTab.PROP_EXTRA_PARAMS, extraParams);
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ extraOptions.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ String selected = (String) extraOptions.getSelectedItem();
+
+ masterModel.removeElement(selected);
+ masterModel.insertElementAt(selected, 0);
+
+ while (masterModel.getSize() > HISTORY_LIMIT) {
+ masterModel.removeElementAt(masterModel.getSize() - 1);
+ }
+
+ storeMasterData();
+
+ extraOptions.getModel().setSelectedItem(selected); //the above changes the selection
+
+ decompiled.setAttribute(DecompiledTab.PROP_EXTRA_PARAMS, selected);
+ originalSource.setAttribute(DecompiledTab.PROP_EXTRA_PARAMS, selected);
+
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ });
+ }
+
+ private static MutableComboBoxModel<String> masterModel;
+
+ private static final String KEY_EXTRA_PARAMS_HISTORY = "extraParamsHistory";
+
+ private static Preferences getHistoryNode() {
+ Preferences prefs = NbPreferences.forModule(DecompileToolbar.class);
+
+ return prefs.node(KEY_EXTRA_PARAMS_HISTORY);
+ }
+
+ private ComboBoxModel<String> createModel() {
+ if (masterModel == null) {
+ masterModel = new DefaultComboBoxModel<>();
+
+ try {
+ Preferences prefs = getHistoryNode();
+ List<String> keys = new ArrayList<>(Arrays.asList(prefs.keys()));
+
+ Collections.sort(keys);
+
+ for (String key : keys) {
+ masterModel.addElement(prefs.get(key, ""));
+ }
+ } catch (BackingStoreException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ return new DelegatingModel(masterModel);
+ }
+
+ private static void storeMasterData() {
+ try {
+ Preferences prefs = getHistoryNode();
+
+ prefs.clear();
+
+ for (int i = 0; i < masterModel.getSize(); i++) {
+ prefs.put(Integer.toString(i), masterModel.getElementAt(i));
+ }
+ } catch (BackingStoreException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ jLabel1 = new javax.swing.JLabel();
+ compiler = new javax.swing.JComboBox();
+ jLabel2 = new javax.swing.JLabel();
+ decompiler = new javax.swing.JComboBox();
+ jLabel3 = new javax.swing.JLabel();
+ extraOptions = new javax.swing.JComboBox();
+
+ org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DecompileToolbar.class, "DecompileToolbar.jLabel1.text")); // NOI18N
+
+ compiler.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
+
+ org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(DecompileToolbar.class, "DecompileToolbar.jLabel2.text")); // NOI18N
+
+ decompiler.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
+
+ org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(DecompileToolbar.class, "DecompileToolbar.jLabel3.text")); // NOI18N
+
+ extraOptions.setEditable(true);
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel1)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(compiler, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(jLabel2)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(decompiler, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(jLabel3)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(extraOptions, 0, 338, Short.MAX_VALUE))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(jLabel1)
+ .addComponent(compiler, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel2)
+ .addComponent(decompiler, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(jLabel3)
+ .addComponent(extraOptions, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ );
+ }// </editor-fold>//GEN-END:initComponents
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JComboBox compiler;
+ private javax.swing.JComboBox decompiler;
+ private javax.swing.JComboBox extraOptions;
+ private javax.swing.JLabel jLabel1;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JLabel jLabel3;
+ // End of variables declaration//GEN-END:variables
+
+ private static class DelegatingModel implements ComboBoxModel<String>, ListDataListener {
+ private final ComboBoxModel<String> master;
+ private final List<ListDataListener> listeners = new ArrayList<>();
+
+ private Object selected;
+
+ public DelegatingModel(ComboBoxModel<String> master) {
+ this.master = master;
+ this.master.addListDataListener(WeakListeners.create(ListDataListener.class, this, master));
+ }
+
+ @Override
+ public void setSelectedItem(Object anItem) {
+ this.selected = anItem;
+ ListDataEvent del = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1);
+
+ for (ListDataListener l : listeners) {
+ l.contentsChanged(del);
+ }
+ }
+
+ @Override
+ public Object getSelectedItem() {
+ return selected;
+ }
+
+ @Override
+ public int getSize() {
+ return master.getSize();
+ }
+
+ @Override
+ public String getElementAt(int index) {
+ return master.getElementAt(index);
+ }
+
+ @Override
+ public void addListDataListener(ListDataListener l) {
+ listeners.add(l);
+ }
+
+ @Override
+ public void removeListDataListener(ListDataListener l) {
+ listeners.remove(l);
+ }
+
+ @Override
+ public void intervalAdded(ListDataEvent e) {
+ ListDataEvent del = new ListDataEvent(this, e.getType(), e.getIndex0(), e.getIndex1());
+
+ for (ListDataListener l : listeners) {
+ l.intervalAdded(del);
+ }
+ }
+
+ @Override
+ public void intervalRemoved(ListDataEvent e) {
+ ListDataEvent del = new ListDataEvent(this, e.getType(), e.getIndex0(), e.getIndex1());
+
+ for (ListDataListener l : listeners) {
+ l.intervalRemoved(del);
+ }
+ }
+
+ @Override
+ public void contentsChanged(ListDataEvent e) {
+ ListDataEvent del = new ListDataEvent(this, e.getType(), e.getIndex0(), e.getIndex1());
+
+ for (ListDataListener l : listeners) {
+ l.contentsChanged(del);
+ }
+ }
+ }
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompiledTab.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompiledTab.java
new file mode 100644
index 0000000..e147644
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompiledTab.java
@@ -0,0 +1,474 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+import java.util.WeakHashMap;
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+import javax.swing.JScrollPane;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.StyledDocument;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.editor.EditorRegistry;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.api.java.source.support.EditorAwareJavaSourceTaskFactory;
+import org.netbeans.core.spi.multiview.CloseOperationState;
+import org.netbeans.core.spi.multiview.MultiViewElement;
+import org.netbeans.core.spi.multiview.MultiViewElementCallback;
+import org.netbeans.editor.GuardedDocument;
+import org.netbeans.modules.editor.NbEditorDocument;
+import org.netbeans.modules.editor.NbEditorKit;
+import org.netbeans.modules.editor.NbEditorUtilities;
+import org.netbeans.modules.java.debugjavac.Decompiler.Input;
+import org.netbeans.modules.java.debugjavac.Decompiler.Result;
+import org.netbeans.modules.parsing.api.Source;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.editor.errorstripe.UpToDateStatus;
+import org.netbeans.spi.editor.errorstripe.UpToDateStatusProvider;
+import org.netbeans.spi.editor.errorstripe.UpToDateStatusProviderFactory;
+import org.openide.awt.UndoRedo;
+import org.openide.cookies.EditorCookie;
+import org.openide.cookies.SaveCookie;
+import org.openide.filesystems.FileAttributeEvent;
+import org.openide.filesystems.FileChangeAdapter;
+import org.openide.filesystems.FileChangeListener;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataObject;
+import org.openide.text.NbDocument;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.windows.TopComponent;
+
+/**
+ *
+ * @author lahvac
+ */
+public class DecompiledTab {
+
+ public static final String PROP_EXTRA_PARAMS = DecompiledTab.class.getName() + ".extraParams";
+
+ private static final String ATTR_DECOMPILED = "decompiled-temporary";
+ private static final Map<FileObject, FileObject> source2Decompiled = new WeakHashMap<>();
+ private static final RequestProcessor DECOMPILE_RUNNER = new RequestProcessor(DecompiledTab.class.getName(), 1, false, false);
+
+ public static synchronized FileObject findDecompiled(FileObject source) {
+ return findDecompiled(source, true);
+ }
+
+ private static synchronized FileObject findDecompiled(FileObject source, boolean create) {
+ FileObject result = source2Decompiled.get(source);
+
+ if (result == null && create) {
+ try {
+ FileObject decompiledFO = FileUtil.createMemoryFileSystem().getRoot().createData(source.getName(), "djava");
+
+ decompiledFO.setAttribute(ATTR_DECOMPILED, Boolean.TRUE);
+
+ source2Decompiled.put(source, result = decompiledFO);
+ } catch (IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ return result;
+ }
+
+ private static @CheckForNull Document decompiledCodeDocument(FileObject file) {
+ return decompiledCodeDocument(file, true);
+ }
+
+ private static @CheckForNull Document decompiledCodeDocument(FileObject file, boolean create) {
+ try {
+ FileObject decompiled = findDecompiled(file, create);
+
+ if (decompiled == null) return null;
+
+ DataObject decompiledDO = DataObject.find(decompiled);
+ EditorCookie ec = decompiledDO.getLookup().lookup(EditorCookie.class);
+ return ec.openDocument();
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ return null;
+ }
+ }
+
+ private static DecompilerDescription findDecompiler(String id) throws MalformedURLException {
+ for (DecompilerDescription decompiler : DecompilerDescription.getDecompilers()) {
+ if (id.equals(decompiler.id)) return decompiler;
+ }
+
+ return null;
+ }
+
+ private static void decompileIntoDocumentLater(final FileObject source) {
+ DECOMPILE_RUNNER.post(new Runnable() {
+ @Override public void run() {
+ doDecompileIntoDocument(source);
+ }
+ });
+
+ }
+
+ private static void doDecompileIntoDocument(FileObject source) {
+ FileObject decompiled = findDecompiled(source, true);
+ final Document doc = decompiledCodeDocument(source);
+
+ if (doc == null || doc.getProperty(DECOMPILE_TAB_ACTIVE) != Boolean.TRUE) return ;
+
+ try {
+ Object compilerDescription = decompiled.getAttribute(CompilerDescription.class.getName());
+ Object decompilerId = decompiled.getAttribute(DecompilerDescription.class.getName());
+ Object extraParams = decompiled.getAttribute(PROP_EXTRA_PARAMS);
+
+ if (!(compilerDescription instanceof CompilerDescription) || !(decompilerId instanceof String)) {
+ return ;
+ }
+
+ if (!(extraParams instanceof String) || extraParams == null) {
+ extraParams = "";
+ }
+
+ final String decompiledCode;
+
+ if (((CompilerDescription) compilerDescription).isValid()) {
+ final String code = Source.create(source).createSnapshot().getText().toString();
+ UpToDateStatusProviderImpl.get(doc).update(UpToDateStatus.UP_TO_DATE_PROCESSING);
+ DecompilerDescription decompiler = findDecompiler((String) decompilerId);
+ Result decompileResult = ((CompilerDescription) compilerDescription).decompile(decompiler, new Input(code, Utilities.commandLineParameters(source, (String) extraParams)));
+ if (decompileResult.exception != null) {
+ decompiledCode = "#Section(text/plain) Ooops, an exception occurred while decompiling:\n" + decompileResult.exception;
+ } else {
+ decompiledCode = (decompileResult.decompiledOutput != null ? "#Section(" + decompileResult.decompiledMimeType + ") Output:\n" + decompileResult.decompiledOutput + "\n" : "") +
+ (decompileResult.compileErrors != null ? "#Section(text/plain) Processing Errors:\n" + decompileResult.compileErrors + "\n" : "");
+ }
+ } else {
+ decompiledCode = "Unusable compiler";
+ }
+
+ NbDocument.runAtomic((StyledDocument) doc, new Runnable() {
+ @Override public void run() {
+ try {
+ doc.remove(0, doc.getLength());
+ if (doc instanceof GuardedDocument) {
+ ((GuardedDocument) doc).getGuardedBlockChain().removeEmptyBlocks();
+ }
+ doc.insertString(0, decompiledCode, null);
+ if (doc instanceof GuardedDocument) {
+ ((GuardedDocument) doc).getGuardedBlockChain().addBlock(0, doc.getLength(), true);
+ }
+ } catch (BadLocationException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ });
+
+ SaveCookie sc = DataObject.find(decompiled).getLookup().lookup(SaveCookie.class);
+
+ if (sc != null) sc.save();
+
+ UpToDateStatusProviderImpl.get(doc).update(UpToDateStatus.UP_TO_DATE_OK);
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ private static final String DECOMPILE_TAB_ACTIVE = "decompile-tab-active";
+
+ @MultiViewElement.Registration(
+ displayName="Decompile",
+// iconBase="org/netbeans/modules/java/resources/class.gif",
+ persistenceType=TopComponent.PERSISTENCE_ONLY_OPENED,
+ preferredID="java.decompile",
+ mimeType="text/x-java",
+ position=5000
+ )
+ public static MultiViewElement createMultiViewEditorElement(Lookup context) {
+ final DataObject d = context.lookup(DataObject.class);
+ final FileObject decompiled = findDecompiled(d.getPrimaryFile(), true);
+ return new MultiViewElement() {
+ private JEditorPane pane;
+ private JComponent scrollPane;
+ private final FileChangeListener fileListener = new FileChangeAdapter() {
+ @Override public void fileAttributeChanged(FileAttributeEvent fe) {
+ decompileIntoDocumentLater(d.getPrimaryFile());
+ }
+ };
+ @Override
+ public JComponent getVisualRepresentation() {
+ if (pane == null) {
+ pane = new JEditorPane();
+ pane.setContentType("text/x-java-decompiled");
+ pane.setEditorKit(new NbEditorKit() {
+ @Override public String getContentType() {
+ return "text/x-java-decompiled";
+ }
+ });
+ Document doc = decompiledCodeDocument(d.getPrimaryFile());
+ if (doc != null)
+ pane.setDocument(doc);
+ scrollPane = doc instanceof NbEditorDocument ? (JComponent) ((NbEditorDocument) doc).createEditor(pane) : new JScrollPane(pane);
+ }
+ return scrollPane;
+ }
+
+ private DecompileToolbar toolbar;
+ @Override
+ public JComponent getToolbarRepresentation() {
+ if (toolbar == null) {
+ FileObject decompiled = findDecompiled(d.getPrimaryFile(), true);
+ toolbar = new DecompileToolbar(decompiled, d.getPrimaryFile());
+ }
+ return toolbar;
+ }
+
+ @Override
+ public Action[] getActions() {
+ return new Action[0];
+ }
+
+ @Override
+ public Lookup getLookup() {
+ return Lookup.EMPTY;
+ }
+
+ @Override
+ public void componentOpened() {
+ }
+
+ @Override
+ public void componentClosed() {
+ }
+
+ @Override
+ public void componentShowing() {
+ Document doc = decompiledCodeDocument(d.getPrimaryFile());
+ if (doc != null) doc.putProperty(DECOMPILE_TAB_ACTIVE, true);
+ decompiled.addFileChangeListener(fileListener);
+ decompileIntoDocumentLater(d.getPrimaryFile());
+ }
+
+ @Override
+ public void componentHidden() {
+ Document doc = decompiledCodeDocument(d.getPrimaryFile());
+ if (doc != null) doc.putProperty(DECOMPILE_TAB_ACTIVE, null);
+ decompiled.removeFileChangeListener(fileListener);
+ }
+
+ @Override
+ public void componentActivated() {
+ }
+
+ @Override
+ public void componentDeactivated() {
+ }
+
+ @Override
+ public UndoRedo getUndoRedo() {
+ return null;
+ }
+
+ @Override
+ public void setMultiViewCallback(MultiViewElementCallback callback) {
+ }
+
+ @Override
+ public CloseOperationState canCloseElement() {
+ return CloseOperationState.STATE_OK;
+ }
+
+ };
+ }
+
+ static {
+ EditorRegistry.addPropertyChangeListener(new DocL());
+ }
+
+ private static final class DocL implements PropertyChangeListener, DocumentListener {
+
+ private Document lastFocused;
+
+ @Override public void propertyChange(PropertyChangeEvent evt) {
+ JTextComponent fc = EditorRegistry.focusedComponent();
+ Document doc = fc != null ? fc.getDocument() : null;
+
+ if (doc == lastFocused) return ;
+
+ if (lastFocused != null) {
+ lastFocused.removeDocumentListener(this);
+ }
+
+ if (doc != null) {
+ doc.addDocumentListener(this);
+ }
+
+ lastFocused = doc;
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ update(e);
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ update(e);
+ }
+
+ private void update(DocumentEvent e) {
+ FileObject file = NbEditorUtilities.getFileObject(e.getDocument());
+
+ if (file == null) return ;
+
+ Document doc = decompiledCodeDocument(file, false);
+
+ if (doc == null) return ;
+
+ UpToDateStatusProviderImpl.get(doc).update(UpToDateStatus.UP_TO_DATE_DIRTY);
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ }
+
+ }
+
+ private static final class Updater implements CancellableTask<CompilationInfo> {
+ @Override public void run(CompilationInfo parameter) throws Exception {
+ doDecompileIntoDocument(parameter.getFileObject());
+// FileObject sourceFile = parameter.getFileObject();
+//// if (sourceFile.getAttribute(ATTR_DECOMPILED) == Boolean.TRUE) return;
+// final FileObject decompiled = findDecompiled(sourceFile, false);
+//
+// if (decompiled == null) return ;
+//
+// final ElementHandle<?> handle = ElementHandle.create(parameter.getTopLevelElements().get(0));
+//
+// JavaSource.create(parameter.getClasspathInfo(), decompiled).runModificationTask(new Task<WorkingCopy>() {
+// @Override public void run(WorkingCopy copy) throws Exception {
+// copy.toPhase(Phase.RESOLVED);
+//
+// copy.rewrite(copy.getCompilationUnit(), CodeGenerator.generateCode(copy, (TypeElement) handle.resolve(copy)));
+// }
+// }).commit();
+//
+// SaveCookie sc = DataObject.find(decompiled).getLookup().lookup(SaveCookie.class);
+//
+// if (sc != null) sc.save();
+ }
+
+ @Override public void cancel() {
+ }
+
+ }
+
+ @ServiceProvider(service=JavaSourceTaskFactory.class)
+ public static final class UpdaterFactory extends EditorAwareJavaSourceTaskFactory {
+
+ public UpdaterFactory() {
+ super(Phase.RESOLVED, Priority.LOW, TaskIndexingMode.ALLOWED_DURING_SCAN);
+ }
+
+ @Override
+ protected CancellableTask<CompilationInfo> createTask(FileObject file) {
+ return new Updater();
+ }
+
+ }
+
+ @MimeRegistration(mimeType="text/x-java-decompiled", service=UpToDateStatusProviderFactory.class)
+ public static final class UpToDateStatusProviderFactoryImpl implements UpToDateStatusProviderFactory {
+ @Override public UpToDateStatusProvider createUpToDateStatusProvider(Document document) {
+ return UpToDateStatusProviderImpl.get(document);
+ }
+ }
+
+ private static final class UpToDateStatusProviderImpl extends UpToDateStatusProvider {
+
+ public static UpToDateStatusProviderImpl get(Document doc) {
+ UpToDateStatusProviderImpl result = (UpToDateStatusProviderImpl) doc.getProperty(UpToDateStatusProviderImpl.class);
+
+ if (result == null) {
+ result = new UpToDateStatusProviderImpl(doc);
+ }
+
+ return result;
+ }
+
+ private final Document doc;
+
+ private UpToDateStatusProviderImpl(Document doc) {
+ this.doc = doc;
+ }
+
+ @Override
+ public UpToDateStatus getUpToDate() {
+ UpToDateStatus status = (UpToDateStatus) doc.getProperty(UpToDateStatusProviderImpl.class.getName() + "-status-value");
+
+ if (status == null) status = UpToDateStatus.UP_TO_DATE_DIRTY;
+
+ return status;
+ }
+
+ private void update(UpToDateStatus newValue) {
+ UpToDateStatus oldValue = getUpToDate();
+
+ doc.putProperty(UpToDateStatusProviderImpl.class.getName() + "-status-value", newValue);
+ firePropertyChange(PROP_UP_TO_DATE, oldValue, newValue);
+
+ //TODO: the event above does not always repaint the error stripe, workarounding:
+ NbDocument.runAtomic((StyledDocument) doc, new Runnable() {
+ @Override public void run() {
+ try {
+ doc.insertString(0, " ", null);
+ doc.remove(0, 1);
+ } catch (BadLocationException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ });
+
+ try {
+ FileObject decompiledFO = NbEditorUtilities.getFileObject(doc);
+ SaveCookie sc = decompiledFO != null ? DataObject.find(decompiledFO).getLookup().lookup(SaveCookie.class) : null;
+
+ if (sc != null) sc.save();
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Decompiler.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Decompiler.java
new file mode 100644
index 0000000..c02a0da
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Decompiler.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.util.List;
+
+/**
+ *
+ * @author lahvac
+ */
+public interface Decompiler {
+ public Result decompile(Input input);
+
+ public final class Input {
+ public String source;
+ public List<String> params;
+
+ public Input() {
+ }
+
+ public Input(String source, List<String> params) {
+ this.source = source;
+ this.params = params;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public List<String> getParams() {
+ return params;
+ }
+
+ public void setParams(List<String> params) {
+ this.params = params;
+ }
+
+ }
+// public final class Input {
+// public final String source;
+// public final List<String> params;
+// public Input(String source, List<String> params) {
+// this.source = source;
+// this.params = params;
+// }
+// }
+ public final class Result {
+ public String compileErrors;
+ public String decompiledOutput;
+ public String decompiledMimeType;
+ public String exception;
+
+ public Result() {
+ }
+
+ public Result(String compileErrors, String decompiledOutput, String decompiledMimeType) {
+ this.compileErrors = compileErrors.trim().isEmpty() ? null : compileErrors;
+ this.decompiledOutput = decompiledOutput.trim().isEmpty() ? null : decompiledOutput;
+ this.decompiledMimeType = decompiledMimeType;
+ }
+
+ public Result(String exception) {
+ this.exception = exception;
+ }
+
+ public String getCompileErrors() {
+ return compileErrors;
+ }
+
+ public void setCompileErrors(String compileErrors) {
+ this.compileErrors = compileErrors;
+ }
+
+ public String getDecompiledOutput() {
+ return decompiledOutput;
+ }
+
+ public void setDecompiledOutput(String decompiledOutput) {
+ this.decompiledOutput = decompiledOutput;
+ }
+
+ public String getDecompiledMimeType() {
+ return decompiledMimeType;
+ }
+
+ public void setDecompiledMimeType(String decompiledMimeType) {
+ this.decompiledMimeType = decompiledMimeType;
+ }
+
+ public String getException() {
+ return exception;
+ }
+
+ public void setException(String exception) {
+ this.exception = exception;
+ }
+ }
+// public final class Result {
+// public final String compileErrors;
+// public final String decompiledOutput;
+// public final String decompiledMimeType;
+// public Result(String compileErrors, String decompiledOutput, String decompiledMimeType) {
+// this.compileErrors = compileErrors.trim().isEmpty() ? null : compileErrors;
+// this.decompiledOutput = decompiledOutput.trim().isEmpty() ? null : decompiledOutput;
+// this.decompiledMimeType = decompiledMimeType;
+// }
+// }
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompilerDescription.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompilerDescription.java
new file mode 100644
index 0000000..0f4bfe8
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompilerDescription.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.util.Arrays;
+
+/**
+ *
+ * @author lahvac
+ */
+public final class DecompilerDescription {
+ public final String id;
+ public final String displayName;
+ public final String className;
+
+ private DecompilerDescription(String id, String displayName, String className) {
+ this.id = id;
+ this.displayName = displayName;
+ this.className = className;
+ }
+
+ public Decompiler createDecompiler(ClassLoader from) {
+ try {
+ Class<?> loadClass = from.loadClass(className);
+
+ return Decompiler.class.cast(loadClass.newInstance());
+ } catch (ReflectiveOperationException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ private static final Iterable<? extends DecompilerDescription> DECOMPILERS = Arrays.asList(
+ new DecompilerDescription("javap", "javap", "org.netbeans.modules.java.debugjavac.impl.JavapDecompilerImpl"),
+ new DecompilerDescription("lower", "Desugared source", "org.netbeans.modules.java.debugjavac.impl.DesugarDecompilerImpl")
+ );
+
+ public static Iterable<? extends DecompilerDescription> getDecompilers() {
+ return DECOMPILERS;
+ }
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelLexer.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelLexer.java
new file mode 100644
index 0000000..548bfe8
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelLexer.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.netbeans.api.lexer.Token;
+import org.netbeans.spi.lexer.Lexer;
+import org.netbeans.spi.lexer.LexerInput;
+import org.netbeans.spi.lexer.LexerRestartInfo;
+import org.netbeans.spi.lexer.TokenFactory;
+
+/**
+ *
+ * @author lahvac
+ */
+public class TopLevelLexer implements Lexer<TopLevelTokenId> {
+
+ private static final Pattern SECTION_PATTERN = Pattern.compile("#Section\\(([^)]*)\\)[^\n]*\n");
+ private final TokenFactory<TopLevelTokenId> factory;
+ private final LexerInput input;
+ private TopLevelTokenId futureToken;
+
+ public TopLevelLexer(LexerRestartInfo<TopLevelTokenId> restart) {
+ this.factory = restart.tokenFactory();
+ this.input = restart.input();
+ this.futureToken = restart.state() != null ? (TopLevelTokenId) restart.state() : TopLevelTokenId.OTHER;
+ }
+
+ @Override
+ public Token<TopLevelTokenId> nextToken() {
+ StringBuilder text = new StringBuilder();
+ int read;
+
+ while ((read = input.read()) != LexerInput.EOF) {
+ text.append((char) read);
+
+ Matcher m = SECTION_PATTERN.matcher(text);
+
+ if (m.find()) {
+ if (m.start() == 0) {
+ String mimeType = m.group(1);
+
+ switch (mimeType) {
+ case "text/x-java": futureToken = TopLevelTokenId.JAVA; break;
+ case "text/x-java-bytecode": futureToken = TopLevelTokenId.ASM; break;
+ default: futureToken = TopLevelTokenId.OTHER; break;
+ }
+
+ return factory.createToken(TopLevelTokenId.SECTION_HEADER);
+ } else {
+ input.backup(input.readLength() - m.start());
+ break;
+ }
+ }
+ }
+
+ if (input.readLength() > 0)
+ return factory.createToken(futureToken);
+
+ return null;
+ }
+
+ @Override
+ public Object state() {
+ return futureToken;
+ }
+
+ @Override
+ public void release() {}
+
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelTokenId.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelTokenId.java
new file mode 100644
index 0000000..5c1d401
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelTokenId.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.lexer.InputAttributes;
+import org.netbeans.api.lexer.Language;
+import org.netbeans.api.lexer.LanguagePath;
+import org.netbeans.api.lexer.Token;
+import org.netbeans.api.lexer.TokenId;
+import org.netbeans.spi.lexer.EmbeddingPresence;
+import org.netbeans.spi.lexer.LanguageEmbedding;
+import org.netbeans.spi.lexer.LanguageHierarchy;
+import org.netbeans.spi.lexer.Lexer;
+import org.netbeans.spi.lexer.LexerRestartInfo;
+
+/**
+ *
+ * @author lahvac
+ */
+public enum TopLevelTokenId implements TokenId {
+ SECTION_HEADER,
+ JAVA,
+ ASM,
+ OTHER;
+
+ @Override
+ public String primaryCategory() {
+ return this == SECTION_HEADER ? "comment" : "code";
+ }
+
+ private static final Language<TopLevelTokenId> LANGUAGE = new LanguageHierarchy<TopLevelTokenId>() {
+ @Override protected Collection<TopLevelTokenId> createTokenIds() {
+ return Arrays.asList(TopLevelTokenId.values());
+ }
+ @Override protected Lexer<TopLevelTokenId> createLexer(LexerRestartInfo<TopLevelTokenId> info) {
+ return new TopLevelLexer(info);
+ }
+ @Override protected String mimeType() {
+ return "text/x-java-decompiled";
+ }
+ @Override protected LanguageEmbedding<?> embedding(Token<TopLevelTokenId> token, LanguagePath languagePath, InputAttributes inputAttributes) {
+ switch (token.id()) {
+ case JAVA:
+ return LanguageEmbedding.create(Language.find("text/x-java"), 0, 0);
+ default:
+ return null;
+ }
+ }
+
+ }.language();
+
+ @MimeRegistration(mimeType="text/x-java-decompiled", service=Language.class)
+ public static final Language<TopLevelTokenId> language() {
+ return LANGUAGE;
+ }
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Utilities.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Utilities.java
new file mode 100644
index 0000000..dfd635c
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Utilities.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.ClassPath.PathConversionMode;
+import org.netbeans.api.java.platform.JavaPlatform;
+import org.netbeans.api.java.queries.SourceLevelQuery;
+import org.openide.filesystems.FileObject;
+
+/**
+ *
+ * @author lahvac
+ */
+public class Utilities {
+ public static List<String> commandLineParameters(FileObject source, String extraParameters) throws IOException {
+ List<String> extraParams = new ArrayList<>();
+ Set<String> extraParamsSet = new HashSet<>();
+ StringBuilder param = new StringBuilder();
+ char currentQuote = '\0';
+ for (char c : extraParameters.toCharArray()) {
+ switch (c) {
+ case '\r': continue;
+ case ' ': case '\n': case '\t':
+ if (currentQuote == '\0') {
+ String p = param.toString();
+ param.delete(0, param.length());
+ extraParams.add(p);
+ extraParamsSet.add(p);
+ continue;
+ }
+ break;
+ case '"':
+ if (currentQuote == '"') {
+ currentQuote = '\0';
+ continue;
+ } else if (currentQuote == '\0') {
+ currentQuote = '"';
+ continue;
+ }
+ break;
+ case '\'':
+ if (currentQuote == '\'') {
+ currentQuote = '\0';
+ continue;
+ } else if (currentQuote == '\0') {
+ currentQuote = '\'';
+ continue;
+ }
+ break;
+ }
+
+ param.append(c);
+ }
+ if (param.length() > 0) {
+ String p = param.toString();
+ extraParams.add(p);
+ extraParamsSet.add(p);
+ }
+ List<String> result = new ArrayList<>();
+// if (!extraParamsSet.contains("-bootclasspath")) {
+// ClassPath boot = ClassPath.getClassPath(source, ClassPath.BOOT);
+// if (boot == null) boot = JavaPlatform.getDefault().getBootstrapLibraries();
+// result.add("-bootclasspath");
+// result.add(boot.toString(PathConversionMode.PRINT));
+// }
+ if (!extraParamsSet.contains("-classpath")) {
+ ClassPath compile = ClassPath.getClassPath(source, ClassPath.COMPILE);
+ if (compile == null) compile = ClassPath.EMPTY;
+ result.add("-classpath");
+ result.add(compile.toString(PathConversionMode.PRINT));
+ }
+ String sourceLevel = SourceLevelQuery.getSourceLevel(source);
+ sourceLevel = sourceLevel != null ? sourceLevel : "1.8";
+ if (!extraParamsSet.contains("-source")) {
+ result.add("-source");
+ result.add(sourceLevel);
+ }
+ if (!extraParamsSet.contains("-target")) {
+ result.add("-target");
+ result.add(sourceLevel);
+ }
+
+ result.addAll(extraParams);
+
+ return result;
+ }
+
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/DesugarDecompilerImpl.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/DesugarDecompilerImpl.java
new file mode 100644
index 0000000..016ad4b
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/DesugarDecompilerImpl.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac.impl;
+
+import com.sun.source.tree.ImportTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.util.Context.Factory;
+import com.sun.tools.javac.util.Pair;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Queue;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileObject;
+import org.netbeans.modules.java.debugjavac.Decompiler;
+
+/**
+ *
+ * @author lahvac
+ */
+public class DesugarDecompilerImpl implements Decompiler {
+
+ @Override
+ public Result decompile(Input input) {
+ StringWriter errors = new StringWriter();
+ StringWriter decompiled = new StringWriter();
+
+ try {
+ DiagnosticListener<JavaFileObject> errorsListener = Utilities.errorReportingDiagnosticListener(errors);
+ JavaFileObject file = Utilities.sourceFileObject(input.source);
+ JavacTask task = JavacTool.create().getTask(null,
+ null,
+ errorsListener, Utilities.augmentCommandLineParameters(input), null, Arrays.asList(file));
+
+ JavaCompilerOverride.preRegister(((JavacTaskImpl) task).getContext(), decompiled);
+ task.generate();
+ } catch (IOException ex) {
+ ex.printStackTrace(new PrintWriter(errors));
+ }
+
+ return new Result(errors.toString(), decompiled.toString().trim(), "text/x-java");
+ }
+
+ static class JavaCompilerOverride extends JavaCompiler {
+ public static void preRegister(com.sun.tools.javac.util.Context context, final StringWriter out) {
+ context.put(compilerKey, new Factory<JavaCompiler>() {
+ @Override public JavaCompiler make(com.sun.tools.javac.util.Context c) {
+ return new JavaCompilerOverride(out, c);
+ }
+ });
+ }
+ private final StringWriter out;
+
+ public JavaCompilerOverride(StringWriter out, com.sun.tools.javac.util.Context context) {
+ super(context);
+ this.out = out;
+ }
+
+ @Override public void generate(Queue<Pair<Env<AttrContext>, JCClassDecl>> queue, Queue<JavaFileObject> results) {
+ Pair<Env<AttrContext>, JCClassDecl> first = queue.peek();
+
+ if (first != null) {
+ if (first.fst.toplevel.getPackageName() != null) {
+ out.write("package ");
+ out.write(first.fst.toplevel.getPackageName().toString());
+ out.write(";\n\n");
+ }
+
+ boolean hasImports = false;
+
+ for (Tree importCandidate : first.fst.toplevel.defs) {
+ if (importCandidate != null && importCandidate.getKind() == Kind.IMPORT) {
+ out.write("import ");
+ ImportTree importTree = (ImportTree) importCandidate;
+ if (importTree.isStatic()) {
+ out.write("static ");
+ }
+ out.write(importTree.getQualifiedIdentifier().toString());
+ out.write(";\n");
+ hasImports = true;
+ }
+ }
+
+ if (hasImports) {
+ out.write("\n");
+ }
+
+ for (Pair<Env<AttrContext>, JCClassDecl> q : queue) {
+ out.write(q.snd.toString());
+ out.write("\n");
+ }
+ }
+ }
+ }
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/JavapDecompilerImpl.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/JavapDecompilerImpl.java
new file mode 100644
index 0000000..a6afba7
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/JavapDecompilerImpl.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac.impl;
+
+import com.sun.tools.classfile.ConstantPoolException;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javap.Context;
+import com.sun.tools.javap.JavapTask;
+import com.sun.tools.javap.JavapTask.BadArgs;
+import com.sun.tools.javap.JavapTask.ClassFileInfo;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.tools.DiagnosticListener;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import org.netbeans.modules.java.debugjavac.Decompiler;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author lahvac
+ */
+public class JavapDecompilerImpl implements Decompiler {
+
+ @Override
+ public Result decompile(Input input) {
+ StringWriter errors = new StringWriter();
+ StringWriter decompiled = new StringWriter();
+ try {
+ final Map<String, byte[]> bytecode = compile(input, errors);
+
+ if (!bytecode.isEmpty()) {
+ for (final Entry<String, byte[]> e : bytecode.entrySet()) {
+ class JavapTaskImpl extends JavapTask {
+ public Context getContext() {
+ return context;
+ }
+ }
+ JavapTaskImpl t = new JavapTaskImpl();
+ List<String> options = new ArrayList<String>();
+ options.add("-private");
+ options.add("-verbose");
+ options.add(e.getKey());
+ t.handleOptions(options.toArray(new String[0]));
+ t.getContext().put(PrintWriter.class, new PrintWriter(decompiled));
+ ClassFileInfo cfi = t.read(new SimpleJavaFileObject(URI.create("mem://mem"), Kind.CLASS) {
+ @Override public InputStream openInputStream() throws IOException {
+ return new ByteArrayInputStream(e.getValue());
+ }
+ });
+
+ t.write(cfi);
+ }
+ }
+ } catch (IOException | ConstantPoolException ex) {
+ ex.printStackTrace(new PrintWriter(errors));
+ } catch (BadArgs ex) {
+ Exceptions.printStackTrace(ex);
+ }
+
+ return new Result(errors.toString(), decompiled.toString(), "text/x-java-bytecode");
+ }
+
+ private static Map<String, byte[]> compile(Input input, final StringWriter errors) throws IOException {
+ DiagnosticListener<JavaFileObject> errorsListener = Utilities.errorReportingDiagnosticListener(errors);
+ StandardJavaFileManager sjfm = JavacTool.create().getStandardFileManager(errorsListener, null, null);
+ final Map<String, ByteArrayOutputStream> class2BAOS = new HashMap<String, ByteArrayOutputStream>();
+
+ JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, javax.tools.FileObject sibling) throws IOException {
+ final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+ class2BAOS.put(className, buffer);
+ return new SimpleJavaFileObject(sibling.toUri(), kind) {
+ @Override
+ public OutputStream openOutputStream() throws IOException {
+ return buffer;
+ }
+ };
+ }
+ };
+
+ JavaFileObject file = Utilities.sourceFileObject(input.source);
+ JavacTool.create().getTask(null, jfm, errorsListener, /*XXX:*/Utilities.augmentCommandLineParameters(input), null, Arrays.asList(file)).call();
+
+ Map<String, byte[]> result = new HashMap<String, byte[]>();
+
+ for (Map.Entry<String, ByteArrayOutputStream> e : class2BAOS.entrySet()) {
+ result.put(e.getKey(), e.getValue().toByteArray());
+ }
+
+ return result;
+ }
+
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Main.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Main.java
new file mode 100644
index 0000000..932ec28
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Main.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac.impl;
+
+import java.beans.XMLDecoder;
+import java.beans.XMLEncoder;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import org.netbeans.modules.java.debugjavac.Decompiler.Input;
+import org.netbeans.modules.java.debugjavac.Decompiler.Result;
+import org.netbeans.modules.java.debugjavac.DecompilerDescription;
+
+/**
+ *
+ * @author lahvac
+ */
+public class Main {
+ public static void main(String... args) {
+ try {
+ Input input;
+
+ try (XMLDecoder decoder = new XMLDecoder(System.in)) {
+ input = (Input) decoder.readObject();
+ }
+
+ String id = args[0];
+
+ for (DecompilerDescription desc : DecompilerDescription.getDecompilers()) {
+ if (id.equals(desc.id)) {
+ Result result = desc.createDecompiler(Main.class.getClassLoader()).decompile(input);
+ try (XMLEncoder enc = new XMLEncoder(System.out)) {
+ enc.writeObject(result);
+ }
+ return ;
+ }
+ }
+
+ throw new IllegalStateException("Cannot find: " + id);
+ } catch (Throwable t) {
+ try (XMLEncoder enc = new XMLEncoder(System.out)) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ pw.close();
+ enc.writeObject(new Result(sw.toString()));
+ }
+ }
+ }
+}
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Utilities.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Utilities.java
new file mode 100644
index 0000000..768f01d
--- /dev/null
+++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Utilities.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.debugjavac.impl;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+import org.netbeans.modules.java.debugjavac.Decompiler.Input;
+
+/**
+ *
+ * @author lahvac
+ */
+public class Utilities {
+ public static List<String> augmentCommandLineParameters(Input input) throws IOException {
+ try {
+ Class.forName("com.sun.tools.javac.comp.Repair");
+ List<String> augmentedParams = new ArrayList<>(input.params);
+ augmentedParams.add("-XDshouldStopPolicy=GENERATE");
+ return augmentedParams;
+ } catch (ClassNotFoundException ex) {
+ //OK
+ return input.params;
+ }
+ }
+
+ public static JavaFileObject sourceFileObject(final String code) {
+ return new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) {
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+ return code;
+ }
+ @Override public boolean isNameCompatible(String simpleName, Kind kind) {
+ return true;
+ }
+ };
+ }
+
+ public static DiagnosticListener<JavaFileObject> errorReportingDiagnosticListener(final StringWriter out) {
+ return new DiagnosticListener<JavaFileObject>() {
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+ out.write(diagnostic.getMessage(null));
+ out.write("\n");
+ }
+ }
+ };
+ }
+}
diff --git a/extra/java.debugjavac/src/main/nbm/manifest.mf b/extra/java.debugjavac/src/main/nbm/manifest.mf
new file mode 100644
index 0000000..f8a1cfd
--- /dev/null
+++ b/extra/java.debugjavac/src/main/nbm/manifest.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/debugjavac/Bundle.properties
+
diff --git a/extra/java.debugjavac/src/main/resources/org/netbeans/modules/java/debugjavac/Bundle.properties b/extra/java.debugjavac/src/main/resources/org/netbeans/modules/java/debugjavac/Bundle.properties
new file mode 100644
index 0000000..fbf3218
--- /dev/null
+++ b/extra/java.debugjavac/src/main/resources/org/netbeans/modules/java/debugjavac/Bundle.properties
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+#Localized module labels. Defaults taken from POM (<name>, <description>, <groupId>) if unset.
+#OpenIDE-Module-Name=
+#OpenIDE-Module-Short-Description=
+#OpenIDE-Module-Long-Description=
+#OpenIDE-Module-Display-Category=
+#Fri Jan 24 07:23:58 CET 2020