Bug 404956: [xsl][launch] Launching an XML file as 'XSL Transformation'
doesn't transform anything (fix copyrights)
diff --git a/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/Messages.java b/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/Messages.java
index 653a397..64d8611 100644
--- a/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/Messages.java
+++ b/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/Messages.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007 Chase Technology Ltd - http://www.chasetechnology.co.uk
+ * Copyright (c) 2007, 2013 Chase Technology Ltd - http://www.chasetechnology.co.uk
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
* Doug Satchwell (Chase Technology Ltd) - initial API and implementation
* David Carver (STAR) - updated to meet Galileo requirements
* Stuart Harper - added "open files" selector
+ * Jesper Steen Moller - Bug 404956: Launching an XML file as 'XSL Transformation' doesn't transform anything
*******************************************************************************/
package org.eclipse.wst.xsl.internal.debug.ui;
@@ -242,6 +243,7 @@
public static String XSLLaunchShortcut_1;
public static String XSLLaunchShortcut_2;
public static String XSLLaunchShortcut_6;
+ public static String XSLLaunchShortcut_7;
public static String XSLSelectExisting;
private Messages() {
diff --git a/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/XMLProcessingInstructionSniffer.java b/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/XMLProcessingInstructionSniffer.java
new file mode 100644
index 0000000..3e182a0
--- /dev/null
+++ b/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/XMLProcessingInstructionSniffer.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Jesper Steen Moller and others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Jesper Steen Moller - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.wst.xsl.internal.debug.ui;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * @since 3.5
+ */
+public final class XMLProcessingInstructionSniffer extends DefaultHandler {
+ /**
+ * An exception indicating that the parsing should stop. This is usually
+ * triggered when the top-level element has been found.
+ */
+ private class StopParsingException extends SAXException {
+ /**
+ * All serializable objects should have a stable serialVersionUID
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs an instance of <code>StopParsingException</code> with a
+ * <code>null</code> detail message.
+ */
+ public StopParsingException() {
+ super((String) null);
+ }
+ }
+
+ private Map<String, List<Map<String,String>>> processingInstructions = new TreeMap<String, List<Map<String,String>>>();
+
+ void addInstruction(String key, Map<String,String> attributes) {
+ List<Map<String, String>> list = processingInstructions.get(key);
+ if (list == null) {
+ list = new LinkedList<Map<String,String>>();
+ processingInstructions.put(key, list);
+ }
+ list.add(attributes);
+ }
+
+ public List<Map<String,String>> getProcessingInstructions(String key) {
+ return processingInstructions.get(key);
+ }
+
+ Pattern PSEUDO_ATTRIBUTES_REGEX = Pattern.compile("\\s*(\\w+)\\s*=\\s*['\"]([^'\"]+)['\"]\\s*"); //$NON-NLS-1$
+
+ private Map<String,String> parsePseudoAttributes(String pseudoAttributes) {
+ Map<String, String> attrs = new TreeMap<String, String>();
+ Matcher matcher = PSEUDO_ATTRIBUTES_REGEX.matcher(pseudoAttributes);
+ while (matcher.find()) {
+ String value = matcher.group(2);
+ // TODO: Unescape character references and predefined entity references: "&" | "<" | ">" | """ | "'"
+ attrs.put(matcher.group(1), value);
+ }
+ return attrs;
+ }
+
+ public XMLProcessingInstructionSniffer() {
+ }
+
+ /**
+ * Creates a new SAX parser for use within this instance.
+ *
+ * @return The newly created parser.
+ *
+ * @throws ParserConfigurationException
+ * If a parser of the given configuration cannot be created.
+ * @throws SAXException
+ * If something in general goes wrong when creating the parser.
+ * @throws SAXNotRecognizedException
+ * If the <code>XMLReader</code> does not recognize the
+ * lexical handler configuration option.
+ * @throws SAXNotSupportedException
+ * If the <code>XMLReader</code> does not support the lexical
+ * handler configuration option.
+ */
+ private final SAXParser createParser(SAXParserFactory parserFactory) throws ParserConfigurationException, SAXException, SAXNotRecognizedException, SAXNotSupportedException {
+ // Initialize the parser.
+ final SAXParser parser = parserFactory.newSAXParser();
+ final XMLReader reader = parser.getXMLReader();
+// reader.setProperty("http://xml.org/sax/properties/lexical-handler", this); //$NON-NLS-1$
+ // disable DTD validation (bug 63625)
+ try {
+ // be sure validation is "off" or the feature to ignore DTD's will not apply
+ reader.setFeature("http://xml.org/sax/features/validation", false); //$NON-NLS-1$
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); //$NON-NLS-1$
+ } catch (SAXNotRecognizedException e) {
+ // not a big deal if the parser does not recognize the features
+ } catch (SAXNotSupportedException e) {
+ // not a big deal if the parser does not support the features
+ }
+ return parser;
+ }
+
+ public boolean parseContents(InputSource contents) throws IOException, ParserConfigurationException, SAXException {
+ // Parse the file into we have what we need (or an error occurs).
+
+ try {
+ final SAXParser parser = createParser(SAXParserFactory.newInstance());
+ // to support external entities specified as relative URIs (see bug 63298)
+ contents.setSystemId("/"); //$NON-NLS-1$
+ parser.parse(contents, this);
+ } catch (StopParsingException e) {
+ // Abort the parsing normally. Fall through...
+ }
+ return true;
+ }
+
+ /*
+ * Resolve external entity definitions to an empty string. This is to speed
+ * up processing of files with external DTDs. Not resolving the contents
+ * of the DTD is ok, as only the System ID of the DTD declaration is used.
+ * @see org.xml.sax.helpers.DefaultHandler#resolveEntity(java.lang.String, java.lang.String)
+ */
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
+ return new InputSource(new StringReader("")); //$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
+ * java.lang.String, java.lang.String, org.xml.sax.Attributes)
+ */
+ public final void startElement(final String uri, final String elementName, final String qualifiedName, final Attributes attributes) throws SAXException {
+ throw new StopParsingException();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.xml.sax.ext.LexicalHandler#startEntity(java.lang.String)
+ */
+ public final void startEntity(final String name) {
+ // Not interested.
+ }
+
+ @Override
+ public void processingInstruction(String target, String data)
+ throws SAXException {
+ addInstruction(target, parsePseudoAttributes(data));
+ }
+
+// public static void main(String[] args) throws IOException, ParserConfigurationException, SAXException {
+// String xml = "<?xml-stylesheet href='where.xsl'?><root><element/></root>";
+// XMLProcessingInstructionSniffer sniffer = new XMLProcessingInstructionSniffer();
+// sniffer.parseContents(new InputSource(new StringReader(xml)));
+// List<Map<String, String>> instructions = sniffer.getProcessingInstructions("xml-stylesheet");
+// System.out.println(instructions);
+// }
+}
diff --git a/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/XSLLaunchShortcut.java b/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/XSLLaunchShortcut.java
index 3c0dd50..0ddb436 100644
--- a/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/XSLLaunchShortcut.java
+++ b/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/XSLLaunchShortcut.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007 Chase Technology Ltd - http://www.chasetechnology.co.uk
+ * Copyright (c) 2007, 2013 Chase Technology Ltd - http://www.chasetechnology.co.uk
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,12 +7,20 @@
*
* Contributors:
* Doug Satchwell (Chase Technology Ltd) - initial API and implementation
+ * Jesper Steen Moller - Bug 404956: Launching an XML file as 'XSL Transformation' doesn't transform anything
*******************************************************************************/
package org.eclipse.wst.xsl.internal.debug.ui;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
@@ -46,10 +54,13 @@
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.wst.xsl.core.XSLCore;
import org.eclipse.wst.xsl.internal.debug.ui.tabs.main.InputFileBlock;
+import org.eclipse.wst.xsl.internal.debug.ui.tabs.main.TransformsBlock;
import org.eclipse.wst.xsl.launching.XSLLaunchConfigurationConstants;
import org.eclipse.wst.xsl.launching.config.BaseLaunchHelper;
import org.eclipse.wst.xsl.launching.config.LaunchPipeline;
import org.eclipse.wst.xsl.launching.config.LaunchTransform;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
/**
* <table border=1>
@@ -92,9 +103,12 @@
* @since 1.0
*/
public class XSLLaunchShortcut implements ILaunchShortcut {
+ private static final String XML_STYLESHEET_PI = "xml-stylesheet"; //$NON-NLS-1$
private IFile xmlFile;
private IPath xmlFilePath;
private IFile[] xslFiles;
+ private String xslFilePath; // always an external path
+ private LaunchPipeline pipeline;
public void launch(ISelection selection, String mode) {
if (selection instanceof IStructuredSelection) {
@@ -115,8 +129,12 @@
private void searchAndLaunch(Object[] objects, String mode) {
if (fillFiles(objects)) {
// ensure we have an input file
- if (xmlFile == null)
+ if (xmlFile == null) {
promptForInput();
+ }
+ if (xslFiles == null || xslFiles.length == 0 && xslFilePath == null) {
+ promptForStylesheet();
+ }
if (xmlFile != null || xmlFilePath != null)
launch(mode);
}
@@ -149,6 +167,11 @@
@Override
protected void okPressed() {
+ saveSelectedXmlFile();
+ super.okPressed();
+ }
+
+ private void saveSelectedXmlFile() {
IResource res = inputFileBlock.getResource();
if (res == null)
xmlFilePath = new Path(inputFileBlock.getText());
@@ -156,9 +179,54 @@
res.getFullPath())
&& res.getType() == IResource.FILE)
xmlFile = (IFile) res;
+ }
+ };
+ dialog.setHelpAvailable(false);
+ dialog.setStatusLineAboveButtons(true);
+ dialog.setTitle(Messages.XSLLaunchShortcut_1);
+ dialog.open();
+ }
+
+ private void promptForStylesheet() {
+ // prompt for input xml file
+ final LaunchPipeline promptedPipeline = new LaunchPipeline();
+
+ StatusDialog dialog = new StatusDialog(getShell()) {
+ private TransformsBlock transformsBlock = new TransformsBlock();
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite comp = (Composite) super.createDialogArea(parent);
+ comp.setFont(parent.getFont());
+ GridLayout layout = new GridLayout(1, false);
+ comp.setLayout(layout);
+
+ Label label = new Label(comp, SWT.NONE);
+ label.setFont(comp.getFont());
+ GridData gd = new GridData();
+ gd.horizontalIndent = 5;
+ gd.verticalIndent = 5;
+ gd.widthHint = 380;
+ label.setLayoutData(gd);
+ label.setText(Messages.XSLLaunchShortcut_7);
+
+ promptedPipeline.setTransformDefs(new ArrayList<LaunchTransform>());
+ transformsBlock.setPipeline(promptedPipeline);
+ transformsBlock.createControl(comp);
+ transformsBlock.initializeFrom(null);
+ return comp;
+ }
+
+ @Override
+ protected void okPressed() {
+ savePipeline();
super.okPressed();
}
+ private void savePipeline() {
+ pipeline = promptedPipeline;
+ }
+
};
dialog.setHelpAvailable(false);
dialog.setStatusLineAboveButtons(true);
@@ -170,6 +238,7 @@
xmlFile = null;
xmlFilePath = null;
List<IFile> xslFileList = new ArrayList<IFile>();
+ xslFilePath = null;
for (Object object : selections) {
IResource resource = (IResource) object;
if (resource.getType() == IResource.FILE) {
@@ -185,6 +254,42 @@
}
}
}
+ if (xslFileList.isEmpty() && xmlFile != null) {
+ // Could it be we have a directive in the file, near the top
+ // <?xml-stylesheet type="text/xsl" href="test1.xsl"?>
+ XMLProcessingInstructionSniffer sniffer = new XMLProcessingInstructionSniffer();
+ try {
+ sniffer.parseContents(new InputSource(xmlFile.getContents()));
+ List<Map<String, String>> instructions = sniffer.getProcessingInstructions(XML_STYLESHEET_PI);
+ if (instructions != null) {
+ for (Map<String, String> attrs : instructions) {
+ String alternative = attrs.get("alternative"); //$NON-NLS-1$
+ if (alternative != null && alternative.equalsIgnoreCase("yes")) continue; //$NON-NLS-1$
+
+ String href = attrs.get("href"); //$NON-NLS-1$
+ if (href == null) continue;
+
+ // This is the one, now compute the path
+ if (new URI(href).isAbsolute()) {
+ xslFilePath = href;
+ } else {
+ xslFileList.add(xmlFile.getProject().getFile(xmlFile.getParent().getProjectRelativePath().append(href)));
+ }
+ }
+ }
+ } catch (IOException e) {
+ // ignored
+ } catch (ParserConfigurationException e) {
+ // ignored
+ } catch (SAXException e) {
+ // ignored
+ } catch (CoreException e) {
+ // ignored
+ } catch (URISyntaxException e) {
+ // ignored
+ }
+ }
+
xslFiles = xslFileList.toArray(new IFile[0]);
return true;
}
@@ -308,12 +413,16 @@
wc.setAttribute(XSLLaunchConfigurationConstants.ATTR_OPEN_FILE,
true);
- LaunchPipeline pipeline = new LaunchPipeline();
+ if (pipeline == null) pipeline = new LaunchPipeline();
for (IFile element : xslFiles) {
pipeline.addTransformDef(new LaunchTransform(element
.getFullPath().toPortableString(),
LaunchTransform.RESOURCE_TYPE));
}
+ if (xslFilePath != null) {
+ pipeline.addTransformDef(new LaunchTransform(xslFilePath,
+ LaunchTransform.EXTERNAL_TYPE));
+ }
wc.setAttribute(XSLLaunchConfigurationConstants.ATTR_PIPELINE,
pipeline.toXML());
if (xmlFile != null)
diff --git a/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/messages.properties b/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/messages.properties
index fc15a2f..1a03282 100644
--- a/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/messages.properties
+++ b/bundles/org.eclipse.wst.xsl.debug.ui/src/org/eclipse/wst/xsl/internal/debug/ui/messages.properties
@@ -3,6 +3,7 @@
XSLLaunchShortcut_1=Input File
XSLLaunchShortcut_2=Select a Launch Configuration
XSLLaunchShortcut_6=Error
+XSLLaunchShortcut_7=Select an XSLT file for the transformation
XSLSelectExisting=&Select existing configuration:
#Action Messages
RemoveAction_Text=Remove
diff --git a/bundles/org.eclipse.wst.xsl.jaxp.debug/src/org/eclipse/wst/xsl/jaxp/debug/invoker/internal/JAXPSAXProcessorInvoker.java b/bundles/org.eclipse.wst.xsl.jaxp.debug/src/org/eclipse/wst/xsl/jaxp/debug/invoker/internal/JAXPSAXProcessorInvoker.java
index 28ec4cd..6fcb337 100644
--- a/bundles/org.eclipse.wst.xsl.jaxp.debug/src/org/eclipse/wst/xsl/jaxp/debug/invoker/internal/JAXPSAXProcessorInvoker.java
+++ b/bundles/org.eclipse.wst.xsl.jaxp.debug/src/org/eclipse/wst/xsl/jaxp/debug/invoker/internal/JAXPSAXProcessorInvoker.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007 Chase Technology Ltd - http://www.chasetechnology.co.uk
+ * Copyright (c) 2007, 2013 Chase Technology Ltd - http://www.chasetechnology.co.uk
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
*
* Contributors:
* Doug Satchwell (Chase Technology Ltd) - initial API and implementation
+ * Jesper Steen Moller - Bug 404956: Launching an XML file as 'XSL Transformation' doesn't transform anything
*******************************************************************************/
package org.eclipse.wst.xsl.jaxp.debug.invoker.internal;
@@ -202,17 +203,14 @@
SAXSource saxSource = new SAXSource(inputsource);
Source src = saxSource;
String media = null, title = null, charset = null;
- while (true)
+ src = tFactory.getAssociatedStylesheet(src, media, title, charset);
+ if (src != null)
{
- src = tFactory.getAssociatedStylesheet(src, media, title, charset);
- if (src != null)
- {
- addStylesheet(saxSource, null, Collections.EMPTY_MAP, new Properties());
- }
- else
- {
- throw new TransformationException(Messages.getString("JAXPSAXProcessorInvoker.7") + inputsource.getSystemId()); //$NON-NLS-1$
- }
+ addStylesheet(src, null, Collections.EMPTY_MAP, new Properties());
+ }
+ else
+ {
+ throw new TransformationException(Messages.getString("JAXPSAXProcessorInvoker.7") + inputsource.getSystemId()); //$NON-NLS-1$
}
}
th.setResult(res);
diff --git a/bundles/org.eclipse.wst.xsl.jaxp.launching/src/org/eclipse/wst/xsl/jaxp/launching/internal/PluginProcessorJar.java b/bundles/org.eclipse.wst.xsl.jaxp.launching/src/org/eclipse/wst/xsl/jaxp/launching/internal/PluginProcessorJar.java
index f6ce3ff..8377dcd 100644
--- a/bundles/org.eclipse.wst.xsl.jaxp.launching/src/org/eclipse/wst/xsl/jaxp/launching/internal/PluginProcessorJar.java
+++ b/bundles/org.eclipse.wst.xsl.jaxp.launching/src/org/eclipse/wst/xsl/jaxp/launching/internal/PluginProcessorJar.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007 Chase Technology Ltd - http://www.chasetechnology.co.uk
+ * Copyright (c) 2007, 2013 Chase Technology Ltd - http://www.chasetechnology.co.uk
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
*
* Contributors:
* Doug Satchwell (Chase Technology Ltd) - initial API and implementation
+ * Jesper Steen Moller - Bug 404956: Launching an XML file as 'XSL Transformation' doesn't transform anything
*******************************************************************************/
package org.eclipse.wst.xsl.jaxp.launching.internal;
@@ -40,7 +41,9 @@
// There is surely a better way, but I can'd find it.
if (path == null)
{
- url = Platform.getBundle(pluginId).getEntry("/"); //$NON-NLS-1$
+ Bundle bundle = Platform.getBundle(pluginId);
+ if (bundle == null) return null;
+ url = bundle.getEntry("/"); //$NON-NLS-1$
url = FileLocator.resolve(url);
String s = url.getPath();
if (s.endsWith("!/")) //$NON-NLS-1$