From 35b004c5d48eff490a097a80530fea2bbe06ea81 Mon Sep 17 00:00:00 2001 From: Andrew Gvozdev Date: Thu, 10 Oct 2013 16:44:12 -0400 Subject: [PATCH] Bug 414831 - Preserve line delimiters when writing xml Change-Id: Ib6c023fb73193f3b15450f52cefcc67068d1398d Also-by: Andrew Gvozdev Signed-off-by: Andrew Gvozdev Signed-off-by: Marc-Andre Laperle Reviewed-on: https://git.eclipse.org/r/17187 --- .../LanguageSettingsProvidersSerializer.java | 13 +- .../eclipse/cdt/internal/core/model/Util.java | 153 +++++++++++++----- .../xml/XmlProjectDescriptionStorage.java | 9 +- .../ErrorParserExtensionManager.java | 7 +- .../eclipse/cdt/internal/core/XmlUtil.java | 62 ++++--- 5 files changed, 179 insertions(+), 65 deletions(-) diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java index d7ea664574b..a9ea09964e7 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java @@ -36,6 +36,7 @@ import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.internal.core.model.Util; import org.eclipse.cdt.internal.core.settings.model.CConfigurationSpecSettings; import org.eclipse.cdt.internal.core.settings.model.IInternalCCfgInfo; import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages; @@ -557,7 +558,11 @@ public class LanguageSettingsProvidersSerializer { try { serializingLockWsp.acquire(); - XmlUtil.serializeXml(doc, uriStoreWsp); + String eol = Util.getLineSeparator(uriStoreWsp); + if (eol == null) { + eol = Util.getDefaultLineSeparator(); + } + XmlUtil.serializeXml(doc, uriStoreWsp, eol); // manufacture events while inside the lock events = createLanguageSettingsChangeEvents(broadcastingWorkspaceProviders); } finally { @@ -881,7 +886,11 @@ public class LanguageSettingsProvidersSerializer { if (isWorkspaceStoreEmpty) { new java.io.File(uriStoreWsp).delete(); } else { - XmlUtil.serializeXml(docStoreWsp, uriStoreWsp); + String eol = Util.getLineSeparator(uriStoreWsp); + if (eol == null) { + eol = Util.getDefaultLineSeparator(project); + } + XmlUtil.serializeXml(docStoreWsp, uriStoreWsp, eol); } // manufacture the event only if serialization was successful diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java index d095c94cda7..86bfac18507 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Util.java @@ -16,6 +16,7 @@ package org.eclipse.cdt.internal.core.model; import java.io.BufferedInputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -30,6 +31,7 @@ import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; @@ -412,7 +414,10 @@ public class Util implements ICLogConstants { // not found return null; } - + + /** + * Returns value of line separator preference from the given preference node. + */ private static String getLineSeparatorFromPreferences(Preferences node) { try { // be careful looking up for our node so not to create any nodes as side effect @@ -425,54 +430,103 @@ public class Util implements ICLogConstants { } /** - * Returns line separator appropriate for the given file. The returned value + * Returns the first line separator found in the given file. + * + * @param fileUri - URI if the file on the local file-system. + * @return the first line separator in the given file or {@code null} if none was read. + */ + public static String getLineSeparator(URI fileUri) { + String value = null; + InputStream input = null; + try { + java.io.File file = new java.io.File(fileUri); + if (file.exists()) { + input = new FileInputStream(file); + value = getLineSeparator(input); + } + } catch (Exception e) { + // ignore + } finally { + try { + if (input != null) + input.close(); + } catch (IOException e) { + //ignore + } + } + return value; + } + + /** + * Returns the line separator for the given file. If line separator is not found in the file + * default value for the project or workspace is returned. + * + * @param file - the file to look for a line separator. + * @return the line separator for the given file. The method does not return {@code null}. + */ + public static String getLineSeparator(IFile file) { + String value = null; + InputStream input = null; + try { + input = file.getContents(); + value = getLineSeparator(input); + } catch (CoreException e) { + // ignore + } finally { + try { + if (input != null) + input.close(); + } catch (IOException e) { + //ignore + } + } + + if (value == null) + value = getDefaultLineSeparator(file.getProject()); + + return value; + } + + /** + * Returns default line separator for the given project. The returned value * will be the first available value from the list below: *
    - *
  1. Line separator currently used in that file. *
  2. Line separator defined in project preferences. *
  3. Line separator defined in instance preferences. *
  4. Line separator defined in default preferences. *
  5. Operating system default line separator. *
- * @param file the file for which line separator should be returned - * @return line separator for the given file * - * Note: This was copied from org.eclipse.core.internal.utils.FileUtil + * @param project - the project. If {@code null} no project preferences are inquired. + * @return line separator for the given project. The method does not return {@code null}. */ - public static String getLineSeparator(IFile file) { - if (file.exists()) { - InputStream input = null; - try { - input = file.getContents(); - int c = input.read(); - while (c != -1 && c != '\r' && c != '\n') - c = input.read(); - if (c == '\n') - return "\n"; //$NON-NLS-1$ - if (c == '\r') { - if (input.read() == '\n') - return "\r\n"; //$NON-NLS-1$ - return "\r"; //$NON-NLS-1$ - } - } catch (CoreException e) { - // ignore - } catch (IOException e) { - // ignore - } finally { - try { - if (input != null) - input.close(); - } catch (IOException e) { - //ignore - } - } + public static String getDefaultLineSeparator(IProject project) { + String value = null; + Preferences rootNode = Platform.getPreferencesService().getRootNode(); + // if the file does not exist or has no content yet, try with project preferences + if (project != null) { + value = getLineSeparatorFromPreferences(rootNode.node(ProjectScope.SCOPE).node(project.getName())); + if (value != null) + return value; } + value = getDefaultLineSeparator(); + return value; + } + + /** + * Returns default line separator for the workspace. The returned value + * will be the first available value from the list below: + *
    + *
  1. Line separator defined in instance preferences. + *
  2. Line separator defined in default preferences. + *
  3. Operating system default line separator. + *
+ * @return line separator for the workspace. The method does not return {@code null}. + */ + public static String getDefaultLineSeparator() { Preferences rootNode = Platform.getPreferencesService().getRootNode(); String value = null; - // if the file does not exist or has no content yet, try with project preferences - value = getLineSeparatorFromPreferences(rootNode.node(ProjectScope.SCOPE).node(file.getProject().getName())); - if (value != null) - return value; + // try with instance preferences value = getLineSeparatorFromPreferences(rootNode.node(InstanceScope.SCOPE)); if (value != null) @@ -485,6 +539,31 @@ public class Util implements ICLogConstants { return LINE_SEPARATOR; } + /** + * Returns the first line separator used by the input stream. + * + * @param input - input stream to inspect. + * @return line separator for the given input stream or {@code null} if no line separator was read. + */ + private static String getLineSeparator(InputStream input) { + try { + int c = input.read(); + while (c != -1 && c != '\r' && c != '\n') + c = input.read(); + if (c == '\n') + return "\n"; //$NON-NLS-1$ + if (c == '\r') { + int c2 = input.read(); + if (c2 == '\n') + return "\r\n"; //$NON-NLS-1$ + return "\r"; //$NON-NLS-1$ + } + } catch (IOException e) { + // ignore + } + return null; + } + /** * Return true if the file is not a directory and has length > 0 */ diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/xml/XmlProjectDescriptionStorage.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/xml/XmlProjectDescriptionStorage.java index 3c5b78b11dc..c4b542662b5 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/xml/XmlProjectDescriptionStorage.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/xml/XmlProjectDescriptionStorage.java @@ -121,7 +121,6 @@ public class XmlProjectDescriptionStorage extends AbstractCProjectDescriptionSto static final String CONFIGURATION = "cconfiguration"; //$NON-NLS-1$ private static final QualifiedName LOAD_FLAG = new QualifiedName(CCorePlugin.PLUGIN_ID, "descriptionLoadded"); //$NON-NLS-1$ - private static final String LINE_SEPARATOR = "line.separator"; //$NON-NLS-1$ public XmlProjectDescriptionStorage(CProjectDescriptionStorageTypeProxy type, IProject project, Version version) { super(type, project, version); @@ -573,12 +572,8 @@ public class XmlProjectDescriptionStorage extends AbstractCProjectDescriptionSto // Get the ProjectDescription as a utf-8 string stream = write(element); utfString = stream.toString("UTF-8"); //$NON-NLS-1$ - - // Make sure we keep the same line separator if the file exists - // or use the preferences if it's a new file - String fileLineSeparator = Util.getLineSeparator(projectFile); - String sysLineSeparator = System.getProperty(LINE_SEPARATOR); - utfString = utfString.replace(sysLineSeparator, fileLineSeparator); + String eol = Util.getLineSeparator(projectFile); + utfString = XmlUtil.replaceLineSeparatorInternal(utfString, eol); } finally { if (stream != null) stream.close(); // Cleanup the stream diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java index dfdbf9120e4..adb4d29a5e2 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/errorparsers/ErrorParserExtensionManager.java @@ -35,6 +35,7 @@ import org.eclipse.cdt.core.errorparsers.ErrorParserNamedWrapper; import org.eclipse.cdt.core.errorparsers.RegexErrorParser; import org.eclipse.cdt.core.errorparsers.RegexErrorPattern; import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.internal.core.model.Util; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; @@ -314,7 +315,11 @@ public class ErrorParserExtensionManager { } } - XmlUtil.serializeXml(doc, getStoreURI(STORAGE_ERRORPARSER_EXTENSIONS)); + URI uri = getStoreURI(STORAGE_ERRORPARSER_EXTENSIONS); + String eol = Util.getLineSeparator(uri); + if (eol == null) + eol = Util.getDefaultLineSeparator(); + XmlUtil.serializeXml(doc, uri, eol); } catch (Exception e) { throw new CoreException(CCorePlugin.createStatus("Failed serializing to file " + STORAGE_ERRORPARSER_EXTENSIONS, e)); //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/XmlUtil.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/XmlUtil.java index 76ffb5efe70..2f860bbab12 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/XmlUtil.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/XmlUtil.java @@ -17,6 +17,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.net.URI; import javax.xml.parsers.DocumentBuilder; @@ -31,6 +32,7 @@ import javax.xml.transform.stream.StreamResult; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.resources.ResourcesUtil; +import org.eclipse.cdt.internal.core.model.Util; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; @@ -45,8 +47,10 @@ import org.w3c.dom.NodeList; * */ public class XmlUtil { + private static final String ENCODING_UTF_8 = "UTF-8"; //$NON-NLS-1$ private static final String EOL_XML = "\n"; //$NON-NLS-1$ private static final String DEFAULT_IDENT = "\t"; //$NON-NLS-1$ + private static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$ /** * Convenience method to create new XML DOM Document. @@ -97,7 +101,7 @@ public class XmlUtil { Document doc = parent instanceof Document ? (Document)parent : parent.getOwnerDocument(); Element element = doc.createElement(name); if (attributes!=null) { - int attrLen = attributes.length; + int attrLen = attributes.length; for (int i=0;iDo not use outside of CDT. This method is a workaround for {@link javax.xml.transform.Transformer} + * not being able to specify the line separator. This method replaces a string generated by + * {@link javax.xml.transform.Transformer} which contains the system line.separator with the line separators + * from an existing file or the preferences if it's a new file. + * + * @param string - the string to be replaced + * @param lineSeparator - line separator to be used in the string + * + * @noreference This method is not intended to be referenced by clients. + * This is an internal method which ideally should be made private. + */ + public static String replaceLineSeparatorInternal(String string, String lineSeparator) { + string = string.replace(LINE_SEPARATOR, lineSeparator); + return string; + } + /** * Serialize XML Document into a workspace file.
* Note: clients should synchronize access to this method. @@ -361,11 +380,18 @@ public class XmlUtil { public static void serializeXml(Document doc, IFile file) throws CoreException { XmlUtil.prettyFormat(doc); - InputStream input = new ByteArrayInputStream(toByteArray(doc)); - if (file.exists()) { - file.setContents(input, IResource.FORCE, null); - } else { - file.create(input, IResource.FORCE, null); + try { + String utfString = new String(toByteArray(doc), ENCODING_UTF_8); + String lineSeparator = Util.getLineSeparator(file); + utfString = XmlUtil.replaceLineSeparatorInternal(utfString, lineSeparator); + InputStream input = new ByteArrayInputStream(utfString.getBytes(ENCODING_UTF_8)); + + if (file.exists()) { + file.setContents(input, IResource.FORCE, null); + } else { + file.create(input, IResource.FORCE, null); + } + } catch (UnsupportedEncodingException e) { } }