From fa141daf3c63f922b8997c67cccabe91b2978648 Mon Sep 17 00:00:00 2001 From: Jaxsun McCarthy Huggan Date: Fri, 17 Jul 2015 13:28:29 -0700 Subject: 472975: Mylyn escapes special characters before writing repository properties Change-Id: I8228411b427631b0e1ffd8f6bf6566e49af71098 Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=472975 Signed-off-by: Jaxsun McCarthy Huggan --- .../tasks/core/SaxRepositoriesContentHandler.java | 62 ++++++++++++---- .../internal/tasks/core/SaxRepositoriesWriter.java | 86 ++++++++++++++-------- .../tasks/core/TaskRepositoriesExternalizer.java | 10 ++- .../mylyn/internal/tasks/core/XmlReaderUtil.java | 24 +----- 4 files changed, 116 insertions(+), 66 deletions(-) (limited to 'org.eclipse.mylyn.tasks.core/src/org') diff --git a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/SaxRepositoriesContentHandler.java b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/SaxRepositoriesContentHandler.java index 7f0aca415..3f2b108ce 100644 --- a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/SaxRepositoriesContentHandler.java +++ b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/SaxRepositoriesContentHandler.java @@ -11,17 +11,24 @@ package org.eclipse.mylyn.internal.tasks.core; +import static org.eclipse.mylyn.internal.commons.core.XmlStringConverter.convertXmlToString; + import java.util.HashSet; import java.util.Set; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; +import com.google.common.base.Strings; + /** * Adapted from SaxContextContentHandler - * + * * @author Rob Elves */ public class SaxRepositoriesContentHandler extends DefaultHandler { @@ -30,27 +37,52 @@ public class SaxRepositoriesContentHandler extends DefaultHandler { private final Set taskRepositories = new HashSet(); - @SuppressWarnings({ "deprecation", "restriction" }) + private TaskRepository currentRepository; + @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { try { - if (localName.equals(TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY) && attributes != null) { - String kind = org.eclipse.mylyn.internal.commons.core.XmlStringConverter.convertXmlToString(attributes.getValue(IRepositoryConstants.PROPERTY_CONNECTOR_KIND)); - String url = org.eclipse.mylyn.internal.commons.core.XmlStringConverter.convertXmlToString(attributes.getValue(IRepositoryConstants.PROPERTY_URL)); - if (kind != null && kind.length() > 0 && url != null && url.length() > 0) { - TaskRepository repository = new TaskRepository(kind, url); - for (int index = 0; index < attributes.getLength(); index++) { - String key = org.eclipse.mylyn.internal.commons.core.XmlStringConverter.convertXmlToString(attributes.getLocalName(index)); - String value = org.eclipse.mylyn.internal.commons.core.XmlStringConverter.convertXmlToString(attributes.getValue(index)); - repository.setProperty(key, value); - } - taskRepositories.add(repository); - } + if (localName.equals(TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY)) { + handleRepositoryElement(attributes); + } else if (localName.equals(TaskRepositoriesExternalizer.ELEMENT_PROPERTY) && currentRepository != null) { + // properties are stored as attributes on the repository node as well as children property nodes + handleProperty(attributes); } } catch (Exception e) { - e.printStackTrace(); + StatusHandler.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN, "Could not read repositories" //$NON-NLS-1$ + , e)); + } + } + + @Override + public void endElement(String uri, String localName, String qName) { + if (currentRepository != null && localName.equals(TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY)) { + taskRepositories.add(currentRepository); + currentRepository = null; + } + } + + @SuppressWarnings({ "deprecation", "restriction" }) + private void handleRepositoryElement(Attributes attributes) throws SAXException { + String kind = convertXmlToString(attributes.getValue(IRepositoryConstants.PROPERTY_CONNECTOR_KIND)); + String url = convertXmlToString(attributes.getValue(IRepositoryConstants.PROPERTY_URL)); + if (!Strings.isNullOrEmpty(kind) && !Strings.isNullOrEmpty(url)) { + currentRepository = new TaskRepository(kind, url); + // properties are stored as attributes on the repository node as well as children property nodes + for (int index = 0; index < attributes.getLength(); index++) { + String key = convertXmlToString(attributes.getLocalName(index)); + String value = convertXmlToString(attributes.getValue(index)); + currentRepository.setProperty(key, value); + } } + } + private void handleProperty(Attributes attributes) throws SAXException { + String key = attributes.getValue(TaskRepositoriesExternalizer.PROPERTY_KEY); + String value = attributes.getValue(TaskRepositoriesExternalizer.PROPERTY_VALUE); + if (key != null && value != null) { + currentRepository.setProperty(key, value); + } } public Set getRepositories() { diff --git a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/SaxRepositoriesWriter.java b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/SaxRepositoriesWriter.java index a0b669707..11e5368a8 100644 --- a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/SaxRepositoriesWriter.java +++ b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/SaxRepositoriesWriter.java @@ -11,6 +11,8 @@ package org.eclipse.mylyn.internal.tasks.core; +import static org.eclipse.mylyn.internal.commons.core.XmlStringConverter.convertToXmlString; + import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; @@ -22,6 +24,7 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; +import org.apache.xerces.util.XMLChar; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.mylyn.commons.core.StatusHandler; @@ -39,7 +42,7 @@ import org.xml.sax.helpers.AttributesImpl; /** * Adapted from SaxContextWriter - * + * * @author Rob Elves */ public class SaxRepositoriesWriter { @@ -84,16 +87,6 @@ public class SaxRepositoriesWriter { private static class RepositoriesWriter implements XMLReader { -// private static final String ELEMENT_TASK_REPOSITORIES = "TaskRepositories"; -// -// public static final String ELEMENT_TASK_REPOSITORY = "TaskRepository"; -// -// private static final String ATTRIBUTE_VERSION = "xmlVersion"; - -// private static final String ATTRIBUTE_URL = "Url"; -// -// private static final String ATTRIBUTE_KIND = "Kind"; - private ContentHandler handler; private ErrorHandler errorHandler; @@ -145,7 +138,6 @@ public class SaxRepositoriesWriter { return errorHandler; } - @SuppressWarnings({ "deprecation", "restriction" }) public void parse(InputSource input) throws IOException, SAXException { if (!(input instanceof TaskRepositoriesInputSource)) { throw new SAXException("Can only parse writable input sources"); //$NON-NLS-1$ @@ -154,40 +146,76 @@ public class SaxRepositoriesWriter { Collection repositories = ((TaskRepositoriesInputSource) input).getRepositories(); handler.startDocument(); + writeRepositories(repositories); + handler.endDocument(); + } + + private void writeRepositories(Collection repositories) throws IOException, SAXException { AttributesImpl rootAttributes = new AttributesImpl(); rootAttributes.addAttribute("", TaskRepositoriesExternalizer.ATTRIBUTE_VERSION, //$NON-NLS-1$ - TaskRepositoriesExternalizer.ATTRIBUTE_VERSION, "", "1"); //$NON-NLS-1$ //$NON-NLS-2$ + TaskRepositoriesExternalizer.ATTRIBUTE_VERSION, "", "2"); //$NON-NLS-1$ //$NON-NLS-2$ handler.startElement("", TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORIES, //$NON-NLS-1$ TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORIES, rootAttributes); for (TaskRepository repository : new ArrayList(repositories)) { + writeRepository(repository); + } + + handler.endElement("", TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORIES, //$NON-NLS-1$ + TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORIES); + } - AttributesImpl ieAttributes = new AttributesImpl(); - for (String key : repository.getProperties().keySet()) { - ieAttributes.addAttribute( - "", //$NON-NLS-1$ - key, - key, - "", //$NON-NLS-1$ - org.eclipse.mylyn.internal.commons.core.XmlStringConverter.convertToXmlString(repository.getProperties() - .get(key))); + @SuppressWarnings({ "deprecation", "restriction" }) + private void writeRepository(TaskRepository repository) throws SAXException { + // write properties as attributes to support reading by older versions + AttributesImpl repositoryPropertyAttributes = new AttributesImpl(); + for (String key : repository.getProperties().keySet()) { + // avoid emitting XML we cannnot read + if (XMLChar.isValidName(key)) { + repositoryPropertyAttributes.addAttribute("", //$NON-NLS-1$ + key, key, "", //$NON-NLS-1$ + convertToXmlString(repository.getProperties().get(key))); } + } + + handler.startElement("", TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY, //$NON-NLS-1$ + TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY, repositoryPropertyAttributes); - handler.startElement("", TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY, //$NON-NLS-1$ - TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY, ieAttributes); - handler.endElement("", TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY, //$NON-NLS-1$ - TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY); + // write properties as child nodes to support attributes with special characters in their names + for (String key : repository.getProperties().keySet()) { + writeProperty(repository, key); } - handler.endElement("", TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORIES, //$NON-NLS-1$ - TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORIES); - handler.endDocument(); + handler.endElement("", TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY, //$NON-NLS-1$ + TaskRepositoriesExternalizer.ELEMENT_TASK_REPOSITORY); + } + + private void writeProperty(TaskRepository repository, String key) throws SAXException { + if (!(key.equals(IRepositoryConstants.PROPERTY_CONNECTOR_KIND) + || key.equals(IRepositoryConstants.PROPERTY_URL))) { + AttributesImpl propertiesAttributes = new AttributesImpl(); + addAttribute(propertiesAttributes, TaskRepositoriesExternalizer.PROPERTY_VALUE, + repository.getProperties().get(key)); + addAttribute(propertiesAttributes, TaskRepositoriesExternalizer.PROPERTY_KEY, key); + + handler.startElement("", //$NON-NLS-1$ + TaskRepositoriesExternalizer.ELEMENT_PROPERTY, TaskRepositoriesExternalizer.ELEMENT_PROPERTY, + propertiesAttributes); + handler.endElement("", //$NON-NLS-1$ + TaskRepositoriesExternalizer.ELEMENT_PROPERTY, TaskRepositoriesExternalizer.ELEMENT_PROPERTY); + } } public void parse(String systemId) throws IOException, SAXException { throw new SAXException("Can only parse writable input sources"); //$NON-NLS-1$ } + private void addAttribute(AttributesImpl attribute, String key, String value) { + attribute.addAttribute("", //$NON-NLS-1$ + key, key, "", //$NON-NLS-1$ + value); + } + } } diff --git a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/TaskRepositoriesExternalizer.java b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/TaskRepositoriesExternalizer.java index 393aa1e83..655eae07c 100644 --- a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/TaskRepositoriesExternalizer.java +++ b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/TaskRepositoriesExternalizer.java @@ -44,6 +44,12 @@ public class TaskRepositoriesExternalizer { public static final String ATTRIBUTE_VERSION = "OutputVersion"; //$NON-NLS-1$ + public static final String ELEMENT_PROPERTY = "Property"; //$NON-NLS-1$ + + public static final String PROPERTY_KEY = "key"; //$NON-NLS-1$ + + public static final String PROPERTY_VALUE = "value"; //$NON-NLS-1$ + public void writeRepositoriesToXML(Collection repositories, File file) { ZipOutputStream outputStream = null; try { @@ -117,8 +123,8 @@ public class TaskRepositoriesExternalizer { return contentHandler.getRepositories(); } catch (Throwable e) { file.renameTo(new File(file.getAbsolutePath() + "-save")); //$NON-NLS-1$ - StatusHandler.log(new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN, - "Error reading task repositories", e)); //$NON-NLS-1$ + StatusHandler.log( + new Status(IStatus.ERROR, ITasksCoreConstants.ID_PLUGIN, "Error reading task repositories", e)); //$NON-NLS-1$ return null; } finally { if (inputStream != null) { diff --git a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/XmlReaderUtil.java b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/XmlReaderUtil.java index 482d017fc..1aa8866f5 100644 --- a/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/XmlReaderUtil.java +++ b/org.eclipse.mylyn.tasks.core/src/org/eclipse/mylyn/internal/tasks/core/XmlReaderUtil.java @@ -11,36 +11,20 @@ package org.eclipse.mylyn.internal.tasks.core; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - +import org.apache.xerces.parsers.SAXParser; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; /** * Utility to create {@link XMLReader} instances. Uses Xerces if available to ensure XML 1.1 parsing works correctly. - * + * * @author Steffen Pingel */ public class XmlReaderUtil { public static XMLReader createXmlReader() throws SAXException { - try { - // use Xerces to ensure XML 1.1 is handled correctly - Class clazz = Class.forName("org.apache.xerces.parsers.SAXParser"); //$NON-NLS-1$ - return (XMLReader) clazz.newInstance(); - } catch (Throwable e) { - SAXParser saxParser; - try { - SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); - saxParserFactory.setNamespaceAware(true); - saxParser = saxParserFactory.newSAXParser(); - } catch (ParserConfigurationException e2) { - throw new SAXException(e2); - } - return saxParser.getXMLReader(); - } + // use Xerces to ensure XML 1.1 is handled correctly + return new SAXParser(); } private XmlReaderUtil() { -- cgit v1.2.3