| /* |
| * Copyright 2002-2007 Sascha Weinreuter |
| * |
| * Licensed 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.intellij.plugins.xsltDebugger; |
| |
| import com.intellij.diagnostic.logging.AdditionalTabComponent; |
| import com.intellij.diagnostic.logging.LogConsoleManagerBase; |
| import com.intellij.execution.CantRunException; |
| import com.intellij.execution.configurations.AdditionalTabComponentManager; |
| import com.intellij.execution.configurations.SimpleJavaParameters; |
| import com.intellij.execution.process.ProcessHandler; |
| import com.intellij.execution.process.ProcessListener; |
| import com.intellij.execution.runners.RunTab; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.plugins.IdeaPluginDescriptor; |
| import com.intellij.ide.plugins.PluginManager; |
| import com.intellij.ide.plugins.PluginManagerCore; |
| import com.intellij.openapi.application.PathManager; |
| import com.intellij.openapi.application.ex.ApplicationManagerEx; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.extensions.PluginId; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.projectRoots.Sdk; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.UserDataHolder; |
| import com.intellij.openapi.vfs.JarFileSystem; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.util.PlatformIcons; |
| import com.intellij.util.net.NetUtils; |
| import org.intellij.lang.xpath.xslt.XsltSupport; |
| import org.intellij.lang.xpath.xslt.impl.XsltChecker; |
| import org.intellij.lang.xpath.xslt.run.XsltRunConfiguration; |
| import org.intellij.lang.xpath.xslt.run.XsltRunnerExtension; |
| import org.intellij.plugins.xsltDebugger.ui.OutputTabComponent; |
| import org.intellij.plugins.xsltDebugger.ui.StructureTabComponent; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.jar.Attributes; |
| import java.util.jar.Manifest; |
| |
| /** |
| * Extension for XPathView that hooks into the execution of XSLT-scripts. Manages the creation of the XSLT-Debugger UI |
| * and ensures the debugged process is started with the required JVM properties. |
| */ |
| public class XsltDebuggerExtension extends XsltRunnerExtension { |
| private static final Logger LOG = Logger.getInstance(XsltDebuggerExtension.class.getName()); |
| |
| public static final Key<XsltChecker.LanguageLevel> VERSION = Key.create("VERSION"); |
| private static final Key<Integer> PORT = Key.create("PORT"); |
| private static final Key<Manifest> MANIFEST = Key.create("MANIFEST"); |
| |
| @NonNls |
| private static final String SAXON_6_JAR = "saxon.jar"; |
| |
| @NonNls |
| private static final String SAXON_9_JAR = "saxon9he.jar"; |
| |
| protected boolean supports(XsltRunConfiguration config, boolean debugger) { |
| return (debugger || XsltDebuggerRunner.ACTIVE.get() == Boolean.TRUE) && |
| config.getOutputType() == XsltRunConfiguration.OutputType.CONSOLE; // ? |
| } |
| |
| public ProcessListener createProcessListener(Project project, UserDataHolder extensionData) { |
| final Integer port = extensionData.getUserData(PORT); |
| assert port != null; |
| return new DebugProcessListener(project, port); |
| } |
| |
| public boolean createTabs(Project project, |
| AdditionalTabComponentManager manager, |
| AdditionalTabComponent outputConsole, |
| ProcessHandler process) { |
| if (manager instanceof RunTab) { |
| LogConsoleManagerBase runTab = ((RunTab)manager).getLogConsoleManager(); |
| runTab.addAdditionalTabComponent(new OutputTabComponent(outputConsole), "XSLT-Output", AllIcons.Debugger.Console); |
| runTab.addAdditionalTabComponent(StructureTabComponent.create(process, outputConsole), "XSLT-Structure", PlatformIcons.FLATTEN_PACKAGES_ICON); |
| } |
| else { |
| manager.addAdditionalTabComponent(new OutputTabComponent(outputConsole), "XSLT-Output"); |
| manager.addAdditionalTabComponent(StructureTabComponent.create(process, outputConsole), "XSLT-Structure"); |
| } |
| return true; |
| } |
| |
| public void patchParameters(final SimpleJavaParameters parameters, XsltRunConfiguration configuration, UserDataHolder extensionData) |
| throws CantRunException { |
| final XsltRunConfiguration.OutputType outputType = configuration.getOutputType(); |
| |
| final Sdk jdk = configuration.getEffectiveJDK(); |
| assert jdk != null; |
| |
| final String ver = jdk.getVersionString(); |
| if (ver == null || (ver.contains("1.0") || ver.contains("1.1") || ver.contains("1.2") || ver.contains("1.3") || ver.contains("1.4"))) { |
| throw new CantRunException("The XSLT Debugger can only be used with JDK 1.5+"); |
| } |
| |
| // TODO: fix and remove |
| if (outputType != XsltRunConfiguration.OutputType.CONSOLE) { |
| throw new CantRunException("XSLT Debugger requires Output Type == CONSOLE"); |
| } |
| |
| try { |
| final int port = NetUtils.findAvailableSocketPort(); |
| parameters.getVMParametersList().defineProperty("xslt.debugger.port", String.valueOf(port)); |
| extensionData.putUserData(PORT, port); |
| } catch (IOException e) { |
| LOG.info(e); |
| throw new CantRunException("Unable to find a free network port"); |
| } |
| |
| final char c = File.separatorChar; |
| |
| final PluginId pluginId = PluginManagerCore.getPluginByClassName(getClass().getName()); |
| assert pluginId != null || System.getProperty("xslt-debugger.plugin.path") != null; |
| |
| final File pluginPath; |
| if (pluginId != null) { |
| final IdeaPluginDescriptor descriptor = PluginManager.getPlugin(pluginId); |
| assert descriptor != null; |
| pluginPath = descriptor.getPath(); |
| } else { |
| // -Dxslt-debugger.plugin.path=C:\work\java\intellij/ultimate\out\classes\production\xslt-debugger-engine |
| pluginPath = new File(System.getProperty("xslt-debugger.plugin.path")); |
| } |
| |
| File rtClasspath = new File(pluginPath, "lib" + c + "xslt-debugger-engine.jar"); |
| if (rtClasspath.exists()) { |
| parameters.getClassPath().addTail(rtClasspath.getAbsolutePath()); |
| |
| final File rmiStubs = new File(pluginPath, "lib" + c + "rmi-stubs.jar"); |
| assert rmiStubs.exists() : rmiStubs.getAbsolutePath(); |
| parameters.getClassPath().addTail(rmiStubs.getAbsolutePath()); |
| |
| final File engineImpl = new File(pluginPath, "lib" + c + "rt" + c + "xslt-debugger-engine-impl.jar"); |
| assert engineImpl.exists() : engineImpl.getAbsolutePath(); |
| parameters.getClassPath().addTail(engineImpl.getAbsolutePath()); |
| } else { |
| if (!(rtClasspath = new File(pluginPath, "classes")).exists()) { |
| if (ApplicationManagerEx.getApplicationEx().isInternal() && new File(pluginPath, "org").exists()) { |
| rtClasspath = pluginPath; |
| final File engineImplInternal = new File(pluginPath, ".." + c + "xslt-debugger-engine-impl"); |
| assert engineImplInternal.exists() : engineImplInternal.getAbsolutePath(); |
| parameters.getClassPath().addTail(engineImplInternal.getAbsolutePath()); |
| } else { |
| throw new CantRunException("Runtime classes not found"); |
| } |
| } |
| |
| parameters.getClassPath().addTail(rtClasspath.getAbsolutePath()); |
| |
| final File rmiStubs = new File(rtClasspath, "rmi-stubs.jar"); |
| assert rmiStubs.exists() : rmiStubs.getAbsolutePath(); |
| parameters.getClassPath().addTail(rmiStubs.getAbsolutePath()); |
| } |
| |
| File trove4j = new File(PathManager.getLibPath() + c + "trove4j.jar"); |
| if (!trove4j.exists()) { |
| trove4j = new File(PathManager.getHomePath() + c + "community" + c + "lib" + c + "trove4j.jar"); |
| assert trove4j.exists() : trove4j.getAbsolutePath(); |
| } |
| parameters.getClassPath().addTail(trove4j.getAbsolutePath()); |
| |
| final String type = parameters.getVMParametersList().getPropertyValue("xslt.transformer.type"); |
| if ("saxon".equalsIgnoreCase(type)) { |
| addSaxon(parameters, pluginPath, SAXON_6_JAR); |
| } else if ("saxon9".equalsIgnoreCase(type)) { |
| addSaxon(parameters, pluginPath, SAXON_9_JAR); |
| } else if ("xalan".equalsIgnoreCase(type)) { |
| final Boolean xalanPresent = isValidXalanPresent(parameters); |
| if (xalanPresent == null) { |
| addXalan(parameters, pluginPath); |
| } else if (!xalanPresent) { |
| throw new CantRunException("Unsupported Xalan version is present in classpath."); |
| } |
| } else if (type != null) { |
| throw new CantRunException("Unsupported Transformer type '" + type + "'"); |
| } else if (parameters.getClassPath().getPathsString().toLowerCase().contains("xalan")) { |
| if (isValidXalanPresent(parameters) == Boolean.TRUE) { |
| parameters.getVMParametersList().defineProperty("xslt.transformer.type", "xalan"); |
| } |
| } |
| |
| final VirtualFile xsltFile = configuration.findXsltFile(); |
| final PsiManager psiManager = PsiManager.getInstance(configuration.getProject()); |
| final XsltChecker.LanguageLevel level; |
| if (xsltFile != null) { |
| level = XsltSupport.getXsltLanguageLevel(psiManager.findFile(xsltFile)); |
| } else { |
| level = XsltChecker.LanguageLevel.V1; |
| } |
| extensionData.putUserData(VERSION, level); |
| |
| if (!parameters.getVMParametersList().hasProperty("xslt.transformer.type")) { |
| // add saxon for backward-compatibility |
| if (level == XsltChecker.LanguageLevel.V2) { |
| parameters.getVMParametersList().defineProperty("xslt.transformer.type", "saxon9"); |
| addSaxon(parameters, pluginPath, SAXON_9_JAR); |
| } else { |
| parameters.getVMParametersList().defineProperty("xslt.transformer.type", "saxon"); |
| addSaxon(parameters, pluginPath, SAXON_6_JAR); |
| } |
| } |
| |
| parameters.getVMParametersList().defineProperty("xslt.main", "org.intellij.plugins.xsltDebugger.rt.XSLTDebuggerMain"); |
| } |
| |
| @Nullable |
| private static Boolean isValidXalanPresent(SimpleJavaParameters parameters) { |
| final List<VirtualFile> files = parameters.getClassPath().getVirtualFiles(); |
| for (VirtualFile file : files) { |
| if (file.getName().matches(".*xalan.*\\.jar")) { |
| final VirtualFile root = JarFileSystem.getInstance().getJarRootForLocalFile(file); |
| final VirtualFile manifestFile = root != null ? root.findFileByRelativePath("META-INF/MANIFEST.MF") : null; |
| if (manifestFile != null) { |
| try { |
| Manifest manifest = manifestFile.getUserData(MANIFEST); |
| if (manifest == null) { |
| manifest = new Manifest(manifestFile.getInputStream()); |
| manifestFile.putUserData(MANIFEST, manifest); |
| } |
| Attributes attributes = manifest.getAttributes("org/apache/xalan/"); |
| if (attributes == null) { |
| attributes = manifest.getAttributes("org/apache/xalan"); |
| } |
| if (attributes == null) { |
| LOG.info("No manifest attributes for 'org/apache/xalan/' in " + manifestFile.getPresentableUrl()); |
| continue; |
| } |
| final String version = attributes.getValue("Implementation-Version"); |
| if (version != null) { |
| final String[] parts = version.split("\\."); |
| if (parts.length >= 2) { |
| if (Integer.parseInt(parts[0]) >= 2 && Integer.parseInt(parts[1]) >= 6) { |
| return true; |
| } |
| } |
| LOG.info("Unsupported Xalan version: " + version); |
| } else { |
| LOG.info("No Xalan version information in " + file.getPath()); |
| } |
| } catch (IOException e) { |
| LOG.warn("Unable to read manifest from " + file.getName(), e); |
| } |
| } else { |
| LOG.info("No manifest file in " + file.getPath()); |
| } |
| return false; |
| } |
| } |
| |
| return null; |
| } |
| |
| private static void addXalan(SimpleJavaParameters parameters, File pluginPath) { |
| final File xalan = findTransformerJar(pluginPath, "xalan.jar"); |
| parameters.getClassPath().addTail(xalan.getAbsolutePath()); |
| parameters.getClassPath().addTail(new File(xalan.getParentFile(), "serializer.jar").getAbsolutePath()); |
| } |
| |
| private static void addSaxon(SimpleJavaParameters parameters, File pluginPath, final String saxonJar) { |
| final File saxon = findTransformerJar(pluginPath, saxonJar); |
| parameters.getClassPath().addTail(saxon.getAbsolutePath()); |
| } |
| |
| private static File findTransformerJar(File pluginPath, String jarFile) { |
| final char c = File.separatorChar; |
| File transformerFile = new File(pluginPath, "lib" + c + "rt" + c + jarFile); |
| if (!transformerFile.exists()) { |
| if (!(transformerFile = new File(pluginPath, "lib" + c + jarFile)).exists()) { |
| if (!(transformerFile = new File(new File(pluginPath, ".." + c + "xslt-debugger-engine-impl"), jarFile)).exists()) { |
| transformerFile = new File(pluginPath, jarFile); |
| assert transformerFile.exists() : transformerFile.getAbsolutePath(); |
| } |
| } |
| } |
| return transformerFile; |
| } |
| } |