package krasa.formatter.eclipse;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import krasa.formatter.common.ModifiableFile;
import krasa.formatter.exception.FileDoesNotExistsException;
import krasa.formatter.exception.FormattingFailedException;
import krasa.formatter.plugin.Notifier;
import krasa.formatter.settings.Settings;
import krasa.formatter.settings.provider.JavaPropertiesProvider;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.command.impl.DummyProject;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.JavaPsiImplementationHelper;
/**
* @author Vojtech Krasa
*/
public class JavaCodeFormatterFacade extends CodeFormatterFacade {
private static final Logger LOG = Logger.getInstance(JavaCodeFormatterFacade.class.getName());
private Settings.FormatterVersion version;
private Project project;
private String pathToEclipse;
protected EclipseFormatterAdapter codeFormatter;
private LanguageLevel effectiveLanguageLevel;
private JavaPropertiesProvider javaPropertiesProvider;
protected ModifiableFile.Monitor lastState;
public JavaCodeFormatterFacade(JavaPropertiesProvider javaPropertiesProvider, Settings.FormatterVersion version,
Project project, String pathToEclipse) {
this.javaPropertiesProvider = javaPropertiesProvider;
this.version = version;
this.project = project;
this.pathToEclipse = pathToEclipse;
}
@Override
public String format(String text, int startOffset, int endOffset, PsiFile psiFile)
throws FileDoesNotExistsException {
LanguageLevel languageLevel = getLanguageLevel(psiFile);
return getCodeFormatter(languageLevel).format(text, startOffset, endOffset, languageLevel);
}
private EclipseFormatterAdapter getCodeFormatter(LanguageLevel level) throws FileDoesNotExistsException {
if (codeFormatter == null || javaPropertiesProvider.wasChanged(lastState)
|| this.effectiveLanguageLevel != level) {
return newCodeFormatter(level);
}
return codeFormatter;
}
private EclipseFormatterAdapter newCodeFormatter(LanguageLevel level) {
lastState = javaPropertiesProvider.getModifiedMonitor();
Properties options = javaPropertiesProvider.get();
String substring = level.name().replace("_", ".").substring(4);
// test
new Double(substring);
options.setProperty("org.eclipse.jdt.core.compiler.source", substring);
options.setProperty("org.eclipse.jdt.core.compiler.codegen.targetPlatform", substring);
options.setProperty("org.eclipse.jdt.core.compiler.compliance", substring);
this.effectiveLanguageLevel = level;
try {
Class<?> aClass;
if (version == Settings.FormatterVersion.ECLIPSE_44) {
aClass = getAdapter44();
} else {
if (version == Settings.FormatterVersion.CUSTOM && SystemInfo.isJavaVersionAtLeast("1.7")) {
aClass = getCustomAdapter(pathToEclipse);
} else if (SystemInfo.isJavaVersionAtLeast("1.8")) {
aClass = getAdapter();
} else {
aClass = getAdapter44();
Notifier.notifyOldJRE(project);
}
}
Constructor<?> constructor = aClass.getConstructor(Map.class);
codeFormatter = (EclipseFormatterAdapter) constructor.newInstance(toMap(options));
} catch (FormattingFailedException e) {
throw e;
} catch (Throwable e) {
// rethrow to have this plugin as a cause of the error report
throw new RuntimeException(e);
}
return codeFormatter;
}
/**
* TODO CACHE BETWEEN PROJECTS
*/
private Class<?> getCustomAdapter(String pathToEclipse) throws ClassNotFoundException {
ConfigurableEclipseLocation configurableEclipseLocation = new ConfigurableEclipseLocation();
List<URL> urlList = configurableEclipseLocation.run(pathToEclipse.trim());
if (urlList.isEmpty()) {
throw new FormattingFailedException("Invalid path to Eclipse, no jars found in '" + pathToEclipse + "'", true);
}
ClassLoader classLoader = Classloaders.getCustomClassloader(urlList);
Class<?> aClass = Class.forName("krasa.formatter.adapter.EclipseJavaFormatterAdapter", true, classLoader);
return aClass;
}
private Class<?> getAdapter44() throws ClassNotFoundException {
ClassLoader classLoader = Classloaders.getEclipse44();
Class<?> aClass = Class.forName("krasa.formatter.adapter.EclipseJavaFormatterAdapter44", true, classLoader);
return aClass;
}
private Class<?> getAdapter() throws ClassNotFoundException {
ClassLoader classLoader = Classloaders.getEclipse();
Class<?> aClass = Class.forName("krasa.formatter.adapter.EclipseJavaFormatterAdapter", true, classLoader);
return aClass;
}
@NotNull
protected LanguageLevel getLanguageLevel(@NotNull PsiFile psiFile) {
if (DummyProject.getInstance() == project) {
return LanguageLevel.JDK_1_7; // tests hack
}
JavaPsiImplementationHelper instance = JavaPsiImplementationHelper.getInstance(project);
LanguageLevel languageLevel = null;
try {
Method getClassesLanguageLevel = instance.getClass().getMethod("getClassesLanguageLevel",
VirtualFile.class);
languageLevel = (LanguageLevel) getClassesLanguageLevel.invoke(instance, psiFile.getVirtualFile());
} catch (Exception e) {
try {
Method getClassesLanguageLevel = instance.getClass().getMethod("getEffectiveLanguageLevel",
VirtualFile.class);
languageLevel = (LanguageLevel) getClassesLanguageLevel.invoke(instance, psiFile.getVirtualFile());
} catch (Exception e1) {
LOG.error("Please report this", e);
LOG.error("Please report this", e1);
}
}
if (languageLevel == null) {
languageLevel = LanguageLevel.JDK_1_7;
}
return languageLevel;
}
}